写点什么

波卡跨链通信源码探秘: 要素篇

作者:彭亚伦
  • 2022 年 6 月 30 日
  • 本文字数:5522 字

    阅读完需:约 18 分钟

波卡跨链通信源码探秘: 要素篇

Polkadot 最具特色的优势就是其跨链能力, 因此要深入波卡生态世界, 就必须要研究其跨链技术.


总的来说, 波卡跨链解决方案有以下几个特点:


  • 互操作性强

  • 优异的可扩展性

  • 超强的普适性

  • 异构跨链具备强一致性

  • 独有的共享安全机制, 安全性好


作为研究波卡跨链技术的第一步, 首先我们来看看跨链通信所涉及到的几个要素.


同任何通信系统一样, 要探究其运行机制, 我们大致需要了解以下几点:


  1. 通信的参与对象

  2. 通信过程中传递的内容

  3. 通信过程如何实施, 即通信协议


下面我就以上三个方面, 来对 Polkadot 的跨链通信机制做初步探索.

波卡跨链的参与对象

说到跨链通信参与对象, 你可能会觉得没啥讨论的必要, 因为所谓跨链通信, 顾名思义不就是“不同区块链”之间的通信嘛? 参与对象就是不同的区块链咯~


这么想当然也是没错, 但实际上波卡跨链的野心远不止于此.


波卡创世大佬 Gavin Wood 对波卡跨链的目标参与者, 定义为不同的 “consensus system” 即“共识系统”, 或者说 “consensus environment”即“共识环境”.


XCM aims to be a language communicating ideas between consensus systems


这里提到 XCM 即波卡跨链所用的交换语言, 下文我们再详细讲.


原话中的“consensus system"翻译过来即”共识系统" , 但是请注意, 这里的共识系统, 跟我们平时在区块链开发中所提的共识系统的含义是不同的.


我们在开发中的所说的共识系统指的是共识的机制或者共识算法, 也被称为**“共识引擎”**, 比如 PoW, PoS, DBFT, BABE, GRANPA 等,


而林嘉文大佬这句话里的共识系统, 则指的是基于某种共识引擎而运作的实体系统, 也就是说波卡跨链的参与对象是“一个基于某种共识机制而运行的系统”, 如:


  • 区块链

  • 智能合约

  • 转接桥

  • 甚至 pallets.


在波卡的跨链愿景中, 这些共识系统之间可以任意通信, 通信参与者所基于的共识引擎可以完全不同, 只需在通信中遵循一致的 Polkadot 跨链框架即可.


这种“跨共识”的通信解决方案, 是波卡区别于其它跨链解决方案的最特殊的地方.


比如“不同链上合约相互调用”问题: 比如一个部署在 Phala 链上的隐私计算智能合约, 需要调用另一个部署在 Crust 链上的存储合约, 在目前其它跨链方案下基本无法实现, 而波卡则可以做到.


波卡这种“跨共识”的方式, 应该说为开发者提供了无限种可能.

波卡跨链传递的内容

了解的波卡跨链通信中的参与者, 我们来看波卡跨链通信的内容, 或者说跨链沟通的语言. Polkadot 跨链通信, 或者说“跨共识”通信传递的内容是 XCM.

XCM

XCM, 全称 Cross-Consensus Message Format, 直译过来是 跨共识消息格式. 从名字就可以看到, 波卡为跨链通信设计的语言, 就是“跨共识”的.


XCM 起初是作为一种跨链通信消息格式而开发, 你可以简单理解 XCM 为类似于 Web2.0 传统开发中的 JSON 或者 Protobuf 格式.


然而经过多次迭代, 已经演变成一个通用的跨共识通信语言, 其普适性在每次迭代中都不断完善和提高. 最新的 V3 版已经可以做到让基于 Polkadot 的平行链与基于 Kusama 的平行链相互通信.


未来 XCM 也不会仅仅局限于波卡/substrate 生态内, 而是成为任意不同共识系统直接的通用通信语言. 后续我们会有单独文章详细探究 XCM, 这里我们仅仅通过 XCM 的源码来略窥一二.


