Dynamic Fine-Grained Cobuild OTX Design
本方案在基于原本的 Cobuild 方案之上,对其进行了相关修改和补充,如需理解本方案,请先阅读:
本方案主要聚焦于简单实现 OpenTransaction 的动态性,从而在 UTXO 模型上解决 Intent 表达,带状态的合约调用模拟等功能。
DEMO(进行中): ckb-cobuild-poc (github.com)
首先,对于 OTX 的数据结构,本方案进行了以下改动。
// 一个完整的 CKB Transaction 中只能包含唯一的一个 OtxStart 结构,用于表示当前 CKB Transaction
// 中,连续出现的 Otx 的开始。OtxStart 中标记在 input / output cells 中,以及
// cell / header deps 中,第一个属于 Otx 的结构的位置。这里不需要标记 witness 的开始位置,
// 因为 OtxStart 本身可能就标记了 witness 中第一个属于 Otx 结构的开始位置。
//
// 紧接着 OtxStart 结构,witness 数据中会继续包含若干个 Otx 结构,每一个 Otx 结构都对应着
// 当前 CKB Transaction 中所包含的一个 OTx
//
// 取决于 OtxStart 在 witnesses 数组中具体所处的位置,OtxStart 结构本身,有可能并不被 CKB
// Transaction 中的任何一个签名覆盖。
table OtxStart {
start_input_cell: Uint32,
start_output_cell: Uint32,
start_cell_deps: Uint32,
start_header_deps: Uint32,
}
table SealPair {
script_hash: Byte32,
seal: ByteVec,
}
vector Seals <SealPair>;
// Otx 用于表示某一个确定的 Otx 结构,对于一个 Otx 来说,它有且仅有惟一一个以 Otx 结构呈现
// 的 witness。同时以 Otx 结构表示的 witness 在 CKB Transaction 中所处的位置,与该 Otx
// 实际包含的 input / output cells 在当前 CKB Transaction 中所处的位置之间没有任何确定关系
table Otx {
// 一个 Otx 可能比如有 3 个 input cells,这 3 个 input cells,并不都一定是相同的 lock
// 可能他们都是不同的 lock script,用不同的签名算法分别签名。
// 同时对一个 Otx 来说,只有 Otx 这一个 witness,
// 我们需要在这一个 witness 中,塞入多个不同 lock script 的不同签名。
// 因此这里用了 seals,这里 SealPair 的用法跟 Action 类似,
// 也是先用 script hash 匹配,匹配到再从 SealPair 中取出 Bytes 类型的 lock 字段
seals: Seals,
// flag 将决定下面四项是否允许有 dynamic 的部分
// 当 flag & 0b00000001 == 1 时,input_cells 的数量可变
// 当 flag & 0b00000010 == 1 时,output_cells 的数量可变
// 当 flag & 0b00000100 == 1 时,cell_deps 的数量可变
// 当 flag & 0b00001000 == 1 时,header_deps 的数量可变
// flag & 0b11110000 必须等于0,对应四位保留
flag: byte,
// input_cells 表示当前的 Otx 中,有几个固定的input cells,是一个数字。
// output_cells / cell_deps / header_deps 也类似
fixed_input_cells: Uint32,
fixed_output_cells: Uint32,
fixed_cell_deps: Uint32,
fixed_header_deps: Uint32,
message: Message,
// 在flag允许对应属性动态后,可以在拼交易的过程中动态追加数量,数量和动态追加的值都不被签名覆盖。
dynamic_input_cells: Uint32
dynamic_output_cells: Uint32,
dynamic_cell_deps: Uint32,
dynamic_header_deps: Uint32,
}
如果 flag 指示某一项不允许dynamic,则对应项的dynamic数量必须等于0。
其中 flag, fixed_input_cells, fixed_output_cells, fixed_cell_deps, fixed_header_deps, message 将被处于 fixed_input_cells 范围里的 lock 的 signing_hash 覆盖;而处于 dynamic_input_cells 的 lock 的 seal,除了 Fixed 部分所涵盖的,还会额外覆盖全部的dynamic_inputs。
在 SealPair 中寻找 lock 对应 的 seal 时,对于 fixed_input_cells 的 lock,从前往后找,对于 dynamic_input_cells 的 lock,从后往前找。所以如果一个lock同时出现在这两个区域,则它会对应两个 seal,fixed_input 对应的 Seal 将在前面,dynamic_input 对应的 seal 将在后面。
另一个改动是针对 Cobuild 的 Action,在原本的Action结构中,增加了一项 script_location,用于指明被调用的脚本处于交易的位置,目前有 input_lock,input_type, output_type 三种类型可选,从而实现更精细的控制。
table Action {
script_info_hash: Byte32, // script info
script_location: byte, // script_location 0-input_lock, 1-input_type, 2-output_type
script_hash: Byte32, // script
data: Bytes, // action data
}
当 Otx 的对应属性允许动态追加后,在拼交易的过程中,可以动态追加数据。
一个简单的例子:
inputs:
Capacity Cell
outputs:
<empty>
Witness:
message:
dob_mint_script_hash
mint_dynamic_dob
outputs 可以被动态添加
在上面这个例子中,用户想使用调用 dob_mint_script 进行 DOB Mint,对应的 outputs 是空的。
Aggregator 收到对应 OTX 后,可以动态追加outputs,并根据以下规则mint,当处理 OTX 时 mint 的序号为 17 的倍数时,则 mint 稀有度 1 的 DOB, 当处理 OTX 时 mint 的序号为 29 的倍数时,则 mint 稀有度为 2 的 DOB,否则mint稀有度为0的普通DOB。
在 mint 时,扣除一定量的 CKB 作为费用。
Aggregator根据规则补上对应 DOB 输出以及找零 Cell,并更改 dynamic_output_cells 的数量以符合实际情况。
对应的mint脚本应检查:
- mint逻辑的正确性,即 aggregator 按规则产生了对应输出。
- Capacity变化的前后一致,即处理结束后还属于用户的 Capacity,应在输出中返还给用户。