選單

Gavin Wood:跨共識訊息格式 XCM

作者:Gavin Wood

隨著最終的 Polkadot 1。0 版本和平行鏈的臨近,跨共識訊息格式(簡稱 XCM)即將釋出其第一個生產就緒版本。這是對格式、其目標、工作原理的介紹,可用於實現典型的跨鏈任務。

我們從一個有趣的事實講起。XCM 是「跨共識」訊息格式,而不僅僅是「跨鏈」。這種差異是該格式在最終實現的目標上的標誌,該格式不僅在鏈之間交流,而且在智慧合約和模組之間,以及透過橋和分片(如 Polkadot 的 Spree)中傳送各種想法。

一種格式,而不是一個協議

為了更好地理解 XCM,重要的是要了解它的邊界以及它在 Polkadot 技術堆疊中的位置。XCM 是一種訊息格式。它不是訊息傳遞協議。它不能用於在系統之間實際「傳送」任何訊息,它的作用僅在於表達接收者應該做什麼。

不包括橋和合約模組,Polkadot 帶有三個不同的系統,用於在其組成鏈之間實際通訊 XCM 訊息:UMP、DMP 和 XCMP。UMP (向上訊息傳遞)允許平行鏈向它們的中繼鏈傳送訊息。DMP (向下訊息傳遞)允許中繼鏈將訊息向下傳遞到其平行鏈。XCMP 可能是其中最著名的,這允許平行鏈之間傳送訊息。XCM 可用於透過這三個通訊通道中的任意一個來表達訊息的含義。

Gavin Wood:跨共識訊息格式 XCM

除了在鏈之間傳送訊息之外,XCM 在其他語境也很有用,比如,可以用於在你之前不是很瞭解它交易格式的鏈上進行交易。對於業務邏輯變化很小的鏈(例如比特幣),交易格式 —— 或者錢包用來向鏈傳送指令的格式 —— 往往會無限期地保持完全相同,或者至少相容。使用高度可進化的基於元協議的鏈,例如 Polkadot 及其組成的平行鏈,業務邏輯可以透過單個交易跨網路升級。這可以改變任何事情,包括交易格式,給錢包維護者帶來潛在的問題,特別是對於需要離線儲存的錢包(例如 Parity Signer)。由於 XCM 版本良好、抽象且通用,因此它可以用作一種為錢包提供持久交易格式的手段,用於建立許多常見交易。

目標

XCM 旨在成為共識系統之間交流思想的語言。它應該足夠通用,以便在整個不斷髮展的生態系統中正確並有用。它應該是可擴充套件的,由於可擴充套件性不可避免地意味著變化,它也應該是面向未來和向前相容的。最後,它應該足夠高效可以在鏈上執行,並且還能在計量環境中執行。

像所有語言一樣,有些人會比其他人更傾向於使用某些元素。XCM 的設計方式並不是讓每個支援 XCM 的系統都能夠解釋任何可能的 XCM 訊息。有些訊息在某些系統下不會有合理的解釋。其他的可能是合理的,但由於資源限制或因為可以以更清晰、更規範的方式表達相同的內容,直譯器仍然故意不支援。系統將不可避免地只支援可能訊息的一個子集。資源嚴重受限的系統(如智慧合約)可能只支援非常有限的「方言」。

這種普遍性甚至延伸到諸如為執行 XCM 訊息支付費用之類的概念。由於我們知道 XCM 可用於多種系統,包括 gas 計量的智慧合約平臺和社群平行鏈,一直到系統平行鏈與其中繼鏈之間的可信互動,因此我們不想將費用支付等元素烤得太深和在協議中變得不可逆轉。

為什麼不直接使用本地訊息格式

在某些情況下,捎帶在本地資訊 / 交易格式的鏈或智慧合約可能很有用,但確實有一些很大的缺點,使其對 XCM 的目標不太有用。首先,鏈之間缺乏相容性,因此打算向多個目的地傳送訊息的系統需要了解如何為每個目的地編寫訊息。在這一點上,即使是單個目的地也可能隨著時間的推移改變其本地交易 / 訊息格式。智慧合約可能會升級,區塊鏈可能會引入新功能或改變現有功能,從而改變其交易格式。

