撰文:Chakra Research

概述

與以太坊等圖靈完備的區塊鏈相比,比特幣上的腳本被認爲存在很大限制,只能進行基本操作,甚至不支持乘除法。更加重要的是,區塊鏈自身的數據也幾乎無法被腳本讀取,導致靈活性嚴重不足,可編程性低。因此,人們長期以來都在試圖尋找讓比特幣腳本(Script)獲得內省(Introspection)的方法。

內省指的是允許比特幣腳本檢查和約束交易數據的能力。這使得腳本可以根據交易的具體細節來控制資金的使用,從而實現更復雜的功能。當前比特幣的操作碼(Opcode)大多隻具備兩種功能,將用戶提供的數據推送到堆棧上,或者操作堆棧上已有的數據。內省操作碼則能夠將當前交易的數據(例如時間戳、金額、交易 ID 等)推送到堆棧上,對 UTXO 的支出方式進行更加細粒度的控制。

當前比特幣腳本中只有三個主要的操作碼支持內省: CHECKLOCKTIMEVERIFY、CHECKSEQUENCEVERIFY、CHECKSIG;CHECKSIG 還有 CHECKSIGVERIFY、CHECKSIGADD、CHECKMULTISIG 和 CHECKMULTISIGVERIFY 等變體。

契約(Covenant)簡單來講,是對代幣如何轉移的限制,用戶通過契約能夠指定 UTXO 的分發方式。許多契約都是通過內省操作碼實現的,現在 Bitcoin Optech 中內省已經被歸類至契約條目下討論。

比特幣現有兩個契約,CSV (CheckSequenceVerify) 和 CLTV (CheckLockTimeVerify),都爲時間契約,這是許多擴容方案運行的基礎,例如閃電網絡。從中我們也可以看出,比特幣的擴容方案,極大程度上依賴內省與契約。

如何對代幣的轉移添加限制條件?在加密世界中,我們最常用的方式是承諾(Commitment),往往通過哈希(Hash)來實現。而爲了證明我們滿足轉移的要求,又需要通過簽名機制來驗證。因此,契約中存在許多對哈希與簽名的調整。

下面我們將描述被廣泛討論的契約操作碼提案。

CTV (CheckTemplateVerify) BIP-119

CTV (CheckTemplateVerify) 是一項被社區熱議的比特幣升級,包含在 BIP-119 中。CTV 使輸出腳本能夠指定支出交易中使用這筆資金的模板,包括交易的 nVersion、 nLockTime、 scriptSig hash、 input count、 sequences hash、 output count、 outputs hash、 input index 等字段,這些模板限制是通過一個哈希承諾來實現的,未來花費時腳本將檢查支出交易中指定字段的哈希值與輸入腳本中的哈希值匹配,這些模板實際限定了該 UTXO 未來支出交易的時間、方式、金額等細節。

值得注意的是,輸入的 TXID 從哈希承諾中被排除了。這種排除是必需的,無論是在 Legacy 或者 Segwit 交易中,在默認的 SIGHASH_ALL 簽名類型中,TXID 都要依賴 scriptPubKey 的值生成。因此,包含 TXID 將導致哈希承諾的循環,無法成功構建。

CTV 實現內省的方式是通過新的操作碼直接獲取交易的指定信息進行哈希,並與堆棧中的承諾進行對比。這種內省方式對鏈上空間的消耗較少,但是缺乏一定的靈活性。

閃電網絡等比特幣二層方案的基礎是預簽名交易。預簽名通常指的是提前生成並簽名交易,但在一定條件滿足之前不將其廣播到網絡。從本質上講,CTV 實現了一個更加嚴格的預簽名功能,將預簽名的承諾直接發佈在了鏈上,只能按照預先模板進行交易。

CTV 最初被提出是爲了緩解比特幣的擁堵,也可以稱之爲擁塞控制。在比特幣較爲擁堵時,可以通過 CTV 通過單筆交易承諾多個未來交易,而無需在擁堵時廣播多個交易,待區塊擁堵緩解後,再完成實際交易。擁塞控制在交易所遭到擠兌時可能起到很大的幫助。此外,模板還可用於保險庫(Vault)的實現,防範黑客攻擊。由於資金的去向被確定,黑客無法將使用 CTV 腳本的 UTXO 發送到自己的地址。

