On Client-Side-Validation of RGB++ Tx Sequences

For a BTC Layer2 protocol based on CSV to support the client-side-validation, it needs to achieve the following:

  1. All data requiring verification, namely the UTXO transaction sequence of Layer2, must be able to propagate among users or be retrieved from the DA layer.
  2. For pure transactions that do not involve jumps, the retrieved UTXO transaction sequence must be unambiguously bound by commitments on BTC.
  3. For transactions jumped to Layer2, the resistance to double spending of the UTXO transaction sequence is guaranteed by Layer2 until it jumps back to BTC.

For an RGB++ transaction, it includes:

  • version: Indicates the version used by the transaction.
  • cell_deps: A transaction can depend on other transaction outputs without consuming them, bound by the transaction hash and index of another RGB++ transaction, mostly used by script_code using.
  • header_deps: A transaction can depend on a block header of Layer 2 (i.e., the CKB chain) and read some information contained in the block header.
  • inputs: Inputs for this RGB++ transaction, bound by the transaction hash and index of another RGB++ transaction, i.e., depending on which output of a transaction. Due to the isomorphism binding with BTC UTXO, a cell can only be spent once to achieve resistance to double spending.
  • outputs: Outputs for this RGB++ transaction, can be consumed by other RGB++ transactions.
  • outputs_data: For each output of the RGB++ transaction, it stores the state of the corresponding output.
  • witnesses: Witness data of this RGB++ transaction, containing signature-related information.

Which parts of RGB++ should be covered by commitments on BTC?

First, the version, inputs, outputs, and outputs_data must be included, but the BTC transaction information bound in this RGB++ transaction needs to be removed to avoid infinite recursion.

Witnesses do not need to be constrained by commitments because, due to segregated witness, inputs and outputs determine the effect of the transaction deterministically. Therefore, as long as the witness data makes the transaction valid, it is sufficient. Thus, witnesses are constrained by the necessity for transaction validity.

Should cell_deps and header_deps be included? I believe they should, for two reasons:

  1. Suppose an upgradable script is used to support client-side verification. In that case, besides the UTXO sequence of the state, the upgrade history of the referenced scripts should also be retrieved. The cell_dep constrains which version of the script is used. Since cell_dep contains the transaction hash, it effectively constrains the script used, enabling verification without requiring CKB.
  2. Suppose a block header is referenced, and information such as timestamp or difficulty is used. In that case, not binding header_dep will render the corresponding transaction sequence unverifiable without CKB.

There is also a field in inputs called since, and users should determine whether the corresponding since is legal based on local time.

Based on the above analysis, the process of exporting a verifiable RGB++ transaction sequence should be as follows:

  • Trace RGB++ transactions based on Inputs until the genesis and verify the correctness of each transaction. Note that Capacity cells without type_scripts do not need to be traced.
  • Obtain all cell dependencies referenced by cell_dep. If a cell depended on has a type script, trace the transaction history of that cell until the transaction creating that type, and verify the correctness of every transaction in the sequence. This is to ensure that upgradable scripts can be fully verified on the client-side without the need for CKB. Due to most script codes are reused, user can cache them and avoid useless repeated verification.
  • If the transaction has header_dep, obtain the corresponding header.
  • Verify all parts committed on BTC are consistent with the commitments.

The above process is carried out in a recursive-like logic until there is no more data to obtain, and all transactions become resolved_transactions, thus enabling complete off-chain verification.

一个基于客户端验证的 BTC Layer2 协议为了支持客户端验证机制,需要做到以下几点:

  1. 与当前接收到状态的所有需要验证的数据,即 Layer2 的 UTXO交易序列,可以在用户之间传播或者从DA层检索。
  2. 对于不涉及 jump的交易,检索出来的UTXO 交易序列,必须可无歧义地被 BTC 上的承诺所约束。
  3. 对于 jump 至 Layer2 的交易,对于仅发生在 Layer2 的 UTXO 交易序列,其抗双花由 Layer2 保证,直到 Jump 回 BTC 为止。

对于一笔 RGB++ 交易,其包含的部分为:

  • version:指明交易使用的版本
  • cell_deps:一个交易可以依赖另外的交易碎片而不消费它,其和一个 RGB++交易的交易哈希以及 index 绑定,即依赖某笔交易的第几个输出。
  • header_deps:一个交易可以依赖一个 Layer2 (即 CKB 链) 的区块头,并读取区块头里包含的一些信息。
  • inputs:为该 RGB++ 交易的输入,其和一个 RGB++交易的交易哈希以及 index 绑定,即依赖某笔交易的第几个输出,由于与 BTC UTXO 的同构绑定,一个 cell 只能被消费一次,从而实现抗双花。
  • outputs:为该 RGB++ 交易的输出,可以被其他 RGB++ 交易消费。
  • outputs_data:对于每个 RGB++ 交易的输出,其存储了对应输出的状态。
  • witnesses:该 RGB++交易的见证数据,包含有签名相关的信息。

