LOADING...
LOADING...
LOADING...
当前位置: 玩币族首页 > 区块链资产 > RFC:可组合的 Open Transaction lock script

RFC:可组合的 Open Transaction lock script

2021-06-24 Nervos 来源:区块链网络

本文介绍了一个在 Nervos CKB 上能实现 Open Transaction 的 lock script。它的灵感来自于之前 Open Tx Brainstorm的设计,具有在 Open Transaction 中重新排序和重新安排签名组件的新能力。


Open Tx Brainstorm:

https://talk.nervos.org/t/open-tx-protocol-brainstorm-4-an-implementation-proposal/4427



数据结构



哈希阵列


受最初的 Open Tx 头脑风暴的文章的启发,我们在可组合的 OpenTx lock script 使用的签名前面添加了一个新的hash_array的数据结构。hash_array包含一个hash item列表,如下所示:


| NAME | Command | Arg1 | Arg2 ||------|---------|------|------||BITS|8|12|12|


一个 Hash item 包含 3 个 32 位(4 字节)长的物件。hash_array不要求在开始处是有长度的字段,一个特殊的命令将标记哈希阵列的结束。在某种程度上,我们可以将哈希阵列看作是一个小型的虚拟机输入程序。这个虚拟机的目的是为了给 blake2b 哈希函数提供数据。来自哈希函数的哈希将用来作签名用的签名信息。


命令


本节将介绍接受 hash item 的有效命令,以及描述和所接受的参数。


首先,我们有一些常见的命令:


| COMMAND | DESCRIPTION | ARG 1 | ARG 2 ||---------|-------------------------------------------------------------|-----------------------|--------------|| 0x00 | Hash the full current transaction hash | ignored | ignored || 0x01 | Hash length of input & output cells in current script group | ignored | ignored ||0xF0|Terminateandgeneratethefinalblake2bhash|ignored|ignored|


当虚拟机开始执行hash_array时,一个blake2b的哈希事件(hash instance)会被创建,大多数命令会生成一些数据。这些数据作为要哈希的内容,并放入blake2b事件中。例如,命令0x00将通过 CKB syscall 获取当前正在运行的交易的哈希值,然后将交易哈希值作为数据片段提供给blake2b事件。稍后我们将看到更多为blake2b哈希物件生成数据的命令。


看到hash_array的另一种方法是,每个哈希物件将生成的数据(除了一些项目不这么做以外,我们可以把这些哈希物件生成空数据),然后将所有数据连接到通过blake2b哈希算法的单一数据入口,并用之作为后续签名验证阶段的签名消息。


命令0x01会计算当前 lock script 组中输入和输出的 cell 的数量,并用 ** 位无符号小端序格式的两个数字提供给blake2b事件来进行哈希。这可用于防止 Open tx 聚合器任意添加未处理的 cell。


命令0xf0填补了另一个不同的目的:一方面,它标示着hash_array,另一方面,它通知这个小型虚拟机在此运行所有已经传送到虚拟机的数据,而且我们现在还可以从blake2b事件生成相应的哈希。此相应的 hash 也用作签名消息,用于稍后的签名验证阶段。


有了大体的工作流程后,我们就可以使用更多生成数据的命令了:


| COMMAND | DESCRIPTION | ARG 1 | ARG 2 ||---------|-----------------------------------------------|-----------------------|--------------|| 0x11 | Hash part or the whole output cell | index of output cell | `Cell mask` || 0x12 | Hash part or the whole input cell | index of input cell | `Cell mask` || 0x19 | Hash part or the whole output cell | offset of output cell | `Cell mask` ||0x1A|Hashpartorthewholeinputcell|offsetofinputcell|`Cellmask`|


这 4 个命令将首先定位在输入或输出 cell ,然后生成作为一部分或整个 cell 的数据。cell 的来源(无论它是输入或输出 cell)由命令表示,cell 的索引则由命令和ARG 1表示:


对于命令 0x11 和 0x12,ARG 1表示当前交易中的 cell 的绝对索引。对于命令 0x19 和 0x1A,ARG 1表示在指定 cell 中的offset(偏移量)。稍后我们将看到在 witness 中两个变量base input index和base output index,还有hash_array和签名。对于命令 0x19,添加ARG 1和base intput index将产生当前交易中指定输出 cell 的绝对索引,而对于命令 0x1A,添加 ARG 1 和base output index将产生当前交易中指定输入 cell 的绝对索引。offset 提供了一种重新排序 cell 的方法,因此一个 CKB 交易中有让许多不冲突的 Open Tx 并存的空间。
从 cell 生成的数据,由ARG 2或Cell mask确定,mask 中的有效位包括:

| BIT | INCLUDED DATA ||-------|------------------|| 0x1 | Capacity || 0x2 | type.code_hash || 0x4 | type.args || 0x8 | type.hash_type || 0x10 | lock.code_hash || 0x20 | lock.args || 0x40 | lock.hash_type || 0x80 | Cell data || 0x100 | Type script hash || 0x200 | Lock script hash ||0x400|Thewholecell|