CTV 能爲二層網絡帶來極大的改善。例如,對於閃電網絡中 Timeout Trees 與通道工廠 的實現,通過 CTV,單個 UTXO 能夠展開成一顆 CTV 樹,同時打開多個狀態通道,而在鏈上僅有一筆交易與一個確認。此外,CTV 也爲 Ark 協議中的原子交易 ATLC 提供了支持。

APO (SIGHASH_ANYPREVOUT) BIP-118

BIP-118 爲 tapscript 提出了一種新型的簽名哈希標誌,以便於編寫更加靈活的支出邏輯,稱爲 SIGHASH_ANYPREVOUT。APO 與 CTV 在許多方面都比較相似,面對 scriptPubKeys 和 TXID 之間的循環問題,APO 的解決方案是將輸入的相關信息排除在外,僅對輸出進行簽名,使得交易能夠動態綁定到不同的 UTXO。

從邏輯上來劃分,驗籤操作 OP_CHECKSIG(及其同類操作碼)有三個功能:

  1. 組裝花費交易的各個部分

  2. 對其進行哈希處理。

  3. 驗證哈希是否已由給定密鑰簽名。

而簽名的具體內容,具備很大的調整空間,組裝哪些交易字段進行簽字,是由 SIGHASH 標誌決定的。根據 BIP 342 簽名操作碼的定義,SIGHASH 標誌分爲 SIGHASH_ALL、SIGHASH_NONE、SIGHASH_SINGLE、SIGHASH_ANYONECANPAY 等,其中 SIGHASH_ANYONECANPAY 是控制輸入的,其餘都是控制輸出的。

SIGHASH_ALL 是默認的 SIGHASH 標誌,對所有輸出進行簽名;SIGHASH_NONE 不簽署任何輸出;SIGHASH_SINGLE 對指定的一個輸出進行簽名。SIGHASH_ANYONECANPAY 可以和前三個 SIGHASH 標誌共同設置,如果設置了 SIGHASH_ANYONECANPAY,則僅對指定的輸入簽名,否則需要對所有的輸入簽名。

顯然,這些 SIGHASH 標誌都無法消除輸入的影響,哪怕是 SIGHASH_ANYONECANPAY,都需要對一個輸入完成承諾。

因此,BIP 118 提出 SIGHASH_ANYPREVOUT。APO 簽名無需承諾被花費的輸入 UTXO(稱爲 PREVOUT),而只需對輸出簽字,爲比特幣的控制提供了更高的靈活性。通過預先構建交易並構建對應的單次使用簽名與公鑰,發送到該公鑰地址的資產必須通過預先構建的交易進行花費,從而實現契約。APO 的靈活性還可以用於交易修復,如果一筆交易因費用低在鏈上卡住,可以輕鬆創建另一筆交易提高費用,而無需新的簽名。此外,對於多重簽名錢包而言,簽名不依賴花費的輸入使得操作更加簡便。

由於消除了 scriptPubKeys 和輸入 TXID 之間的循環。APO 可以通過在見證(Witness)中添加輸出數據來實現內省 ,當然這仍然需要額外的見證數據空間消耗。

對於閃電網絡與保險庫等鏈下協議,APO 減少了需要保存的中間狀態,大大降低了存儲需求與複雜性。APO 最直接的用例是 Eltoo,簡化了通道工廠,構建了輕量化且便宜的瞭望塔,且允許單方面退出,而不遺留錯誤狀態,從各方面提升了閃電網絡的性能。APO 也可以用於模擬 CTV 的功能,不過需要個人存儲簽名及預簽名交易,成本更高,效率不如 CTV。

APO 受到的質疑主要集中在其需要全新的密鑰版本,而無法通過單純向後兼容實現。此外,新的簽名哈希類型可能會帶來潛在的雙花風險。經過社區的廣泛討論後,APO 在原先簽名基礎上要求加入普通簽名,安全問題得到緩解,也因此獲得 BIP-118 編號。