其次,鏈上的常見用例不容易適合單個交易;可能需要特殊的技巧來提取資金,交換資金,然後將結果全部存入單個交易中。連貫的儲備資產框架所需的後續轉移通知並不存在於不知道其他人的鏈中。

第三,諸如支付費用之類的操作不容易適應假設已經像智慧合約訊息一樣協商費用支付的模型。相比之下,交易信封提供了一些支付處理的系統,但通常也被設計為包含一個簽名,這在共識系統之間進行通訊時沒有意義。

一些初始用例

雖然 XCM 的目標是通用的、靈活的和麵向未來的,但它當然必須滿足實際需求,尤其是鏈之間的代幣轉移。可選的費用支付(可能使用這些代幣)是另一種方式,就像進行交換服務的通用介面一樣,在整個 DeFi 世界中都很常見。最後,應該可以使用 XCM 語言進行一些特定於平臺的操作。例如,在 Substrate 鏈中,可能需要將遠端呼叫分派到其模組之一以訪問專門的功能。

最重要的是,有許多我們希望支援的代幣轉移模型:我們可能只想簡單地控制遠端鏈上的帳戶,允許本地鏈在遠端鏈上擁有一個地址以接收資金並最終將其控制的資金轉移到該遠端鏈上的其他賬戶中。

我們可能有兩個共識系統,它們都是特定代幣的「本地家園」。想象一下像 USDT 或 USDC 這樣的代幣,它在幾個不同的鏈上都有例項,並且完全可以互換。應該可以在一條鏈上銷燬這樣的代幣,並在另一條支援的鏈上鑄造相應的代幣。在 XCM 的說法中,我們之所以稱之為傳送(teleport),是因為資產的轉移實際上是透過在一側銷燬它,並在另一側建立一個克隆來實現的。

最後,可能有兩條鏈想要提名第三條鏈,其中一條鏈上的資產可能被視為本地資產,用作該資產的儲備。每個鏈上資產的衍生形式將得到完全支援,允許衍生資產交換為支援它的儲備鏈上的基礎資產。這可能是兩條鏈不一定相互信任的情況,但(至少就相關資產而言)願意信任資產的本地鏈。這裡的一個例子是我們有幾個社群平行鏈想要在彼此之間傳送 DOT。它們每個都有一個本地形式的 DOT,由 Statemint 鏈(DOT 的本地中心)上的平行鏈控制的 DOT 完全支援。當在鏈之間傳送本地形式的 DOT 時,在後臺,「真正的」DOT 在 Statemint 上的平行鏈帳戶之間移動。

即使這種看似適度的功能水平也有相對大量的配置,它們的使用可能是可取的,並且需要一些有趣的設計來避免過度擬合。

XCM 剖析

XCM 格式的核心在於 XCVM。與某些人的看法相反,這不是(有效的)羅馬數字(儘管如果是,它可能意味著 905)。事實上,這代表跨共識虛擬機器。這是一臺超高級別的非圖靈完備計算機,其指令設計為與交易大致處於同一級別。

XCM 中的「訊息」實際上只是一個執行在 XCVM 上的程式。它是一個或多個 XCM 指令。程式會一直執行,直到它執行到最後或遇到錯誤為止,此時它會結束(我現在有意不解釋這一點)並停止。

XCVM 包括許多 Register,以及訪問託管它的共識系統的整體狀態。指令可能會改變一個 Register,它們可能會改變共識系統的狀態,或者兩者兼而有之。

這種指令的一個例子是 TransferAsset,它用於將資產轉移到遠端系統上的某個其他地址。需要告知要轉讓哪些資產以及資產要轉讓給誰 / 在哪裡。在 Rust 中,它是這樣宣告的:

enum Instruction { TransferAsset { assets: MultiAssets, beneficiary: MultiLocation, } /* snip */ }

正如你可能猜到的那樣,資產是表示要轉讓哪些資產的引數,而受益人則說明這些資產要交給誰 / 在哪裡。當然,我們還缺少另一條資訊,即從誰 / 何處獲取資產。這是從原 Register 自動推斷出來的。程式開始時,這個 Register 一般是根據傳輸系統(網、XCMP 或者其他什麼)來設定的,以反映訊息實際來自哪裡,和受益人是同一型別的資訊。Origin Register 作為一個受保護的 Register 執行 —— 程式不能任意設定它,儘管有兩條指令可以用來以某種方式改變它。

使用的型別是 XCM 中非常基本的思想:資產,由 MultiAsset 表示,共識內的位置,由 MultiLocation 表示。Origin Register 是一個可選的 MultiLocation (可選,因為如果需要它可以完全清除)。

在 XCM 的位置

MultiLocation 型別標識存在於共識世界中的任何單個位置。這是一個相當抽象的想法,可以代表共識中存在的所有事物,從可擴充套件的多分片區塊鏈(如 Polkadot)一直到平行鏈上的低階 ERC-20 資產賬戶。在計算機科學術語中,它實際上只是一個全域性單例資料結構,無論其大小或複雜性如何。

MultiLocation 始終表示相對於當前位置的位置。你可以把它想象成一個檔案系統路徑,但是沒有辦法直接表達檔案系統樹的「根」。這有一個簡單的原因:在 Polkadot 的世界中,區塊鏈可以合併到其他區塊鏈中,也可以從其他區塊鏈中分離出來。區塊鏈可以非常獨立地開始生命,並最終被提升為更大共識中的平行鏈。如果這樣做,那麼 「root」 的含義將在一夜之間改變,這可能會給 XCM 訊息和其他任何使用 MultiLocation 的訊息帶來混亂。為了簡單起見,我們完全排除了這種可能性。

XCM 中的位置是分層的,共識中的一些地方完全封裝在共識中的其他地方。Polkadot 的平行鏈完全存在於整個 Polkadot 共識中,我們稱之為內部位置。更嚴格地說,我們可以說,只要有一個共識系統的任何變化都意味著另一個共識系統的變化,那麼前一個系統是後者的內部系統。例如,Canvas 智慧合約位於託管它的合約模組的內部。比特幣中的 UTXO 是比特幣區塊鏈的內部。

這意味著 XCM 沒有區分 「誰」 和 「在哪裡」 兩個問題。從像 XCM 這樣相當抽象的東西的角度來看,區別並不重要 —— 兩者模糊併成為本質上相同的東西。

MultiLocations 用於識別傳送 XCM 訊息的位置,可以接收資產的位置,然後甚至可以幫助描述資產本身的型別,正如我們將看到的。非常有用的東西。

當用本文這樣的文字寫下來時,它們表示為一些 (或「父」,封裝共識系統)元件,後跟一些連線點,所有連線點都用 / 分隔。(當我們用 Rust 之類的語言表達它們時,通常不會發生這種情況,但它在書面上是有意義的,因為它很像廣泛使用的熟悉的目錄路徑。)連線在其封裝共識中標識了一個內部位置系統。如果根本沒有父節點 / 連線點,那麼我們只說位置是這裡。

例如 :

: 在平行鏈中進行評估,這將識別我們索引為 1000 的兄弟平行鏈。(在 Rust 中,我們將編寫 )

: 在平行鏈中進行評估,這將識別中繼鏈上的 32 位元組帳戶 。

: 在中繼鏈上進行評估,這將識別平行鏈編號 42 上的 20 位元組帳戶 (大概類似於承載以太坊相容帳戶的 Moonbeam)。

有許多不同型別的連線點,用於以各種方式識別你可能在鏈上找到的位置,例如鍵、索引、二進位制 blob 和複數描述。

