從比特幣應用編程理解CKB的可編程性
原文作者:Ajian
摘要
理解一個系統的可編程性要求我們辨識這個系統在結構上的特徵。對基於比特幣腳本的應用編程的探索,有助於我們理解 CKB Cell 的基本結構及其編程範式。不僅如此,它還能將 CKB 的編程元件分解為恰當的部分,並幫助我們理解每一部分所帶來的可編程性增益。
一. 引言
“可編程性(programmability)” 是人們在比較區塊鏈系統時經常採取的一個維度。然而,關於可編程性的描述方法,卻常見分歧。一種常見的表述是,“XX 區塊鏈支持圖靈完備的編程語言”,或者, “XX 區塊鏈支持通用的編程”,意在表示這裏的 “XX 區塊鏈” 具備強大的可編程性。這些語句的暗示有一些道理:支持圖靈完備編程的系統一般都比不支持的更容易編程。但是,智能合約系統的結構性特徵有多個方面,這一語句只涉及其中一個方面,因此,不足以憑它獲得足夠深的理解:开發者從中得不到指引,普通用戶也無法憑此分辨詐騙。
智能合約系統在結構上的特徵包括:
-
狀態表達(合約)的基本形式(账戶 vs. 交易輸出)
-
是否允許編程任意計算(“圖靈完備” 的說法關涉的就是這個方面)
-
執行過程可創造新數據,還是只傳出布爾值?(計算 vs. 驗證)
-
是否允許在合約內記錄額外的狀態
-
一個合約在執行的時候是否可以訪問另一個合約的狀態
所以,在 “可否編程任意計算” 之外,至少還有四個方面的特徵會影響一個智能合約系統的可編程性。甚至可以說,這些其它方面的特徵是更為重要的,因為它們更深層地決定了什么容易實現、什么難以實現;什么是較為經濟的實現,而什么是較為低效的實現。
舉個例子,人們常常拿以太坊作為良好可編程性的例子,但是,以太坊的狀態表達的基本形式是账戶,它難以編程點對點的合約(例如,支付通道、一對一的打賭合約) —— 並非絕對不能實現,只是喫力不討好。以太坊生態並非從未有過嘗試實現 支付通道/狀態通道 的項目,理論探討也有很多,但時至今日這些項目似乎都不活躍了 —— 這顯然不能歸咎於开發者不努力。如今在以太坊上活躍的項目都採取了 “資金池” 的形式,而非 “點對點合約” 的形式,也不是偶然。同樣地,當前人們也許對以太坊的可編程性很滿意,但是,若要實現 “账戶抽象(account abstract)”(也可以理解為錢包概念的泛化) ,账戶模型卻可以說是先天不足。
同理,探究 CKB 的可編程性,也要求我們理解 CKB 智能合約系統在這些方面的結構特徵。我們已經知曉的是,CKB 允許編程任意計算、允許在合約內記錄額外的狀態、也允許一個合約在執行時訪問另一個合約的狀態。但是,其合約的形式是交易的輸出(稱為 “Cell”),這使得它跟以太坊產生了根本性的差異。因此,對以太坊智能合約系統以及其中的合約實例的了解,並不能幫助我們理解 CKB 是如何實現這些結構特性的,也不能幫助我們認識 CKB 的可編程性。
幸運的是,比特幣上的智能合約,似乎為我們理解 CKB 的可編程性提供了最好的基礎。這不僅是因為比特幣的狀態表達的基本形式也是交易的輸出(稱為 “UTXO”),更是因為,借助比特幣社區提出的一個概念 “限制條款(covenants)”,我們可以理解 CKB 具備上述結構特性的原因,並將最終的效果恰當地分拆成幾個部分、逐一辨識它們所帶來的可編程性增益。
二. CKB v.s. BTC:多了什么?
(一)基本結構
作為比特幣狀態表達的基本形式,比特幣的 UTXO(“未花費的交易輸出”)有兩個字段:
-
數額,以聰( Satoshi )為單位,表示該 UTXO 具備的比特幣價值;
-
腳本公鑰,也稱鎖定腳本,表示花費這筆資金所需滿足的條件,也即為解鎖這筆資金設定條件的智能合約程序。
與後來出現的智能合約系統相比,比特幣腳本是相當受限的:
-
它不允許編程任意計算;可用來驗證的較為實用的計算只有幾種(籤名檢查、哈希原像檢查、時間檢查)
-
它不允許在合約內記錄額外的狀態;(例如,你無法用腳本來限制單次花費的 比例/額度 上限;也無法在其中暗藏一種 token)
-
它也不允許在執行的時候訪問另一個合約的狀態(每個腳本都是獨立的宇宙,互不依賴)。
這種腳本雖然有限,但並不缺乏編程出讓人驚嘆的應用的能力,而且也正好是我們探索 CKB 可編程性的基礎。後文將有專門的一節來介紹比特幣腳本編程的兩個例子。
與之相對的,CKB 的狀態單元稱為 “Cell”,有四個字段:
-
Capacity,類似於 UTXO 的數額,表達的是該 Cell 可以佔據的空間大小,以字節(Bytes)為單位;
-
Lock Script,類似於 UTXO 的腳本公鑰,定義該 Cell 的所有權;只有所提供的數據能夠通過 Lock Script 時,才能 “更新” 這個 Cell(也可以說釋放這個 Cell 並用這些 Capacity 來鑄造新的 Cell);
-
Data,數據,任意數據,其體積受 Capacity 的限制;
-
Type Script,可選的腳本,用於為 Data 的更新設定條件。
此外,Lock Script 和 Type Script 還可以編程任意計算。你可以編程出任意的籤名驗證算法,也可以編程出任意一種哈希算法的原像檢查,等等等等。
讀者很容易就能看出,Cell 相比 UTXO 在可編程性上的提升:
-
Cell 可以編程任意計算,而不是只能編程特定的幾種計算;它的所有權驗證會更加靈活;
-
因為 Data 和 Type Script 字段,Cell 可以記錄額外的狀態;這就允許 Cell 承載所謂的 “UDT(用戶自定義的 Token”)。
結合 Cell 本身的 “交易輸出” 結構,這兩點本身能帶來的好處已然非常非常巨大,但是,僅從上面的描述,我們尚不知曉 Cell 是如何實現 “一個合約在運行時訪問另一個合約的狀態” 的。為此,我們需要借助比特幣社區探討了很長時間的一個概念:“限制條款(covenants)”。
(二)限制條款與內省
限制條款的本意是限制一筆錢能被花到哪裏去。在當前的比特幣(尚未部署任何限制條款提議)上,一筆資金一旦解鎖,就可以花到任何地方(可以支付給任意的腳本公鑰)。但限制條款的想法是,可以用某種方式,限制它只能花到某些地方去,比如,某一個 UTXO 將只能被某一筆交易花費,那么,即便有人能夠為這個 UTXO 提供籤名,它可以花到什么地方也已經被這筆交易決定了。這種功能看起來有點奇怪,卻能產生一些有趣的應用,後文會有專門的一節介紹。重要的是,它是我們進一步理解 CKB 可編程性的關鍵。
Rusty Russell 正確地指出,限制條款可以理解為對交易的 “內省” 能力,即,當一個 UTXO A 被一筆交易 B 花費時,腳本運算程序可以讀取交易 B 的部分(或者全部),然後檢查它們是否與腳本預先要求的參數一致。例如,交易 A 的第一個輸出的腳本公鑰,是否與 UTXO A 的腳本公鑰所要求的一致(這就是限制條款的最初含義)。
敏銳的讀者會意識到,如果具備了完全的內省能力,那么一個交易的輸入就可以讀取同一交易的另一個輸入的狀態,這就實現了我們前面說的 “一個合約在運行時訪問另一個合約的狀態” 的能力。事實上,CKB Cell 正是這么設計的。
基於此,我們又可以將這種完全的內省能力分成四種情形:
-
Lock Script 讀取其它(輸入和輸出)的 Lock Script
-
Lock Script 讀取其它(輸入和輸出)的 Type Script(以及 Data)
-
Type Script 讀取其它(輸入和輸出)的 Lock Script
-
Type Script 讀取其它(輸入和輸出)的 Type Script(以及 Data)
這就允許我們在一定的假設(Lock Script 和 Type Script 的功能分工)之下分析每一部分的內省能力在不同應用場景中的作用,也即分析每一部分為我們帶來的可編程性增益。
在下面的兩個章節,我們將分別了解當前(尚未限制條款提議)的比特幣腳本編程,以及限制條款提議有望實現功能,從而具體地理解 CKB Cell 如何編程並做得更好。
三. 比特幣腳本編程
本節將使用 “閃電通道/閃電網絡” 和 “謹慎日志合約(DLC)” 作為基於比特幣腳本的應用編程的案例。在展开之前,我們要先了解兩個概念。
(一)OP_IF 以及 “承諾交易”
第一個概念是比特幣腳本中的流程控制操作碼,比如:OP_IF、OP_ELSE。這些操作碼跟計算機編程中的 IF 沒有什么區別,它的作用就是根據不同的輸入執行不同的的語句。在比特幣腳本的語境下,這意味着我們可以設置資金的多個解鎖路徑;搭配時間鎖特性,這意味着我們可以分配行動的優先權。
以著名的 “哈希時間鎖合約(HTLC)” 為例,這種腳本翻譯成大白話就是:
要么,Bob 可以揭曉某個哈希值 H 背後的原像,再給出自己的籤名,即可花費這筆資金
要么, Alice 可以在一段時間 T 過後,憑借自己的籤名花費這筆資金
這種 “要么 …… 要么 ……” 的效果,就是通過流程控制操作碼實現的。
HTLC 最突出的優點是它可以將多個操作捆綁在一起、實現原子化。例如,Alice 希望跟 Bob 以 BTC 交換 CKB,那么,Bob 可以先給出一個哈希值,並在 Nervos Network 上創造一個 HTLC;然後 Alice 在比特幣上創造一個使用相同哈希值的 HTLC。要么,Bob 在比特幣上拿走 Alice 支付的 BTC,同時也揭曉原像,從而允許 Alice 在 Nervos Network 上取走 CKB。要么,Bob 不揭曉原像,兩個合約都過期,Alice 和 Bob 都可以取回自己投入的資金。
在 Taproot 軟分叉激活之後,這種多解鎖路徑的特性因為 MAST (默克爾抽象語法樹) 的引入而得到進一步的強化:我們可以將一條解鎖路徑變成默克爾樹上的一個葉子,每個葉子都是獨立的,因此不再需要使用這樣的流程控制操作碼;而且,因為揭曉一條路徑時無需曝光其它路徑,我們可以為一個輸出加入更多數量的解鎖路徑,而不必擔心經濟性問題。
第二個概念是 “承諾交易”。承諾交易的想法是,在一些情況下,一筆有效的比特幣交易,即使它不得到區塊鏈的確認,也同樣是真實的,有約束力的。
例如,Alice 和 Bob 共同擁有一個 UTXO,這個 UTXO 需要他們兩人的籤名才能花費。這時候,Alice 構造一筆交易來花費它,將其中 60% 的價值轉移給 Bob,剩下的價值轉移給自己;Alice 為這筆交易提供自己的籤名,然後發送給 Bob。那么,對 Bob 來說,不必將這筆交易廣播到比特幣網絡中,也不必讓這筆交易得到區塊鏈的確認,這筆交易的支付效果也是真實的,可信的。因為 Alice 無法獨自花費這個 UTXO(因此無法重復花費),也因為 Alice 所提供的籤名是有效的,Bob 隨時可以加上自己的籤名,然後廣播該交易,從而兌現這筆支付。也即,Alice 通過這筆有效的(不上鏈的)交易,給 Bob 提供了一個 “可信的承諾”。
承諾交易是比特幣應用編程的核心概念。如前所述,比特幣的合約是基於驗證的、無狀態的、不允許交叉訪問的;但是,如果合約不攜帶狀態,那這些狀態存放在哪裏、合約如何安全推進(變更狀態)?承諾交易給出了直截了當的答案:合約的狀態可以用交易的形式來表達,從而,合約的參與者可以自己保存狀態,而不必將它們展現在區塊鏈上;合約的狀態變更問題,也可以轉化成如何安全地更新承諾交易的問題;此外,如果我們擔心進入一個合約會有危險(例如,進入一個要求雙方都籤名才能花費的合約,會面臨對方不響應從而卡死的風險),那么,只需提前生成花費該合約的承諾交易並獲得籤名,就可以化解風險、消除對其他參與者的信任。
(二)閃電通道與閃電網絡
閃電通道是一種一對一的合約,在這種合約中,雙方可以無限次地相互支付,而不必讓任何一次支付獲得區塊鏈的確認。如你所料,它用到了承諾交易。
在解釋 “承諾交易” 的部分,我們已經介紹了一種支付通道。但是,這種僅利用 2-of-2 多籤名的合約僅能實現單向支付。即,要么一直是 Alice 向 Bob 支付,要么一直是 Bob 向 Alice 支付,直至用盡自己在合約中的余額。如果是雙向支付,那就意味着,在某一次狀態更新之後,一方的余額可能變得比以前更少,但是,TA 卻擁有對方籤過名的上一筆承諾交易 —— 有什么辦法阻止 TA 廣播舊的這筆承諾交易、讓 TA 只能廣播最新一筆承諾交易呢?
閃電通道解決這個問題的辦法叫做 “LN-Penalty”。現在,假設 Alice 和 Bob 在一條通道中各擁有 5 BTC;現在 Alice 要給 Bob 支付 1 BTC ,於是籤名這樣一筆承諾交易,並發送給 Bob:
輸入 #0 , 10 BTC:
Alie-Bob 2-of-2 多籤名輸出(即通道合約)
輸出 #0 , 4 BTC:
Alice 單籤名
輸出 #1 , 6 BTC:
要么
Alice-Bob 聯合臨時公鑰 #1 單籤名
要么
T 1 時間鎖,Bob 單籤名
Bob 也籤名一筆(跟上述交易恰成對應的)承諾交易,並發送給 Alice:
輸入 #0 , 10 BTC:
Alie-Bob 2-of-2 多籤名輸出(即通道合約)
輸出 #0 , 6 BTC:
Bob 單籤名
輸出 #1 , 4 BTC:
要么
Bob-Alice 聯合臨時公鑰 #1 單籤名
要么
T 1 時間鎖,Alice 單籤名
這裏的訣竅,就在於這個 “聯合臨時公鑰”,它是使用己方的一個公鑰和對方提供的一個公鑰生成的,例如,Alice-Bob 聯合臨時公鑰,是 Alice 使用自己的一個公鑰,和 Bob 提供的一個公鑰,各自乘以一個哈希值再相加,得出來的。這樣一個公鑰,在生成出來的時候,是誰也不知道其私鑰的。但是,如果 Bob 把自己所提供的公鑰的私鑰告訴了 Alice,Alice 就可以計算出這個聯合臨時公鑰的私鑰。—— 這就是閃電通道可以 “撤銷” 舊狀態的關鍵。
在下一次雙方要更新通道狀態(發起支付)時,雙方就交換上一輪中交給對方的臨時公鑰的私鑰。如此一來,參與者就再也不敢廣播自己得到的上一筆承諾交易:這筆承諾交易為己方分配價值的輸出有兩個路徑,而臨時公鑰路徑的私鑰已被對方知道;所以一旦廣播舊的承諾交易,對方就可以立即動用這個聯合臨時私鑰,從而將這個輸出中的資金全部拿走。 —— 這就是 “LN-Penalty” 的含義。
具體來說,交互的順序是:發起支付的一方先向對方請求新的臨時公鑰,然後構造一筆新的承諾交易並交給對方;得到了承諾交易的一方向對方曝光自己在上一輪給出的臨時公鑰的私鑰。這樣的交互順序保證了參與者總是先得到新的承諾交易,然後才作廢自己在上一輪中得到的承諾交易,因此是免信任的。
綜上,閃電通道的關鍵設計有:
-
雙方總是使用承諾交易來表達合約內部的狀態,並以數額的變化來表示支付;
-
承諾交易總是花費同一個輸入(需要雙方同時提供籤名的輸入),因此所有承諾交易都是相互競爭的,最終只有一筆能夠得到區塊鏈的確認;
-
兩個參與者籤名的並不是同一筆承諾交易(雖然它們是成對的);而他們所籤名的總是對自己更有利的交易,換句話說,參與者收到的承諾交易,總是對自己不利的;
-
這種不利體現在,為自己分配價值的輸出帶有兩個解鎖路徑:一條路徑可以憑自己的籤名解鎖,卻需要等待一段時間;而另一條路徑則用到了對方的公鑰,僅當自己的一個臨時私鑰不暴露,才受到保護;
-
在每一次支付中,雙方都以新的一筆承諾交易來交換對方在上一輪使用的臨時私鑰,從而,交出了臨時私鑰的一方就不再敢廣播舊的一筆承諾交易,因此,就 “撤銷” 了上一筆承諾交易、更新了合約的狀態;(實際上,這些承諾交易都是有效的交易,都是可以廣播到區塊鏈上的,只是參與者迫於懲罰,不敢再廣播了)
-
任何一方隨時都可以拿對方籤過名的承諾交易退出合約;但是,如果雙方愿意合作,他們可以籤名一筆新的交易,讓雙方都可以立即拿回屬於自己的錢。
最後,因為承諾交易中也可置入 HTLC,所以,閃電通道也可以轉發支付。假定 Alice 可以找出一條由閃電通道前後相接所組成的路徑、觸達 Daniel,那么無需跟 Daniel 开設通道就可以實現免信任的多跳支付。這便是閃電網絡:
Alice -- HTLC --> Bob -- HTLC --> Carol -- HTLC --> Daniel
Alice <-- 原像 -- Bob <-- 原像 -- Carol <-- 原像 -- Daniel
當 Alice 找出了這樣的路徑並希望給 Daniel 支付時,她向 Daniel 請求一個哈希值,據以構造一個 HTLC 給 Bob,並提示 Bob 給 Carol 轉發一條消息並提供相同的 HTLC;消息中又提示 Carol 給 Daniel 轉發消息並提供相同的 HTLC。當消息傳到 Daniel 手上時,他向 Carol 揭示原像,從而獲得 HTLC 的價值、更新合約狀態;Carol 也如法炮制,獲得 Bob 的支付並更新通道狀態;最後,Bob 向 Alice 揭示原像、更新狀態。由於 HTLC 的特性,這一連串的支付要么一起成功,要么一起失敗,因此,它是免信任的。
閃電網絡是由一條又一條的通道組成的,每一條通道(合約)都是相互獨立的。這意味着 Alice 只需知曉自己跟 Bob 的通道內發生的事情,而不必理會其他人的通道中發生了多少次交互,也不必理會這些交互使用了哪一種貨幣,甚至不必知曉他們是不是真的利用了通道)。
閃電網絡的可擴展性不僅體現在一條閃電通道內部的支付速度僅受限於雙方的硬件資源投入,還在於,由於狀態的分散存儲,單體節點可以用最低的成本撬動最大的槓杆。
(三)謹慎日志合約
謹慎日志合約(DLC)使用了一種叫做 “適配器籤名(adaptor signature)” 的密碼學技巧,使得比特幣腳本可以編程出依賴於外部事件的金融合約。
適配器籤名可以讓一個籤名僅在加入一個私鑰之後,才會變成有效的籤名。以 Schnorr 籤名為例,Schnorr 籤名的標准形式是 (R, s),其中:
R = r.G
# 籤名所用 nonce 值 r 乘以橢圓曲线生成點,也可以說是 r 的公鑰s = r + Hash(R || m || P) * p
# p 即為籤名私鑰,P 為公鑰驗證籤名即驗證 s.G = r.G + Hash(R || m || P) * p.G = R + Hash(R || m || P) * PK
假設我給出了一對數據 (R, s'),其中:
R = R 1 + R 2 = r 1.G + r 2.G
s' = r 1 + Hash(R || m || P) * p
顯然,這並不是一個有效的 Schnorr 籤名,它無法通過驗籤公式,但是,我卻可以向驗證者證明,只需 TA 知道 R 2 的私鑰 r 2 ,就可以讓它變成一個有效的籤名:
s'.G + R 2 = R 1 + Hash(R || m || P) * P + R 2 = R + Hash(R || m || P) * P
適配器籤名讓一個籤名的有效性依賴於一個祕密數據,並且是可驗證的。但是,這跟金融合約有什么關系呢?
假定 Alice 和 Bob 希望打賭一場球賽的結果。Alice 和 Bob 分別賭綠魔和阿林納勝出,賭注是 1 BTC。並且,球評網站 Carol 承諾會在球賽結果揭曉時,用一個 nonce R_c 發布對結果的籤名 s_c_i。
可以看出,一共有三種可能的結果(因此 Carol 的籤名有 3 種可能):
- 綠魔勝出,Alice 贏得 1 BTC
- 阿林納勝出,Bob 贏得 1 BTC
- 平局,兩人的資金原路返回
為此,兩人為每一種結果創建一筆承諾交易。例如,他們為第一種結果創建的承諾交易是這樣的:
輸入 #0 , 2 BTC:
Alie-Bob 2-of-2 多籤名輸出(即打賭合約)
輸出 #0 , 2 BTC:
Alice 單籤名
但是,Alice 和 Bob 為這筆交易創建的籤名卻不是 (R, s),而是適配器籤名 (R, s');也即,雙方交給對方的籤名都是不能直接用來解鎖這個合約的,而必須揭曉一個祕密值才可以。這個祕密值,正是 s_c_ 1.G 的原像,也即 Carol 的籤名!因為 Carol 的籤名 nonce 值已經確定了(是 R_c),所以,s_c_ 1.G 是可以構造出來的(s_c_ 1.G = R_c + Hash(R_c || '綠魔勝出' || PK_c) * PK_c)。
當結果揭曉的時候,假定綠魔勝出,Carol 就會發布籤名 (R_c, s_c_ 1),那么無論 Alice 還是 Bob,都可以補完對手的適配器籤名,再加上自己的籤名,使上述交易成為一筆有效交易,並廣播到網絡中、觸發結算效果。但如果綠魔沒有勝出,Carol 就不會發布 s_c_ 1 ,這筆承諾交易也就不可能成為一筆有效交易。
以此類推,另外兩筆交易也是如此。就這樣,Alice 和 Bob 讓這個合約的執行依賴於外部事件(准確來說是依賴於斷言機對外部事件的播報,其形式是個籤名),而且不需要信任對手方。大大小小的金融合約,比如期貨、期權,都可以用這種方式來實現。
與其它形式的實現相比,謹慎日志合約最大的特點在於其隱私性:(1)Alice 和 Bob 不需要告知 Carol 自己正在使用 Carol 的數據,這完全不影響合約的執行;(2)鏈上觀察者(也包括 Carol 在內),也無法通過 Alice 和 Bob 的合約執行交易來判定他們正在使用哪個網站的服務,甚至無法斷定他們的合約是一個打賭合約(而不是一個閃電通道)。
四. 限制條款應用簡介
(一)OP_CTV 與擁堵控制
比特幣社區的开發者曾提出過多種可被歸類為限制條款的提議。從當前來看,最著名的一個提議當屬 OP_CHECKTEMPLATEVERIFY(OP_CTV),其概念較為簡單,卻保留了相當大的靈活性,因此受到崇尚簡潔的比特幣社區的歡迎。OP_CTV 的想法是,在腳本中承諾一個哈希值,以約束這筆資金只能被這個哈希值所表示的的交易花費;這個哈希值承諾了交易的輸出以及大部分字段,但不承諾交易的輸入,只承諾輸入的數量。
“擁堵控制” 是一個可以體現 OP_CTV 特性的好例子。其基本應用場景是幫助大量的用戶從交易所(一個需要信任的環境)退出到一個資金池中;由於這個資金池使用 OP_CTV 規劃了未來的花費方式,因此它可以保證用戶可以免信任地退出這個資金池,不需要任何人的幫助;又因為這個資金池只表現為一個 UTXO,它避免了在鏈上交易需求高漲時支付大量手續費(從 n 個輸出減少到了 1 個輸出;也從 n 筆交易減少到了 1 個交易)。池內用戶可以伺機再從池中退出。
假設 Alice、Bob、Carol 分別想從交易所中取出 5 BTC、 3 BTC 和 2 BTC。那么交易所可以制作一個帶有 3 個 OP_CTV 分支的、數額為 10 BTC 的輸出。假設 Alice 想要取款,她可以動用分支 1 ;該分支的 OP_CTV 所用的哈希值所代表的交易,將形成兩個輸出,一個輸出是為 Alice 分配 5 BTC;另一個輸出又是一個資金池,也使用 OP_CTV 承諾一筆交易,只允許 Bob 取出 3 BTC,並將剩下的 2 BTC 發送給 Carol。
Bob 或者 Carol 想要取款,也是同理。他們在取款時,將只能使用能夠通過相應 OP_CTV 檢查的交易,也即只能給自己支付相應的數額,而不能任意取款;剩余的資金將又進入一個使用 OP_CTV 鎖的資金池,從而保證無論用戶的取款順序如何,剩余的用戶都能免信任地從池中退出。
抽象地說,OP_CTV 在這裏的作用是為合約規劃走向合約生命終結的路徑,使得這裏的資金池合約不論走哪一條路徑、走到了哪個狀態,都能保持免信任退出的屬性。
這種 OP_CTV 還有一種非常有趣的用法:“隱而不發的單向支付通道”。假設 Alice 形成了這樣一個資金池,並保證資金可以免信任地退出到一個帶有如下腳本的輸出中:
要么, Alice 和 Bob 一起花費它
要么,一段時間後,Alice 可以獨自花費它
如果 Alice 不向 Bob 揭曉,Bob 就不會知道有這樣的輸出存在;一旦 Alice 向 Bob 揭曉,Bob 就可以把這個輸出當成一個有時效性的單向支付通道,Alice 可以立即用其中的資金給 Bob 支付,而不必等待區塊鏈的確認。Bob 只需在 Alice 可以獨自花費它之前,讓 Alice 給他的承諾交易上鏈即可。
(二)OP_Vault 與保險櫃
OP_VAULT 是一種專為構造 “保險櫃合約(vaults)” 而提出的限制條款提議。
保險櫃合約旨在成為一種更安全、更高級的自主保管形式。當前的多籤名合約雖然能免去單個私鑰的單點故障,但如果攻擊者真的獲得了閾值數量的私鑰,錢包的主人是無計可施的。保險櫃希望能為資金施加單次花費的限額;同時,使用常規路徑從中取款時,取款操作將強制執行一個等待期;而在等待期內,取款操作可以被緊急恢復錢包的操作打斷。這樣的合約,即使被攻破,錢包的主人也可以(使用緊急復原分支)發起反制操作。
理論上,OP_CTV 也可以編程出這樣的合約,但卻有許多的不便利,其中之一是手續費:在承諾交易的同時,它也承諾了該交易將支付的手續費。考慮到這種合約的用途,設置合約和取款的時間間隔必定很長,那就幾乎不可能預測出合適的手續費。盡管 OP_CTV 沒有限制輸入,因此可以通過增加輸入來增加手續費,但所提供的輸入將全部變成手續費,因此是不現實的;另一種方式是 CPFP,也即通過花費取出的資金,在新的交易中提供手續費。此外,使用了 OP_CTV 也意味着這樣的保險櫃合約無法批量取款(當然也無法批量復原)。
OP_VAULT 提議則嘗試通過提出新的操作碼(OP_VAULT 和 OP_UNVAULT)來解決這些問題。OP_UNVAULT 是專為批量復原而設計的,我們暫時不提。OP_VAULT 的動作則是這樣的:當我們把它放在腳本樹的一個分支上時,它可以用來承諾一個可以動用的操作碼(例如 OP_CTV)而不設具體的參數;在花費這個分支時,交易可以傳入具體的參數,但不能更改其它分支。由此,它不必預設手續費,可以在花費這個分支時再設定手續費;假定這個分支也帶有時間鎖,那么它就會強制執行一個時間鎖;最後,因為只可改變自身所在的分支,新的腳本樹上的其它分支(包括緊急復原分支)不會被改變,因此允許我們打斷這樣的取款操作。
此外,還有兩點值得一提:(1)OP_VAULT 操作碼的動作類似於另一個限制條款提議:OP_TLUV ;Jeremy Rubin 正確地指出,這在一定程度上已經產生了 “計算” 的概念:OP_TLUV/OP_VAULT 先承諾了一個操作碼,以允許使用者通過新的一筆交易為該操作碼傳入參數,從而更新整個腳本樹葉子;這就已經不是 “根據一定的條件驗證傳入的數據” 了,而是 “根據傳入的數據產生新的有意義的數據” 了,雖然它可以啓用的計算是比較有限的。
(2)完整的 OP_VAULT 提議也利用了一些交易池策略(mempool policy)上的提議(比如 v3 格式的交易)以取得更好的效果。這提醒了我們 “編程” 的含義可以比我們想象的更為廣泛。(一個相似的例子是 Nervos Network 裏面的 Open Transaction。)
五. 理解 CKB
在上述兩個章節中,我們介紹了在一種更為受限的結構( Bitcoin UTXO)上,我們如何用腳本編程出有趣的應用;也介紹了嘗試為這種結構加入內省能力的提議。
UTXO 雖然不乏編程出這些應用的能力,但讀者也很容易覺察出它們的缺點,或者說可以優化的地方,比如:
-
在 LN-Penalty 中,通道的參與者必須保存過往的每一筆承諾交易以及相應的懲罰祕密值,以應對對手的欺詐,這構成了存儲上的負擔。如果有一種機制,可以確保只有最新的一筆承諾交易才會生效,而舊的承諾交易無法生效,那就可以免去這種負擔,而且,也可以消除節點因為故障而誤將較舊的承諾交易上鏈,因此被意外懲罰的問題。
-
在 DLC 中,假設事件的可能結果有很多,雙方要提前生成、交給對方的籤名也便有很多,這也是一種巨大的負擔;此外,DLC 合約的收益是直接綁定在公鑰上的,因此其倉位是不便於轉移的,有沒有辦法可以轉移合約的倉位呢?
實際上,比特幣社區已經為這些問題提出了答案,基本上跟一種 Sighash 提議(BIP-118 AnyPrevOut)有關。
但是,如果我們是在 CKB 上編程,BIP-118 等於是現在就可用了(可以用內省和針對性驗證籤名的能力模擬出這種 Sighash 標籤)。
通過學習比特幣編程,我們不僅知道了 “交易輸出” 這種格式下可以如何編程(CKB 能編程什么),還能知道這些應用的改進方法(如果我們在 CKB 上編程這些應用,可以如何運用 CKB 的能力來改進它們)。對於 CKB 开發者來說,簡直可以將基於比特幣腳本的編程當成一種學習的教材,甚至是捷徑。
下面,我們逐一分析 CKB 編程的各個模塊的可編程性。我們先不考慮內省能力。
(一)可編程任意計算的 Lock Script
如上所述,UTXO 是不能編程任意計算的。而 Lock Script 可以,這就意味着 Lock Script 可以編程出(限制條款部署前)基於 UTXO 編程的所有東西,包括但不限於上文所述的閃電通道和 DLC。
此外,這種可驗證任意計算的能力,還使得 Lock Script 可以動用的身份驗證手段比 UTXO 更多,更靈活。比如說,我們可以在 CKB 上實現一種一方使用 ECDSA 籤名、另一方使用 RSA 籤名的閃電通道。
實際上,這正是人們在 CKB 上最早开始探索的領域之一:將這種靈活的身份驗證能力用在用戶的自主保管中,從而實現所謂的 “账戶抽象” —— 交易有效性的授權和控制權的恢復都非常靈活,幾乎沒有限制。原理上,這就是 “多種花費分支” 以及 “任意身份驗證手段” 的結合。實現的例子有:joyid wallet、 UniPass 。
此外,Lock Script 也可以實現 eltoo 提議,從而實現只需保留最新一筆承諾交易的閃電通道(實際上,eltoo 可以簡化一切點對點合約)。
(二)可編程任意計算的 Type Script
如上所述,Type Script 的一大用途是編程 UDT。結合 Lock Script,這意味着我們可以實現以 UDT 為標的的閃電通道(以及其它類型的合約)。
實際上,Lock Script 和 Type Script 的分割可以視為一種安全性升級:Lock Script 專注於實現保管方法或者合約式協議,而 Type Script 專注於 UDT 的定義。
此外,基於 UDT 的定義啓動檢查的能力,還使得 UDT 能夠以跟 CKB 類似的方式參與合約(UDT is first-class citizen)。
舉個例子:筆者曾經提出過一種在比特幣上實現免信任 NFT 擔保借貸的協議。這種協議的關鍵是一種承諾交易,其輸入的價值是小於輸出的價值的(因此它還不算是一筆有效的交易),但是,一旦能夠為這筆交易提供足額的輸入,它就是一筆有效的交易:一旦貸款人能夠還款,放貸者就不能將質押的 NFT 據為己有。但是,這個承諾交易的免信任性基於交易對輸入和輸出的數額的檢查,所以貸款人只能使用比特幣來還款 —— 即使貸款人和放貸者都愿意接受另一種貨幣(比如以 RGB 協議發行的 USDT),比特幣的承諾交易也無法保證只要貸款人歸還了足額的 USDT 就能拿回自己的 NFT,因為比特幣交易根本不知道 USDT 的狀態!(修訂:換言之,無法構造出以 USDT 還款為條件的承諾交易。)
如果我們能夠根據 UDT 的定義發起檢查,將可以讓放貸者籤名另一筆承諾交易,允許貸款人使用 USDT 來還款。交易將檢查輸入的 USDT 數量和輸出的 USDT 數量,從而為用戶使用 USDT 還款賦予免信任性。
修訂:假定這裏用作抵押的 NFT 和用於還款的 token 是使用同一套協議(比如 RGB)發行的,那么,這裏的問題是能夠解決的,我們可以根據 RGB 協議構造一種承諾交易,使得 NFT 的狀態轉換和還款可以同步發生(在 RGB 協議內用交易綁定兩個狀態轉換)。但是,因為 RGB 的交易也要依賴於比特幣交易,這裏的承諾交易的構造會有一定的難度。總而言之,盡管問題可以解決,但做不到 token is first-class citizen。
接下來我們再考慮內省能力。
(三)Lock Script 讀取其它 Lock Scripts
這意味着限制條款提議實施之後,比特幣 UTXO 上的全部編程可能性。包括上文提到的保險櫃合約,以及基於 OP_CTV 的應用(比如擁堵控制)。
XueJie 曾經提過一個非常有趣的例子:你可以在 CKB 上實現一種收款账戶 Cell,在使用這種 Cell 作為交易的輸入時,如果它輸出的 Cell (使用相同 Lock Script 的 Cell)具備更多的 Capacity,那么這個輸入無需提供籤名也不會影響交易的有效性。實際上,如果沒有內省的能力,這種 Cell 是無法實現的。這種收款账戶 Cell 非常適合作為機構的收款方式,因為它可以將資金歸集起來,缺點是它的隱私性不佳。
(四)Lock Script 讀取其它 Type Scripts(以及 Data)
這種能力的一個有趣的應用是股權 Token。Lock Script 將根據其它輸入中的 Token 的數量來決定能否動用自身的 Capacity,以及這些 Capacity 能夠花到哪裏去(需要內省 Lock Script 的能力)。
(五)Type Script 讀取其它 Lock Scripts
不確定,但可以假設有用。例如,可以在 Type Script 中檢查交易的輸入和輸出的 Lock Scripts 保持不變。
(六)Type Scirpt 讀取其它 Type Scripts(以及 Data)
集換卡?集齊 n 個 token 可以換取更大的一個 token : )
六. 結論
與此前出現的可編程任意計算的智能合約系統(如以太坊)相比,Nervos Network 採取了不同的結構;因此,對以往那些智能合約系統的了解,往往難以成為理解 Nervos Network 的基礎。本文從一種比 CKB Cell 更為受限的結構 —— BTC UTXO —— 的應用編程出發,提出了一種理解 CKB Cell 可編程性的方法。並且,運用 “內省” 的概念來理解 Cell 的 “跨合約訪問” 的能力,我們可以劃分運用內省能力的情形,並為它們確定具體的用途。
修訂:
-
不考慮 Cell 的交叉訪問能力(即內省能力),lock scripts 可以認為是帶狀態、編程能力已趨極致的 Bitcoin Script,因此單憑這一點就可以編程所有基於 Bitcoin Script 的應用;
-
不考慮 Cell 的交叉訪問能力(即內省能力),lock scripts 和 type scripts 的區分可以認為是一種安全性升級:它切分了 UDT 的 資產定義 與 保管方法;此外,可暴露狀態的 type scripts(以及 Data)實現了 UDT is first-class citizen 的效果。
以上兩點意味着一種跟 “BTC + RGB” 相同範式但編程能力更強的東西;
-
考慮 Cell 的內省能力,Cell 可以獲得比 post-covenants BTC UTXO 更強的編程能力,並實現一些 BTC + RGB 難以實現的東西(因為 BTC 無法閱讀 RGB 的狀態)
關於這些用途,本文無法提出很多具體的例子,但這是因為筆者對 CKB 的生態缺乏了解的緣故。假以時日,相信人們會在其中投入越來越多的想象力,組合出如今難以想象的應用。
致謝
感謝 Retric,Jan Xie 和 Xue Jie 在文章撰寫過程中提供的反饋。當然,文中所有的錯誤都由我自己負責。
鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播信息之目的,不構成任何投資建議,如有侵權行為,請第一時間聯絡我們修改或刪除,多謝。
24H熱門幣種與要聞 | Michael Saylor發布數字資產框架提案;Azuki疑似即將發幣(12.23)
24 H 熱門幣種 1、CEX 熱門幣種 CEX 成交額 Top 10 及 24 小時漲跌幅: B...
星球日報
文章數量
7681粉絲數
0