以下是一些实际的例子:
0x11 0x00 0x30 0x21将获取当前交易中绝对索引为 3 的输出 cell ,然后提取其 capacity ,然后将 lock script 参数作为blake2b事件的数据哈希假设base input index是 5,0x1A 0x01 0x04 0x00会取当前交易中绝对索引为 21 的输入 cell,然后将整个 cell 作为blake2b事件所哈希的数据

除了 Cell ,还有一个CellInput 结构与每个输入 cell 相关联,提供有价值的信息,如since和OutPoint。下面的命令提供了一种将CellInput数据进行哈希的方法:

CellInput:

https://github.com/nervosnetwork/ckb/blob/85d04c329d4478df5ca40e4161152f3eab858d59/util/types/schemas/blockchain.mol#L41-L44



| COMMAND | DESCRIPTION | ARG 1 | ARG 2 ||---------|-----------------------------------------------|-----------------------|--------------|| 0x15 | Hash part or the whole cell input structure | index of input cell | `Input mask` ||0x1D|Hashpartorthewholecellinputstructure|offsetofinputcell|`Inputmask`|


定位 cell 的相同程序也用于定位CellInput的结构,唯一的区别在于要生成的实际数据,或保存在ARG 2中的Input mask:

| BIT | INCLUDED DATA ||------|-------------------------------|| 0x1 | previous_output.tx_hash || 0x2 | previous_output.index || 0x4 | since || 0x8 | previous_output ||0x10|ThewholeCellInputstructure|


这里是一些实际的范例:
0x15 0x00 0x00 0x04会取当前交易中绝对索引为 0 的 CellInput 结构,然后使用它的 since 值作为blake2b事件的哈希数据假设base input index为 2,0x1D 0x00 0x10 0x0C将在当前交易中使用绝对索引 3 的 CellInput 结构,然后使用它的 since 值,然后使用序列化的previous_output字段(这是一个OutPoint结构)作为blake2b事件哈希的数据
有了这些背景知识,我们可以开始看一些更复杂的命令:

| COMMAND | DESCRIPTION | ARG 1 | ARG 2 ||---------|------------------------------------------------------------------------------------------------------------------------------|-----------------------|---------------|| 0x21 | Push cell data to stack | index of output cell | `Data format` || 0x22 | Push cell data to stack | index of input cell | `Data format` || 0x23 | Push capacity to stack | index of output cell | ignored || 0x24 | Push capacity to stack | index of input cell | ignored || 0x29 | Push cell data to stack | offset of output cell | `Data format` || 0x2A | Push cell data to stack | offset of input cell | `Data format` || 0x2B | Push capacity to stack | index of output cell | ignored || 0x2C | Push capacity to stack | index of input cell | ignored || 0x2F | Concatenate ARG 1 and ARG 2, push the resulting value to stack | higher 12 bit | lower 12 bit || 0x40 | Pop the top value from stack, then convert it to data of 32 bytes to hash | ignored | ignored || 0x41 | Pop top 2 values from stack, add them, then push the result back to stack | ignored | ignored || 0x42 | Pop top 2 values from stack, subtract them, then push the result back to stack | ignored | ignored || 0x43 | Pop top 2 values from stack, multiply them, then push the result back to stack | ignored | ignored ||0x44|Poptop2valuesfromstack,dividethem,thenpushtheresultbacktostack.Whendivisoriszero,exitwithanerrorcode.|ignored|ignored|


我们已经在上面讨论了一个微型虚拟机。但是上面所有的交易,只是为blake2b事件发出数据。可组合的 Open Tx 锁脚本中的微型虚拟机,实际上是在内部维护一个堆栈。堆栈最多可以容纳 8 个元素,每个元素都是 256 位整数。从 0x21 到 0x2F 的命令可以用来将数据推送到堆栈:
命令 0x2F 将连接存储在ARG 1和ARG 2中的值,然后将结果值转换为 256 位整数,然后将其推入堆栈中。命令 0x23, 0x24, 0x2B 和 0x2C 会在上面描述的方法中首先找到一个 cell,然后取该 cell 的 capacity,将其转换为 256 位整数,然后将其推入堆栈。命令 0x21, 0x22, 0x29 和 0x2A 将首先在上面描述的方法中找到一个 cell ,然后按照Data format中定义的格式提取部分 cell 的数据,将其转换为 256 位整数,然后将其推入堆栈。Data format的精确输出如下:

| BITS | MEANING ||--------|--------------------------------------------------------------------------------------------------------------|| 0 | Endianness, 0 for little endian, 1 for big endian || 1 - 3 | Length of data to extract, expressed in power of 2, for example, 3 here means 8 bytes, 5 here means 32 bytes ||4-11|Startoffsetofdatatoextract|