OP_VAULT BIP-345

BIP-345 提議新增 OP_VAULT 和 OP_VAULT_RECOVER 兩個操作碼,與 CTV 結合,實現一種專用的契約,允許用戶對指定的代幣的花費強制一個延遲期,在延遲期中,可以通過恢復路徑將之前的花費「撤銷」。

用戶可以通過創建一個特定的 Taproot 地址來構造一個保險庫,MAST 中需要包含至少兩個腳本,一個 OP_VAULT 的腳本以促進預期的提款過程,另一個 OP_VAULT_RECOVER 腳本確保在提款完成前的任何時候都可以恢復硬幣。

OP_VAULT 如何實現可中斷的時間鎖定提款?簡單而言,OP_VAULT 操作碼完成這樣一件事:將所花費的 OP_VAULT 腳本替換爲指定的腳本,實際上完成了 MAST 的單個葉子節點更新,其餘葉子節點不變。與 TLUV 的設計類似,只不過 OP_VAULT 不支持內部密鑰的更新。

在腳本更新的過程中引入模板,就能夠實現限制支付的效果。其中,時間鎖參數由 OP_VAULT 指定,CTV 操作碼帶來的模板則限制了通過該腳本路徑花費的輸出集合。

BIP-345 是爲保險庫而生的,藉助 OP_VAULT 和 OP_VAULT_RECOVER,用戶可以擁有一個安全的託管方式,將一個高度安全的密鑰(紙錢包、分佈式多籤)作爲恢復路徑,其餘的日常支付配置一定的支出延遲。用戶的設備持續監控保險庫的的支出情況,如果發生意外的轉移,用戶能夠進行恢復。

BIP-345 實現保險庫的過程中需要考慮費用問題,尤其是恢復交易,可行的解決方案包括 CPFP、臨時錨點以及 SIGHASH_GROUP 等新的簽名哈希標誌。

TLUV (TapleafUpdateVerify)

TLUV 方案圍繞着 Taproot 構建,旨在解決共享 UTXO 的高效退出問題。其指導思想是,當一個 Taproot 輸出被花費時,我們可以通過根據 TLUV 腳本所描述的更新步驟,利用 Taproot 地址的內部結構與密碼學變換,對內部密鑰與 MAST 進行部分更新,進而實現契約功能。

TLUV 方案的想法就是通過一個新的操作碼 TAPLEAF_UPDATE_VERIFY,可以通過執行以下一項或多項操作,來創建一個基於當前消費輸入的新 Taproot 地址:

  • 更新內部公鑰

  • 裁剪默克爾路徑

  • 移除當前正在執行的葉子節點

  • 在默克爾路徑的末端添加一個新葉子節點

具體而言,TLUV 接收三個輸入:

  • 一個指定如何更新內部公鑰

  • 一個爲默克爾路徑指定一個新葉子節點

  • 一個指定是否移除當前葉子節點和 / 或移除多少默克爾路徑葉子節點

TLUV 操作碼會計算出更新後的 scriptPubKey,並驗證與當前輸入對應的輸出是否消費到該 scriptPubKey。

TLUV 的啓發場景就是聯合資金池(CoinPool)。今天可以僅使用預簽名的交易創建聯合池,但如果希望實現無許可的退出,則需要創建指數級增長的簽名數量,而 TLUV 能夠實現無需任何預簽名的無許可退出。舉例而言,一羣夥伴利用 Taproot 構建了一個共享的 UTXO,彙集了彼此的資金。他們之間可以使用 Taproot 密鑰在內部移動資金,也可以共同簽名對外發起支付。個人可以隨時退出該共享資金池,刪除掉自己的支付路徑,其餘人仍然可以通過原先的路徑完成支付,同時個人的退出不會暴露內部其他人的額外信息。相比於非池化的交易,這種方式更加高效與隱私。

TLUV 操作碼通過對原 MAST 的更新實現了部分花費限制,然而沒有實現輸出金額的內省。因此還需要包括一個新的操作碼 IN_OUT_AMOUNT,它將兩條數據推送到堆棧:此輸入的 UTXO 的金額和對應輸出的金額,然後期望使用 TLUV 的人使用數學運算符來驗證資金是否被適當地保留在更新的 scriptPubKey 中。

