以下內容轉載自Nervos Talk 論壇,作者為Ajian(比特幣內容平台BTC Study 編輯)。

摘要

理解一個系統的可程式性要求我們辨識這個系統在結構上的特徵。對基於比特幣腳本的應用程式設計的探索,有助於我們理解CKB Cell 的基本結構及其程式設計範式。不僅如此,它還能將CKB 的程式元件分解為適當的部分,並幫助我們理解每個部分所帶來的可程式性增益。

一. 引言

「可程式性(programmability)」 是人們在比較區塊鏈系統時經常採取的一個維度。然而,關於可程式性的描述方法,卻常見分歧。一種常見的表述是,“XX 區塊鏈支援圖靈完備的程式語言”,或者, “XX 區塊鏈支援通用的程式設計”,意在表示這裡的“XX 區塊鏈” 具備強大的可編程性。這些語句的暗示有一些道理:支援圖靈完備程式的系統一般都比不支援的更容易程式設計。但是,智能合約系統的結構性特徵有多個方面,這一語句只涉及其中一個方面,因此,不足以憑它獲得足夠深的理解:開發者從中得不到指引,普通用戶也無法憑此分辨詐騙。

智能合約系統在結構上的特徵包括:

  • 狀態表達(合約)的基本形式(帳戶vs. 交易輸出)
  • 是否允許程式任意計算(「圖靈完備」 的說法關涉的就是這個面向)
  • 執行過程可創造新數據,還是只傳出布林值? (計算vs. 驗證)
  • 是否允許在合約內記錄額外的狀態
  • 一個合約在執行的時候是否可以存取另一個合約的狀態

所以,在「可否程式任意計算」 之外,至少還有四個面向的特徵會影響一個智慧合約系統的可程式性。甚至可以說,這些其它方面的特徵是更為重要的,因為它們更深層地決定了什麼容易實現、什麼難以實現;什麼是較為經濟的實現,而什麼是較為低效的實現。

舉個例子,人們常常拿以太坊作為良好可編程性的例子,但是,以太坊的狀態表達的基本形式是帳戶,它難以編程點對點的合約(例如,支付通道、一對一的打賭合約) — — 並非絕對不能實現,只是吃力不討好。以太坊生態並非從未有過嘗試實現支付通道/狀態通道的項目,理論探討也有很多,但時至今日這些項目似乎都不活躍了—— 這顯然不能歸咎於開發者不努力。如今在以太坊上活躍的專案都採取了「資金池」 的形式,而非「點對點合約」 的形式,也不是偶然。同樣地,當前人們也許對以太坊的可編程性很滿意,但是,若要實現“帳戶抽象(account abstract)”(也可以理解為錢包概念的泛化) ,帳戶模型卻可以說是先天不足。

同理,探究CKB 的可程式性,也要求我們理解CKB 智能合約系統在這些方面的結構特徵。我們已經知道的是,CKB 允許編程任意計算、允許在合約內記錄額外的狀態、也允許一個合約在執行時存取另一個合約的狀態。但是,其合約的形式是交易的輸出(稱為“Cell”),這使得它跟以太坊產生了根本性的差異。因此,對以太坊智能合約系統以及其中的合約實例的了解,並不能幫助我們理解CKB 是如何實現這些結構特性的,也不能幫助我們認識CKB 的可程式性。

幸運的是,比特幣上的智能合約,似乎為我們理解CKB 的可編程性提供了最好的基礎。這不僅是因為比特幣的狀態表達的基本形式也是交易的輸出(稱為“UTXO”),更是因為,借助比特幣社區提出的一個概念“限制條款(covenants)”,我們可以理解CKB 具備上述結構特性的原因,並將最終的效果適當地分拆成幾個部分、逐一辨識它們所帶來的可編程性增益。

二. CKB vs 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 單簽名要么T1 時間鎖,Bob 單簽名

Bob 也簽署一筆(跟上述交易恰成對應的)承諾交易,並發送給Alice:

輸入#0,10 BTC: Alie-Bob 2-of-2 多簽名輸出(即通道合約)

輸出#0,6 BTC: Bob 單簽名