XCM 中的資產

在 XCM 中工作時,通常需要引用某種資產。這是因為幾乎所有現有的公共區塊鏈都依賴於一些原生數字資產來為其內部經濟和安全機制提供支柱。對於比特幣等工作量證明區塊鏈,原生資產(BTC)用於獎勵開發區塊鏈的礦工並防止雙重支出。對於 Polkadot 等權益證明區塊鏈,原生資產 (DOT) 用作一種抵押形式,網路管理員(稱為權益人)必須承擔風險才能生成有效區塊並獲得實物獎勵。

一些區塊鏈管理多種資產,例如以太坊的 ERC-20 框架允許在鏈上管理許多不同的資產。一些管理不可替代的資產,例如以太坊的 ETH,而是不可替代的 —— 獨一無二的例項;Crypto-kitties 是此類不可替代代幣或 NFT 的早期示例。

XCM 旨在能夠毫不費力地處理所有此類資產。為此,有資料型別 MultiAsset 及其關聯型別 MultiAssets、WildMultiAsset 和 MultiAssetFilter。讓我們看看 Rust 中的 MultiAsset:

struct MultiAsset { id: AssetId, fun: Fungibility, }

所以有兩個欄位定義了我們的資產:id 和 fun,這很好地表明瞭 XCM 如何處理資產。首先,必須提供整體資產身份。對於可替代資產,這只是標識資產。對於 NFT,這標識了整個資產「類別」 —— 不同的資產例項可能在這個類別中。

plain enum AssetId { Concrete(MultiLocation), Abstract(BinaryBlob), }

資產身份以兩種方式之一表示;無論是具體的還是抽象的。Abstract 並沒有真正使用,但它允許透過名稱指定資產 ID。這很方便,但依賴於接收者以傳送者期望的方式解釋名稱,這可能並不總是那麼容易。Concrete 在一般用途中使用並使用位置來明確地識別資產。對於原生資產(例如 DOT),資產往往被識別為鑄造資產的鏈(在這種情況下是 Polkadot 中繼鏈,這將是其平行鏈的位置 )。主要在鏈模組內管理的資產可以透過包括其在該模組內的索引的位置來識別。例如,Karura 平行鏈可能指的是 Statemine 平行鏈上的資產,位置為 。

enum Fungibility { Fungible(NonZeroAmount), NonFungible(AssetInstance), }

其次,它們必須是可替代的或不可替代的。如果它們是可替代的,那麼應該有一些相關的非零數量。如果它們不可替代,那麼應該有一些指示它們是哪個例項而不是數量。(這通常用索引表示,但 XCM 還允許使用各種其他資料型別,例如陣列和二進位制 blob。) 這涵蓋了 MultiAsset,但我們有時會使用其他三種相關型別。MultiAssets 就是其中之一,實際上只是意味著一組 MultiAsset 專案。然後我們有 WildMultiAsset;這是一個萬用字元,可用於匹配一個或多個 MultiAsset 專案。它實際上只支援兩種萬用字元:All (匹配所有資產)和 AllOf 匹配特定身份 (AssetId) 和可替代性的所有資產。值得注意的是,對於後者,不需要指定數量(在可替代的情況下)或例項(對於非可替代的),並且全部匹配。

最後,還有 MultiAssetFilter。這是最常用的,實際上只是 MultiAssets 和 WildMultiAsset 的組合,允許指定萬用字元或明確(即非萬用字元)資產列表。

在 Rust XCM API 中,我們提供了很多轉換,以儘可能輕鬆地處理這些資料型別。例如,當我們在 Polkadot 中繼鏈上時,要指定等於 100 個不可分割的 DOT 資產單位的可替代 MultiAsset (普朗克,對於那些知道的人),那麼我們將使用 。

Holding Register