對於輸出金額的內省又增加了一個複雜性,因爲比特幣的金額用聰來表示需要多達 51 位,但腳本只允許進行 32 位的數學運算,需要通過重新定義操作碼行爲來升級腳本中的運算符,或者使用 SIGHASH_GROUP 來替換 IN_OUT_AMOUNT。

TLUV 有望爲去中心化的 Layer 2 的資金池提供解決方案,當然,在 Taproot 公鑰的 tweak 方面,可靠性還有待確認。

MATT

MATT (Merkleize All The Things) 試圖實現三個目標:Merkle 化狀態、Merkle 化腳本與 Merkle 化執行,並進而實現通用的智能合約。

Merkle 化狀態:構建一個 Merkle Trie,其中每個葉子節點都是狀態的哈希值,Merkle Root 就代表了整個合約的狀態。

Merkle 化腳本:即 Tapscript 構成的 MAST,每個葉子節點都是一個可能的狀態轉換路徑。

Merkle 化執行:通過加密承諾與欺詐挑戰機制實現 Merkle 化執行,對於任意計算函數,參與方可以鏈下計算後發佈承諾,f(x)=y,其他參與者發現計算結果錯誤 f(x)=z,則可以進行挑戰,通過二分法進行仲裁,與 Optimistic Rollup 原理相同。

Merkle 化執行 欺詐挑戰

爲了實現 MATT,比特幣編程腳本需要具備以下功能性:

  1. 強制一個輸出擁有特定的腳本(以及它們的金額)

  2. 將一段數據附加到一個輸出

  3. 讀取當前輸入(或其他輸入)的數據

第二點非常重要,動態的數據意味着狀態可以通過花費者提供的輸入數據來計算,因爲這提供了狀態機的模擬,並且能夠決定下一個狀態與附加的數據。MATT 方案通過提出 OP_CHECKCONTRACTVERIFY(OP_CCV)操作碼實現,是對原先提出的 OP_CHECKOUTPUTCONTRACTVERIFY 與 OP_CHECKINPUTCONTRACTVERIFY 的操作碼合併,通過一個額外的 flags 參數來指定操作的對象。

對輸出金額的控制:最直接的方式是通過直接的內省,然而輸出金額是一個 64 位數,需要 64 位的運算,這在比特幣腳本的實現有很高的複雜性。CCV 採用延遲檢查方式,和 OP_VAULT 類似,所有向同一輸出具有 CCV 的輸入,其輸入金額被求和,作爲該輸出金額的下限。延遲是因爲該檢查是在交易過程中而不是在輸入的腳本評估期間進行的。

鑑於欺詐證明的通用性,MATT 契約的某些變體應該能實現所有類型的智能合約或二層構建,儘管額外的要求(例如資本鎖定和挑戰期延遲)需要準確評估;需要進一步研究來評估哪些應用是可接受的交易。例如,用加密承諾與欺詐挑戰機制模擬 OP_ZK_VERIFY 函數,實現比特幣上的去信任 Rollup。

實際上,事情已經在發生了。Johan Torås Halseth 利用 MATT 軟分叉提案中的 OP_CHECKCONTRACTVERIFY 操作碼實現了 elftrace,能夠將所有支持 RISC-V 編譯的程序在比特幣鏈上完成驗證,允許合約協議中的一方在通過合約領取資金,實現了比特幣原生驗證的橋接。

CSFS (OP_CHECKSIGFROMSTACK)

從 APO 操作碼的介紹中,我們已經瞭解到,OP_CHECKSIG(及其相關操作)負責組裝交易、哈希處理、驗證簽名。但是其所驗證的消息,是由使用該操作碼的交易序列化得到的,不允許指定其他消息。簡單來說,OP_CHECKSIG(及其相關操作)起到的是通過簽名機制,驗證作爲交易輸入的 UTXO 是否被授權給簽名的持有者花費,從而保護比特幣的安全性。

