對於開發者來説,cell 編程模型無疑是 Nervos CKB 中最有趣的部分。在這裏,我們先對 cell 模型作一個簡短的描述:
Cell 是通用版的 UTXO
一個 cell 是一個包含任意數據和可定製腳本的 UTXO
當一筆交易嘗試銷燬或創建一個 cell,cell 中的腳本將被加載並執行,在執行腳本中返回任何錯誤,都將導致交易失敗。
Cell 模型不同於賬户模型:
驗證而非計算
將數據存儲在單獨的 cell 中,而不是將數據存儲在一個賬户中
當你比較兩個模型時,你會發現還有許多其他的不同之處,如果你對該話題感興趣,可以在 talk.nervos.org 上找到更多關於 cell 模型 VS 賬户模型的討論。
Cell 模型中缺失的那一塊
UTXO 是一個非常棒的模型,cell 模型繼承了它的靈活性。我們(通過 cell 模型)可以發行 UDT(用户自定義代幣,類似於 ERC20),存儲鏈上資產,玩石頭剪刀布遊戲,或者與比特幣實現原子交換等。Cell 模型可以實現很多人們最初認為不可能實現的事情。
不幸的是,有一些合約確實很難在 cell 模型上實現:
投票
眾籌
去中心化定價的虛擬機
...
這些難題合約有一個共性,那就是需要共享狀態。
在一個 UTXO 類的模型內,狀態是自然分離的。
在 CKB 中,用户可以在獨立的 cell 中進行投票。這樣我們就需要一個鏈下的角色收集這些投票的 cells 並計算結果。
當我們只想「看到」結果時,它實現的非常好。但是我們不能在另一個合約中使用投票結果,比如一個基於投票的 DAO 合約。我們很難在一個鏈上合約中驗證統計後的結果。所以我們必須證明每一個投票 cell 的存在,交易時就需要引用每一個投票的 cell,這可能非常昂貴。
再舉個例子,我們來看一個眾籌合約:
一個 cell 中有所有的眾籌代幣,用户可以支付 CKB 來獲得相應數量的代幣。
問題在於,當我們分割這個 cell,眾籌 cell 的 outpoint 就被改變了;其他用户必須等到下一個區塊才能看到新的 outpoint。所以在一個區塊時間內,只有一個用户可以參與眾籌,這是不可接受的。
與投票的例子類似,一個典型的解決方案就是引入一個鏈下角色,用户在個人的 cells 中發出眾籌請求;然後鏈下角色需要收集這些 cells 並將結果放在一個 cell 中。
我們可以看到,由於在 cell 模型中,狀態是自然分離的,因此我們必須依賴某個鏈下角色來收集狀態。
這樣的方法是可行的,但是仍然會有一些問題:
如何有效地證明收集的結果
如何保證在引入了鏈下角色後的去中心化
用户如何與鏈下角色進行交互
當然,解決這些問題並不難;我們可以通過支付鏈下角色一些費用來激勵他們;使用某種挑戰機或者零知識證明(zk proof)來驗證收集的結果;定義一些協議來規定和鏈下角色的交互。我們總能解決這些問題。
等等,我想要的只是一個投票合約。我為什麼需要整這麼多(複雜的)東西?
一個合約管理一切
可不是嘛!我們可不想為每一個合約都整這麼多東西,我們只需要構建一次:
Godwoken 是一個建立在 CKB 上的基於賬户模型的編程層,目標就是管理一切狀態共享的合約。
Godwoken 由以下幾個部分組成:
主合約 —— 一個維護所有賬户、所有區塊的全局狀態的類型腳本(layer 1.5 層)
挑戰合約 —— 一個處理挑戰需求的類型腳本
聚合器 —— 一個收集 layer 1.5 層交易的鏈下程序,並定期向主合約提交 layer 1.5 層的區塊。
驗證器 —— 一個持續監控兩個合約的鏈下程序。驗證器在一個無效區塊被提交時會發送一個挑戰請求,在一個錯誤的挑戰請求被提交時會發送一個無效的挑戰請求。
你可能發現,這聽起來像是最近非常流行的 rollup 的解決方法。是的,沒錯。但是我們關注的是聚合的問題,而非可擴展性的問題。Godwoken 提供了 CKB 基於賬户模型的編程能力來解決聚合問題。
一些人將 Rollup 稱之為 layer 1.5 層;也有一些人認為它是 layer 2 層;還有人認為它是 layer 1 層(根據信任級別)。本文將 Godwoken 稱為 layer 1.5 層,以便將其與 layer 1 層的 CKB 區分開。
Godwoken 使用和原生 CKB 合約相同的技術棧。唯一的區別在於 Godwoken 合約是基於賬户模型的;它將驗證賬户的狀態而不是 cells 的狀態。賬户狀態和 layer 1 層的 cells 之間的映射關係是由 Godwoken 的主合約處理的,對於 layer 1.5 層的合約來説,是透明的。
對於需要創建一個投票合約的開發者來説,只需要使用腳本簡單地創建一個賬户,這個腳本就會驗證輸入的數據和賬户狀態。
// pseudo codefn verify_voting(i, votes) -> bool { state[i] = votes; merkle_root(state) == output_account_root}
從上面的偽代碼中,我們可以看到驗證模型類似於 layer 1 層。
Godwoken 主合約使用了一個稀疏 merkle tree來存儲所有的賬户和賬户狀態。
因此,如果我們想要在 layer 1.5 層合約之間使用一個狀態,我們可以簡單的為這個狀態生成一個 merkle proof,並在合約中驗證 merkle proof。
如果我們想在 layer 1 層的合約中使用一個 layer 1.5 層的狀態,我們可以在交易的 cell_deps 字段中引用 Godwoken 主合約 cell,並從 cell 中讀取 Godwoken 中的全局狀態來獲得 merkle root,這樣就可以驗證狀態和 merkle proof 了。
通過創建一個抽象的賬户層,我們可以在 CKB 上通過最小的成本創建一個狀態共享的合約。
在後續的文章中,我們將會討論 Godwoken 中的詳細信息,我們如何維護 layer 1.5 層的賬户和合約,以及基於賬户模型的合約是如何工作的。
Godwoken 設計文檔
https://github.com/jjyr/godwoken/blob/master/docs/design.md
Sparse merkle tree
https://justjjy.com/An-optimized-compact-sparse-merkle-tree