我們再來看看另一個 XCM 指令:WithdrawAsset。從表面上看,這有點像 TransferAsset 的前半部分:它從起源的賬戶中提取了一些資產。但這與他們有什麼關係?如果他們沒有在任何地方存入,那麼這肯定是一個非常無用的操作。如果我們檢視它的 Rust 宣告,我們會發現更多有關其用法的線索:

WithdrawAsset(MultiAssets),

所以,這次只有一個引數(MultiAssets 型別,它規定哪些資產必須從 Origin Register 的所有權中撤出)。但是沒有指定放置資產的位置。

這些暫時持有的未動用資產稱為持有 Register。(「Holding」是因為它們處於不能無限期持續的臨時位置,「Register」是因為它有點像 CPU Register,是一個存放工作資料的地方。)有許多指令對持有暫存器進行操作。一種非常簡單的指令是 DepositAsset 指令。讓我們來看看它:

enum Instruction { DepositAsset { assets: MultiAssetFilter, max_assets: u32, beneficiary: MultiLocation, }, /* snip */ }

啊哈!精明的讀者會發現這看起來很像 TransferAsset 指令中缺失的一半。我們有 assets 引數,該引數指定應從持有暫存器中刪除哪些資產以存放在鏈上。max_assets 讓 XCM 作者通知接收者打算存入多少獨特的資產。(這在知道 Holding Register 的內容之前計算費用時很有幫助,因為存入資產可能是一項代價高昂的操作。)最後是受益人,這與我們之前在 TransferAsset 操作中遇到的引數相同。有許多指令表示要在 Holding Register 上執行的操作,而 DepositAsset 是最簡單的指令之一。其他一些則更復雜。

XCM 中的費用支付

XCM 中的費用支付是一個相當重要的用例。Polkadot 社群中的大多數平行鏈都會要求其對話者為他們希望進行的任何操作付費,以免為 「交易垃圾」 和拒絕服務攻擊敞開大門。當鏈有充分的理由相信它們的對話者會表現良好時,也存在例外情況 —— 當 Polkadot 中繼鏈與 Polkadot Statemint 公共利益鏈通訊時就是這種情況。但是,對於一般情況而言,費用是確保 XCM 訊息及其傳輸協議不會被過度使用的好方法。我們來看看 XCM 訊息到達 Polkadot 時如何支付費用。

正如前文提到的一樣,XCM 不包括作為一等公民的費用和費用支付:與以太坊交易模型不同,對於費用支付不是協議中不需要的東西,就必須進行規避。就像 Rust 的零成本抽象一樣,費用支付在 XCM 中沒有很大的設計開銷。

對於確實需要支付一定費用的系統,XCM 提供了使用資產購買執行資源的能力。概括來講,這包括了三個部分:

首先,需要提供一些資產。

其次,必須就計算時間(用 Substrate 中的說法就是 weight)交換資產。

最後,XCM 操作將按照指示執行。

第一部分由提供資產的多個 XCM 指令之一管理。我們已經知道其中的一個指令了( ),但還有其他幾個我們稍後會看到。_Holding Register_中的所得資產當然將用於支付與執行 XCM 相關的費用。任何未用於支付費用的資產都將被存入某個目的地賬戶。在我們的示例中,我們假設 XCM 發生在 Polkadot 中繼鏈上,交易的是 1 個 DOT (即 10,000,000,000 個不可分割的單位)。

目前我們的 XCM 指令是這樣的:

WithdrawAsset((Here, 10_000_000_000)。into()),

這將我們帶到了第二部分,交換(一部分)這些資產以換取計算時間來支付我們的 XCM。為此,我們有 XCM 指令 。我們來看看它是什麼樣的:

enum Instruction { /* snip */ BuyExecution { fees: MultiAsset, weight: u64, }, }

第一個專案 是應從 Holding Register 中提取並用於支付費用的金額。從技術上講,這只是最大值,因為任何未使用的餘額都會立即退還。

最終花費的金額由解釋系統決定—— 只是限制它,如果解釋系統需要為所需的執行支付更多費用,那麼 指令將導致錯誤。第二個專案指定要購買的執行時間量。這一般不應小於 XCM 程式的總 weight。