CSFS,顧名思義,從堆棧(Stack)中檢查簽名。CSFS 操作碼從堆棧接收三個參數:一個簽名、一個消息以及一個公鑰,並驗證簽名的有效性,這意味着人們能夠通過見證數據向堆棧傳遞任意消息,並且通過 CSFS 進行驗證,這使得比特幣上的一些創新成爲可能。

CSFS 靈活的特點使得其能夠實現 支付簽名、權限委託、預言機合約、雙花保護債券等多種機制,更重要的是能夠實現交易的內省。使用 CSFS 進行交易內省的原理非常簡單,如果通過見證向堆棧上推送 OP_CHECKSIG 所使用的交易內容,對該內容使用同一個公鑰和簽名對用 CSFS 和 OP_CHECKSIG 都進行一次驗籤,如果都順利通過,那麼傳遞給 CSFS 的任意消息的內容與隱式用於 OP_CHECKSIG 的序列化花費交易(及其他數據)相同,我們在堆棧上就獲得了經過驗證的交易數據,可以運用其他操作碼對花費交易施加限制。

CSFS 通常與 OP_CAT 共同出現,因爲通過 OP_CAT 能夠連接交易的不同字段以完成序列化,能夠更精確地選取需要內省的交易字段。沒有 OP_CAT,腳本無法從可單獨檢查的數據重新計算哈希,所以它真正能做的就是檢查哈希是否與特定值相等,這意味着幣只能通過單一特定交易來花費。

CSFS 能實現 CLTV、CSV、CTV、APO 等操作碼,是一個通用的內省操作碼,因此也能幫助比特幣 Layer 2 的擴容方案。不足之處在於需要在堆棧上添加一份完整的簽名交易副本,這可能會顯著增加想要用 CSFS 進行內省的交易的大小。相比之下,單一目的內省操作碼如 CLTV 和 CSV 使用的開銷最小,但添加每個新的特殊內省操作碼都需要共識變更。

TXHASH (OP_TXHASH)

OP_TXHASH 是一個非常直接的內省操作碼,讓操作者選擇某個字段的哈希推送至堆棧。具體而言,OP_TXHASH 將從堆棧中彈出一個 txhash 標誌,並根據該標誌計算一個(標記的)txhash,然後將結果哈希推送到堆棧上。

由於 TXHASH 與 CTV 的相似性,社區內產生了大量關於二者的探討。

TXHASH 可以被視爲 CTV 的通用升級,提供了一種更高級的交易模板,允許用戶明確支出交易的一部分,這解決了關於交易費用的很多問題。對比其他的契約操作碼,TXHASH 無需在見證中提供所需數據的副本,進一步降低了對存儲的需要;與 CTV 不同,TXHASH 不是 NOP 兼容的,只能在 tapscript 中實現;TXHASH 與 CSFS 的組合可以被當作 CTV 與 APO 的替代方案。

從構建契約的方式來看,TXHASH 更容易實現「加法契約」,將你希望固定的交易數據的所有部分推到堆棧上,將它們全部一起哈希,並驗證結果哈希與固定值匹配;CTV 更容易實現「減法契約」,將你希望保持自由的交易數據的所有部分推到堆棧上。然後使用滾動 OP_SHA256 從固定的中間狀態開始,該中間狀態承諾了交易哈希數據的前綴。自由部分被哈希到該中間狀態中。

TXHASH 的規範中定義的 TxFieldSelector 字段有望拓展到其他操作碼中,例如 OP_TX。

TXHASH 相關的 BIP 當前在 Github 上處於 Draft 狀態,未確定編號。

OP_CAT

OP_CAT 是一個頗具神祕色彩的操作碼,曾被中本聰因安全問題廢棄,又在近期引起大量比特幣核心開發者討論,甚至引發了互聯網上的 Meme 文化,最終 OP_CAT 獲批 BIP-347,被多人稱爲是近期最有可能通過的 BIP 提案。

實際上,OP_CAT 的行爲非常簡單,將堆棧上的兩個元素拼接爲一個。如何實現契約功能?

