CKB/CKB-VM/交易结构与交易验证逻辑
在上一篇里, 我们说 Cell 像一个个上锁的抽屉: 想更新状态, 你不能修改旧抽屉, 只能销毁旧的, 创建新的.
这一篇我们把镜头拉近到交易本身: 在 CKB 里, 一笔交易其实就是一次有约束的 Cell 的状态转换. 如果我们把 Cell 看成是状态的容器, 那么交易就是状态的转换操作请求. 当 CKB 节点收到一笔交易时, 它要做的就是验证这次转换是否合法.
交易的本质
一笔 CKB 交易最重要的是描述它在状态机里完成了什么动作. 它做的事情非常朴素:
- 指出哪些旧 Cell 将被销毁.
- 说明将诞生哪些新 Cell.
- 给出一组证明材料, 证明这次转换被授权且满足规则.
前两件事回答状态怎么变, 第三件事回答"我有权限这么做".
如果只看抽象层, 交易可以写成这样:
Old Cells --> [Transaction + Proof] --> New Cells
交易的本质: Cells 到 Cells 的受约束转换, 因此也可以粗略将 CKB 区块链看作是一个分布式状态机.
例: Ada 有一个 900 CKB 的 Cell, 想转给 Bob 600 CKB. 帮他创建一笔交易, 描述它将销毁哪些 Cell, 创建哪些 Cell.
答: 交易的输入是这个 900 CKB 的 Cell, 输出是一个给 Bob 的 600 CKB 的 Cell 和一个给自己的 300 CKB 的找零 Cell.
+---------------+ +---------------+
| Capacity: 900 | ---+---> | Capacity: 600 |
| Lock: Ada | | | Lock: Bob |
+---------------+ | +---------------+
| +---------------+
+---> | Capacity: 300 |
| Lock: Ada |
+---------------+
证明
并非所有的 Cell 转换都是合法的. 你不能随便销毁一个 Cell, 也不能随便创建一个 Cell. 你必须提供证明来让节点相信这次转换是被授权的.
正如前面提到的, 每一个 Cell 里都有一个 Lock Script, 这个脚本定义了谁有权花掉这个 Cell. 当你想销毁一个 Cell, 你必须满足它的 Lock Script 的要求. 这通常意味着你需要提供一些签名数据. Cell 里还有一个可选的 Type Script, 定义了 Cell 中数据的约束. 如果你想销毁或者创建一个带 Type Script 的 Cell, 你也需要满足 Type Script 的要求.
总结来说:
- 要销毁一个 Cell, 你必须满足它的 Lock Script 和 Type Script.
- 要创建一个 Cell, 你只须满足它的 Type Script.
这些额外证明材料通常被附带在交易的证明(witness)字段里, 以供节点验证时使用. 理论上来说, 证明可以是任意的字节数据, 但在实践中, 通常会有一个约定的结构, 以便于组织签名等信息.
通常情况下, 我们期望一个 Lock Script 包含签名验证逻辑, 以确保只有合法的私钥持有者才能花掉这个 Cell. Type Script 则包含一些业务逻辑, 比如说限制这个 Cell 只能被特定的交易模式使用, 或者说这个 Cell 里必须存储某种特定格式的数据.
脚本的验证环境
CKB 语境里脚本(Lock Script 和 Type Script)实际上是指在 CKB-VM 虚拟机上运行的 RISC-V 格式的二进制文件. CKB-VM 虚拟机会在一个特定的环境里执行这些脚本, 这个环境提供了一些系统调用接口, 让脚本能够访问交易数据, 以及进行一些基本的计算. 当程序执行完毕后, 它会返回一个状态码. 如果状态码是 0, 就表示验证通过; 如果是非零, 就表示验证失败.
因此只有:
- 交易的所有输入 Cell 的 Lock Script 都返回状态码 0 且
- 交易的所有输入 Cell 和输出 Cell 的 Type Script 都返回状态码 0
交易才会被认为是合法的, 可以被打包进区块.
在下一篇文章中, 我们将通过一个最简洁的代码示例, 展示如何实际编写和部署 CKB 脚本.