Gavin Wood: 跨共識(shí)消息格式 XCM
加入 PolkaWorld 社區(qū),共建 Web 3.0!

隨著最終的 Polkadot 1.0 版本和平行鏈的臨近,跨共識(shí)消息格式(簡(jiǎn)稱 XCM)即將發(fā)布其第一個(gè)生產(chǎn)就緒版本。這是對(duì)格式、其目標(biāo)、工作原理的介紹,可用于實(shí)現(xiàn)典型的跨鏈任務(wù)。
我們從一個(gè)有趣的事實(shí)講起。XCM 是“跨共識(shí)”消息格式,而不僅僅是“跨鏈”。這種差異是該格式在最終實(shí)現(xiàn)的目標(biāo)上的標(biāo)志,該格式不僅在鏈之間交流,而且在智能合約和模塊之間,以及通過橋和分片(如 Polkadot 的 Spree)中發(fā)送各種想法。
?? 一種格式,而不是一個(gè)協(xié)議
為了更好地理解 XCM,重要的是要了解它的邊界以及它在 Polkadot 技術(shù)堆棧中的位置。XCM 是一種消息格式。它不是消息傳遞協(xié)議。它不能用于在系統(tǒng)之間實(shí)際“發(fā)送”任何消息,它的作用僅在于表達(dá)接收者應(yīng)該做什么。
不包括橋和合約模塊,Polkadot 帶有三個(gè)不同的系統(tǒng),用于在其組成鏈之間實(shí)際通信 XCM 消息:UMP、DMP 和 XCMP。UMP(向上消息傳遞)允許平行鏈向它們的中繼鏈發(fā)送消息。DMP(向下消息傳遞)允許中繼鏈將消息向下傳遞到其平行鏈。XCMP 可能是其中最著名的,這允許平行鏈之間發(fā)送消息。XCM 可用于通過這三個(gè)通信通道中的任意一個(gè)來表達(dá)消息的含義。