實際上,拼接兩個元素的功能對應了一個強大的密碼學數據結構 Merkle Trie。Merkle Trie 的構建過程,只需要拼接與哈希兩種操作,而比特幣腳本中,哈希函數是可用的。因此,有了 OP_CAT 之後,我們在理論上就能夠在比特幣腳本驗證 Merkle Proof,這是區塊鏈中最常用的輕量化驗證方式。

正如之前所提到的,CSFS 藉助 OP_CAT,能夠實現通用的契約方案。實際上,無需 CSFS,利用 Schnorr 簽名的結構,OP_CAT 自身就能實現對交易的內省。

在 Schnorr 簽名中,需要簽名的消息,是由以下字段構成的:

這些字段包含了交易的主要元素,通過將其放置在 scriptPubKey 或者見證中,利用 OP_CAT 與 OP_SHA256,我們可以構建一個 Schnorr 簽名,並利用 OP_CHECKSIG 檢驗。如果檢驗通過,則堆棧中保留了經過驗證的交易數據,實現了交易的內省,能夠提取並「檢查」交易的各個部分,例如其輸入、輸出、目標地址或所涉及的比特幣金額。

具體的密碼學原理,可以參照 Andrew Poelstra 所發表的 CAT and Schnorr Tricks 文章。

總結而言,OP_CAT 的靈活性使得其幾乎可以模擬任何契約操作碼,並且大量契約操作碼要依賴於 OP_CAT 的功能,這使得其在合併列表上的位置顯著提前。從理論上講,僅依靠 OP_CAT 與現有的比特幣操作碼,我們有望構建一個信任最小化的 BTC ZK Rollup,Starknet、Chakra 以及其他生態夥伴都在積極推進這件事的發生。

Conclusion

隨着我們探索擴展比特幣和增強其可編程性的多種策略,顯而易見的是,前進的道路涉及本地改進、鏈下計算和複雜的腳本功能的結合。

沒有靈活的基礎層,就無法構建更靈活的第二層。

鏈下計算擴容是未來,但比特幣上的可編程性必須有所突破,才能更好地支持擴容,成爲真正的世界貨幣。

但是比特幣的計算與以太坊的計算實際有着本質區別,比特幣只支持「驗證」作爲一種計算形式,不能進行通用計算,而以太坊本質是計算性的,驗證是計算的副產品。從一點就可以看出這種差異:以太坊會爲執行失敗的交易收取以 Gas 爲形式的手續費,而比特幣不會。

契約實現了一種基於驗證而不是計算的智能合約形式。關於契約,除了極少數中本聰原教旨主義者,似乎所有人都認爲契約是改進比特幣的一個好選擇。然而,社區對於該通過哪個方案實現契約始終爭論不休。

APO、OP_VAULT、TLUV 更偏向直接的應用,選擇它們能夠以更便宜、高效的方式實現特定應用。閃電網絡的愛好者會偏愛 APO,因爲能夠實現 LN-Symmetry;想要實現保險庫,那麼最好使用 OP_VAULT;構建 CoinPool,用 TLUV 更加具備隱私和效率。OP_CAT、TXHASH 更加通用,存在安全漏洞的可能性也更小,通過與其他操作碼的組合能夠實現更多的用例,當然也許需要以腳本的複雜性作爲代價。CTV 與 CSFS 對區塊鏈的處理過程做了調整,CTV 實現了延遲輸出,CSFS 實現了延遲簽名。MATT 相對特立獨行,利用樂觀執行與欺詐證明的思想,藉助 Merkle Trie 的結構實現了通用的智能合約,不過仍然需要新操作碼帶來內省功能。

我們看到,比特幣社區已經在激烈地討論通過軟分叉讓比特幣獲得契約的可能性。Starknet 正式宣佈進入比特幣生態,計劃在 OP_CAT 合併後六個月內實現比特幣網絡上的結算。Chakra 將持續關注比特幣生態的最新動向,推動 OP_CAT 軟分叉的合併,並利用內省與契約帶來的可編程性構建一個更加安全、高效的比特幣結算層。

感謝 Jeffrey、Ben、Mutourend、Lynndell 對本文的審閱與建議