目前的 Cobuild-OTX 只能决定 Input_cells, output_cells,cell_deps, header_deps,被 OTX 的哈希所覆盖,而无法更细粒度地控制,如某个 output 不包含 output_data,这样的 OTX 在设计应用的灵活性上受到了约束,之前 xuejie 设计了一个更复杂的 OTX RFC: Composable Open Transaction Lock Script,可以细粒度控制签名覆盖范围,但是在某些情形下会有安全问题,所以方案换成了如今的方案。
但是基于现在的 Cobuild-OTX 方案,有没有可能通过一些小改动,使其支持细粒度控制呢?
首先需要分析,哪些部分可以细粒度控制?cell_deps 和 header_deps 是不需要的,因为这两部分只有固定的单个属性。
而对于 inputs 和 outputs,则可以细粒度控制。
对于 Input,其有 since, outpoint,两个属性可供调整,这可以使用 2bit 来表达,那么已知input_cells是inputs的数量,只需要 inputs_cell * 2 数量的比特,即可细粒度控制每个input在签名里涵盖的范围,例如 第一个 bit 代表 since,当其为 0 时,覆盖since,当其为1时,不覆盖since,同理,第二个 bit 代表outpoint。当这个flag为11时,相当于不对input做任何约束,但由于最后的签名验证需要解锁lock,所以就隐含了对lock的约束。
对于 Output,其有 lock, type, capacity, output_data,四个属性可供调整,一个元素个数为4的集合拥有16个子集,排除掉空集剩15个,可以使用4bit表达每一个output的覆盖范围,只要确定映射关系即可,例如 第一个 bit 代表 lock,当其为 0 时,覆盖lock,当其为1时,不覆盖lock,同理,第二个 bit 代表type,第三个 bit 代表 capacity,第四个代表 output_data。根据这个规则,0000 代表全签名,0001,代表不签名 output_data,1000代表不签名lock,1010 代表不签名 lock 和 capacity,1111 代表什么都不涵盖,但是在对应位置需要有一个output占位。由此,只需要 output 的数量乘以4的bit数,即可做到细粒度控制,例如对于20个output,10byte 数据即可约束。
基于 CKB Open Transaction (OTX) CoBuild Protocol Overview 中的讨论,我们进行如下扩展:
// 一个完整的 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,
// input_cells 表示当前的 Otx 中,有几个 input cells,是一个数字。
// output_cells / cell_deps / header_deps 也类似
// input_flags 表示当前的 otx 中,每个 input 应被签名覆盖的范围
// output_flags 也类似
input_cells: Uint32,
input_flags: ByteVec,
output_cells: Uint32,
output_flags: ByteVec,
cell_deps: Uint32,
header_deps: Uint32,
message: Message,
}
补充内容,对于cell_deps和header_deps,我们可以对每个项使用1bit来细粒度控制,当其为0时,表示签名覆盖该属性,当其为1时,表示签名不覆盖该属性,仅验证占位。