除了在鏈之間發(fā)送消息之外,XCM 在其他語境也很有用,比如,可以用于在你之前不是很了解它交易格式的鏈上進(jìn)行交易。對(duì)于業(yè)務(wù)邏輯變化很小的鏈(例如比特幣),交易格式 —— 或者錢包用來向鏈發(fā)送指令的格式 —— 往往會(huì)無限期地保持完全相同,或者至少兼容。使用高度可進(jìn)化的基于元協(xié)議的鏈,例如 Polkadot 及其組成的平行鏈,業(yè)務(wù)邏輯可以通過單個(gè)交易跨網(wǎng)絡(luò)升級(jí)。這可以改變?nèi)魏问虑?,包括交易格式,給錢包維護(hù)者帶來潛在的問題,特別是對(duì)于需要離線保存的錢包(例如 Parity Signer)。由于 XCM 版本良好、抽象且通用,因此它可以用作一種為錢包提供持久交易格式的手段,用于創(chuàng)建許多常見交易。
?? 目標(biāo)
XCM 旨在成為共識(shí)系統(tǒng)之間交流思想的語言。它應(yīng)該足夠通用,以便在整個(gè)不斷發(fā)展的生態(tài)系統(tǒng)中正確并有用。它應(yīng)該是可擴(kuò)展的,由于可擴(kuò)展性不可避免地意味著變化,它也應(yīng)該是面向未來和向前兼容的。最后,它應(yīng)該足夠高效可以在鏈上運(yùn)行,并且還能在計(jì)量環(huán)境中運(yùn)行。
像所有語言一樣,有些人會(huì)比其他人更傾向于使用某些元素。XCM 的設(shè)計(jì)方式并不是讓每個(gè)支持 XCM 的系統(tǒng)都能夠解釋任何可能的 XCM 消息。有些消息在某些系統(tǒng)下不會(huì)有合理的解釋。其他的可能是合理的,但由于資源限制或因?yàn)榭梢砸愿逦?、更?guī)范的方式表達(dá)相同的內(nèi)容,解釋器仍然故意不支持。系統(tǒng)將不可避免地只支持可能消息的一個(gè)子集。資源嚴(yán)重受限的系統(tǒng)(如智能合約)可能只支持非常有限的“方言”。
這種普遍性甚至延伸到諸如為執(zhí)行 XCM 消息支付費(fèi)用之類的概念。由于我們知道 XCM 可用于多種系統(tǒng),包括 gas 計(jì)量的智能合約平臺(tái)和社區(qū)平行鏈,一直到系統(tǒng)平行鏈與其中繼鏈之間的可信交互,因此我們不想將費(fèi)用支付等元素烤得太深和在協(xié)議中變得不可逆轉(zhuǎn)。
?? 為什么不直接使用本地消息格式
在某些情況下,捎帶在本地信息/交易格式的鏈或智能合約可能很有用,但確實(shí)有一些很大的缺點(diǎn),使其對(duì) XCM 的目標(biāo)不太有用。首先,鏈之間缺乏兼容性,因此打算向多個(gè)目的地發(fā)送消息的系統(tǒng)需要了解如何為每個(gè)目的地編寫消息。在這一點(diǎn)上,即使是單個(gè)目的地也可能隨著時(shí)間的推移改變其本地交易/消息格式。智能合約可能會(huì)升級(jí),區(qū)塊鏈可能會(huì)引入新功能或改變現(xiàn)有功能,從而改變其交易格式。
其次,鏈上的常見用例不容易適合單個(gè)交易;可能需要特殊的技巧來提取資金,交換資金,然后將結(jié)果全部存入單個(gè)交易中。連貫的儲(chǔ)備資產(chǎn)框架所需的后續(xù)轉(zhuǎn)移通知并不存在于不知道其他人的鏈中。
第三,諸如支付費(fèi)用之類的操作不容易適應(yīng)假設(shè)已經(jīng)像智能合約消息一樣協(xié)商費(fèi)用支付的模型。相比之下,交易信封提供了一些支付處理的系統(tǒng),但通常也被設(shè)計(jì)為包含一個(gè)簽名,這在共識(shí)系統(tǒng)之間進(jìn)行通信時(shí)沒有意義。
?? 一些初始用例
雖然 XCM 的目標(biāo)是通用的、靈活的和面向未來的,但它當(dāng)然必須滿足實(shí)際需求,尤其是鏈之間的代幣轉(zhuǎn)移。可選的費(fèi)用支付(可能使用這些代幣)是另一種方式,就像進(jìn)行交換服務(wù)的通用接口一樣,在整個(gè) DeFi 世界中都很常見。最后,應(yīng)該可以使用 XCM 語言進(jìn)行一些特定于平臺(tái)的操作。例如,在 Substrate 鏈中,可能需要將遠(yuǎn)程調(diào)用分派到其模塊之一以訪問專門的功能。
最重要的是,有許多我們希望支持的代幣轉(zhuǎn)移模型:我們可能只想簡(jiǎn)單地控制遠(yuǎn)程鏈上的帳戶,允許本地鏈在遠(yuǎn)程鏈上擁有一個(gè)地址以接收資金并最終將其控制的資金轉(zhuǎn)移到該遠(yuǎn)程鏈上的其他賬戶中。
我們可能有兩個(gè)共識(shí)系統(tǒng),它們都是特定代幣的“本地家園”。想象一下像 USDT 或 USDC 這樣的代幣,它在幾個(gè)不同的鏈上都有實(shí)例,并且完全可以互換。應(yīng)該可以在一條鏈上銷毀這樣的代幣,并在另一條支持的鏈上鑄造相應(yīng)的代幣。在 XCM 的說法中,我們之所以稱之為傳送(teleport),是因?yàn)橘Y產(chǎn)的轉(zhuǎn)移實(shí)際上是通過在一側(cè)銷毀它,并在另一側(cè)創(chuàng)建一個(gè)克隆來實(shí)現(xiàn)的。
最后,可能有兩條鏈想要提名第三條鏈,其中一條鏈上的資產(chǎn)可能被視為本地資產(chǎn),用作該資產(chǎn)的儲(chǔ)備。每個(gè)鏈上資產(chǎn)的衍生形式將得到完全支持,允許衍生資產(chǎn)交換為支持它的儲(chǔ)備鏈上的基礎(chǔ)資產(chǎn)。這可能是兩條鏈不一定相互信任的情況,但(至少就相關(guān)資產(chǎn)而言)愿意信任資產(chǎn)的本地鏈。這里的一個(gè)例子是我們有幾個(gè)社區(qū)平行鏈想要在彼此之間發(fā)送 DOT。它們每個(gè)都有一個(gè)本地形式的 DOT,由 Statemint 鏈(DOT 的本地中心)上的平行鏈控制的 DOT 完全支持。當(dāng)在鏈之間發(fā)送本地形式的 DOT 時(shí),在后臺(tái),“真正的”DOT 在 Statemint 上的平行鏈帳戶之間移動(dòng)。
即使這種看似適度的功能水平也有相對(duì)大量的配置,它們的使用可能是可取的,并且需要一些有趣的設(shè)計(jì)來避免過度擬合。
XCM 剖析
XCM 格式的核心在于 XCVM。與某些人的看法相反,這不是(有效的)羅馬數(shù)字(盡管如果是,它可能意味著 905)。事實(shí)上,這代表跨共識(shí)虛擬機(jī)。這是一臺(tái)超高級(jí)別的非圖靈完備計(jì)算機(jī),其指令設(shè)計(jì)為與交易大致處于同一級(jí)別。
XCM 中的“消息”實(shí)際上只是一個(gè)運(yùn)行在 XCVM 上的程序。它是一個(gè)或多個(gè) XCM 指令。程序會(huì)一直執(zhí)行,直到它運(yùn)行到最后或遇到錯(cuò)誤為止,此時(shí)它會(huì)結(jié)束(我現(xiàn)在有意不解釋這一點(diǎn))并停止。
XCVM 包括許多 Register,以及訪問托管它的共識(shí)系統(tǒng)的整體狀態(tài)。指令可能會(huì)改變一個(gè) Register,它們可能會(huì)改變共識(shí)系統(tǒng)的狀態(tài),或者兩者兼而有之。
這種指令的一個(gè)例子是 TransferAsset,它用于將資產(chǎn)轉(zhuǎn)移到遠(yuǎn)程系統(tǒng)上的某個(gè)其他地址。需要告知要轉(zhuǎn)讓哪些資產(chǎn)以及資產(chǎn)要轉(zhuǎn)讓給誰/在哪里。在 Rust 中,它是這樣聲明的:
enum Instruction {
TransferAsset {
assets: MultiAssets,
beneficiary: MultiLocation,
}
/* snip */
}
正如你可能猜到的那樣,資產(chǎn)是表示要轉(zhuǎn)讓哪些資產(chǎn)的參數(shù),而受益人則說明這些資產(chǎn)要交給誰/在哪里。當(dāng)然,我們還缺少另一條信息,即從誰/何處獲取資產(chǎn)。這是從原 Register自動(dòng)推斷出來的。程序開始時(shí),這個(gè) Register 一般是根據(jù)傳輸系統(tǒng)(網(wǎng)、XCMP 或者其他什么)來設(shè)置的,以反映消息實(shí)際來自哪里,和受益人是同一類型的信息。Origin Register 作為一個(gè)受保護(hù)的 Register 運(yùn)行 —— 程序不能任意設(shè)置它,盡管有兩條指令可以用來以某種方式改變它。
使用的類型是 XCM 中非?;镜乃枷耄嘿Y產(chǎn),由 MultiAsset 表示,共識(shí)內(nèi)的位置,由 MultiLocation 表示。Origin Register 是一個(gè)可選的 MultiLocation(可選,因?yàn)槿绻枰梢酝耆宄?/p>
?? 在 XCM 的位置
MultiLocation 類型標(biāo)識(shí)存在于共識(shí)世界中的任何單個(gè)位置。這是一個(gè)相當(dāng)抽象的想法,可以代表共識(shí)中存在的所有事物,從可擴(kuò)展的多分片區(qū)塊鏈(如 Polkadot)一直到平行鏈上的低級(jí) ERC-20 資產(chǎn)賬戶。在計(jì)算機(jī)科學(xué)術(shù)語中,它實(shí)際上只是一個(gè)全局單例數(shù)據(jù)結(jié)構(gòu),無論其大小或復(fù)雜性如何。
MultiLocation 始終表示相對(duì)于當(dāng)前位置的位置。你可以把它想象成一個(gè)文件系統(tǒng)路徑,但是沒有辦法直接表達(dá)文件系統(tǒng)樹的“根”。這有一個(gè)簡(jiǎn)單的原因:在 Polkadot 的世界中,區(qū)塊鏈可以合并到其他區(qū)塊鏈中,也可以從其他區(qū)塊鏈中分離出來。區(qū)塊鏈可以非常獨(dú)立地開始生命,并最終被提升為更大共識(shí)中的平行鏈。如果這樣做,那么 “root” 的含義將在一夜之間改變,這可能會(huì)給 XCM 消息和其他任何使用 MultiLocation 的消息帶來混亂。為了簡(jiǎn)單起見,我們完全排除了這種可能性。
XCM 中的位置是分層的,共識(shí)中的一些地方完全封裝在共識(shí)中的其他地方。Polkadot 的平行鏈完全存在于整個(gè) Polkadot 共識(shí)中,我們稱之為內(nèi)部位置。更嚴(yán)格地說,我們可以說,只要有一個(gè)共識(shí)系統(tǒng)的任何變化都意味著另一個(gè)共識(shí)系統(tǒng)的變化,那么前一個(gè)系統(tǒng)是后者的內(nèi)部系統(tǒng)。例如,Canvas 智能合約位于托管它的合約模塊的內(nèi)部。比特幣中的 UTXO 是比特幣區(qū)塊鏈的內(nèi)部。
這意味著 XCM 沒有區(qū)分 “誰” 和 “在哪里” 兩個(gè)問題。從像 XCM 這樣相當(dāng)抽象的東西的角度來看,區(qū)別并不重要 —— 兩者模糊并成為本質(zhì)上相同的東西。
MultiLocations 用于識(shí)別發(fā)送 XCM 消息的位置,可以接收資產(chǎn)的位置,然后甚至可以幫助描述資產(chǎn)本身的類型,正如我們將看到的。非常有用的東西。
當(dāng)用本文這樣的文本寫下來時(shí),它們表示為一些 .. (或“父”,封裝共識(shí)系統(tǒng))組件,后跟一些連接點(diǎn),所有連接點(diǎn)都用 / 分隔。(當(dāng)我們用 Rust 之類的語言表達(dá)它們時(shí),通常不會(huì)發(fā)生這種情況,但它在書面上是有意義的,因?yàn)樗芟駨V泛使用的熟悉的目錄路徑。)連接在其封裝共識(shí)中標(biāo)識(shí)了一個(gè)內(nèi)部位置系統(tǒng)。如果根本沒有父節(jié)點(diǎn)/連接點(diǎn),那么我們只說位置是這里。
例如:
../Parachain(1000): 在平行鏈中進(jìn)行評(píng)估,這將識(shí)別我們索引為 1000 的兄弟平行鏈。(在 Rust 中,我們將編寫ParentThen(Parachain(1000)).into())../AccountId32(0x1234...cdef): 在平行鏈中進(jìn)行評(píng)估,這將識(shí)別中繼鏈上的 32 字節(jié)帳戶0x1234…cdef。Parachain(42)/AccountKey20(0x1234...abcd): 在中繼鏈上進(jìn)行評(píng)估,這將識(shí)別平行鏈編號(hào) 42 上的 20 字節(jié)帳戶0x1234…abcd(大概類似于承載以太坊兼容帳戶的 Moonbeam)。
有許多不同類型的連接點(diǎn),用于以各種方式識(shí)別你可能在鏈上找到的位置,例如鍵、索引、二進(jìn)制 blob 和復(fù)數(shù)描述。
?? XCM 中的資產(chǎn)
在 XCM 中工作時(shí),通常需要引用某種資產(chǎn)。這是因?yàn)閹缀跛鞋F(xiàn)有的公共區(qū)塊鏈都依賴于一些原生數(shù)字資產(chǎn)來為其內(nèi)部經(jīng)濟(jì)和安全機(jī)制提供支柱。對(duì)于比特幣等工作量證明區(qū)塊鏈,原生資產(chǎn)(BTC)用于獎(jiǎng)勵(lì)開發(fā)區(qū)塊鏈的礦工并防止雙重支出。對(duì)于 Polkadot 等權(quán)益證明區(qū)塊鏈,原生資產(chǎn) (DOT) 用作一種抵押形式,網(wǎng)絡(luò)管理員(稱為權(quán)益人)必須承擔(dān)風(fēng)險(xiǎn)才能生成有效區(qū)塊并獲得實(shí)物獎(jiǎng)勵(lì)。
一些區(qū)塊鏈管理多種資產(chǎn),例如以太坊的 ERC-20 框架允許在鏈上管理許多不同的資產(chǎn)。一些管理不可替代的資產(chǎn),例如以太坊的 ETH,而是不可替代的 —— 獨(dú)一無二的實(shí)例;Crypto-kitties 是此類不可替代代幣或 NFT 的早期示例。
XCM 旨在能夠毫不費(fèi)力地處理所有此類資產(chǎn)。為此,有數(shù)據(jù)類型 MultiAsset 及其關(guān)聯(lián)類型 MultiAssets、WildMultiAsset 和 MultiAssetFilter。讓我們看看 Rust 中的 MultiAsset:
struct MultiAsset {
id: AssetId,
fun: Fungibility,
}
所以有兩個(gè)字段定義了我們的資產(chǎn):id 和 fun,這很好地表明了 XCM 如何處理資產(chǎn)。首先,必須提供整體資產(chǎn)身份。對(duì)于可替代資產(chǎn),這只是標(biāo)識(shí)資產(chǎn)。對(duì)于 NFT,這標(biāo)識(shí)了整個(gè)資產(chǎn)“類別” —— 不同的資產(chǎn)實(shí)例可能在這個(gè)類別中。
plain
enum AssetId {
Concrete(MultiLocation),
Abstract(BinaryBlob),
}
資產(chǎn)身份以兩種方式之一表示;無論是具體的還是抽象的。Abstract 并沒有真正使用,但它允許通過名稱指定資產(chǎn) ID。這很方便,但依賴于接收者以發(fā)送者期望的方式解釋名稱,這可能并不總是那么容易。Concrete 在一般用途中使用并使用位置來明確地識(shí)別資產(chǎn)。對(duì)于原生資產(chǎn)(例如 DOT),資產(chǎn)往往被識(shí)別為鑄造資產(chǎn)的鏈(在這種情況下是 Polkadot 中繼鏈,這將是其平行鏈的位置 .. )。主要在鏈模塊內(nèi)管理的資產(chǎn)可以通過包括其在該模塊內(nèi)的索引的位置來識(shí)別。例如,Karura 平行鏈可能指的是 Statemine 平行鏈上的資產(chǎn),位置為 ../Parachain(1000)/PalletInstance(50)/GeneralIndex(42) 。
enum Fungibility {
Fungible(NonZeroAmount),
NonFungible(AssetInstance),
}
其次,它們必須是可替代的或不可替代的。如果它們是可替代的,那么應(yīng)該有一些相關(guān)的非零數(shù)量。如果它們不可替代,那么應(yīng)該有一些指示它們是哪個(gè)實(shí)例而不是數(shù)量。(這通常用索引表示,但 XCM 還允許使用各種其他數(shù)據(jù)類型,例如數(shù)組和二進(jìn)制 blob。) 這涵蓋了 MultiAsset,但我們有時(shí)會(huì)使用其他三種相關(guān)類型。MultiAssets 就是其中之一,實(shí)際上只是意味著一組 MultiAsset 項(xiàng)目。然后我們有 WildMultiAsset;這是一個(gè)通配符,可用于匹配一個(gè)或多個(gè) MultiAsset 項(xiàng)目。它實(shí)際上只支持兩種通配符:All(匹配所有資產(chǎn))和 AllOf 匹配特定身份 (AssetId) 和可替代性的所有資產(chǎn)。值得注意的是,對(duì)于后者,不需要指定數(shù)量(在可替代的情況下)或?qū)嵗▽?duì)于非可替代的),并且全部匹配。
最后,還有 MultiAssetFilter。這是最常用的,實(shí)際上只是 MultiAssets 和 WildMultiAsset 的組合,允許指定通配符或明確(即非通配符)資產(chǎn)列表。
在 Rust XCM API 中,我們提供了很多轉(zhuǎn)換,以盡可能輕松地處理這些數(shù)據(jù)類型。例如,當(dāng)我們?cè)?Polkadot 中繼鏈上時(shí),要指定等于 100 個(gè)不可分割的 DOT 資產(chǎn)單位的可替代 MultiAsset(普朗克,對(duì)于那些知道的人),那么我們將使用 (Here, 100).into()。
?? Holding Register
我們?cè)賮砜纯戳硪粋€(gè) XCM 指令:WithdrawAsset。從表面上看,這有點(diǎn)像 TransferAsset 的前半部分:它從起源的賬戶中提取了一些資產(chǎn)。但這與他們有什么關(guān)系?如果他們沒有在任何地方存入,那么這肯定是一個(gè)非常無用的操作。如果我們查看它的 Rust 聲明,我們會(huì)發(fā)現(xiàn)更多有關(guān)其用法的線索:
WithdrawAsset(MultiAssets),
所以,這次只有一個(gè)參數(shù)(MultiAssets 類型,它規(guī)定哪些資產(chǎn)必須從 Origin Register 的所有權(quán)中撤出)。但是沒有指定放置資產(chǎn)的位置。
這些暫時(shí)持有的未動(dòng)用資產(chǎn)稱為持有 Register。(“Holding”是因?yàn)樗鼈兲幱诓荒軣o限期持續(xù)的臨時(shí)位置,“Register”是因?yàn)樗悬c(diǎn)像 CPU Register,是一個(gè)存放工作數(shù)據(jù)的地方。)有許多指令對(duì)持有寄存器進(jìn)行操作。一種非常簡(jiǎn)單的指令是 DepositAsset 指令。讓我們來看看它:
enum Instruction {
DepositAsset {
assets: MultiAssetFilter,
max_assets: u32,
beneficiary: MultiLocation,
},
/* snip */
}
啊哈!精明的讀者會(huì)發(fā)現(xiàn)這看起來很像 TransferAsset 指令中缺失的一半。我們有 assets 參數(shù),該參數(shù)指定應(yīng)從持有寄存器中刪除哪些資產(chǎn)以存放在鏈上。max_assets 讓 XCM 作者通知接收者打算存入多少獨(dú)特的資產(chǎn)。(這在知道 Holding Register 的內(nèi)容之前計(jì)算費(fèi)用時(shí)很有幫助,因?yàn)榇嫒胭Y產(chǎn)可能是一項(xiàng)代價(jià)高昂的操作。)最后是受益人,這與我們之前在 TransferAsset 操作中遇到的參數(shù)相同。有許多指令表示要在 Holding Register 上執(zhí)行的操作,而 DepositAsset 是最簡(jiǎn)單的指令之一。其他一些則更復(fù)雜。
?? XCM 中的費(fèi)用支付
XCM 中的費(fèi)用支付是一個(gè)相當(dāng)重要的用例。Polkadot 社區(qū)中的大多數(shù)平行鏈都會(huì)要求其對(duì)話者為他們希望進(jìn)行的任何操作付費(fèi),以免為 “交易垃圾” 和拒絕服務(wù)攻擊敞開大門。當(dāng)鏈有充分的理由相信它們的對(duì)話者會(huì)表現(xiàn)良好時(shí),也存在例外情況 —— 當(dāng) Polkadot 中繼鏈與 Polkadot Statemint 公共利益鏈通信時(shí)就是這種情況。但是,對(duì)于一般情況而言,費(fèi)用是確保 XCM 消息及其傳輸協(xié)議不會(huì)被過度使用的好方法。我們來看看 XCM 消息到達(dá) Polkadot 時(shí)如何支付費(fèi)用。
正如前文提到的一樣,XCM 不包括作為一等公民的費(fèi)用和費(fèi)用支付:與以太坊交易模型不同,對(duì)于費(fèi)用支付不是協(xié)議中不需要的東西,就必須進(jìn)行規(guī)避。就像 Rust 的零成本抽象一樣,費(fèi)用支付在 XCM 中沒有很大的設(shè)計(jì)開銷。
對(duì)于確實(shí)需要支付一定費(fèi)用的系統(tǒng),XCM 提供了使用資產(chǎn)購(gòu)買執(zhí)行資源的能力。概括來講,這包括了三個(gè)部分:
首先,需要提供一些資產(chǎn)。 其次,必須就計(jì)算時(shí)間(用 Substrate 中的說法就是 weight)交換資產(chǎn)。 最后,XCM 操作將按照指示執(zhí)行。
第一部分由提供資產(chǎn)的多個(gè) XCM 指令之一管理。我們已經(jīng)知道其中的一個(gè)指令了( WithdrawAsset ),但還有其他幾個(gè)我們稍后會(huì)看到。Holding Register中的所得資產(chǎn)當(dāng)然將用于支付與執(zhí)行 XCM 相關(guān)的費(fèi)用。任何未用于支付費(fèi)用的資產(chǎn)都將被存入某個(gè)目的地賬戶。在我們的示例中,我們假設(shè) XCM 發(fā)生在 Polkadot 中繼鏈上,交易的是 1 個(gè) DOT(即 10,000,000,000 個(gè)不可分割的單位)。
目前我們的 XCM 指令是這樣的:
WithdrawAsset((Here, 10_000_000_000).into()),
這將我們帶到了第二部分,交換(一部分)這些資產(chǎn)以換取計(jì)算時(shí)間來支付我們的 XCM。為此,我們有 XCM 指令 BuyExecution 。我們來看看它是什么樣的:
enum Instruction {
/* snip */
BuyExecution {
fees: MultiAsset,
weight: u64,
},
}
第一個(gè)項(xiàng)目 fees 是應(yīng)從 Holding Register 中提取并用于支付費(fèi)用的金額。從技術(shù)上講,這只是最大值,因?yàn)槿魏挝词褂玫挠囝~都會(huì)立即退還。
最終花費(fèi)的金額由解釋系統(tǒng)決定—— fees 只是限制它,如果解釋系統(tǒng)需要為所需的執(zhí)行支付更多費(fèi)用,那么 BuyExecution 指令將導(dǎo)致錯(cuò)誤。第二個(gè)項(xiàng)目指定要購(gòu)買的執(zhí)行時(shí)間量。這一般不應(yīng)小于 XCM 程序的總 weight。
在我們的示例中,我們假設(shè)所有 XCM 指令的 weight 為 100 萬,因此到目前為止,我們的兩個(gè)項(xiàng)目(WithdrawAsset 和 BuyExecution)為 200 萬,接下來還有一個(gè)。我們將只使用我們必須支付這些費(fèi)用的所有 DOT(僅當(dāng)我們相信目的地鏈沒有瘋狂的費(fèi)用時(shí),這樣做才合理 - 假設(shè)情況如此)。到這里,讓我們看看我們的 XCM:
WithdrawAsset((Here, 10_000_000_000).into()),
BuyExecution {
fees: (Here, 10_000_000_000).into(),
weight: 3_000_000,
},
我們 XCM 的第三部分是存入 Holding Register 中剩余的資金。為此,我們將只使用 DepositAsset 指令。我們實(shí)際上并不知道 Holding Register 中還剩下多少,但這并不重要,因?yàn)槲覀兛梢詾閼?yīng)該存入的資產(chǎn)指定一個(gè)通配符。我們將它們放在 Statemint 的主權(quán)賬戶中(標(biāo)識(shí)為 Parachain(1000))。
所以我們最終的 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 在鏈之間移動(dòng)資產(chǎn)
將資產(chǎn)發(fā)送到另一條鏈可能是鏈間消息傳遞的最常見用例。允許一條鏈管理另一條鏈的本地資產(chǎn),將允許各種衍生用例(無雙關(guān)語),最簡(jiǎn)單的是去中心化交易所,但通常歸為去中心化金融或 De-Fi。
一般來說,資產(chǎn)在鏈之間移動(dòng)有兩種方式,這取決于鏈之間是否信任彼此的安全性和邏輯。
? 傳送(Teleporting)
對(duì)于相互信任的鏈(例如在相同的整體共識(shí)和安全保護(hù)傘下的同質(zhì)分片),我們可以使用 Polkadot 稱為傳送的框架,這基本上就意味著在發(fā)送方銷毀資產(chǎn)并在接收方鑄造它。這種國(guó)防法既簡(jiǎn)單又高效——它只需要兩條鏈的協(xié)調(diào),并且每一側(cè)只涉及一個(gè)動(dòng)作。遺憾的是,如果接收鏈不能 100% 信任發(fā)送鏈實(shí)際銷毀它正在鑄造的資產(chǎn)(并且確實(shí)不鑄造超出資產(chǎn)約定規(guī)則的資產(chǎn)),那么發(fā)送鏈確實(shí)沒有根據(jù)消息鑄造資產(chǎn)的理由。
我們來看看 XCM 將(大部分的)1 個(gè) DOT 從 Polkadot 中繼鏈傳送到它在 Statemint 上的主權(quán)帳戶時(shí)是什么樣的。我們假設(shè) Polkadot 這邊已經(jīng)支付了費(fèi)用。
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(),
},
]),
}
如你所見,這看起來和我們上次看到的直接 “提款-購(gòu)買-存入” 的模式非常相似。不同之處在于 InitiateTeleport 指令,它插入在最后兩條指令(BuyExecution 和 DepositAsset )周圍。在幕后,發(fā)送鏈(Polkadot 中繼鏈)在執(zhí)行InitiateTeleport指令時(shí)正在創(chuàng)建一條全新的消息;它獲取 xcm 字段并將其放入新的 XCMReceiveTeleportedAsset中,然后將此 XCM 發(fā)送到接收鏈 (Statemint) 。Statemint 相信 Polkadot 中繼鏈在發(fā)送消息之前已經(jīng)銷毀了其一側(cè)的 1 個(gè) DOT。(事實(shí)確實(shí)如此?。?/p>
beneficiary (受益人)被聲明為 Parent.into() ,精明的讀者可能想知道這在 Polkadot 中繼鏈的上下文中指的是什么。答案是 “什么也不是”,但這里沒有錯(cuò)誤。xcm 參數(shù)中的所有內(nèi)容都是從接收方的角度編寫的,因此盡管這是整個(gè) XCM 的一部分,它被饋送到 Polkadot 中繼鏈,但它實(shí)際上只在 Statemint 上執(zhí)行,因此它的上下文是跟著 Statemint 走的。
當(dāng) Statemint 最終收到這條消息時(shí),它長(zhǎng)這樣:
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(),
},
你可能注意到了,這看起來跟之前的 WithdrawAsset XCM 非常像。唯一的主要區(qū)別是,它不是通過從本地賬戶提款來為費(fèi)用和存款提供資金,而是通過相信 DOT 在發(fā)送方(Polkadot 中繼鏈)上確實(shí)被銷毀并尊重 ReceiveTeleportedAsset 消息。值得注意的是,我們?cè)?Polkadot 中繼鏈上發(fā)送的 1 個(gè) DOT 的資產(chǎn)標(biāo)識(shí)符(Here,指的是中繼鏈本身是 DOT 的原生環(huán)境)已自動(dòng)變異為它在 Statemint 上的表示: Parent.into(),即 Statemint 上下文中中繼鏈的位置。
beneficiary 也被指定為 Polkadot 中繼鏈,因此其(在 Statemint 上的)主權(quán)賬戶被記入新鑄造的 1 DOT 減去費(fèi)用。XCM 可能只是輕松地為beneficiary 指定了一個(gè)帳戶或其他地方。實(shí)際上,可以使用從中繼鏈發(fā)送的后續(xù) TransferAsset 來移動(dòng)這 1 個(gè) DOT。
?? 準(zhǔn)備金(Reserves)
跨鏈轉(zhuǎn)移資產(chǎn)的另一個(gè)方式稍微復(fù)雜一些。用到了稱為準(zhǔn)備金(reserve)的第三方。這個(gè)名字來自銀行的準(zhǔn)備金制度,也就是資產(chǎn)被 “儲(chǔ)備” 起來,來讓某些已發(fā)布的價(jià)值承諾具有可信度。例如,如果我們有理由相信在一條獨(dú)立的平行鏈上發(fā)行的每個(gè) “衍生” DOT 恰好可以兌換 1 個(gè) “真” DOT(例如 Statemint 或中繼鏈上的 DOT),那么我們就可以將平行鏈的 DOT 視為在經(jīng)濟(jì)上等同于真實(shí)的 DOT 。(大多數(shù)銀行都做部分準(zhǔn)備金銀行業(yè)務(wù),這意味著他們保留的準(zhǔn)備金少于面值。這種做法平時(shí)沒什么問題,但是當(dāng)太多人都希望贖回,就會(huì)很快出問題。)所以,準(zhǔn)備金是存儲(chǔ) “真實(shí)” 資產(chǎn)的地方,出于傳輸目的,其邏輯和安全性受到發(fā)送方和接收方的信任。發(fā)送方和接收方的任何相應(yīng)資產(chǎn)都將是衍生品,但它們將得到 100% 的 “真實(shí)” 儲(chǔ)備資產(chǎn)支持。假設(shè)平行鏈表現(xiàn)良好(即它沒有漏洞,并且其治理沒有決定偷走準(zhǔn)備金跑路),這將使衍生品 DOT 與基礎(chǔ)儲(chǔ)備 DOT 的價(jià)值基本相同。儲(chǔ)備資產(chǎn)保存在儲(chǔ)備鏈上的發(fā)送者/接收者的主權(quán)賬戶(即由發(fā)送者或接收者鏈控制的賬戶)中,所以除非平行鏈出現(xiàn)問題,否則有充分的理由相信這些資產(chǎn)會(huì)受到很好的保護(hù)。
說回到轉(zhuǎn)賬機(jī)制,發(fā)送方將指示準(zhǔn)備金把發(fā)送方擁有的資產(chǎn)(并將其用作其自己版本的相同資產(chǎn)的準(zhǔn)備金)轉(zhuǎn)移到接收方的主權(quán)賬戶中,而準(zhǔn)備金(而不是發(fā)送方!)通知接收方他們的新資產(chǎn)。這意味著發(fā)送方和接收方不需要信任彼此的邏輯或安全性,而只需信任用作準(zhǔn)備金的鏈的邏輯或安全性。然而,這確實(shí)意味著三方需要協(xié)調(diào),這增加了整體成本、時(shí)間和復(fù)雜性。
讓我們看看所需的 XCM。這次我們將從平行鏈 2000 發(fā)送 1 個(gè) DOT 到平行鏈 2001,它在平行鏈 1000 上使用準(zhǔn)備金支持的 DOT。同樣,我們假設(shè)費(fèi)用已經(jīng)在發(fā)送方支付過了。
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(),
},
]),
},
]),
},
就像我之前說的,這會(huì)有點(diǎn)復(fù)雜。讓我們來看看這個(gè)過程。外部部分負(fù)責(zé)在發(fā)送方(平行鏈 2000)上提取 1 個(gè) DOT 并撤回 Statemint(平行鏈 1000)上相應(yīng)的 1 個(gè) DOT —— 為此它使用了 InitiateReserveWithdraw ,其作用就是字面意思(開啟準(zhǔn)備金提?。?。
WithdrawAsset((Parent, 10_000_000_000).into()),
InitiateReserveWithdraw {
assets: All.into(),
dest: ParentThen(Parachain(1000)).into(),
xcm: /* snip */
}
現(xiàn)在我們?cè)?Statemint 的 Holding Register 中持有 1 個(gè) DOT。在我們可以做其他事情之前,我們需要在 Statemint 上購(gòu)買一些執(zhí)行時(shí)間。這個(gè)過程看起來也很眼熟:
/*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 來支付費(fèi)用,我們假設(shè)每個(gè) XCM 操作有 100 萬。支付了這一項(xiàng)操作后,我們將 1 個(gè) DOT(減去費(fèi)用,而且我們很懶,所以我們只使用 All.into())存入平行鏈 2001 的主權(quán)賬戶,但這樣做是作為儲(chǔ)備資產(chǎn),這意味著我們也要求 Statemint 向該接收鏈發(fā)送通知 XCM,通知它傳輸以及要對(duì)生成的衍生資產(chǎn)執(zhí)行的一些指令。DepositReserveAsset 指令并不總是奏效;為了讓它奏效, dest 必須是一個(gè)可以在準(zhǔn)備金鏈上合理持有資金的位置,而且也是儲(chǔ)備鏈可以向其發(fā)送 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*/
最后一部分定義了到達(dá)平行鏈 2001 的消息的一部分。就像啟動(dòng)傳送操作一樣, DepositReserveAsset 編寫并發(fā)送一條新消息,在這里是 ReserveAssetDeposited 。正是這個(gè)消息,雖然包含我們定義的 XCM 程序,但它到達(dá)了接收平行鏈。它看起來像這樣:
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(),
},
(這假設(shè) Statemint 實(shí)際上沒有收取任何費(fèi)用,并且整個(gè) 1 DOT 都傳過去了。這不是特別現(xiàn)實(shí),因此 assets 這一行的數(shù)字可能會(huì)更低。)
這條消息中的大部分地方看起來應(yīng)該很熟悉;與我們?cè)谏弦还?jié)中看到的 ReceiveTeleportedAsset 消息的唯一顯著區(qū)別是頂層指令 ReserveAssetDeposited ,它實(shí)現(xiàn)了類似的目的,只是表示 “發(fā)送鏈銷毀了資產(chǎn),以便你可以鑄造等價(jià)資產(chǎn)”,它的意思是 “發(fā)送鏈?zhǔn)盏搅速Y產(chǎn)并為你保留它們,因此你可以鑄造有全額資產(chǎn)背書的衍生品”。無論哪種方式,目的地鏈都會(huì)將它們鑄造到 Holding Reserve 中,然后我們將它們存入接收鏈上的發(fā)送者主權(quán)賬戶中。??
?? 結(jié)論
以上就是本文的內(nèi)容。希望它有助于解釋 XCM 是什么,以及它是如何設(shè)計(jì)以運(yùn)轉(zhuǎn)的基礎(chǔ)知識(shí)。在下一篇文章中,我們將深入研究 XCVM 的架構(gòu)、其執(zhí)行模型及其錯(cuò)誤處理、XCM 的版本控制系統(tǒng);如何在連接良好的、相互依賴的生態(tài)系統(tǒng)中管理格式升級(jí);其查詢-響應(yīng)系統(tǒng);以及 XCM 在 Substrate 中的工作原理。我們還將討論 XCM 的一些未來發(fā)展方向、計(jì)劃的功能以及發(fā)展它的過程。
“原文:https://medium.com/polkadot-network/xcm-the-cross-consensus-message-format-3b77b1373392
關(guān)注 PolkaWorld 視頻號(hào)「波卡世界」,每周四晚 19:00 與大家直播間見!
歡迎學(xué)習(xí) Substrate:
https://substrate.dev/
關(guān)注 Substrate 進(jìn)展:
https://github.com/paritytech/substrate
關(guān)注 Polkadot 進(jìn)展:
https://github.com/paritytech/polkadot

更多內(nèi)容:
這樣參與 Kusama 插槽拍賣,竟然可以獲得雙重獎(jiǎng)勵(lì)!
掃碼關(guān)注公眾號(hào),回復(fù) “1” 加入波卡群
關(guān)注 PolkaWorld
發(fā)現(xiàn) Web 3.0 時(shí)代新機(jī)遇
點(diǎn)個(gè) “在看” 再走吧!
