附录:CoBuild-Only or CoBuild+Legacy?
CKB script 现在有两种输入数据 (witness) 编码模式:
- Cobuild 模式:使用本文定义的
WitnessLayout
格式读取数据 - Legacy 模式:使用
WitnessArgs
格式读取数据
CKB 交易只关心包含的 scripts 和 witnesses 是否能验证通过,并不关心这些 scripts 是如何解析 witnesses 的。这意味着一个交易里面使用不同输入数据编码的 scripts 可以共存。
一个 CKB script 也可以做到两种模式都支持:例如优先寻找 WitnessLayout
格式读取数据,发现WitnessLayout
类型的 witness 缺失时,再从 WitnessArgs
格式中读取数据。
通常我们讨论 CoBuild 模式或者 Legacy 模式时,是指某一个 script 在已经确定了要用哪种输入数据格式后所处的某一个模式。此时这个 script 已经确定:要么只从 WitnessLayout
中读取数据(CoBuild 模式),要么只从 WitnessArgs
中读取数据(Legacy 模式)。
但是在下文中谈到 “一个 script 支持 CoBuild 模式”,是指一个 script 可以运作在 CoBuild 模式下(而不是“只能”或者“已经确定”运作在 CoBuild 模式下),从 WitnessLayout
中读取数据。与此对应的,“一个 script 支持 legacy 模式”,是指这个合约可以运作在 Legacy 模式下(而不是“只能”或者“已经确定”运行在 Legacy 模式下),从 WitnessArgs
中读取数据。
Lock Script
在过渡时期(现在直到 CoBuild 被广泛使用成为默认之前),我们不建议一个 lock script 只支持 CoBuild 模式而不支持 Legacy 模式。
Lock script 要同时支持 CoBuild 模式与 Legacy 模式并不复杂。它们的差异点只在 signing message hash
的局部计算上。除此之外,一个架构良好的 lock script 完全可以在两种模式下共享其余几乎所有的验签代码。
如果一个 lock script / 钱包希望尽可能支持尽可能多的 CKB apps,那么同时支持 CoBuild 模式以及 Legacy 模式是最优解。
Type Script
Type script 的考量点与 lock script 有一些不同。
不考虑 OTX 中一些极端情况,一个只支持 CoBuild 模式的 lock script,是无法与一个只能从 WitnessArgs
类型的 witness 中读取输入数据的 type script 在一个 cell 中一起使用的。
如果反过来呢?一个只支持 CoBuild 模式的 type script,可不可以与一个只能从 WitnessArgs
类型的 witness 中读取签名的 lock script(比如创始块中的单签多签)一起使用?答案是可以。
让我么看下面这个例子:
假设这里 I1 和 I2 都使用了创始块中的单签 lock,同时 I1 中使用了一个只支持 CoBuild 模式的 type script。一个有效的交易可以使用如上布局:witness #0 和 witness #1 使用 WitnessArgs
类型,witness #2 使用 CoBuild 模式的 WitnessLayout::SighashAll
类型,包含 I1 中 type script 所需要的 Action
。
按照传统意义上 lock script 的 convention,虽然 I1 和 I2 使用的 lock script 不能解析 witness #2 所使用的 WitnessLayout
格式,但是这里的 lock script 依然会把 witness #2 的全部数据签入签名。同时,I1 所使用的 type script 可以从 witness #2 中解析 Message
,并找到对应自己的 Action。完成校验。
由此可以看出,只支持 CoBuild 模式不支持 Legacy 模式的 type script 不会有兼容性问题。对于 type script 来说,可以自行选择是只支持 CoBuild 模式,还是两种模式都兼容。我们的建议是实现 type script 时默认只支持 CoBuild 模式,有特别需要再考虑兼容 Legacy 模式。
Action 可能不存在
大部分情况下,CoBuild 交易中都会有 WitnessLayout::SighashAll
witness,其中包含 Message
,也包含当前交易用到的 type script 所对应的 Action
s。但需要注意的是,CoBuild 协议没有要求 WitnessLayout::SighashAll
必须存在。
假设有一个 type script 只支持 CoBuild 模式,它需要面对的一个问题是:它是否应该能够处理因为 WitnessLayout::SighashAll
不存在,导致当前交易中没有对应自己的 Action
的情况呢?
对于这个问题,不同的 type script 需要根据自己的需求做决定。虽然 CoBuild 协议上没有禁止 Action
不存在的情况,但是 type script 可以选择在自己需要的 Action
不存在时,是直接报错退出,还是忽略 Action
继续校验。
应当指出的是,对于大部分场景,Action
的存在可以大大简化链上校验逻辑。不是所有的场景都能通过识别交易而(容易地)反向分析出输入数据。
因此对于 type script, 我们建议增加一条校验规则:一定要能在交易中找到所需的 Action
才能继续执行。
NervosDAO Type Script
NervosDAO type script 对于 CoBuild 流程来说是一个非常特殊的例子,它的特别之处在于其 type script 代码是以一个无法花费的 lock (lock hash = 0x0000…, all zero) 部署在创世块中的,这意味着 NervosDAO type script 无法通过硬分叉之外的方法升级。
如果想要在不做硬分叉不升级 NervosDAO type script 的情况下让它能兼容 CoBuild 流程,需要额外的特殊处理。一种做法是,在支撑 CoBuild 工具中 hardcode 针对 NervosDAO 的特殊逻辑,如果发现一个交易是 Nervos DAO 交易,就先做一些预处理:
- 如果这个交易不包含 Nervos DAO
Action
,CoBuild 工具应该利用 raw tx data 构造一个可以用于前端呈现的 Nervos DAOAction
。 - 如果这个交易包含 Nervos DAO
Action
,CoBuild 工具应该对这个 Nervos DAOAction
进行校验,在前端实现本应该是 Nervos DAO type script 要做的工作。
注意前面说过:在过渡时期(现在直到 CoBuild 被广泛使用成为默认之前),我们不建议一个 CKB lock script 只支持 CoBuild 模式而不支持 Legacy 模式。但由于 Nervos DAO type script 的特殊性,我们建议对于所有 Nervos DAO 操作,无论一个 lock 支持 CoBuild 模式与否,都应该只生成使用 WitnessArgs
类型的交易。即使有部分操作因为 Nervos DAO 本身不需要读取 witness,可以用 CoBuild 来做,也不应该用 CoBuild 来做,这样把所有情况都统一成用 WitnessArgs
,简化实现。
在这个基础上,等未来某一天 Nervos DAO type script 由于某个原因升级时(目前没有明确计划),再通过升级 Nervos DAO type script,实现对 CoBuild 的完全支持。