在我們的示例中,我們假設所有 XCM 指令的 weight 為 100 萬,因此到目前為止,我們的兩個專案( 和 )為 200 萬,接下來還有一個。我們將只使用我們必須支付這些費用的所有 DOT (僅當我們相信目的地鏈沒有瘋狂的費用時,這樣做才合理 - 假設情況如此)。到這裡,讓我們看看我們的 XCM:

WithdrawAsset((Here, 10_000_000_000)。into()), BuyExecution { fees: (Here, 10_000_000_000)。into(), weight: 3_000_000, },

我們 XCM 的第三部分是存入 Holding Register 中剩餘的資金。為此,我們將只使用 指令。我們實際上並不知道 Holding Register 中還剩下多少,但這並不重要,因為我們可以為應該存入的資產指定一個萬用字元。我們將它們放在 Statemint 的主權賬戶中(標識為 )。

所以我們最終的 XCM 指令是這樣的:

WithdrawAsset((Here, 10_000_000_000)。into()), BuyExecution { fees: (Here, 10_000_000_000)。into(), weight: 3_000_000, }, DepositAsset { assets: All。into(), max_assets: 1, beneficiary: Parachain(1000)。into(), },

使用 XCM 在鏈之間移動資產

將資產傳送到另一條鏈可能是鏈間訊息傳遞的最常見用例。允許一條鏈管理另一條鏈的本地資產,將允許各種衍生用例(無雙關語),最簡單的是去中心化交易所,但通常歸為去中心化金融或 De-Fi。

一般來說,資產在鏈之間移動有兩種方式,這取決於鏈之間是否信任彼此的安全性和邏輯。

傳送(Teleporting)

對於相互信任的鏈(例如在相同的整體共識和安全保護傘下的同質分片),我們可以使用 Polkadot 稱為

傳送

的框架,這基本上就意味著在傳送方銷燬資產並在接收方鑄造它。這種國防法既簡單又高效——它只需要兩條鏈的協調,並且每一側只涉及一個動作。遺憾的是,如果接收鏈不能 100% 信任傳送鏈實際銷燬它正在鑄造的資產(並且確實不鑄造超出資產約定規則的資產),那麼傳送鏈確實沒有根據訊息鑄造資產的理由。

我們來看看 XCM 將(大部分的) 1 個 DOT 從 Polkadot 中繼鏈傳送到它在 Statemint 上的主權帳戶時是什麼樣的。我們假設 Polkadot 這邊已經支付了費用。

WithdrawAsset((Here, 10_000_000_000)。into()), InitiateTeleport { assets: All。into(), dest: Parachain(1000)。into(), xcm: Xcm(vec![ BuyExecution { fees: (Parent, 10_000_000_000)。into(), weight: 3_000_000, }, DepositAsset { assets: All。into(), max_assets: 1, beneficiary: Parent。into(), }, ]), }

如你所見,這看起來和我們上次看到的直接 「提款-購買-存入」 的模式非常相似。不同之處在於 指令,它插入在最後兩條指令( 和 )周圍。在幕後,傳送鏈(Polkadot 中繼鏈)在執行 指令時正在建立一條全新的訊息;它獲取 欄位並將其放入新的 XCM 中,然後將此 XCM 傳送到接收鏈 (Statemint) 。Statemint 相信 Polkadot 中繼鏈在傳送訊息之前已經銷燬了其一側的 1 個 DOT。(事實確實如此!)

(受益人)被宣告為 ,精明的讀者可能想知道這在 Polkadot 中繼鏈的上下文中指的是什麼。答案是 「什麼也不是」,但這裡沒有錯誤。 引數中的所有內容都是從接收方的角度編寫的,因此儘管這是整個 XCM 的一部分,它被饋送到 Polkadot 中繼鏈,但它實際上只在 Statemint 上執行,因此它的上下文是跟著 Statemint 走的。

