写点什么

Substrate 跨链技术源码级探索: XCVM 的概览

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

    阅读完需:约 7 分钟

Substrate 跨链技术源码级探索: XCVM的概览

接上一篇关于 Subtrate 跨链几个要素组件的初步探索, 这篇文章我们来解码一下 XCVM, 在此之前, 我们需要先知道 XCVM 执行的是 XCM 消息, 所以先来稍微复习一下 XCM 消息的格式.

前提: XCM 消息格式和版本

XCM 从开始开发到现在, 已经近 2 年多的时间, 期间版本从 v0 到如今正式上线的 V2, 以及正在完善的 V3, 经历了不少变化, 各版本之间是一种累积叠加, 又大幅度改动的过程.

尤其是在 V1 到 V2 的过程中, 其源码演变比较厉害, 在之前的文章中, 我们看到:

在 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>的枚举.


因此, 如果调用 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 RegisterError Handler

  • 与普通的fetch/dispatch机制不同的是, XCVM的执行中有Error HandlerAppendix寄存器, 需要注意的是:

  • 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寄存器中减少资产

  • AppendixError Handler也是类似

  • on-chain术语, 应该理解为"在本地共识系统下的持久状态(within the persistent state of the local consensus system)", 不应该仅仅只当区块链的作为本地共识场景下对待.

  • Compact, WeightQueryId类型应该理解为SCALE中定义的压缩整数类型


关于 XCVM 的寄存器和指令执行流程就介绍到这里, 下一篇我们来探索具体的 XCM 指令含义和使用.

发布于: 15 小时前阅读数: 8
用户头像

彭亚伦

关注

A Rustacean and Substrate Evangelist 2021.01.25 加入

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

评论

发布
暂无评论
Substrate 跨链技术源码级探索: XCVM的概览_Substrate_彭亚伦_InfoQ写作社区