首先,RGB++ 的哪些部分应该被 BTC 上的commitment所涵盖呢?首先,version,inputs 以及 outputs 和 outputs_data 是必须包含在内的,但是需要去除该 RGB++ 交易中,绑定的 BTC 交易信息,否则这里会出现无限递归。

Witnesses 无需被承诺约束,因为隔离见证的缘故,inputs和outputs已经确定性地决定了交易的效果,那么无论见证数据是什么,只要可以使得交易验证通过即可,所以 Witnesses 是被交易有效性的必然要求所约束的。

那么 cell_deps 和 header_deps 是否需要被包含在内呢?我认为是需要的,原因有两点:

  1. 假设使用了可升级脚本,为了支持客户端验证,那么除了状态的UTXO序列,对应引用的脚本的升级历史也应该被检索出来,并且由 cell_dep 约束可升级脚本到底使用了哪个版本的脚本,由于 cell_dep 包含交易哈希,其实已经约束了其使用的脚本,从而可以在不需要 CKB 的情况下完成验证。
  2. 假设引用了某个区块头,并使用了里面的时间戳或者难度值之类的信息,那么不绑定 header_dep 将使得对应的交易序列无法在脱离 CKB 的情况下得到验证。

在input中还存在一个字段即since,用户应根据本地时间来判定对应的since是否合法。

经过以上分析,可知导出一个可验证的 RGB++ 交易序列的流程应如下所示:

  • 根据 Inputs 追溯 RGB++ 交易,直到起始,验证每一笔交易的正确性。注意,对于没有类型脚本的Capacity cell 无需追溯。
  • 获取所有 cell_dep 依赖的 cell,如果被依赖的某个cell具有类型脚本,则追溯该 cell 的交易历史,直到该type被创建的交易为止,并验证整个序列每一笔交易的正确性,这是为了保证可升级脚本在链下可完全在客户端验证。由于大部分交易使用的脚本代码是复用的,所以这部分可以直接缓存在本地,无需重复验证。
  • 如果交易存在 header_dep,获取对应的 header。
  • 验证所有在 BTC 上进行承诺的部分与承诺一致。

以上过程以类似递归的逻辑进行,直到不再存在需要获取的数据,所有的交易将成为 resolved_transaction,故可以在链下完全脱链验证。

2 Likes

要完全做到链下验证记录下 cell deps 和 header deps 也不够。

除了完整的交易,交易的发生时间也很重要,比如交易提交时的 Soft/Hard Forks 的状态也决定了交易的执行结果。如果某个 Hard Fork 修复了某个合约的漏洞,在 Hard Fork 之后就不能再利用这个漏洞了,但是完全脱链的话,可以一直伪造 Hard Fork 之前 CKB 交易,并使用这些 CKB 交易生成 BTC Commitment,这样就可以一直利用漏洞。

除了 Forks,交易升级也是一样。如果是 CKB 链上验证,合约升级的时间点是可以确认的,一旦升级就只能用新合约执行。但是完全链下,也可以一直伪造使用老合约的 CKB 交易。

综上,要实现完整的脱链验证,Commitment 还必须包含下面的内容:

  1. 除了完整交易链的快照,还必须把对应的 Soft/Hard Forks 事件也记录下来
  2. 要对难度进行采样,通过类似 light client 协议进行 PoW 验证提高伪造交易提交时间的成本

合约升级可以使用哪个版本已经被cellDep里的outpoint所约束了。

可以在 Bitcoin Commitment 里就使用没有上链的 CKB tx。

比如 CKB 合约升级前有个漏洞可以 1 token 变 2 tokens,但是在没人发现和利用的时候就升级堵住了。但是依然可以伪造未上链 tx 交易使用老的合约做 dep cell,这样的资产如果对方是依赖 CKB 链上的话,会发现历史上有 CKB tx 不在链上过不了验证,但是如果对方是完全链下的话,整个运行历史交易也是能通过的。

客户端理应不接受这个交易序列,即从某个BTC高度开始,后续使用的版本应该是新版本。而且客户端验证其实只使用了ckb-vm和一些基础的syscall,script中也绑定了vm的版本(很难受硬分叉影响),出问题的概率是很低的。