當 Statemint 最終收到這條訊息時,它長這樣:

ReceiveTeleportedAsset((Parent, 10_000_000_000)。into()), BuyExecution { fees: (Parent, 10_000_000_000)。into(), weight: 3_000_000, }, DepositAsset { assets: All。into(), max_assets: 1, beneficiary: Parent。into(), },

你可能注意到了,這看起來跟之前的 XCM 非常像。唯一的主要區別是,它不是透過從本地賬戶提款來為費用和存款提供資金,而是透過相信 DOT 在傳送方(Polkadot 中繼鏈)上確實被銷燬並尊重 訊息。值得注意的是,我們在 Polkadot 中繼鏈上傳送的 1 個 DOT 的資產識別符號(,指的是中繼鏈本身是 DOT 的原生環境)已自動變異為它在 Statemint 上的表示: ,即 Statemint 上下文中中繼鏈的位置。

也被指定為 Polkadot 中繼鏈,因此其(在 Statemint 上的)主權賬戶被記入新鑄造的 1 DOT 減去費用。XCM 可能只是輕鬆地為 指定了一個帳戶或其他地方。實際上,可以使用從中繼鏈傳送的後續 來移動這 1 個 DOT。

準備金(Reserves)

跨鏈轉移資產的另一個方式稍微複雜一些。用到了稱為準備金(reserve)的第三方。這個名字來自銀行的準備金制度,也就是資產被 「儲備」 起來,來讓某些已釋出的價值承諾具有可信度。例如,如果我們有理由相信在一條獨立的平行鏈上發行的每個 「衍生」 DOT 恰好可以兌換 1 個 「真」 DOT (例如 Statemint 或中繼鏈上的 DOT),那麼我們就可以將平行鏈的 DOT 視為在經濟上等同於真實的 DOT 。(大多數銀行都做部分準備金銀行業務,這意味著他們保留的準備金少於面值。這種做法平時沒什麼問題,但是當太多人都希望贖回,就會很快出問題。)所以,準備金是儲存 「真實」 資產的地方,出於傳輸目的,其邏輯和安全性受到傳送方和接收方的信任。傳送方和接收方的任何相應資產都將是衍生品,但它們將得到 100% 的 「真實」 儲備資產支援。假設平行連結串列現良好(即它沒有漏洞,並且其治理沒有決定偷走準備金跑路),這將使衍生品 DOT 與基礎儲備 DOT 的價值基本相同。儲備資產儲存在儲備鏈上的傳送者 / 接收者的主權賬戶(即由傳送者或接收者鏈控制的賬戶)中,所以除非平行鏈出現問題,否則有充分的理由相信這些資產會受到很好的保護。

說回到轉賬機制,傳送方將指示準備金把傳送方擁有的資產(並將其用作其自己版本的相同資產的準備金)轉移到接收方的主權賬戶中,而準備金(而不是傳送方!)通知接收方他們的新資產。這意味著傳送方和接收方不需要信任彼此的邏輯或安全性,而只需信任用作準備金的鏈的邏輯或安全性。然而,這確實意味著三方需要協調,這增加了整體成本、時間和複雜性。

讓我們看看所需的 XCM。這次我們將從平行鏈 2000 傳送 1 個 DOT 到平行鏈 2001,它在平行鏈 1000 上使用準備金支援的 DOT。同樣,我們假設費用已經在傳送方支付過了。

WithdrawAsset((Parent, 10_000_000_000)。into()), InitiateReserveWithdraw { assets: All。into(), dest: ParentThen(Parachain(1000))。into(), xcm: Xcm(vec![ BuyExecution { fees: (Parent, 10_000_000_000)。into(), weight: 3_000_000, }, DepositReserveAsset { assets: All。into(), max_assets: 1, dest: ParentThen(Parachain(2001))。into(), xcm: Xcm(vec![ BuyExecution { fees: (Parent, 10_000_000_000)。into(), weight: 3_000_000, }, DepositAsset { assets: All。into(), max_assets: 1, beneficiary: ParentThen(Parachain(2000))。into(), }, ]), }, ]), },

