头图

This article introduces a lock script that can implement Open Transaction on Nervos CKB. It is inspired by the previous design of Open Tx Brainstorm, and has the new ability to reorder and rearrange signature components in Open Transaction.

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

 

data structure

 

Hash array

 
Inspired by the original Open Tx brainstorming article, we added a new hash_array data structure in front of the signature used by the composable OpenTx lock script. hash_array contains a list of hash items, as shown below:

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

A Hash item contains three 32-bit (4 bytes) long objects. hash_array does not require a length field at the beginning, a special command will mark the end of the hash array. To some extent, we can think of the hash array as a small virtual machine input program. The purpose of this virtual machine is to provide data to the blake2b hash function. The hash from the hash function will be used as signature information for signing.
 

command

 
This section will introduce the effective commands that accept the hash item, as well as the description and the accepted parameters.

First, we have some common commands:

| 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      |

When the virtual machine starts executes hash_array, a blake2b hash event (hash instance) will be created, and most commands will generate some data. These data are used as the content to be hashed and put into the blake2b event. For example, the command 0x00 will obtain the hash value of the currently running transaction through CKB syscall, and then provide the transaction hash value as a data fragment to the blake2b event. We will see more commands for generating data for blake2b hash objects later.
 
Another way to see hash_array is to generate data for each hash object (except for some projects that do not do this, we can generate empty data for these hash objects), and then connect all data to through blake2b A single data entry for the hash algorithm and used as a signature message in the subsequent signature verification phase.
 
The command 0x01 will calculate the number of input and output cells in the current lock script group, and provide two numbers in 64-bit unsigned little-endian format to the blake2b event for hashing. This can be used to prevent the Open tx aggregator from arbitrarily adding unprocessed cells.
 
The command 0xf0 fills another different purpose: on the one hand, it marks hash_array, on the other hand, it informs this small virtual machine to run all the data that has been transmitted to the virtual machine, and we can now generate the corresponding from the blake2b event The hash. This corresponding hash is also used as a signature message for later signature verification phase.
 
With the general workflow, we can use more commands to generate data:

| 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`  |

These 4 commands will first locate the input or output cell, and then generate data as a part or the entire cell. The source of a cell (whether it is an input or output cell) is indicated by the command, and the cell index is indicated by the command and ARG 1:

  • For commands 0x11 and 0x12, ARG 1 represents the absolute index of the cell in the current transaction.
  • For commands 0x19 and 0x1A, ARG 1 represents the offset in the specified cell. Later we will see two variables base input index and base output index in witness, as well as hash_array and signature. For command 0x19, adding ARG 1 and base intput index will generate the absolute index of the specified output cell in the current transaction, and for command 0x1A, adding ARG 1 and base output index will generate the absolute index of the specified input cell in the current transaction. Offset provides a way to reorder cells, so a CKB transaction has room for many non-conflicting Open Tx to coexist.

The data generated from the cell is determined by ARG 2 or Cell mask. The valid bits in the mask include:

| 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   |

Here are some practical examples:

  • 0x11 0x00 0x30 0x21 will get the output cell whose absolute index is 3 in the current transaction, then extract its capacity, and then use the lock script parameter as the data hash of the blake2b event
  • Assuming that the base input index is 5, 0x1A 0x01 0x04 0x00 will take the input cell with an absolute index of 21 in the current transaction, and then use the entire cell as the data hashed by the blake2b event

In addition to Cell, there is also a CellInput structure associated with each input cell, providing valuable information such as since and OutPoint. The following command provides a way to hash CellInput data:

| 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` |

The same procedure used to locate the cell is also used to locate the structure of the CellInput. The only difference is the actual data to be generated or the Input mask stored in ARG 2:

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

