关于区块链:RFC可组合的-Open-Transaction-lock-script

31次阅读

共计 8393 个字符,预计需要花费 21 分钟才能阅读完成。

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

Open Tx Brainstorm:
https://talk.nervos.org/t/ope…

 

数据结构

 

哈希阵列

 
受最后的 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    | Terminate and generate the final blake2b hash               | ignored               | ignored      |

当虚拟机开始 执行 hash_array 时,一个 blake2b 的哈希事件(hash instance)会被创立,大多数命令会生成一些数据。这些数据作为要哈希的内容,并放入 blake2b 事件中。例如,命令 0x00 将通过 CKB syscall 获取以后正在运行的交易的哈希值,而后将交易哈希值作为数据片段提供给 blake2b 事件。稍后咱们将看到更多为 blake2b 哈希物件生成数据的命令。
 
看到 hash_array 的另一种办法是,每个哈希物件将生成的数据(除了一些我的项目不这么做以外,咱们能够把这些哈希物件生成空数据),而后将所有数据 连贯 到通过 blake2b 哈希算法的繁多数据入口,并用之作为后续签名验证阶段的签名音讯。
 
命令 0x01 会计算以后 lock script 组中输出和输入的 cell 的数量,并用 64 位无符号小端序格局的两个数字提供给 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    | Hash part or the whole input cell             | offset of input cell  | `Cell mask`  |

这 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 | The whole cell   |

以下是一些理论的例子:

  • 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    | Hash part or the whole cell input structure   | offset of input cell  | `Input mask` |

定位 cell 的雷同程序也用于定位 CellInput 的构造,惟一的区别在于要生成的理论数据,或保留在 ARG 2 中的 Input mask:

| BIT  | INCLUDED DATA                 |
|------|-------------------------------|
| 0x1  | previous_output.tx_hash       |
| 0x2  | previous_output.index         |
| 0x4  | since                         |
| 0x8  | previous_output               |
| 0x10 | The whole CellInput structure |

这里是一些理论的范例:

  • 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    | Pop top 2 values from stack, divide them, then push the result back to stack. When divisor is zero, exit with an error code. | 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 | Start offset of data to extract                                                                              |

    留神,堆栈最多能够存储 8 个元素。当堆栈已满时,推入更多数据将导致锁脚本立刻终止,并返回错误代码。

从 0x41 到 0x44 的命令提供了对堆栈顶层的值的基本操作。对于溢出 / 下溢(overflows / underflows),将应用盘绕(wrapping)行为。

作为一个更残缺的例子,上面的程序能够用来确保,只能从一个特定的帐户提取肯定数量的 sUDT token:

0x01 0x00 0x00 0x00    // Hash the length of input & output cells in current script group
0x1A 0x00 0x03 0x00    // Hash the lock script(account) and type script(sUDT ID) for the
                       // input cell at offset 0
0x19 0x00 0x03 0x00    // Hash the lock script(account) and type script(sUDT ID) for the
                       // output cell at offset 0
0x29 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 stack
0x2A 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 stack
0x42 0x00 0x00 0x00    // Substract the top 2 values on stack
0x40 0x00 0x00 0x00    // Hash the top value on stack
0x2B 0x00 0x00 0x00    // Take the output cell at offset 0, push the capacity to stack
0x2C 0x00 0x00 0x00    // Take the input cell at offset 0, push the capacity to stack
0x42 0x00 0x00 0x00    // Substract the top 2 values on stack
0x40 0x00 0x00 0x00    // Hash the top value on stack
0xF0 0x00 0x00 0x00    // Terminate and generate the resulting hash

此程序的 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 hash
Hash type: composable open transaction script hash type
Args: <21 byte identity>

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

<1 byte flag> <20 byte identity content>

依据 flag 的值,identity 的内容有不同的解释:

  • 0x0: identity 内容示意 secp256k1 公钥的 blake160 哈希。锁脚本将执行 secp256k1 的签名验证,就像 secp256k1 /blake160 这个 lock 一样,应用通过执行下面所示的 hash_array 程序计算出来的签名音讯。

稍后,咱们可能会向 identity 数据结构增加更多查看。例如,当 exec(https://github.com/nervosnetw… 准备就绪时,咱们可能还会增加另一种 identity 类型,它将加载用于理论的 identity 验证的新脚本。
 

Witness

 
当解锁一个可组合的 open transaction lock 时,相应的 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 Cell
Inputs:
    <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 的聚合器会喜爱将多个此类交易合并到一个繁多的交易中,以便于收取付款并节俭交易费用。
 

正文完
 0