就像我之前說的,這會有點複雜。讓我們來看看這個過程。外部部分負責在傳送方(平行鏈 2000)上提取 1 個 DOT 並撤回 Statemint (平行鏈 1000)上相應的 1 個 DOT —— 為此它使用了 ,其作用就是字面意思(開啟準備金提取)。

WithdrawAsset((Parent, 10_000_000_000)。into()), InitiateReserveWithdraw { assets: All。into(), dest: ParentThen(Parachain(1000))。into(), xcm: /* snip */ }

現在我們在 Statemint 的 Holding Register 中持有 1 個 DOT。在我們可以做其他事情之前,我們需要在 Statemint 上購買一些執行時間。這個過程看起來也很眼熟:

/*snip*/ xcm: Xcm(vec![ BuyExecution { fees: (Parent, 10_000_000_000)。into(), weight: 3_000_000, }, DepositReserveAsset { assets: All。into(), max_assets: 1, dest: ParentThen(Parachain(2001))。into(), xcm: /* snip */ }, ]), /*snip*/

我們使用自己的 1 DOT 來支付費用,我們假設每個 XCM 操作有 100 萬。支付了這一項操作後,我們將 1 個 DOT (減去費用,而且我們很懶,所以我們只使用 )存入平行鏈 2001 的主權賬戶,但這樣做是作為儲備資產,這意味著我們也要求 Statemint 向該接收鏈傳送通知 XCM,通知它傳輸以及要對生成的衍生資產執行的一些指令。 指令並不總是奏效;為了讓它奏效, 必須是一個可以在準備金鏈上合理持有資金的位置,而且也是儲備鏈可以向其傳送 XCM 的位置。兄弟平行鏈恰好符合要求。

/*snip*/ xcm: Xcm(vec![ BuyExecution { fees: (Parent, 10_000_000_000)。into(), weight: 3_000_000, }, DepositAsset { assets: All。into(), max_assets: 1, beneficiary: ParentThen(Parachain(2000))。into(), }, ]), /*snip*/

最後一部分定義了到達平行鏈 2001 的訊息的一部分。就像啟動傳送操作一樣, 編寫併發送一條新訊息,在這裡是 。正是這個訊息,雖然包含我們定義的 XCM 程式,但它到達了接收平行鏈。它看起來像這樣:

ReserveAssetDeposited((Parent, 10_000_000_000)。into()), BuyExecution { fees: (Parent, 10_000_000_000)。into(), weight: 3_000_000, }, DepositAsset { assets: All。into(), max_assets: 1, beneficiary: ParentThen(Parachain(2000))。into(), },

(這假設 Statemint 實際上沒有收取任何費用,並且整個 1 DOT 都傳過去了。這不是特別現實,因此 這一行的數字可能會更低。) 這條訊息中的大部分地方看起來應該很熟悉;與我們在上一節中看到的 訊息的唯一顯著區別是頂層指令 ,它實現了類似的目的,只是表示 「傳送鏈銷燬了資產,以便你可以鑄造等價資產」,它的意思是 「傳送鏈收到了資產併為你保留它們,因此你可以鑄造有全額資產背書的衍生品」。無論哪種方式,目的地鏈都會將它們鑄造到 Holding Reserve 中,然後我們將它們存入接收鏈上的傳送者主權賬戶中。

結論

以上就是本文的內容。希望它有助於解釋 XCM 是什麼,以及它是如何設計以運轉的基礎知識。在下一篇文章中,我們將深入研究 XCVM 的架構、其執行模型及其錯誤處理、XCM 的版本控制系統;如何在連線良好的、相互依賴的生態系統中管理格式升級;其查詢-響應系統;以及 XCM 在 Substrate 中的工作原理。我們還將討論 XCM 的一些未來發展方向、計劃的功能以及發展它的過程。