Substrate 跨链技术源码级探索: XCVM 的概览
接上一篇关于 Subtrate 跨链几个要素组件的初步探索, 这篇文章我们来解码一下 XCVM, 在此之前, 我们需要先知道 XCVM 执行的是 XCM 消息, 所以先来稍微复习一下 XCM 消息的格式.
前提: XCM 消息格式和版本
XCM 从开始开发到现在, 已经近 2 年多的时间, 期间版本从 v0 到如今正式上线的 V2, 以及正在完善的 V3, 经历了不少变化, 各版本之间是一种累积叠加, 又大幅度改动的过程.
尤其是在 V1 到 V2 的过程中, 其源码演变比较厉害, 在之前的文章中, 我们看到:
在 V0 和 V1 版本中, Xcm 是一个名为Xcm<Call>
的enum
, 为了便于阅读理解, 这里将代码做了精简处理:
而在 V2 和 V3 版本中, Xcm 则换成了一个Tuple Struct
元组结构体:
如果我们仔细去看, 会发现 V2 版以后的 Xcm 元组结构体实际是一个 Vec 集合, 其集合子项元素则是一个名为Instruction<Call>
的枚举.
因此, 如果调用 XCM 时, 就必须标明其版本, 版本的标识如下:
version: u8
message
: the message, 必须在 version 确定的情况下有效Version: u8 = 2
,message: Vec<Instruction>
, 因此, 一个完整的 XCM 消息就是 2 byte 的版本标识+SCALE
编码的XCVM
指令序列一个 XCM 的最终执行效果, 取决于两个
XCVM
是否恰当地初始化两个寄存器:Original Programme
表示消息 和Original Origin
表示消息来源
XCVM 寄存器
XCVM 作为执行 XCM 消息指令的虚拟机, 本质上是一个寄存器架构的非图灵完备的虚拟机.
因此我们需要先看一下XCVM
的寄存器设计; 作为 XCM 消息格式的执行器, XCVM 的寄存器有着强约束, 不能被随意设置, 而是应该放置特定相关的值, 而且只能在特定场景和特定规则下才能改变.
以下是各个寄存器的基本作用和含义:
Programme
存放类型: Vec<Instruction>
初始值: Original Programme
的值
作用: 当前正在执行的XCVM
指令, 当全部指令执行完毕或者错误发生时才可能改变
Programme Counter
存放类型: u32
初始值:0
作用: 当前正在执行的指令在Programme
寄存器中的索引数,每当成功执行一个指令则+1, Programme
寄存器更新时重置为 0
Error
存放类型: Option<(u32, Error)>
初始值: None
作用: 最新的指令程序执行错误, 仅当程序出错时才设置, 可以主动清除.其中两个子值是错误发生时Programme Counter
值和错误类型 Error Handler
Vec<Instruction>
空列表 如果错误发生时, 需要执行的指令, 当程序发生错误, 则清空本寄存器, 并将其值放到Programme
中执行
Appendix
存放类型: Vec<Instruction>
初始值: 空列表
作用: 附加执行的指令, 当程序执行完毕, 或者出错后Error Handler
也被清空后, 就会值放到Programme
里面并清空本寄存器
Origin
存放类型: Option<MultiLocation>
初始值: 内值设为 Original Origin
作用: 当前程序的授权地址, 可以为None
(即没有授权), 或者严格为内部地址(限定的授权子集)
Holding Register
存放类型: MultiAssets
初始值: 空集(无资源)
作用: 当前程序控制下但没有上链的资源, 可当作还没有使用的资源(unspent
)的非持久化
寄存器, 类似资源的临时存放区域
Surplus Weight
存放类型: u64
初始值:0
作用: 当前程序预估值的多余值, 包括没有执行的指令权重, 没有生效的错误处理权重, 和指令成功执行的保守预估权重
Refunded Weight
存放类型: u64
初始值: 0
作用: Surplus Weight
中已退回的部分, XCM
平台没有执行费用因此没有使用此寄存器
XCVM
基本执行流程
XCVM
是一个fetch-dispatch
"抓取-分发" 循环执行的状态机, 其步骤如下:
Programme
寄存器为空时, 停机从
Programme
中拿到Programme Counter
索引值指向的指令如果指令存在, 则执行该指令, 并检查执行结果:
如果没有错误, 代表执行成功, 则将
Programme Counter
加 1若发生错误, 则
设置
Error
寄存器的值(错误发生的索引和错误类型)将
Programme Counter
置为0
将
Programme
的内容替换为Error Handler
的内容, 并清空Error Handler
如果指令不存在, 则:
Programme Counter
置为0
将
Programme
的内容替换为Appendix Register
的内容将
Error Handler
中内容的预估权重, 加到Surplus Weight
中去清空
Appendix Register
和Error Handler
与普通的
fetch/dispatch
机制不同的是,XCVM
的执行中有Error Handler
和Appendix
寄存器, 需要注意的是:Error
寄存器在程序执行完毕后不会被清空, 这样可以让Appendix
寄存器可以使用Error
中保留的值无论程序执行成功与否,
Error Handler
寄存器都会被清空, 这样保证前一个程序的错误处理逻辑不会影响后续的代码执行
注意
指令类型
Instruction
是个标签联合类型(Rust 中的enum
), 每个指令由SCALE
编码的以0
为初始索引的指令列表中的位置+其操作构建而成, 需要注意Origin
术语在此处, 表示Origin
寄存器中值代表的位置, 因此controlled by the Origin
意思等同"由Origin
寄存器中的值代表的地址控制Holding
术语表示Holding
寄存器中的值, 因此reduce Holding by the asset
等于"从Holding
寄存器中减少资产Appendix
和Error Handler
也是类似on-chain
术语, 应该理解为"在本地共识系统下的持久状态(within the persistent state of the local consensus system)", 不应该仅仅只当区块链的作为本地共识场景下对待.Compact
,Weight
和QueryId
类型应该理解为SCALE
中定义的压缩整数类型
关于 XCVM 的寄存器和指令执行流程就介绍到这里, 下一篇我们来探索具体的 XCM 指令含义和使用.
版权声明: 本文为 InfoQ 作者【彭亚伦】的原创文章。
原文链接:【http://xie.infoq.cn/article/4a61bbd1949f20d8b19a36443】。文章转载请联系作者。
评论