RFC:可組合的 Open Transaction lock script

本文介紹了一個在 Nervos CKB 上能實現 Open Transaction 的 lock script。它的靈感來自於以前 Open Tx Brainstorm 的設計,具備在 Open Transaction 中從新排序和從新安排簽名組件的新能力。git

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

 

數據結構

 

哈希陣列

 
受最初的 Open Tx 頭腦風暴的文章的啓發,咱們在可組合的 OpenTx lock script 使用的簽名前面添加了一個新的 hash_array 的數據結構。hash_array 包含一個 hash item 列表,以下所示:github

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

一個 Hash item 包含 3 個 32 位(4 字節)長的物件。hash_array 不要求在開始處是有長度的字段,一個特殊的命令將標記哈希陣列的結束。在某種程度上,咱們能夠將哈希陣列看做是一個小型的虛擬機輸入程序。這個虛擬機的目的是爲了給 blake2b 哈希函數提供數據。來自哈希函數的哈希將用來做簽名用的簽名信息。
 算法

命令

 
本節將介紹接受 hash item 的有效命令,以及描述和所接受的參數。express

首先,咱們有一些常見的命令:segmentfault

| 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 中的有效位包括:app

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

如下是一些實際的例子:ide

  • 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 數據進行哈希的方法:函數

| 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 的聚合器會喜歡將多個此類交易合併到一個單一的交易中,以便於收取付款並節省交易費用。
 
在這裏插入圖片描述

相關文章
相關標籤/搜索