XCM 目前正在运行的有 V0, V1 和 V2 三个版本, 其中 V0 版本即将废弃, 而新 V3 版本已经在今年情人节完成代码, 目前还处于测试分支里面, 截止本文写作的时间, V3 版 XCM 已经在进行 benchmark, 应该不久就会提交代码审计并最终部署.


在 V0 和 V1 版本中, Xcm 是一个名为Xcm<Call>enum, 为了便于阅读理解, 这里将代码做了精简处理:


#[scale_info(bounds(), skip_type_params(Call))]pub enum Xcm<Call> {  //...snippets  WithdrawAsset(MultiAssets),  ReserveAssetDeposited(MultiAssets),  ReceiveTeleportedAsset(MultiAssets),  //...snippets}
复制代码


而在 V2 和 V3 版本中, Xcm 则换成了一个Tuple Struct元组结构体:


#[scale_info(bounds(), skip_type_params(Call))]pub struct Xcm<Call>(pub Vec<Instruction<Call>>);
复制代码


如果我们仔细去看, 会发现 V2 版以后的 Xcm 元组结构体实际是一个 Vec 集合, 其集合子项元素则是一个名为Instruction<Call>的枚举, 再进一步观察这个enum的源码:


//....snippets#[scale_info(bounds(), skip_type_params(Call))]pub enum Instruction<Call> {  WithdrawAsset(MultiAssets),  ReserveAssetDeposited(MultiAssets),  ReceiveTeleportedAsset(MultiAssets),  //....snippets}
复制代码


看到什么? 是不是跟原来 V0 和 V1 版的 Xcm 长的差不多?


是的, 在 V2 和 V3 版本中, 原来的 Xcmenum被重新名为Instruction<Call>, 而整体的 Xcm 则扩展成一个元组结构体, 其本质则是一个 Vec 集合, 集合的元素子项都是将由原来的 Xcm 扩充成的Instruction<Call>枚举.


另外我们也可以看到, Instruction<Call>实际是定义了一系列执行动作指令; 这些指令定义了通信的具体内容, 比如WithdrawAsset指令是告诉origin方 withdraw 某些资源, 并将其放入持有寄存器(Holding Register)中 , 这里不用管其具体含义, 后续我们在 XCM 的专文中我们再详细介绍.


V3 版和 V2 版的Instruction<Call>数据结构定义是一样的, 仅包含的子项比 V2 版本多了一些.


结合上面的源码我们可以看出, 目前的 XCM 就是一个包含一系列连续指令的 Vec 集合; 同时也可以看出, 从 V0 到 V3, XCM 是在不断扩展中并完善中.


有指令就必须要有执行指令的地方, XCVM 的执行环境是 XCVM 虚拟机.

XCVM

XCVM, 全称 Cross-Consensus Virtual Machine, 即跨共识虚拟机, 这里是 XCM 消息实际执行的地方, 在 Polkadot 的源码中, 这一部分的名称是xcm-executor, 其核心定义是一个XcmExecuteor的结构体:


pub struct XcmExecutor<Config: config::Config> {  pub holding: Assets,  pub origin: Option<MultiLocation>,  pub original_origin: MultiLocation,  pub trader: Config::Trader,  /// The most recent error result and instruction index into the fragment in which it occurred,  /// if any.  pub error: Option<(u32, XcmError)>,  /// The surplus weight, defined as the amount by which `max_weight` is  /// an over-estimate of the actual weight consumed. We do it this way to avoid needing the  /// execution engine to keep track of all instructions' weights (it only needs to care about  /// the weight of dynamically determined instructions such as `Transact`).  pub total_surplus: u64,  pub total_refunded: u64,  pub error_handler: Xcm<Config::Call>,  pub error_handler_weight: u64,  pub appendix: Xcm<Config::Call>,  pub appendix_weight: u64,  _config: PhantomData<Config>,}
复制代码


这个XcmExcutor结构体即 XCVM 的主体, 其上实现了一个主要的方法叫 execute, 用来具体执行 XCM 的每条指令:


impl<Config: config::Config> XcmExecutor<Config> {  //...snippets  pub fn execute(&mut self, xcm: Xcm<Config::Call>) -> Result<(), ExecutorError> {    //...snippets    if let Err(e) = self.process_instruction(instr) {      *r = Err(ExecutorError { index: i as u32, xcm_error: e, weight: 0 });    },        //...snippets}
复制代码


execute方法内部则是调用process_instruction函数来具体处理每一个 XCM 指令, 因此我们查看process_instruction方法源码:


fn process_instruction(&mut self, instr: Instruction<Config::Call>) -> Result<(), XcmError> {  match instr {    //...snippets   TransferReserveAsset { mut assets, dest, xcm } => {    let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?;    // Take `assets` from the origin account (on-chain) and place into dest account.    for asset in assets.inner() {      Config::AssetTransactor::beam_asset(asset, origin, &dest)?;      }    let ancestry = Config::LocationInverter::ancestry();    assets.reanchor(&dest, &ancestry).map_err(|()| XcmError::MultiLocationFull)?;    let mut message = vec![ReserveAssetDeposited(assets), ClearOrigin];    message.extend(xcm.0.into_iter());    Config::XcmSender::send_xcm(dest, Xcm(message)).map_err(Into::into)    }  //...snippets  }  //...snippets},
复制代码


从源码中, 我们不难看出对于每一个 XCM 指令的处理, XCVM 都是拿到其参数后直接进行处理执行.


这种直接处理指令的虚拟机构架, 就是所谓的“基于寄存器”的虚拟机构架; 区别于 bitcoin script 等需要使用`数据栈’做数据中转场所然后再处理的方式, 也就是所谓“基于栈”的虚拟机构架.


XCM 不是图灵完备的语言, 同样 XCVM 不是图灵完备的虚拟机. 采用非图灵完备的方式有诸好处, 其中之一是能让 XCVM 在处理和执行 XCM 的保持高效, 从而保证大量消息能够及时有效性传递和被处理.

波卡跨链的通信协议

XCM 作为跨共识的通信语言, 需要一个通信实施机制来传递交换它, 这个通信过程机制, 就是所谓“Protocol”协议. 我们日常所见的 HTTP, FTP, TCP, UDP 这些都是类似的通信协议.


一般来说, 一个 Ptotocol 会定义通信过程中的具体步骤, 例如如何发起, 如何接收, 如何验证, 第一步做什么, 第二步做什么等等.


波卡跨共识的通信 Protocol 目前分为以下几种:


  1. XCMP, 全称 Cross-Chain Message Passing, 即跨链消息协议, 该协议使用 Merkle 树来确保通信安全验证, 并基于此有一个简单的排队系统; 在 XCMP 协议执行过程中, 中继链仅仅将跨链交易相关元数据(metadata)以哈希的形式储存, 因此占用的资源和执行效率极高. XCMP 有两种形式:

  2. 直接通信, xcm 消息数据在平行链之间直接传输, 对于中继链来说仅仅可以只有一个“知会”的动作, 这种方式可扩展性极高, 目前还在开发之中.

  3. 中继通信, 即通过中继链来传递, 具体的就是通过 VMP 来实现, VMP 的详细见下文.

  4. XCMP-Lite, 即 HRMP(Horizontal Relay-routed Message Passing 水平中继路由消息传递协议). 由于 XCMP 中平行链直接通信的模式还在开发中, 因此目前在 Polkadot 和 Kusama 中继链上, 使用的是 HRMP 作为现阶段的过渡.从以上可以看出, HRMP 的消息传递中, 数据是直接以 Vec

  5. HRMP 与 XCMP 使用相同的接口和功能, 但最大的区别在于 HRMP 将所有消息内容都储存在中继链中, 因此占用的资源和执行效率远远不如 XCMP. 以下是 HRMP 源码中的 2 个消息结构体代码, 同样为了便于理解, 这里做了精简处理:


    pub struct InboundHrmpMessage<BlockNumber = crate::BlockNumber> {      pub sent_at: BlockNumber,      pub data: sp_std::vec::Vec<u8>,    }        pub struct OutboundHrmpMessage<Id> {      pub recipient: Id,      pub data: sp_std::vec::Vec<u8>,    }
复制代码


这种近似“明文”的方式存储全部消息数据的.
复制代码


  1. VMP (Vertical Message Passing 垂直消息传递协议) , VMP 其实是一个统称, 用来与 HRMP 的“水平”相对照; HRMP 的 H 即“Horizontal 水平”, 是指平行链与平行链之间的沟通; 而 VMP 的 V 即“Vertical 垂直”则主要指中继链和平行链之间的通讯, VMP 包含 2 个协议:

  2. UMP (Upward Message Passing 上行消息传递协议) : 由平行链往中继链传递消息, 称为“向上传递”, 由于各个平行链向中继链发送的消息的内容可能千差万别, 因此UMP模块主要提供了一个名为 UmpSinktrait来统一约束. 使用 trait 来统一约束的好处, 是尽量给予各个平行链灵活自由.


    pub trait UmpSink {      fn process_upward_message(        origin: ParaId,        msg: &[u8],        max_weight: Weight,      ) -> Result<Weight, (MessageId, Weight)>;    }        impl UmpSink for () {      fn process_upward_message(        _: ParaId,        _: &[u8],        _: Weight,      ) -> Result<Weight, (MessageId, Weight)> {        Ok(0)      }    }
复制代码


- **DMP (Downward Message Passing 下行消息传递协议**) : 由中继链向平行链传递消息, 称为“向下传递”; 这个就可以相对地统一格式, 因此源码中定义了一个`DownwardMessage`类型, 本质是一个`Vec<u8>`类型, 并用`InboundDownwardMessage`结构体包裹起来, 以便放入队列中去处理
复制代码


    pub type DownwardMessage = sp_std::vec::Vec<u8>;        #[derive(Encode, Decode, Clone, sp_runtime::RuntimeDebug, PartialEq, TypeInfo)]    #[cfg_attr(feature = "std", derive(MallocSizeOf))]    pub struct InboundDownwardMessage<BlockNumber = crate::BlockNumber> {      pub sent_at: BlockNumber,      pub msg: DownwardMessage,    }
复制代码


以上就是目前 Polkadot 生态所使用的跨链通信协议, 但从 XCM 的“跨共识”愿景来猜测, 未来也许会有类似 XCM 兼容的协议, 用于非波卡系的区块链网络和波卡系之间进行通信, 甚至两个非波卡系网络之间, 也可以采用 XCM 兼容协议进行通信.

总结

至此, 我们把波卡跨链机制的几个核心要素初步探索了一番, 从中我们可以看到, 波卡的的跨链或者说跨共识设计, 其目标是比较宏大的, 不再局限于波卡生态内部, 而是试图囊括整个去中心化区块链生态, 并且在不断迭代发展中.


由于波卡跨链优秀的构架机制, 极具特色的“共享安全”体系, 使得波卡系目前是业内最为出色的跨链生态, 其上已经有众多各具特色的平行链接入; 下一篇文章, 我们就来探索一下波卡跨链的构架设计.

参考资料

  1. https://wiki.polkadot.network/docs/learn-crosschain

  2. https://research.web3.foundation/en/latest/polkadot/XCMP/index.html

  3. https://cointelegraphcn.com/news/is-cosmos-and-polkadots-cross-chain-the-same-thing ?????

  4. https://medium.com/polkadot-network/xcm-the-cross-consensus-message-format-3b77b1373392

  5. https://github.com/paritytech/xcm-format

发布于: 刚刚阅读数: 3
用户头像

彭亚伦

关注

A Rustacean and Substrate Evangelist 2021.01.25 加入

A Rustacean, and Substrate Evangelist, member of CRVA (RISC-V)

评论

发布
暂无评论
波卡跨链通信源码探秘: 要素篇_Substrate_彭亚伦_InfoQ写作社区