注意,堆栈最多可以存储 8 个元素。当堆栈已满时,推入更多数据将导致锁脚本立即终止,并返回错误代码。
从 0x41 到 0x44 的命令提供了对堆栈顶层的值的基本操作。对于溢出/下溢(overflows / underflows),将使用环绕(wrapping)行为。
作为一个更完整的例子,下面的程序可以用来确保,只能从一个特定的帐户提取一定数量的 sUDT token:

0x01 0x00 0x00 0x00 // Hash the length of input & output cells in current script group0x1A 0x00 0x03 0x00 // Hash the lock script(account) and type script(sUDT ID) for the // input cell at offset 00x19 0x00 0x03 0x00 // Hash the lock script(account) and type script(sUDT ID) for the // output cell at offset 00x29 0x00 0x04 0x00 // Take the output cell at offset 0, extract the first 16 bytes of // data in little endian format(sUDT amount), and push the resulting // value to stack0x2A 0x00 0x04 0x00 // Take the input cell at offset 0, extract the first 16 bytes of // data in little endian format(sUDT amount), and push the resulting // value to stack0x42 0x00 0x00 0x00 // Substract the top 2 values on stack0x40 0x00 0x00 0x00 // Hash the top value on stack0x2B 0x00 0x00 0x00 // Take the output cell at offset 0, push the capacity to stack0x2C 0x00 0x00 0x00 // Take the input cell at offset 0, push the capacity to stack0x42 0x00 0x00 0x00 // Substract the top 2 values on stack0x40 0x00 0x00 0x00 // Hash the top value on stack0xF00x000x000x00//Terminateandgeneratetheresultinghash


此程序的 Open Transaction 将包含一个输入 cell 和一个输出 cell 。所提供的签名包括以下部分:
当前脚本组中输入和输出 cell 的长度输入和输出 cell 中使用的帐户用于输入和输出 cell 的 sUDT ID输入和输出 Cell 之间的 sUDT token 的差异输入和输出单元之间 CKB token 的差异
如果你仔细想想,这个程序甚至没有强制使用某个 cell 作为输入。如果 Open Tx 的构造者有多个满足需求的 Cell,那么聚合器可以自由选择任何输入 cell,而同时聚合器只能选择根据 Open Tx 构造者的需求来生成交易。这么一来所有的代币都是安全的,不会被偷。


Lock Script


一个可组合的 Open Transaction Lock Script 看起来如下:

Code hash: composable open transaction script code hashHash type: composable open transaction script hash typeArgs:<21byteidentity>


他使用与 RC Lock 相同的 Identity(https://talk.nervos.org/t/rfc-regulation-compliance-lock/5788)数据结构:

<1byteflag><20byteidentitycontent>


根据 flag 的值, identity 的内容有不同的解释:
0x0: identity 内容表示 secp256k1 公钥的 blake160 哈希。锁脚本将执行 secp256k1 的签名验证,就像 secp256k1 /blake160 这个 lock 一样,使用通过执行上面所示的hash_array程序计算出来的签名消息。
稍后,我们可能会向 identity 数据结构添加更多检查。例如,当 exec(https://github.com/nervosnetwork/rfcs/pull/237)准备就绪时,我们可能还会添加另一种 identity 类型,它将加载用于实际的 identity 验证的新脚本。


Witness


当解锁一个可组合的open transactionlock时,相应的 witness 必须是一个分子格式的正确WitnessArgs数据结构,以下数据结构必须出现在WitnessArgs的 lock 字段中:

| BYTES | CONTENT ||---------|-------------------|| 0..7 | Base input index || 8..15 | Base output index || 16..n | Hash array ||n..n+65|Signature|


Base input index和base output index由 Open Transaction 聚合器填充,然而hash array和signature则是有 Open Transaction 的创建人所提供



范例



解锁一个 Open Transaction


CellDeps: <vec> Composable Open Transaction Lock Script CellInputs: <vec> Open Transaction Cell Capacity: 100 Lock: code_hash: Composable Open Transaction Lock args: <flag: 0x0> <pubkey hash 1> <...>Outputs: <vec> Open Transaction Cell Capacity: 50 Lock: code_hash: Composable Open Transaction Lock args: <flag: 0x0> <pubkey hash 1> <...>Witnesses: WitnessArgs structure: Lock: base input index: 0 base output index: 0 hash array: <a valid hash array program><...>



整合



在实际开发中,Open Transaction 的创建者可以创建与典型交易相同格式的 Open Transaction,base input index和base output index都填充为 0。
如果我们考虑一下,大多数 Open Transaction 也可以被 CKB 提交和接受,但 Open Transaction 的聚合器会喜欢将多个此类交易合并到一个单一的交易中,以便于收取付款并节省交易费用。


—-

编译者/作者:Nervos

玩币族申明:玩币族作为开放的资讯翻译/分享平台,所提供的所有资讯仅代表作者个人观点,与玩币族平台立场无关,且不构成任何投资理财建议。文章版权归原作者所有。

LOADING...
LOADING...