CKB 交易驗證初探

買賣虛擬貨幣
我們都知道 Nervos CKB 是一個以狀態為中心的架構,並且用交易表示狀態更改和遷移。而且 Nervos CKB 提供基於 Cell Model 和 CKB VM 的程式設計模型。在這個模型中,去中心化的應用邏輯被分成兩個部分:狀態生成和狀態驗證。狀態生成邏輯在客戶端執行,新的狀態被打包成交易,驗證透過後廣播到整個網路。簡單來說 CKB 的程式設計模型分成如下三部分:· 狀態生成:鏈下· 狀態驗證:CKB VM· 狀態儲存:Cell Model之前 CKB 開發者 luochao 給大家介紹瞭如何用最常見最簡單的方法在 CKB 上構建交易——也就是狀態生成。在本篇文章中,CKB 開發者 DingWei Zhang 和 luochao 將繼續分別為大家講解狀態驗證的開發細節:「CKB 交易驗證的生命週期」和「CKB VM 驗證規則」。 

CKB 交易驗證生命週期

RPC

首先,傳送方會構造一筆交易,透過 RPC 提交。交易由提交到的 outputs_validator (從 0.27.0 版本引入)進行驗證。

預設的驗證邏輯包括檢查各種東西:

transaction.outputs.all{ |output|
    let script = output.script
    (script.code_hash == secp256k1_blake160_sighash_all && script.hash_type == "type" && script.args.size == 20) ||
    (script.code_hash == secp256k1_blake160_multisig_all && script.hash_type == "type" && (script.args.size == 20 || (script.args.size == 28 && script.args[20..28].is_valid_since_format))
}
transaction.outputs.all{ |output|
    let script = output.type
    script.is_null || script.code_hash == dao && script.hash_type == "type"
    || (script.has_lock_period() && since.is_absolute())
}

此驗證旨在防止格式不正確的交易,例如在 Common Gotchas 中提到的交易。
https://github.com/nervosnetwork/ckb/wiki/Common-Gotchas#nervos-dao

此外,可以將其配置為 passthrough 以跳過此驗證。

交易提交到本地節點後,節點還會輸出交易 id,您可以使用該 id 跟蹤交易的狀態。

驗證

在廣播交易並進入 mempool 之前,交易將在本地驗證和執行。

步驟 1——Resolve

本質上,交易 input 只是指標,如下所示:

struct OutPoint {
    tx_hash:        Byte32,
    index:          Uint32,
}

我們在交易執行之前透過指標收集引用的資料,這個過程稱為「解析交易」。我們還需要檢查這個交易的所有輸入都是有效的(沒有重複或雙花)。

步驟 2——驗證

驗證步驟需要檢查如下要素: 

1、版本(目前必須是 0)
2、serialized_size 必須小於如下限制:

pub fn serialized_size(&self) -> usize {    // the offset in TransactionVec header is u32
    self.as_slice().len() + molecule::NUMBER_SIZE
    // molecule::NUMBER_SIZE = size_of::<u32>() 4
}

3、inputs 不是空的

inputs().is_empty() || outputs().is_empty()

4、inputs 是成熟的

對於每個 input 和 dep,如果引用的 output 交易是 cellbase,那麼它必須至少經過 4 個 epoch 確認。

5、capacity

input capacity 的和必須小於或等於 output capacity

6、duplicate_deps

deps 不能重複

7、outputs_data_verifier

「output data」欄位的數量必須等於 outputs 的數量

8、since

since 值必須遵循 RFC:Transaction valid since
https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0017-tx-valid-since/0017-tx-valid-since.md

CKB VM 將執行交易指令碼,並輸出所消耗的 cycles 個數。

向網路廣播

如果驗證成功,當前節點將交易(帶有 cycles 值)廣播給它的所有對等節點(它所連線的節點)。

在驗證失敗的情況下,將不再廣播交易。交易流經各個「完整節點」,這些節點重複前面步驟中描述的驗證過程,並檢查 cycle 值是否與驗證交易時使用的實際 cycle 相匹配。

Tx-pool

CKB 使用 two-step 進行交易確認。交易將在 tx-pool 中劃分為不同的狀態(pending 狀態和 proposed 狀態)。當一個塊上鍊時,交易的狀態將改變。當最新的塊更改時,將重新掃描 tx-pool 中的所有交易,以確保它們仍然有效。

BlockAssembler 將從 pending 池和 proposed 池中為塊模板獲取 proposal 和交易,詳細可參考:Two-Step Transaction Confirmation:
https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0020-ckb-consensus-protocol/0020-ckb-consensus-protocol.md#two-step-transaction-confirmation

CKB VM 驗證規則

在編寫程式時,確定操作環境和執行時行為非常重要,以便程式的執行結構儘可能接近程式建立者的期望。例如:在編寫 Python 程式時,需瞭解 Python 中 GIL 的影響、硬體指令的預期執行時間、流水線規劃等。

我們都知道 CKB VM 是基於 RISC-V 指令集開發的虛擬環境。下面,我們將介紹在 CKB 驗證期間 VM 的環境,包括提供的一些系統呼叫等相關背景知識。

環境

在 CKB 中,每個交易都是單獨執行的,即每個交易都有自己獨立的 VM 環境。雖然執行了多交易並行驗證,但是 VM 內部沒有多執行緒環境。

執行單元

當每個單獨的交易進行驗證時,指令碼將首先被分成 group,然後以 script group 的單位順序執行。每個 group 都是將具有相同指令碼 hash 的交易分組在一起而建立的。請注意,output 的 lock script 將不會在交易驗證期間執行。

無論執行哪個 script group,script 都可以在執行期間訪問整個交易資料。這種設計的一個優點是 script 記錄屬於當前 script 的cell 的索引。這相當於刪除交易資料並驗證相同的 lock script 或 type script,但只需執行一次即可完成多個 cell 的驗證。減少了驗證資源的消耗,併為交易的 data set 提供了公有環境。

程式碼如下:

class ScriptGroup:
    def __init__(self, script):
        self.script = script
        self.input_indices = []
        self.output_indices = []

def split_group(tx):
     lock_groups: Dict[Hash, ScriptGroup] = dict()
     type_groups: Dict[Hash, ScriptGroup] = dict()
    for index, input in enumerate(tx.inputs):
        if lock_groups.get(hash(input.lock)):
            lock_groups.get(hash(input.lock)).input_indices.append(index)
        else:
            script_group = ScirptGroup(input.lock)
            script_group.input_indices.append(index)
            lock_groups[hash(input.lock)] = script_group
        if input.type:
            if type_groups.get(hash(input.type)):
                type_groups.get(hash(input.type)).input_indices.append(index)
            else:
                script_group = ScriptGroup(input.type)
                script_group.input_indices.append(index)
                type_groups[hash(input.type)] = script_group
    for index, output in enumerate(tx.outputs):
        if output.type:
            if type_groups.get(hash(input.type)):
                type_groups.get(hash(input.type)).output_indices.append(index)
            else:
                script_group = ScriptGroup(input.type)
                script_group.output_indices.append(index)
                type_groups[hash(input.type)] = script_group
    return list(lock_groups.values()) + list(type_groups.values())

def run():
    for group in split_group(tx):
        if vm_run(group) != 0:
            return error()

當執行每個 script group 時,將記錄 script 的執行成本,並將所有資源消耗的總和與允許的 max_block_cycles 上限進行比較。

假設有一個交易:

Transaction {
    input: [cell_1 {lock: A, type: B}, cell _2 {lock: A, type: B}, cell_3 {lock: C, type: None}]
    output: [cell_4 {lock: D, type: B}, cell_5 {lock: C, type: B}, cell_6 {lock: G, type: None}, cell_7(lock: A, type: F)]
}

它將按這樣的方式分組:

[
    group(A, input:[0, 1], output:[]),
    group(C, input:[2], output:[]),
    group(B, input:[0, 1], output:[0, 1]),
    group(F, input:[], output:[3])
]

VM 的 syscall 可以透過 group(input/output index) 載入這些對應的 cell,以完成一次性驗證。

CKB 將執行所有根據返回值驗證的 script group。此處遵循類 unix 系統中程序退出狀態的約定:返回值為 0 代表驗證透過,而其他返回值代表驗證異常。

請注意:在執行 script 時,script 不知道它是 type script 還是 lock script。script 需要透過檢查 args 或 witness data 來自己解決這個問題。

特定規則

除了一種型別的合約,即 TypeId 合約之外,大多數合約都是如上所述進行驗證的。TypeId 合約使用特殊的規則,直接在指令碼程式碼中編寫,並且不啟動 VM。有關更多資訊,請參見程式碼:
https://github.com/nervosnetwork/ckb/blob/44b0d3595c31a29aef81e74360ba8613cd0dd27f/script/src/type_id.rs

Syscall

關於 Syscall 的內容,請參考:RFC:VM Syscalls 和相關程式碼:

https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0009-vm-syscalls/0009-vm-syscalls.md
https://github.com/nervosnetwork/ckb-system-scripts/blob/865f4d7697cc979d62111e49f2fb12a3607a4eb9/c/ckb_syscalls.h

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读

;