輸出#1,4 BTC: 要么Bob-Alice 聯合臨時公鑰#1 單簽名要么T1 時間鎖,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 = rG # 簽章所用nonce 值r 乘以橢圓曲線產生點,也可以說是r 的公鑰

s = r + Hash(R || m || P) * p # p 即為簽章私鑰,P 為公鑰

驗證簽章即驗證sG = rG + Hash(R || m || P) * pG = R + Hash(R || m || P) * PK

假設我給了一對資料(R, s'),其中:

R = R1 + R2 = r1.G + r2.G

s' = r1 + Hash(R || m || P) * p

顯然,這並不是一個有效的Schnorr 簽名,它無法通過驗簽公式,但是,我卻可以向驗證者證明,只需TA 知道R2 的私鑰r2 ,就可以讓它變成一個有效的簽名:

s'.G + R2 = R1 + Hash(R || m || P) * P + R2 = 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 先承諾了一個操作碼,以允許使用者通過新的一筆交易為該操作碼傳入參數,從而更新整個腳本樹葉子;這就已經不是“根據一定的條件驗證傳入的數據” 了,而是「根據傳入的資料產生新的有意義的資料」 了,雖然它可以啟用的計算是比較有限的。

完整的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 的「跨合約存取」 的能力,我們可以劃分運用內省能力的情形,並為它們確定具體的用途。

修訂:

1. 不考慮Cell 的交叉存取能力(即內省能力),lock scripts 可以認為是帶狀態、程式設計能力已趨極致的Bitcoin Script,因此單憑這一點就可以編程所有基於Bitcoin Script 的應用;

2. 不考慮Cell 的交叉存取能力(即內省能力),lock scripts 和type scripts 的區分可以認為是一種安全性升級:它切分了UDT 的資產定義與保管方法;此外,可暴露狀態的type scripts(以及Data)實現了UDT is first-class citizen 的效果。

以上兩點意味著一種跟“BTC + RGB” 相同範式但編程能力更強的東西;

3. 考慮Cell 的內省能力,Cell 可以獲得比post-covenants BTC UTXO 更強的程式設計能力,並實作一些BTC + RGB 難以實現的東西(因為BTC 無法閱讀RGB 的狀態)

關於這些用途,本文無法提出許多具體的例子,但這是因為筆者對CKB 的生態缺乏了解的緣故。假以時日,相信人們會在其中投入越來越多的想像力,組合出如今難以想像的應用。

致謝

感謝Retric,Jan Xie 和Xue Jie 在文章撰寫過程中提供的回饋。當然,文中所有的錯誤都由我自己負責。

參考文獻:

1.https://forum.celestia.org/t/accounts-strict-access-lists-and-utxos/37

2.https://www.btcstudy.org/2023/07/12/covenants-in-bitcoin-a-useful-review/

3.https://medium.com/nervosnetwork/https-medium-com-nervosnetwork-cell-model-7323fca57571

4.https://medium.com/nervosnetwork/nervos-ckb-in-a-nutshell-7a4ac8f99e0e

5.https://xuejie.space/2019_07_05_introduction_to_ckb_script_programming_validation_model/

6.https://www.btcstudy.org/2022/09/07/on-the-programmability-of-bitcoin-protocol/#(二)謹慎日誌合約(DLC)

7.https://mirror.xyz/0xbeC73ba0817403cd11C11bE891D671EA30443562/95LlE7sLCL4UTvL7rU3ZAXnBvlDbh7X-rm0QWkc43Us

8.https://www.btcstudy.org/2021/09/07/what-is-a-bitcoin-merklized-abstract-syntax-tree-mast/

9.https://www.btcstudy.org/2023/04/13/bitcoin-improvement-proposals-345-op-vault-commit-0b0674c546/

10.https://www.btcstudy.org/2023/06/29/tapleaf-update-verify-covenant-opcode-introduction/

11.https://www.btcstudy.org/2022/05/25/contracting-primitives-and-upgrades-to-bitcoin/

12.https://www.btcstudy.org/2022/01/27/breaking-down-the-bitcoin-lightning-network-eltoo/

13.https://github.com/btc-study/OP_QUESTION/discussions/7