在 我在XCM 上寫的第一篇文章,我介紹了它的基本架構、目標以及如何將其用於一些簡單的用例。在這裡,我們將繼續深入檢查XCM 的一個有趣方面:XCM 如何隨著時間的推移而改變,而不會在它要連接的網絡之間引入中斷。
擁有一種共同語言可以解決很多人際交往的問題。它使我們能夠一起工作,解決衝突並記錄信息以備後用。但是語言僅與它能夠表達的概念一樣有用,在一個不斷變化的世界中,語言必須改變和適應其概念庫,否則就有被廢棄的風險。
不幸的是,過於突然地改變語言會損害其主要目的——促進人與人之間的交流。由於語言必須改變,因此必須有管理這些改變的方法,而不會使外行人無法理解新形式。在這方面,一個非常有用的發明是字典,它可以幫助記錄和存檔一種語言的概念調色板,以便後代能夠更好地理解歷史文本。字典的版本可以被認為是一種語言的正式“版本”。
時代可能會改變,但問題仍然非常熟悉。正如我在上一篇文章中所解釋的,XCM 只不過是一種語言,儘管它是一種非常專業的語言。它是共識系統相互交流的一種方式,隨著對XCM 的需求以加密行業和特別是Polkadot 生態系統的極速發展,那麼必須有一些方法來確保這些變化不會妥協XCM 的最初目標:互操作性。我們現在不僅需要解決共識空間的互操作性,還需要解決共識時間的問題。
由於我們期望XCM 的語言在大量使用時會隨著時間的推移而改變,因此需要採取的一個非常簡單的預防措施是確保在實際消息內容之前確定我們正在通信的XCM 版本。我們通過使用一些 版本包裝器 類型,之所以如此命名是因為它們按版本包裝XCM 消息或其組件。在Rust 代碼中,這看起來非常簡單:
pub enum VersionedXcm {
V0(v0::Xcm),
V1(v1::Xcm),
V2(v2::Xcm),
}
當“通過網絡”發送時(或者,更確切地說,在共識系統之間),XCM 總是被放置在這個版本化的容器中。這確保了太舊而無法解釋消息的系統可以安全地接收它們並識別它們不支持消息的格式。它還允許較新的系統識別並相應地解釋較舊的消息。
不僅僅是XCM 消息被版本化; 在XCM 代碼庫中,我們也版本 MultiLocation
, MultiAsset
,以及它的關聯類型。這是因為當鏈的XCM 邏輯升級時,它們可能需要被存儲和稍後解釋。如果沒有版本控制,我們可能會嘗試解釋舊的 MultiLocation
作為一個新的,發現它是不可理解的(或者更糟的是,可以理解但與原始含義不同)。
版本控制是第一步,可確保我們能夠識別所使用語言的版本。它不保證我們可以解釋它,當然也不保證它是我們優先使用的同一版本。這就是兼容性的用武之地。 “兼容性”是指能夠在XCM 版本中繼續解釋和表達我們自己的能力,這不是我們首選的版本。
如果我們希望能夠按照我們選擇的時間表升級我們的網絡及其XCM 版本,那麼這種兼容性就變得相當重要,因為我們可能希望與尚未升級或實際上已經升級的其他網絡進行通信。這可以分解為 向後兼容 和 向前兼容. 基本上,向後兼容性是升級後的系統在遺留世界中繼續運行的能力,向前兼容性是遺留系統在升級後的世界中繼續運行的能力。
在我們的例子中,我們希望兩者兼而有之,但存在實際限制:在新版本的XCM 提供以前版本中不存在的功能的情況下,期望舊系統能夠解釋這些消息是不現實的。這有點像試圖將“社交媒體”一詞翻譯成拉丁語,然後期望Julius Caesar 從表面上理解它。有些概念根本無法在遺留上下文中表達。
同樣,對XCM 的重大更改可能會導致功能被 移除 從它的概念模型。這種情況較少發生,但類似於將某些古老術語翻譯成現代等效術語的問題。有趣的是,該 “點”的古意 可能是這裡的一個例子(它曾經意味著一種相當特殊的金融禀賦形式)。
因此,新版本的XCM 被設計為 大多 與舊版本和新版本兼容,但通常會有XCM 消息,這些消息在替代上下文中根本沒有意義並且無法翻譯。
如前所述,我們確保所有獨立存在的消息都包含一個版本標識符。這意味著系統之間發送的消息或存儲在存儲中的消息。但它不包括所有消息、位置和資產——作為其他數據的一部分存在的數據不需要版本化,因為它的版本可以從它的上下文中推斷出來。
雖然版本識別和兼容性/翻譯有助於從舊網絡接收消息或將消息發送到新網絡,但是——單獨來看——在相反的情況下不太有用。這是因為從升級的網絡接收消息的傳統網絡本身沒有能夠將新XCM 轉換為它可以解釋的某種形式的邏輯——相反,該邏輯僅存在於具有轉換代碼的發送方用傳統術語重新表達新信息。
因此,發送網絡必須負責確保它發送的消息能夠被接收網絡解釋。具體而言,用於消息的XCM 版本不得高於接收網絡支持的XCM 版本。
出於這個原因,Polkadot 和Kusama 中繼鏈、Statemint、Statemine、Shell 和任何其他基於Substrate/Frame 的鍊及其XCM 引擎都保留了遠程鏈支持的XCM 版本的註冊表。每當這些鏈發送XCM 消息時,它首先會通過查詢其註冊表來確定將消息發送到哪個版本。它將消息轉換為較舊的發送方和接收方支持的XCM 版本。對於保持最新狀態的鏈,大多數情況下它們將是相同的、最新發布的版本,從而提供XCM 的完整功能集。
該註冊表通常由治理流程指定和升級,這有點繁瑣和乏味,尤其是隨著潛在目的地數量的增加。為此,引入了版本跟踪。
版本跟踪是XCM 版本控制故事拼圖中的最後一塊。它的功能是刪除跟踪潛在目的地鏈的XCM 版本所需的任何鏈外或治理流程。相反,該過程是自主和在鏈上發生的。
從本質上講,它的工作原理是允許一個網絡使用XCM 查詢另一個網絡以獲取它支持的最新版本的XCM,並在此更改時收到通知。來自此查詢的回复允許有問題的網絡填充和維護其版本註冊表,確保以最新的可理解版本發送消息。
具體來說,XCM中有3條有價值的指令: SubscribeVersion
,允許一個人要求另一個人通知它現在和更改時的XCM 版本; UnsubscribeVersion
取消該請求; 和 QueryResponse
,一種將一些信息從響應者網絡返回到發起網絡的通用方法。這是它們在Rust 中的樣子:
enum Instruction {
SubscribeVersion {
query_id: QueryId,
max_response_weight: u64,
},
UnsubscribeVersion,
/* snip */
}
所以 SubscribeVersion
需要兩個參數。首先, query_id
是類型 QueryId
,它只是一個整數,用於讓我們識別和區分返回的響應。所有導致發送響應的XCM 指令都有類似的方法來確保它們的響應可以被識別並相應地處理。第二個參數被稱為 max_response_weight
並且是一個 Weight
值(也是一個整數)表示回復返回時我們應該花費的最大計算時間。像 query_id
,這將被放入該指令生成的任何響應消息中,並且需要確保任何重量不可預測的可變重量成本在執行之前至少可以限制為最大值。如果沒有這個,我們將無法獲得回复消息可能需要解釋的時間上限,因此無法安排它執行。
UnsubscribeVersion
作為指令相當貧瘠,主要是因為一次只允許一個版本訂閱在給定位置處於活動狀態。這意味著取消只能通過原產地登記簿的內容來識別。