Here are some practical examples:

  • 0x15 0x00 0x00 0x04 will take the CellInput structure whose absolute index is 0 in the current transaction, and then use its since value as the hash data of the blake2b event
  • Assuming the base input index is 2, 0x1D 0x00 0x10 0x0C will use the CellInput structure of absolute index 3 in the current transaction, then use its since value, and then use the serialized previous_output field (this is an OutPoint structure) as the blake2b event hash The data

With this background knowledge, we can start to look at some more complex commands:

| 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       |

We have discussed a micro virtual machine above. But all the above transactions are just sending data for the blake2b event. The micro virtual machine in the composable Open Tx lock script actually maintains a stack internally. The stack can hold up to 8 elements, and each element is a 256-bit integer. The commands from 0x21 to 0x2F can be used to push data to the stack:

  • The command 0x2F will concatenate the values stored in ARG 1 and ARG 2, then convert the resulting value to a 256-bit integer, and then push it onto the stack.
  • The commands 0x23, 0x24, 0x2B and 0x2C will first find a cell in the method described above, then take the capacity of the cell, convert it to a 256-bit integer, and then push it onto the stack.
  • The commands 0x21, 0x22, 0x29 and 0x2A will first find a cell in the method described above, and then extract the data of part of the cell according to the format defined in the Data format, convert it to a 256-bit integer, and then push it onto the stack. The precise output of Data format is as follows:

    | 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                                                                              |

    Note that the stack can store up to 8 elements. When the stack is full, pushing more data will cause the lock script to terminate immediately and return an error code.

The commands from 0x41 to 0x44 provide basic operations on the value at the top of the stack. For overflows / underflows, wrapping behavior will be used.

As a more complete example, the following procedure can be used to ensure that only a certain number of sUDT tokens can be withdrawn from a specific account:

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

The Open Transaction of this program will include an input cell and an output cell. The provided signature includes the following parts:

  • The length of the input and output cells in the current script group
  • Input and output the account used in the cell
  • SUDT ID for input and output cells
  • The difference of sUDT token between input and output cells
  • CKB token difference between input and output units

If you think about it, this program doesn't even force the use of a certain cell as input. If the Open Tx constructor has multiple cells that meet the requirements, the aggregator can freely choose any input cell, and at the same time the aggregator can only choose to generate transactions according to the needs of the Open Tx constructor. In this way, all tokens are safe and will not be stolen.
 

Lock Script

 
A composable Open Transaction Lock Script looks as follows:

Code hash: composable open transaction script code hash
Hash type: composable open transaction script hash type
Args: <21 byte identity>

He uses the same Identity ( https://talk.nervos.org/t/rfc-regulation-compliance-lock/5788 ) data structure as RC Lock:

<1 byte flag> <20 byte identity content>

According to the value of flag, the content of identity has different interpretations:

  • 0x0: identity content represents the blake160 hash of the secp256k1 public key. The lock script will perform the signature verification of secp256k1, just like the secp256k1 /blake160 lock, using the signature message calculated by executing the hash_array program shown above.

Later, we may add more checks to the identity data structure. For example, when exec ( https://github.com/nervosnetwork/rfcs/pull/237) is ready, we may also add another identity type, which will load a new script for actual identity verification .
 

Witness

 
When unlocking a combinable open transaction lock, the corresponding witness must be a correct WitnessArgs data structure in molecular format. The following data structure must appear in the lock field of WitnessArgs:

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

Base input index and base output index are filled by the Open Transaction aggregator, while the hash array and signature are provided by the creator of Open Transaction.
 

example

 

Unlock an 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>
      <...>

 

Integration

 
In actual development, the creator of Open Transaction can create Open Transaction in the same format as a typical transaction, and both base input index and base output index are filled with 0.

If we think about it, most Open Transactions can also be submitted and accepted by CKB, but the aggregator of Open Transaction will like to combine multiple such transactions into a single transaction in order to collect payments and save transaction fees.
 
在这里插入图片描述


NervosNetwork
241 声望870 粉丝

这是 Nervos 唯一官方 SF 号。在这里,你能了解到关于 Nervos 的一切,enjoy.