比特幣的可擴展性問題是需要面對的主要問題之一,也是許多人努力的方向。比如說,有個設想是「閃電網路」;但是,要在比特幣網路中實現閃電網路,因為比特幣自身的一些缺陷,目前條件仍不具備。本文源自於 Magomed Aliev 的《Segregated Witness for dummies》,由動區專欄作者以太坊愛好者整理、翻譯與撰稿。
(前情提要: 【三分鐘內就看懂】什麼是Segwit隔離見證?)
(背景補充:乾貨|簡單理解 Taproot 軟分叉:爲何是比特幣睽違 4 年最重要升級?)
比特幣可擴展性問題是其面臨的主要問題之一,也是許多人正在努力的方向。舉閃電網路為例,要在比特幣網路中實現,礙於比特幣本身的一些缺陷,仍不具備該條件。
另一個解決方案,隔離見證(Segregated Witness)也致力於提高可擴展性,但它同時也解決了許多問題,包括閃電網路實現所需修補的一些缺陷。我們也將在本文中講解隔離見證的優勢及其工作原理。
隔離見證(SegWit)是一個由多個 BIP(141、142、143、144 和 145)描述的軟分叉,主要用意是優化比特幣交易和區塊的結構,將交易的簽名(也叫 「腳本簽名(scriptSig)」、「witness」 或 「解鎖腳本」)從交易中移到一個獨立的結構中。
它不僅能降低比特幣交易的資料量大小(因此能讓一個區塊塞下更多的交易),也能解決 「交易熔融性(transaction malleability)」 問題(也就是我們開頭提到阻礙閃電網路實現的缺陷),對支付通道和閃電網路來說,這種比特幣交易結構的技術來說極為關鍵。
隔離見證如何工作?
在開始之前,我們先要簡單回顧一下比特幣的支付系統。它並不像銀行那樣,是一套帳戶和餘額的列表。
相反,每個比特幣地址的餘額都是由一系列發送給這個地址的交易來表示的;交易這一資料結構的主要部分就是輸入和輸出。
輸入是我們想要花費的交易(準確來說,輸入不會是完整的一筆交易,而是某筆的輸出,因為我們可能會在一筆交易中將資金轉往多個地址),而交易的輸出就是我們的資金發送的目的地址。下圖展示了比特幣交易的結構:
輸出中的 PubKey Script 欄位(下文簡稱為 「scriptPubKey」)就是我們所說的 「鎖定腳本」。它用來保證只有接受地址的所有者才能使用這個支出。Signature Script 欄位(下文簡稱為 「scriptSig」)也就是所謂的 「解鎖腳本」,因為它是用來打開鎖定腳本的鑰匙,是用來證明地址所有權的。
有關比特幣交易和鎖定腳本、解鎖腳本功能的更多細節,可看此處。
向後相容性
實際上,隔離見證不僅改變了交易的結構,也改變了交易的輸出。不過,這不是說傳統類型的 UTXO(未花費的交易輸出)和 SegWit 類型的 UTXO 無法在同一筆交易中花費:這種情況下,傳統類型的 UTXO 將在輸入(腳本簽名欄位)內載入所有權證明,而隔離見證類型的 UTXO 將在交易輸入以外的結構中載入證明。
不管怎麼說,隔離見證的定位是一個軟分叉,這個升級應該是可以忽略,無需強制的,而且,這也意味著,未升級的節點應該可以處理隔離見證類型的輸出。
實際上,舊的節點和錢包將以為任何人都能花費這些 UTXO,也即這些 UTXO 是空簽名也可花費的,因此即使在交易中沒有看到簽名,交易也仍然是有效的。而升級後的節點和錢包將在交易輸入以外的地方,一個專門的 「witness」 欄位尋找簽名。
案例
Pay-to-Witness-Public-Key-Hash
我們用例子來說明一下隔離見證會如何改變交易的資料結構。從標準的 Pay-to-Public-Key-Hash (P2PKH) 交易類型開始。
我們感興趣的部分是輸出,尤其是其 「scriptPubKey」 欄位(鎖定腳本欄位)。我們先考慮一種標準的鎖定腳本:
1 OP_DUP OP_HASH160 <PubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
而隔離見證之後的鎖定腳本如下所示:
1 0 <PubKeyHash>
如你所見,隔離見證的輸出比傳統類型的輸出要簡單很多:只有兩個值會被推入腳本執行棧中。
如我們上面說得,舊版本的比特幣客戶端會以為這個輸出是掉在地上的錢—— 無需提供簽名就能花費這個輸出。不過,新的客戶端會將第一個數字解釋為版本號,而第二個則對應著一個鎖定腳本(witness 程序)。
在現實中,只有壓縮公鑰(compressed public key)的雜湊值可以用在這裡。這一點我們後面再說。
再來看看這個輸出被花費時的情形。傳統交易的輸出在花費時的數據結構如下:
1 [...] 2 "Vin" : [ 3 { 4 "txid": "8adbca5e652c68f8f3c30ac658115bc4af395d0cc7e6beaea18168295c29d011", 5 "vout": 0, 6 "scriptSig": "<our scriptSig>" 7 } 8 ] 9 [...]
但是,在花費一個隔離見證輸出的時候,交易的 scriptSig 將為空,而所有的簽名都會放到一個專門的地方:
1 [...] 2 "Vin" : [ 3 { 4 "txid": "8adbca5e652c68f8f3c30ac658115bc4af395d0cc7e6beaea18168295c29d011", 5 "vout": 0, 6 "scriptSig": "" 7 } 8 ] 9 [...] 10 "witness": "<Witness data>" 11 [...]
警告
雖然傳統的客戶端可以處理隔離見證的交易(再次提醒,他們會把這些輸出當成人人都可以花的錢),但他們自己沒法花這些錢:舊型的錢包可能會嘗試用空簽名來花用一個隔離見證的輸出,但這筆交易在現實中是無效的(更新之後的節點不會允許這樣的交易上鏈)。
這就意味著,發送者必須知道接受方的錢包支不支持隔離見證,這樣才能為之創建合適類型的輸出。
由 BIP-143 定義,隔離見證的輸出應該用壓縮公鑰的雜湊值來創建。如果你用的是傳統類型的地址或者非壓縮公鑰的雜湊值,這個輸出將變得不可用(你的幣就會鎖死)。
Pay-to-Witness-Script-Hash
另一個關鍵的交易類型是 P2SH。它讓交易可以發送給腳本的雜湊值(而非公鑰的雜湊值,也即比特幣地址)。要花費 P2SH 交易的輸出,花費者需要提供一個腳本(叫做「贖回腳本」),其雜湊值應該與 UTXO 中的腳本雜湊值匹配,並基於這個腳本提供簽名/口令/別的東西。
這個用法可以把解鎖腳本保護起來,讓發送者無從知曉一個地址的內容,並且也能節約空間:舉個例子,一個多簽名錢包的鎖定腳本可能非常長,這樣我們就必須把整個鎖保存起來;有了 P2SH 可以只保存一個雜湊值。
假設現在有一個需要提供 5 個私鑰中的 2 個簽名才能使用的多簽名錢包。如果你使用傳統的交易,P2SH 交易輸出的鎖定腳本將如下:
1 HASH160 54c557e07dde5bb6cb791c7a540e0a4796f5e97e EQUAL
要花費的时候,花費的人(也是上一筆交易的接收方)需要提供一个贖回腳本,這個腳本定義了花費條件(多簽名,2-5),還有兩個簽名。所有這些都要放在交易的輸入中:
1 [...] 2 "Vin" : [ 3 "txid": "abcdef12345...", 4 "vout": 0, 5 "scriptSig": "<SigA> <SigB> <2 PubA PubB PubC PubD PubE 5 CHECKMULTISIG>", 6 ]
再來看看使用隔離見證後的發送者和接收者。輸出的鎖定腳本如下:
1 0 9592d601848d04b172905e0ddb0adde59f1590f1e553ffc81ddc4b0ed927dd73
就像 P2PKH 交易一樣,這個輸出的腳本也變得更簡單。第一個數值表示版本號,第二個是對應於贖回腳本(witness 程式)的 SHA256 雜湊值(32位)。
使用這個函數某種意義上是為了用長度來區分 P2WPKH 的見證程式以及 P2WSH 的見證程式(32 位元組的 SHA256 雜湊值 vs. RIPEMD160(SHA256(script)))。
使用這一輸出的交易如下所示:
1 [...] 2 "Vin" : [ 3 "txid": "abcdef12345...", 4 "vout": 0, 5 "scriptSig": "", 6 ] 7 [...] 8 "witness": "<SigA> <SigB> <2 PubA PubB PubC PubD PubE 5 CHECKMULTISIG>" 9 [...]
在 P2SH 中嵌入隔離見證
我們已經看到,使用隔離見證是有好處的。不過,上面的例子只對發送者和接收者都有升級軟體的情形才適用。但現實並不總是如此。考慮這樣一種情形:
Alice 希望給 Bob 轉帳一些 BTC,Bob 有支持隔離見證的錢包軟體而她沒有。他們顯然只能用標準形式的交易,但 Bob 希望使用 SegWit 來減少手續費。
這時候,Bob 可以創建一個包含了 SegWit 腳本的 P2SH 地址、Alice 會把這個地址當成一個普通的 P2SH 地址,因此可以直接向這個地址轉帳而沒有任何問題。但 Bob 可以使用 SegWit 交易來使用這個輸出,並獲得手續費折扣(下文我們會解釋 SegWit 交易的手續費的新的定價方式)。
這就是 SegWit 交易的兩種類型 P2WSH 和 P2WPKH 在 P2SH 內實現的方式。
P2SH(P2WPKH)
想在 P2SH 交易中實現一筆 P2WPKH 交易,Bob 需要使用其公鑰創建一個見證程序。然後把結果雜湊、轉碼成一個地址:
1 0 ab68025513c3dbd2f7b92a94e0581f5d50f654e7
第一個數值是版本號,而第二個數值是 20 位元組的公開金鑰雜湊值。這個腳本先做 SHA256 雜湊運算,再做 RIPEMD160 運算,就可得到一個 20 位元組的雜湊值。
這是 P2WPKH 見證程式的H ASH160 結果:
1 3e0547268b3b19288b3adef9719ec8659f4b2b0b
轉化成一個地址:
1 37Lx99uaGn5avKBxiW26HjedQE3LrDCZru
發送這個地址的輸出的鎖定腳本,看起來跟一個普通的 P2SH 地址的腳本一模一樣:
1 HASH160 3e0547268b3b19288b3adef9719ec8659f4b2b0b EQUAL
那麼 Bob 花費輸出的時候,交易的結構會像這樣:
1 [...] 2 "Vin" : [ 3 { 4 "txid": "8adbca5e652c68f8f3c30ac658115bc4af395d0cc7e6beaea18168295c29d011", 5 "vout": 0, 6 "scriptSig": "0 ab68025513c3dbd2f7b92a94e0581f5d50f654e7" 7 } 8 ] 9[...] 10 "witness": "<Witness data>" 11 [...]
在一開始,我們創建的贖回腳本(也就是那個見證程式)會經過一次雜湊計算,如果結果符合鎖定腳本中的雜湊值,這個腳本就會得到執行,程式會驗證放在 witness 欄位的簽名。
P2SH(P2WSH)
P2WSH 腳本也可以用 P2SH 來實現。我們考慮上面所說的 2-5 多簽名錢包的例子。所有的步驟都跟 P2SH (P2WPKH) 沒什麼區別:
首先,創建一個見證程序:
1 0 9592d601848d04b172905e0ddb0adde59f1590f1e553ffc81ddc4b0ed927dd73
第一個數值是版本號,第二個數值是 32 位的 SHA256 雜湊值,對應於我們的簽名腳本。然後我們拿這個見證程式的 HASH160 雜湊值轉成一個普通的 P2SH 地址。要使用發送這個地址輸出時,我們需在 scriptSig 欄位公佈這個見證程式,在 witeness 欄位提供完整的多簽名腳本。
隔離見證的好處
梳理清楚技術的部分之後,我們就可以理解隔離見證的主要優點了。
交易熔融性漏洞
SegWit 解決的一個關鍵問題就是比特幣交易的「熔融性」,也即比特幣交易的 ID 是雜湊值這一點所帶來的問題。我們詳細說一下。
在以往的比特幣交易中,簽名是放在交易的輸入部分的,協力廠商可以更改簽名且不會讓交易失效。這使得協力廠商可以在完全不更改交易的「關鍵」 欄位(比如輸入、輸出、轉帳的數量)的前提下更改交易的 ID(也就是交易的雜湊值)。
這樣一來,交易還是有效的,含義也還是一樣的,但是有了另一個 ID,這可以用來執行另一種攻擊,比如 DoS 攻擊(拒絕服務式攻擊)。
SegWit 解決了這個問題,因為所有的簽名都是放在交易外面的,因此簽名的變動不會導致交易的雜湊值變動,也就不會影響交易的 ID。隔離見證還引入了一個專門的標識符,叫做「wtxid」:它是交易和整個 witness 部分的雜湊值,所以如果一筆交易在傳播時沒有附帶任何 witness 數據,交易 ID(txid)就等於 wtxid。
這個解決方案使得我們可以創建一系列前後相繼的未確認交易,而無需擔心任何風險,這對閃電網路這樣的協議來說是非常重要的。(譯者註:這裡的意思可能是,如果不解決交易熔融性問題,支付通道的參與者就無法快速檢索對手有沒有把一筆過時的通道交易上鏈,因為同樣內容的交易可能會以完全不同的 ID 出現)。
網路和儲存的擴展
Witness 數據往往是交易數據中佔比最大的一部分。在使用多簽名腳本的交易中,witness 最多可能佔據交易數據量的 75%。感謝 SegWit,簽名的傳輸變成了一個可選項:只有節點想要驗證交易時,才需要請求這些數據。
而沒有支援 SegWit 的 SPV(簡易支付驗證)客戶端和節點也無需下載額外的數據,可以節省硬碟空間。
可用的區塊空間擴大,降低交易手續費
SegWit 類型的交易比以往的交易類型更便宜,因為它減少了需要儲存的 witness 數據。準確來說,「Size」(數據量大小)的概念在 SegWit 類型的交易上略有不同。
它引入了一個「虛擬大小(virtual size)」 的概念:所有放在 witness 部分的數據都會乘以 0.25 來計算數據量大小,從而一個區塊中可以塞進更多的交易。來看一個例子。
假設我們有一筆傳統類型的交易,數據量大小為 200 位元組。那麼 1MB 的區塊裡面可以放進 5,000 筆這樣的交易。而一筆等效的 SigWit 交易有 120 位元組是放在 witness 區域的,因此其虛擬大小為 80 + 0.25 * 120 = 110 位元組,所以區塊可以放入 9,090 筆這樣的交易。如果上鏈的手續費是每位元組 40 聰,則交易費會從 8,000 聰減低到 4,400 聰,幾乎打了對折。(譯者註:「聰」 為比特幣的數量單位,是 BTC 的億分之一。)
腳本版本
你可能已經注意到了,每個鎖定腳本都會有 1 個位元組來表示腳本的版本。使用不同的版本號就能以軟分叉的形式增加或變更功能(語法改變、新的操作符,等等)。
簽名驗證的效率優化
隔離見證也優化了簽名演算法的效率(如 CHECKSIG、CHECKMULTISIG,等等)。在 SegWit 之前,雜湊計算的次數與簽名數量的平方成正比,但有了隔離見證後,演算法的計算複雜度就減低到了 O(n) (與簽名的數量成正比)。
那還有什麼問題呢?
如果百利而無一害,怎麼還有人會覺得有問題呢?比特幣社群有許多人反對這一升級,因為,即使它有這麼多長處,它也有一些缺點。我們來看看反對方提出的一些意見。
因為 SegWit 是一個軟分叉,許多客戶端可能不會升級,因此兩種類型的 UTXO 會在網路中同時存在;諸如消除交易 ID 熔融性以及雜湊計算次數線性上升這樣的重大變更對非 SegWit 輸出無效,因此網路仍會暴露在交易 ID 熔融性和雜湊時間平方級上升的風險中。
SegWit 會降低網路的安全性,執行完全驗證的節點會大幅減少,因為只有那些適配了 SegWit 的節點才有能力驗證交易的 witness 部分。
SegWit 不能被廢除。如果廢除了它,所有變更都撤銷,那麼所有的 SegWit 輸出就會變成大街上任人撿拾的錢。
SegWit 希望一次解決所有問題,也正因此,它導致了大量的原始碼改動。它會讓未來的工作負荷更重,而且提高了出現驅之不去的軟體 bug 的機會。
結論
雖然由 SW 解決的問題很有可能有更優雅的解決方案,我們仍然相信,在當前,這是提高網路的可擴展性並開啟閃電網路等技術實現的最佳辦法。
📍相關報導📍
比特幣重大升級|軟分叉 Taproot 完成「測試網部署」!節點支持率達到 28.26%
睽違4年,比特幣升級確定!Taproot 軟分岔 11 月啟動,智能合約、Defi 功能將出現
讓動區 Telegram 新聞頻道再次強大!!立即加入獲得第一手區塊鏈、加密貨幣新聞報導。
LINE 與 Messenger 不定期為大家服務