高級交易和腳本

概述

上一章我們介紹了比特幣交易的基本元素,並研究了最常見的交易腳本類型,即P2PKH腳本。在本章中,我們將介紹更高級的腳本以及如何使用它來構建複雜條件下的交易。

首先,我們將介紹 multisignature 腳本。接下來,我們將看一下第二個常見的交易腳本 Pay-to-Script-Hash ,它打開了複雜腳本的世界。然後,我們將研究通過 timelocks 為比特幣添加時間維度的新的腳本運算符。最後,我們將看看 Segregated Witness,這是對交易結構的架構更改。

多重簽名

多重簽名腳本設置了一個條件,N 個公鑰記錄在腳本中,並且需要其中至少 M 個提供簽名才能解鎖資金。這也被稱為 M-of-N 方案,其中 N 是密鑰的總數,M 是驗證所需簽名個數的閾值。例如,一個 2-of-3 的多重簽名是三個公鑰被列為潛在簽名者並且其中至少兩個必須被用來創建簽名,從而創建有效的交易花費資金。

目前,標準的 多重簽名腳本最多隻能列出3個公鑰,這意味著你可以執行從 1-of-1 到 1-of-3 之間的任意組合的多重簽名。本書出版時,列出3個公鑰的限制可能已經解除,因此請檢查 IsStandard() 函數以查看網路當前接受的操作。請注意,3鍵的限制僅適用於標準(也稱為"裸")多重簽名腳本,而不適用於包含在支付到腳本雜湊(P2SH)中的多重簽名腳本。 P2SH多重簽名腳本限於15個鍵,最多允許15-of-15的多重簽名。我們將在 支付到腳本雜湊 Pay-to-Script-Hash (P2SH) 中學習P2SH。

M-of-N 多重簽名條件的鎖定腳本設置通常形式如下:

M <Public Key 1> <Public Key 2> ... <Public Key N> N CHECKMULTISIG

其中 N 是列出的公鑰數量,M 是花費這筆支出所需的簽名個數。

一個 2-of-3 多重簽名條件的鎖定腳本設置如下:

2 <Public Key A> <Public Key B> <Public Key C> 3 CHECKMULTISIG

上面的鎖定腳本可以被包含簽名和公鑰對兒的解鎖腳本滿足:

<Signature B> <Signature C>

或者3個公鑰中的任意兩個對應的私鑰生成的簽名的組合

兩個腳本組合起來形成下面的驗證腳本

<Signature B> <Signature C> 2 <Public Key A> <Public Key B> <Public Key C> 3 CHECKMULTISIG

執行時,只有在解鎖腳本與鎖定腳本設置的條件匹配時,此組合腳本才會評估為TRUE。在這種情況下,條件是解鎖腳本是否具有來自3個公鑰中的兩個對應私鑰的有效簽名。

CHECKMULTISIG執行中的一個錯誤

在 CHECKMULTISIG 的執行過程中有一個錯誤,需要稍微解決一下。當 CHECKMULTISIG 執行時,它應該消耗堆疊中的 M + N + 2 個項目作為參數。 但是,由於該錯誤,CHECKMULTISIG 會彈出額外的值或超出預期的值。

讓我們用前面的驗證示例更詳細地看一下:

<Signature B> <Signature C> 2 <Public Key A> <Public Key B> <Public Key C> 3 CHECKMULTISIG

首先,CHECKMULTISIG彈出頂部元素,它是 N(在本例中為"3")。然後它彈出 N 個元素,這是可簽名的公鑰。在這個例子中,是公鑰 A,B 和 C 。然後,它彈出一個項目,即 M ,仲裁數(需要多少個簽名)。這裡 M = 2。此時,CHECKMULTISIG 應該彈出最後的 M 個元素,這是簽名,並查看它們是否有效。然而,不幸的是,實現中的一個錯誤會導致 CHECKMULTISIG 彈出另一個元素( 總數為M + 1 )。額外的項目在檢查簽名時被忽略,因此它對 CHECKMULTISIG 本身沒有直接影響。但是,必須存在額外的值,因為如果它不存在,當 CHECKMULTISIG 試圖彈出空堆疊時,它將導致堆疊錯誤和腳本失敗(將交易標記為無效)。由於額外的項目被忽略,它可以是任何東西,但通常使用 0。

由於這個bug成為了共識規則的一部分,現在必須永久複製。因此,正確的腳本驗證將如下所示:

0 <Signature B> <Signature C> 2 <Public Key A> <Public Key B> <Public Key C> 3 CHECKMULTISIG

所以,正確的解鎖腳本不是

<Signature B> <Signature C>

而是:

0 <Signature B> <Signature C>

從現在起,如果你看到一個 multisig 解鎖腳本,你應該在開始時看到一個額外的 0,其唯一目的是修正意外成為共識規則的錯誤。

支付到腳本雜湊 Pay-to-Script-Hash (P2SH)

支付到腳本雜湊(P2SH)是2012年推出的一種強大的新型交易,大大簡化了複雜交易腳本的使用。為了解釋對P2SH的需求,我們來看一個實際的例子。

[ch01_intro_what_is_bitcoin] 中,我們介紹了位於迪拜的電子產品進口商Mohammed。Mohammed公司的公司帳戶廣泛使用比特幣的多重簽名功能。多重簽名腳本是比特幣高級腳本功能的最常見用途之一,並且是一個非常強大的功能。Mohammed的公司為所有客戶付款使用多重簽名腳本,在會計術語中稱為"應收賬款"或AR。使用多重簽名方案時,客戶進行的任何付款都會被鎖定,以至於他們需要至少兩個簽名才能從Mohammed及其合作伙伴或擁有備份密鑰的律師處獲得釋放。像這樣的多重簽名方案提供公司治理控制並防止盜竊,盜用或損失。

最終的腳本很長,看起來是這樣的:

2 <Mohammed's Public Key> <Partner1 Public Key> <Partner2 Public Key> <Partner3 Public Key> <Attorney Public Key> 5 CHECKMULTISIG

儘管多重簽名腳本是一個強大的功能,但它們使用起來很麻煩。對於前面的腳本,Mohammed必須在付款之前將此腳本傳達給每位客戶。每個客戶都必須使用特殊的比特幣錢包軟體來創建自定義交易腳本,並且每個客戶都必須瞭解如何使用自定義腳本創建交易。此外,由此產生的交易將比簡單的支付交易大五倍,因為該腳本包含非常長的公鑰。該特大交易的負擔將由客戶以費用的形式承擔。最後,像這樣的大型交易腳本將在每個完整節點的內存中的UTXO集中儲存,直到耗盡內存為止。所有這些問題使得在實踐中使用複雜的鎖定腳本變得困難。

P2SH是為了解決這些實際困難而開發的,使複雜腳本的使用像支付比特幣地址一樣簡單。通過P2SH支付,複雜的鎖定腳本將被其數位指紋(一種加密雜湊)所取代。當試圖花費UTXO的交易在之後出現時,除了解鎖腳本外,它還必須包含與鎖定腳本的指紋相同的腳本。簡而言之,P2SH的意思是"支付給與該雜湊值相匹配的腳本,這個腳本將在稍後花費輸出時使用"。

在P2SH交易中,由雜湊值代替的鎖定腳本稱為 贖回腳本 redeem script,因為它在贖回時提供給系統,而不是作為鎖定腳本。 Complex script without P2SH 顯示沒有P2SH的腳本,Complex script as P2SH 顯示與P2SH編碼的腳本相同。

Table 1. Complex script without P2SH

Locking Script

2 PubKey1 PubKey2 PubKey3 PubKey4 PubKey5 5 CHECKMULTISIG

Unlocking Script

Sig1 Sig2

Table 2. Complex script as P2SH

Redeem Script

2 PubKey1 PubKey2 PubKey3 PubKey4 PubKey5 5 CHECKMULTISIG

Locking Script

HASH160 <20-byte hash of redeem script> EQUAL

Unlocking Script

Sig1 Sig2 <redeem script>

如你所見,使用P2SH時,複雜的腳本詳細說明了花費輸出(贖回腳本)的條件,但不是在鎖定腳本中顯示。只有它的雜湊在鎖定腳本中,而且贖回腳本本身稍後會作為解鎖腳本的一部分在花費輸出時呈現。這將費用和複雜性的負擔從交易的發送者轉移到了接收者(消費者)。

讓我們看看Mohammed的公司,複雜的多重簽名腳本,以及由此產生的P2SH腳本。

首先,Mohammed公司為所有客戶的付款使用多重簽名腳本:

2 <Mohammed's Public Key> <Partner1 Public Key> <Partner2 Public Key> <Partner3 Public Key> <Attorney Public Key> 5 CHECKMULTISIG

如果佔位符被實際的公鑰取代(這裡顯示為以04開頭的520位數字),你可以看到該腳本變得非常長:

2
04C16B8698A9ABF84250A7C3EA7EEDEF9897D1C8C6ADF47F06CF73370D74DCCA01CDCA79DCC5C395D7EEC6984D83F1F50C900A24DD47F569FD4193AF5DE762C58704A2192968D8655D6A935BEAF2CA23E3FB87A3495E7AF308EDF08DAC3C1FCBFC2C75B4B0F4D0B1B70CD2423657738C0C2B1D5CE65C97D78D0E34224858008E8B49047E63248B75DB7379BE9CDA8CE5751D16485F431E46117B9D0C1837C9D5737812F393DA7D4420D7E1A9162F0279CFC10F1E8E8F3020DECDBC3C0DD389D99779650421D65CBD7149B255382ED7F78E946580657EE6FDA162A187543A9D85BAAA93A4AB3A8F044DADA618D087227440645ABE8A35DA8C5B73997AD343BE5C2AFD94A5043752580AFA1ECED3C68D446BCAB69AC0BA7DF50D56231BE0AABF1FDEEC78A6A45E394BA29A1EDF518C022DD618DA774D207D137AAB59E0B000EB7ED238F4D800 5 CHECKMULTISIG

整個腳本可以使用20位元組的加密雜湊取代,首先應用SHA256雜湊演算法,然後對結果應用RIPEMD160演算法。

我們在命令行上使用 libbitcoin-explorer(bx)來生成腳本雜湊,如下所示:

echo \
2 \
[04C16B8698A9ABF84250A7C3EA7EEDEF9897D1C8C6ADF47F06CF73370D74DCCA01CDCA79DCC5C395D7EEC6984D83F1F50C900A24DD47F569FD4193AF5DE762C587] \
[04A2192968D8655D6A935BEAF2CA23E3FB87A3495E7AF308EDF08DAC3C1FCBFC2C75B4B0F4D0B1B70CD2423657738C0C2B1D5CE65C97D78D0E34224858008E8B49] \
[047E63248B75DB7379BE9CDA8CE5751D16485F431E46117B9D0C1837C9D5737812F393DA7D4420D7E1A9162F0279CFC10F1E8E8F3020DECDBC3C0DD389D9977965] \
[0421D65CBD7149B255382ED7F78E946580657EE6FDA162A187543A9D85BAAA93A4AB3A8F044DADA618D087227440645ABE8A35DA8C5B73997AD343BE5C2AFD94A5] \
[043752580AFA1ECED3C68D446BCAB69AC0BA7DF50D56231BE0AABF1FDEEC78A6A45E394BA29A1EDF518C022DD618DA774D207D137AAB59E0B000EB7ED238F4D800] \
5 CHECKMULTISIG \
| bx script-encode | bx sha256 | bx ripemd160
54c557e07dde5bb6cb791c7a540e0a4796f5e97e

上述一系列命令首先將Mohammed的multisig redeem腳本編碼為十六進制的序列化的比特幣腳本。下一個 bx 命令計算其SHA256雜湊值。下一個 bx 命令再次使用RIPEMD160進行雜湊運算,產生最終的腳本雜湊:

Mohammed的贖回腳本的20位元組雜湊值是:

54c557e07dde5bb6cb791c7a540e0a4796f5e97e

P2SH交易使用以下鎖定腳本將輸出鎖定到此雜湊值,而不是之前更長的贖回腳本:

HASH160 54c557e07dde5bb6cb791c7a540e0a4796f5e97e EQUAL

如你所見,它要短得多。與"支付到5個密鑰的多重簽名腳本"不同,P2SH等價交易是"支付到這個雜湊值的腳本"。向Mohammed公司付款的客戶只需在付款中包含更短的鎖定腳本。當Mohammed和他的合作伙伴想要使用這個UTXO時,他們必須出示原始贖回腳本(用雜湊值鎖定UTXO的那個腳本)和解鎖它的必要簽名,如下所示:

<Sig1> <Sig2> <2 PK1 PK2 PK3 PK4 PK5 5 CHECKMULTISIG>

這兩個腳本組合為兩個階段。首先,根據鎖定腳本檢查贖回腳本以確保雜湊值匹配:

<2 PK1 PK2 PK3 PK4 PK5 5 CHECKMULTISIG> HASH160 <redeem scriptHash> EQUAL

如果贖回腳本雜湊值匹配,解鎖腳本將自行執行,以解鎖贖回腳本:

<Sig1> <Sig2> 2 PK1 PK2 PK3 PK4 PK5 5 CHECKMULTISIG

本章介紹的幾乎所有腳本都只能作為P2SH腳本實現。它們不能直接用在UTXO的鎖定腳本中。

P2SH 地址

P2SH功能的另一個重要部分是將腳本雜湊編碼為地址的能力,如BIP-13中所定義的那樣。P2SH地址是腳本的20字節雜湊的Base58Check編碼,就像比特幣地址是公鑰的20字節雜湊的Base58Check編碼一樣。 P2SH地址使用版本前綴"5",這導致以"3"開頭的Base58Check編碼地址。

例如,Mohammed的複雜腳本,通過雜湊和Base58Check編碼,生成P2SH地址 39RF6JqABiHdYHkfChV6USGMe6Nsr66Gzw。我們可以用 bx 命令來確認

echo \
'54c557e07dde5bb6cb791c7a540e0a4796f5e97e'\
 | bx address-encode -v 5
39RF6JqABiHdYHkfChV6USGMe6Nsr66Gzw

現在,Mohammed可以給他的客戶提供這個"地址",他們幾乎可以使用任何比特幣錢包進行簡單付款,就像它是一個比特幣地址一樣。前綴3給他們一個暗示,這是一種特殊的地址類型,對應於腳本而不是公鑰,但是它的作用方式與支付比特幣地址的方式完全相同。

P2SH地址隱藏了所有的複雜性,因此付款人看不到腳本。

P2SH 的好處

與在鎖定輸出時直接使用複雜腳本相比,P2SH具有以下優點:

  • 複雜的腳本在交易輸出中被更短的指紋代替,從而使交易數據更小。

  • 腳本可以編碼為地址,發件人和發件人的錢包不需要複雜的工程來實現P2SH。

  • P2SH將構建腳本的負擔轉移給收件人,而不是發件人。

  • P2SH將長腳本的數據儲存負擔從輸出中(儲存在區塊鏈中的UTXO集中)轉移到輸入中(僅儲存在區塊鏈中)。

  • P2SH將長檔案的數據儲存負擔從當前時間(支付)轉移到未來時間(花費時間)。

  • P2SH將長腳本的交易費用從發件人轉移到收件人,收件人必須包含很長的兌換腳本才能使用。

贖回腳本和驗證

在Bitcoin Core客戶端版本0.9.2之前,Pay-to-Script-Hash通過 IsStandard() 函數僅限於標準類型的比特幣交易腳本。這意味著消費交易中提供的贖回腳本只能是標準類型之一:P2PK,P2PKH或multisig。

從Bitcoin Cor客戶端版本0.9.2開始,P2SH交易可以包含任何有效的腳本,使P2SH標準更加靈活,並允許對許多新型和複雜類型的交易進行實驗。

你無法將P2SH放入P2SH贖回腳本中,因為P2SH規範不是遞歸的。另外,技術上可以在贖回腳本中包含 RETURN( 參見 數據記錄輸出 (RETURN) ),規則中的任何內容都無法阻止你這樣做,但沒有實際意義,因為在驗證期間執行 RETURN 將導致交易被標記為無效。

請注意,因為贖回腳本在你嘗試使用P2SH的輸出之前未呈現給網路,所以如果你使用無效的贖回腳本的雜湊鎖定該輸出,它將被忽略。 UTXO將被成功鎖定,但你將無法花費這筆費用,包含贖回腳本的花費交易不會被接受,因為它是無效的腳本。這會產生風險,因為你可以將比特幣鎖定在以後不能使用的P2SH中。網路會接收對應於無效的贖回腳本的鎖定腳本。

Warning

P2SH鎖定腳本包含贖回腳本的雜湊,但不會提供有關贖回腳本本身內容的線索。即使贖回腳本無效,P2SH交易也將被視為有效的並被接受。你可能會意外鎖定比特幣,之後無法花費。

數據記錄輸出 (RETURN)

比特幣的分佈式時間戳賬本,區塊鏈(blockchain),具有遠遠超出支付範圍的潛在用途。許多開發人員嘗試使用交易腳本語言,利用系統的安全性和靈活性,應用於數位公證服務,股票證書和智慧合約等。將比特幣的腳本語言用於這些目的的早期嘗試包括創建交易輸出,在區塊鏈上記錄數據;例如,記錄檔案的數位指紋,使得任何人都可以通過引用該交易作為該檔案在特定日期存在的證明。

使用比特幣區塊鏈來儲存與比特幣付款無關的數據是一個有爭議的話題。許多開發人員認為這種使用是濫用,並希望阻止它。其他人則認為這是區塊鏈技術強大功能的一個示例,並且希望鼓勵這種實驗。那些反對納入未付款數據的人爭辯說,它會導致"區塊鏈膨脹",使那些運行完整比特幣節點的人承擔儲存區塊鏈無意承載的數據帶來的成本。此外,此類交易創建了不能用於支付的,使用20字節的目標比特幣地址的UTXO。由於該地址用於數據,因此它不對應於私鑰,生成的UTXO不會被花費;這是虛假的付款。因此,永遠不會花費的這些交易永遠不會從UTXO集中移除,並導致UTXO資料庫的大小永遠增加或"膨脹"。

在Bitcoin Core客戶端的0.9版本中,通過引入 RETURN 運算符達成了一個折衷方案。 RETURN 允許開發人員將80個字節的非付款數據添加到交易輸出中。但是,與使用"假"UTXO不同,RETURN 運算符會創建一個顯式的 可驗證不可消費 的輸出,該輸出不需要儲存在UTXO集合中。 RETURN 輸出記錄在區塊鏈中,因此它們消耗硬碟空間並會導致區塊鏈大小的增加,但它們不儲存在UTXO集中,因此不會使UTXOMemory pool膨脹,完整節點頁不用承擔昂貴的內存負擔。

RETURN 腳本看起來如下

RETURN <data>

數據部分被限制為80位元組,並且通常表示雜湊,例如SHA256演算法的輸出(32位元組)。許多應用程式在數據前加上前綴以幫助識別應用。例如,http://proofofexistence.com[Proof of Existence] 數位公證服務使用8位元組前綴 DOCPROOF,十六進制ASCII編碼為 44 4f 43 50 52 4f 4f 46。

請記住,沒有對應於 RETURN 的"解鎖腳本",用於"花費" RETURN 輸出。 RETURN 的全部意義在於你不能把錢鎖定在那個輸出中,因此它不需要被保存在UTXO集合中(潛在可花費的)—— RETURN 是可驗證不可花費的。RETURN 通常是比特幣金額為零的輸出,因為分配給這種輸出的任何比特幣都會永久丟失。如果在交易中引用 RETURN 作為輸入,腳本驗證引擎將暫停驗證腳本的執行並將交易標記為無效。RETURN 的執行本質上導致腳本以 FALSE "返回"並暫停。因此,如果你意外地將 RETURN 輸出引用為交易中的輸入,則該交易無效。

標準交易(符合 IsStandard() 檢查的交易)只能有一個 RETURN 輸出。但是,一個 RETURN 輸出可以與任何其他類型的輸出組合在一個交易中。

Bitcoin Core 0.10中增加了兩個新的命令行選項。選項 datacarrier 控制是否中轉和開採 RETURN 交易,預設設置為"1"以允許。選項 datacarriersize 接受一個數字參數,指定 RETURN 腳本的最大字節數,缺省為83字節,表示 RETURN 數據最多80個字節,加上 RETURN 操作碼的一個字節,和 PUSHDATA 操作碼的兩個字節。

Note

RETURN 最初提出的最大限制為80個位元組,但在發佈功能時限制已減少到40個位元組。2015年2月,在Bitcoin Core版本0.10中,限制提高到80位元組。節點可以選擇不中轉或使用 RETURN,或者只中轉和開採包含少於80位元組數據的 RETURN 交易。

時間鎖 Timelocks

時間鎖是對交易或輸出的限制,只允許在某個時間點之後花費。比特幣從一開始就具有交易級別的時間鎖定功能。它由交易中的 nLocktime 欄位實現。 2015年末和2016年中推出了兩個新的時間鎖功能,可提供UTXO級別的時間鎖定。這些是 CHECKLOCKTIMEVERIFY 和 CHECKSEQUENCEVERIFY。

時間鎖定對於推遲日期的交易非常有用,將資金鎖定在未來的日期。更重要的是,時間鎖將比特幣腳本延伸到時間維度,為複雜的多步智慧合約打開了大門。

交易時間鎖 (nLocktime)

從一開始,比特幣就具有交易級別的時間鎖定功能。交易鎖定時間是交易級別的設置(交易資料結構中的一個欄位),用於定義交易有效的最早時間,並且可以在網路上中轉或添加到區塊鏈。Locktime也被稱為 nLocktime ,來自Bitcoin Core程式碼庫中使用的變數名稱。在大多數交易中它被設置為0以表示立即傳播和執行。如果 nLocktime 非零且低於5億,會被解釋為為區塊高度,表示交易無效並且不會在指定區塊高度之前中轉或包含在區塊鏈中。如果它超過5億,它會被解釋為Unix紀元時間戳(自1970年1月1日以來的秒數),表示交易在指定時間之前無效。使用 nLocktime 指定未來區塊或時間的交易必須由發起的系統持有,只有在它們生效後才傳輸到比特幣網路。如果交易在指定的 nLocktime 之前傳輸到網路,交易將被第一個節點認為無效並拒絕,不會被中轉到其他節點。 nLocktime 的使用等同於推遲日期的紙質支票。

交易鎖定時間限制

nLocktime 具有侷限性,雖然它允許一些輸出在將來被花費,但不會使這些輸出在那個時間之前不能被花費。我們用下面的例子來解釋一下。

Alice簽署了一筆交易,將其的一個輸出指定到Bob的地址,並將 nLocktime 設置為3個月之後。Alice將該交易發送給了Bob。通過這次交易,Alice和Bob知道:

  • 在3個月過去之前,Bob不能發起贖回資金的交易。

  • Bob可能會在3個月後發起交易。

但是:

  • Alice可以創建另一個交易,在沒有鎖定時間的情況下重複使用相同的輸入。因此,Alice可以在3個月過去之前花費相同的UTXO。

  • Bob無法保證Alice不這麼做。

瞭解交易 nLocktime 的侷限性非常重要。唯一的保證是 Bob 在3個月之前不能贖回,而無法保證 Bob 將獲得資金。要達到這樣的保證,時間限制必須放在UTXO上,併成為鎖定腳本的一部分,而不是交易的一部分。這是通過稱為 檢查鎖定時間驗證 Check Lock Time Verify (CLTV) 的下一種時間形式實現的。

Check Lock Time Verify (CLTV)

2015年12月,一種新的時間鎖形式作為軟分叉升級引入了比特幣。根據BIP-65中的規範,一種名為 CHECKLOCKTIMEVERIFYCLTV)的腳本操作符添加到腳本語言中。 CLTV 是每個輸出的時間鎖,而不是 使用 nLocktime 情況下的每個交易的時間鎖。允許時間鎖的應用更加靈活。

簡而言之,通過在輸出的贖回腳本中添加 CLTV 操作碼,可以限制輸出只能在指定的時間過後才能使用。

Tip

nLocktime 是交易級別的時間鎖,CLTV 是基於輸出的時間鎖。

CLTV 並沒有取代 nLocktime,而是限制特定UTXO,以使它們只能在 nLocktime 設置為更大或相等的值的未來交易中使用。

CLTV 操作碼將一個參數作為輸入,該參數以與 nLocktime(區塊高度或Unix紀元時間)相同的格式表示。如 VERIFY 後綴所示,CLTV 是在結果為 FALSE 時停止執行腳本的操作碼。如果結果為TRUE,則繼續執行。

為了用 CLTV 鎖定輸出,可以在創建這筆輸出的交易中,將其插入到輸出的贖回腳本中。例如,如果Alice正在向Bob的地址支付,輸出通常會包含如下所示的P2PKH腳本:

DUP HASH160 <Bob's Public Key Hash> EQUALVERIFY CHECKSIG

為了將其鎖定一段時間,比如從現在開始3個月,這筆交易將帶有如下的贖回腳本:

<now + 3 months> CHECKLOCKTIMEVERIFY DROP DUP HASH160 <Bob's Public Key Hash> EQUALVERIFY CHECKSIG

其中 <now {plus} 3 months> 是從這筆交易被開採後3個月的區塊高度或者時間戳估計,當前區塊高度 + 12,960 (區塊) 或者 當前Unix時間戳 + 7,760,000 (秒). 現在,不要在意 CHECKLOCKTIMEVERIFY 之後的 DROP 操作符,我們之後會解釋。

當Bob嘗試花費這個UTXO時,構建一個以UTXO作為輸入的交易,在輸入的解鎖腳本中使用他的簽名和公鑰,並將交易的 nLocktime 設置為等於或大於 CHECKLOCKTIMEVERIFY 中Alice設置的 timelock,然後在比特幣網路上廣播交易。

Bob的交易被進行如下的評估,如果Alice設置的 CHECKLOCKTIMEVERIFY 的參數小於或等於消費交易的 nLocktime,則腳本執行繼續(如同執行 "no operation" 或NOP操作碼一樣)。否則,腳本執行會停止,並且交易被視為無效。

更準確地說,CHECKLOCKTIMEVERIFY 失敗並暫停執行,標記交易無效,如果達成以下條件(來源:BIP-65):

  1. 堆疊為空;或者

  2. 堆疊頂元素小於0;或者

  3. 堆疊頂元素鎖定時間的類型(區塊高度或時間戳)與 nLocktime 欄位不同;或者

  4. 堆疊頂元素大於交易的 nLocktime 欄位;或者

  5. 輸入的 nSequence 欄位為 0xffffffff。

Note

CLTV 和 nLocktime 使用相同的格式來描述時間鎖,可以是區塊高度,也可以是自Unix紀元以來的秒數。重要的是,當一起使用時,nLocktime 的格式必須與輸出中的 CLTV 的格式匹配 —— 它們都必須表示區塊高度,或以秒為單位的時間。

執行後,如果滿足 CLTV,則其前面的時間參數將保留為堆疊頂元素,需要使用 DROP 將其刪除,以正確執行後續腳本操作碼。出於這個原因,你經常會在腳本中看到 CHECKLOCKTIMEVERIFY 和 DROP。

通過將 nLocktime 與 CLTV 結合使用,交易鎖定時間限制 中描述的場景會發生變化。Alice不能再花費這筆資金了(因為它被Bob的密鑰鎖定),Bob在3個月的鎖定時間到期之前也不能花費。

通過將時間鎖功能直接引入腳本語言,CLTV 允許我們開發一些非常有趣的複雜腳本。

標準的定義參見 BIP-65 (CHECKLOCKTIMEVERIFY)

相對時間鎖

nLocktime 和 CLTV 都是 絕對的時間鎖 absolute timelocks,表示一個絕對的時間點。接下來我們要研究的兩個時間鎖功能是 相對時間鎖 relative timelocks,它們指定從輸出在區塊鏈中被確認時開始的一段時間,作為花費輸出的條件。

相對時間鎖是有用的,它們允許兩個或多個相互依賴的交易組成的交易鏈進行脫鏈處理,對一個依賴於前一個交易確認後一段時間的交易施加時間限制。換句話說,直到UTXO被記錄在區塊鏈上時,時鐘才會開始計數。這個功能在雙向狀態通道(bidirectional state channels)和閃電網路(Lightning Networks)中特別有用,我們將在 [state_channels] 中看到。

相對時間鎖與絕對時間鎖一樣,都是通過交易級功能和腳本級操作碼實現的。交易級別的相對時間鎖實現為 nSequence(每個交易輸入中設置的欄位)值的共識規則。腳本級別的相對時間鎖使用 CHECKSEQUENCEVERIFY(CSV)操作碼實現。

BIP-68和BIP-112於2016年5月作為共識規則的軟分叉升級啟用。

nSequence相對時間鎖

通過設置 nSequence 欄位,可以在交易的每個輸入上設置相對時間鎖。

nSequence的原始含義

nSequence 欄位的本意在於(但從未正確實施)允許修改 mempool 中的交易。在該用途中,包含 nSequence 值低於 232 - 1(0xFFFFFFFF)的輸入的交易指示尚未"完成"的交易。這樣的交易將保留在 mempool 中,直到它被另一個花費相同的輸入但具有更高的 nSequence 值的交易替代。一旦接收到輸入的 nSequence 值為 0xFFFFFFFF 的交易,它將被視為"完成的"並被開採。

nSequence 的原始含義從未正確實現,nSequence 的值通常在不使用時間鎖定的交易中被設置為0xFFFFFFFF。對於具有 nLocktime 或 CHECKLOCKTIMEVERIFY 的交易,必須將 nSequence 值設置為小於 231,才能使時間保護具有效果,如下所述

nSequence 作為共識執行的相對時間鎖

自BIP-68啟用以來,新的共識規則適用於包含 nSequence 值小於231的輸入的任何交易。從寫程式的角度來說,這意味著如果最高有效位(第1<<31位)未設置為1,則表示"相對鎖定時間"。否則(1<<31設置為1),nSequence 的值被保留用於其他用途,例如啟用 CHECKLOCKTIMEVERIFY,nLocktime,Opt-In-Replace-By-Fee以及其他未來的開發。

具有小於 231 的 nSequence 值的交易輸入被解釋為具有相對時間鎖。這種交易只有在輸入已經過相對時間鎖表示的時間後才有效。例如,具有 nSequence 為30個區塊的相對時間鎖的一個輸入的交易,僅在從輸入中引用的UTXO被開採的時間起,至少經過30個區塊時才有效。由於 nSequence 是每個輸入的欄位,交易可能包含任意數量的時間鎖定輸入,所有這些輸入都必須滿足時間要求交易才有效。一個交易可以同時包括時間鎖定的輸入( nSequence < 231 )和沒有時間鎖定的輸入( nSequence >= 231 )。

nSequence 值以區塊或秒為單位,但與我們在 nLocktime 中使用的格式略有不同。類型標誌(type-flag)用於區分表示區塊數還是表示時間(以秒為單位)。類型標誌被設置在第23個最低有效位(即值1 << 22)中。如果類型標誌為1,則 nSequence 值被解釋為512秒的倍數。如果類型標誌為0,則 nSequence 值將被解釋為區塊數。

當將 nSequence 解釋為相對時間鎖時,僅考慮16個最低有效位。一旦標誌位(比特32和23)檢測完成,通常使用 nSequence 的16位掩碼(例如,nSequence & 0x0000FFFF )。

BIP-68 definition of nSequence encoding (Source: BIP-68) 展示了 nSequence 的二進制結構, 由 BIP-68 定義。

BIP-68 definition of nSequence encoding
Figure 1. BIP-68 definition of nSequence encoding (Source: BIP-68)

基於 nSequence 值共識執行的相對時間鎖在BIP-68中定義。

使用CSV的相對時間鎖

與CLTV和 nLocktime 一樣,有一個在腳本中使用 nSequence 值作為相對時間鎖的腳本操作碼。該操作碼是 CHECKSEQUENCEVERIFY,通常簡稱為 CSV。

在UTXO的贖回腳本中執行時,CSV 操作碼僅允許輸入的 nSequence 值大於或等於 CSV 參數的交易。從本質上講,這限制了UTXO直到相對於UTXO開採的時間已經過去了一定數量的區塊或秒之後才能被花費。

與CLTV一樣,CSV 中的值必須與相應的 nSequence 值中的格式匹配。如果 CSV 指定的是區塊數量,nSequence 也必須是區塊數量。如果 CSV 指定的是秒,那麼 nSequence 也必須是秒。

當幾個(鏈接的)交易被創建和簽名,但保持"脫鏈"狀態不會傳播時,使用 CSV 的相對時間鎖特別有用。直到父交易被傳播,開採和沉澱到相對時間鎖定中指定的時間後,才能使用子交易。可以在 [state_channels][lightning_network] 中看到這個用例的應用。

CSV 在 BIP-112, CHECKSEQUENCEVERIFY 中詳細定義。

過去中位時間 Median-Time-Past

作為啟用相對時間鎖的一部分,時間鎖(絕對和相對)的"時間"計算方式有所變化。在比特幣中,實際時間(wall time)和共識時間(consensus time)之間存在著微妙但重要的差異。比特幣是一個去中心化的網路,這意味著每個參與者都有自己的時間視角。網路上的事件並不是每時每刻都在發生。必須在每個節點的角度考慮網路延遲。最終,所有內容都會同步,創建一個公共的賬本。與 過去 一樣,比特幣每隔10分鐘就會對賬本的狀態達成共識。

區塊頭中的時間戳是由礦工設置的。共識規則留有一定的餘地來解決分散的節點之間的時鐘精度差異。然而,這帶來了一種不幸的激勵,促使礦工在一個區塊內對時間撒謊,以便通過納入尚未成熟的時間鎖定交易來收取額外的費用。有關更多訊息,請參見下面的部分。

為了消除對撒謊的激勵,並加強時間鎖的安全性,BIP-113與關於相對時間鎖的BIP同時提出並被啟用,它定義了一個稱為 過去中位時間 Median-Time-Past 的新的一直的時間測量方法。

Median-Time-Past 過去11個區塊的時間戳並的中位數。中位時間成為共識時間,並用於所有時間鎖的計算。通過取過去大約兩個小時的中點,任何一個區塊的時間戳影響都會減小。通過結合11個區塊,沒有一個礦工可以為了獲得尚未成熟的時間鎖定交易費用而影響時間戳。

Median-Time-Past改變了 nLocktime,CLTV,nSequence 和 CSV 的時間計算實現。由Median-Time-Past計算的共識時間總是比實際時間晚大約一個小時。如果你創建時間鎖定交易,應該在估計 nLocktime,nSequence,CLTV 和 CSV 中編碼的時間時考慮這一點。

Median-Time-Past 在 BIP-113 中被定義。

對抗費用狙擊的時間鎖防禦 Timelock Defense Against Fee Sniping

費用狙擊(Fee-sniping)是一種理論上的攻擊場景,表示試圖改寫過去的區塊的礦工"狙擊"未來區塊中更高費用的交易,來最大化盈利的方法。

例如,假設現在的最高區塊是區塊#100,000。一些礦工嘗試重新開採區塊 #100,000,而不是嘗試開採區塊#100,001來增長區塊鏈。這些礦工可以選擇在他們的候選區塊#100,000中包含任何有效的交易(尚未開採)。他們不必通過相同的交易來重新開採區塊。事實上,他們有動力選擇最有利可圖的(每KB最高費用)交易幷包含在他們的區塊中。它們可以包括任何在"舊"區塊#100,000中的交易,也可以包含來自當前mempool的任何交易。本質上,當他們重新創建區塊#100,000時,他們可以選擇將交易從"現在"轉移到重寫的"過去"。

今天,這種攻擊不是很有利可圖,因為區塊獎勵遠高於每個區塊的總費用。但是在將來,交易費用將成為獎勵的一大部分(甚至獎勵的全部)。那時候,這種情況就不可避免了。

在Bitcoin Core創建交易時,為了防止"費用狙擊",會預設使用 nLocktime 將其限制為"下一個區塊"。在我們的場景中,Bitcoin Core會在其創建的任何交易中將 nLocktime 設置為100,001。正常情況下,這個 nLocktime 不起作用 —— 無論如何,交易只能包含在#100001區塊中;這是下一個區塊。

但是,在區塊鏈分叉攻擊下,礦工們將無法從mempool中獲取高額交易,因為所有這些交易都會被鎖定到#100,001區塊。他們只能使用當時有效的交易重新計算#100,000,實質上不會獲得新的費用。

為此,Bitcoin Core將所有新交易的 nLocktime 設置為 <當前區塊#+ 1>,並將所有輸入的 nSequence 設置為0xFFFFFFFE以啟用 nLocktime。

流程控制腳本(條件語句)

比特幣腳本的一個更強大的功能是流程控制,也稱為條件語句。你可能熟悉多種語言中的 IF...THEN...ELSE 流程控制。比特幣的條件語句看起來有點不同,但基本構造是相同的。

比特幣條件操作碼允許我們構建一種有兩種解鎖方式的贖回腳本,取決於條件語句的結果是 TRUE 還是 FALSE。例如,如果 x 為 TRUE,則贖回腳本為A,否則(ELSE),贖回腳本為B.

此外,比特幣條件表達式可以無限"嵌套",條件語句可以包含另一個條件語句。比特幣流程控制腳本可用於構建具有數百或甚至數千個可能的執行路徑的複雜腳本。嵌套沒有限制,但共識規則會對腳本的最大大小(以字節為單位)施加限制。

比特幣使用 IF,ELSE,ENDIF 和 NOTIF 操作碼實現流程控制。此外,條件表達式可以包含布林運算符,例如 BOOLAND,BOOLOR,和 NOT。

乍一看,你可能會對比特幣的流程控制腳本感到困惑。這是因為比特幣腳本是一種堆疊語言。 1 AND 1 逆序表示為 1 1 ADD。

在大多數傳統(過程式)程式語言中,流程控制看起來是這樣的:

Pseudocode of flow control in most programming languages
if (condition):
  code to run when condition is true
else:
  code to run when condition is false
code to run in either case

在類似比特幣腳本的基於堆疊的語音中,邏輯條件放在 IF 之前,使其看起來是逆序的:

Bitcoin Script flow control
condition
IF
  code to run when condition is true
ELSE
  code to run when condition is false
ENDIF
code to run in either case

在閱讀比特幣腳本時,記住條件判斷是在 IF 操作碼之前的。

條件語句的 VERIFY 操作碼

比特幣腳本中另外一種條件形式是以 VERIFY 結尾的任何操作碼。VERIFY 後綴表示如果所評估的條件不是 TRUE,腳本將立即終止執行,並且交易被視為無效。

與 IF 語句提供不同的執行路徑不同,VERIFY 後綴用作 守護語句 guard clause, 只有滿足前面的條件時繼續執行。

例如,以下腳本需要Bob的簽名和產生特定雜湊的原象(pre-image)(密鑰)。必須滿足這兩個條件才能解鎖:

A redeem script with an EQUALVERIFY guard clause.
HASH160 <expected hash> EQUALVERIFY <Bob's Pubkey> CHECKSIG

為了贖回這筆資金, Bob 必須創建提供原象(pre-image)和簽名的解鎖腳本:

An unlocking script to satisfy the above redeem script
<Bob's Sig> <hash pre-image>

在不提供原象的情況下,Bob無法執行到檢查其簽名的腳本部分。

這個腳本可以用 IF 語句寫成:

A redeem script with an IF guard clause
HASH160 <expected hash> EQUAL
IF
   <Bob's Pubkey> CHECKSIG
ENDIF

Bob的解鎖腳本是相同的:

An unlocking script to satisfy the above redeem script
<Bob's Sig> <hash pre-image>

帶 IF 的腳本與使用 VERIFY 後綴的操作碼的功能相同;他們都作為守護語句運行。但是,VERIFY 構造更高效,使用兩個較少的操作碼。

那什麼時候使用 VERIFY,什麼時候使用 IF 呢?如果我們只是附加一個先決條件(guard clause),那麼 VERIFY 更好。但是,如果有多個執行路徑(流程控制),那麼需要使用 IF...ELSE 流程控制語句。

Tip

諸如 EQUAL 之類的操作碼會將結果( TRUE 或 FALSE )推到堆疊上,用於後續操作碼的判斷。相反,操作碼 EQUALVERIFY 不會在堆疊中留下任何內容。以 VERIFY 結尾的操作碼都不會將結果留在堆疊上。

在腳本中使用流程控制

比特幣腳本中,流程控制的一個常見用途是構建贖回腳本,提供多個執行路徑,每種贖回方式都可以贖回UTXO。

看一個簡單的例子,有兩個簽名者,Alice和Bob,任何一個都可以兌換。使用multisig時,這將表示為 1-of-2 的多重簽名腳本。為了演示,我們使用 IF 語句做同樣的事情:

IF
 <Alice's Pubkey> CHECKSIG
ELSE
 <Bob's Pubkey> CHECKSIG
ENDIF

看到這個贖回腳本,你可能會想:"條件在哪裡?在 IF 語句之前沒有任何東西啊!"

條件不是贖回腳本的一部分。而是在解鎖腳本中提供,從而允許 Alice 和 Bob "選擇" 他們想要的執行路徑。

Alice使用以下解鎖腳本進行贖回:

<Alice's Sig> 1

最後的 1 作為條件(TRUE),使 IF 語句執行Alice簽名的第一個贖回路徑。

如果Bob要贖回,他必須通過給 IF 語句提供一個 FALSE 值來選擇第二個執行路徑:

<Bob's Sig> 0

Bob的解鎖腳本在堆疊上放置了 0,導致 IF 語句執行第二個( ELSE )腳本,從而需要Bob的簽名。

由於 IF 語句可以嵌套,我們可以創建執行路徑的"迷宮"。解鎖腳本可以提供一個選擇執行路徑實際執行的"映射":

IF
  script A
ELSE
  IF
    script B
  ELSE
    script C
  ENDIF
ENDIF

在這種情況下,有三個執行路徑(腳本A,腳本B 和 腳本C)。解鎖腳本以 TRUE 或 FALSE 值的順序提供路徑。例如,要選擇路徑 腳本B,解鎖腳本必須以 1 0( TRUE,FALSE )結尾。這些值將被壓入堆疊,以便第二個值( FALSE )作為堆疊頂部。外層的 IF 語句彈出 FALSE 並執行第一個 ELSE 語句。然後 TRUE 移動到堆疊頂,並由內部的(嵌套的)IF 判斷,從而選擇 B 執行路徑。

使用這種構造,我們可以用數十或數百個執行路徑構贖回腳本,每個腳本都提供了一種不同的方式來贖回UTXO。為了花費UTXO,我們構建一個解鎖腳本,通過在每個流程控制點的堆疊上放置相應的 TRUE 和 FALSE 來選擇執行路徑。

複雜腳本示例

在本節中,我們將本章中的許多概念結合到一個示例中。

我們的例子使用了迪拜公司所有者Mohammed的故事,該公司經營進出口業務。

在這個例子中,Mohammed希望建立一個規則靈活的公司資本賬戶。他創建的方案需要根據時間鎖進行不同級別的授權。多重簽名方案的參與者是Mohammed,他的兩個合夥人Saeed和Zaira,以及他們公司的律師Abdul。三位合夥人根據多數規則作出決定,即三位合夥人中的兩位必須同意。但是,如果他們的密鑰出現問題,他們希望他們的律師能夠用三個合夥人中一個的簽名來恢復資金。最後,如果所有合作伙伴都暫時沒空或無法工作,他們希望律師能夠直接管理帳戶。

以下是Mohammed設計的實現此目標的贖回腳本:

Variable Multi-Signature with Timelock
01  IF
02    IF
03      2
04    ELSE
05      <30 days=""> CHECKSEQUENCEVERIFY DROP
06       CHECKSIGVERIFY
07      1
08    ENDIF
09       3 CHECKMULTISIG
10  ELSE
11    <90 days=""> CHECKSEQUENCEVERIFY DROP
12     CHECKSIG
13  ENDIF

Mohammed的腳本使用嵌套的 IF...ELSE 流程控制語句實現了三個執行路徑。

在第一個執行路徑中,這個腳本作為一個簡單的 2-of-3 多重簽名。此執行路徑由第3行和第9行組成。第3行將multisig的法定數設置為 2(2/3)。這個執行路徑可以通過在解鎖腳本的末尾加上 TRUE TRUE 來選擇:

Unlocking script for the first execution path (2-of-3 multisig)
0 <Mohammed's Sig> <Zaira's Sig> TRUE TRUE
Tip

這個解鎖腳本開頭的 0 是因為 CHECKMULTISIG 中的一個錯誤,它會從堆疊中彈出一個額外的值。額外的值被 CHECKMULTISIG 忽略,但它必須存在。正如 CHECKMULTISIG執行中的一個錯誤 中所述,推入 0(通常)是該bug的解決方法。

第二個執行路徑只能在創建 UTXO 30天后才能使用。到時,它需要律師Abdul和三個合夥人之一的前面( 1-of-3 的多重簽名 )。這通過第7行來實現,該行將multisig的法定數設置為 1。要選擇此執行路徑,解鎖腳本將以 FALSE TRUE 結束:

Unlocking script for the second execution path (Lawyer + 1-of-3)
0 <Saeed's Sig> <Abdul's Sig> FALSE TRUE
Tip

為什麼 FALSE TRUE?因為這兩個值被推送到堆疊上,所以首先推入 FALSE ,然後再推入 TRUE。 因此 TRUE 被第一個 IF 操作碼彈出。

最後,第三個執行路徑允許律師Abdul單獨花費資金,但只能在90天后。要選擇此執行路徑,解鎖腳本必須以 FALSE 結尾:

Unlocking script for the third execution path (Lawyer only)
<Abdul's Sig> FALSE

嘗試在紙上運行腳本以查看它在堆疊上的行為。

閱讀本示例時需要考慮幾件事情。看看你能否找到答案:

  • 為什麼律師無法通過在解鎖腳本上選擇 FALSE 執行第三條路徑來隨時贖回?

  • 在UTXO開採之後的5天,35天和105天,分別可以使用的執行路徑數量?

  • 如果律師失去了密鑰,資金是否會流失?如果91天過去了,你的答案會改變嗎?

  • 合夥人如何每隔29或89天"重置"時鐘以防止律師獲得資金?

  • 為什麼這個腳本中的一些 CHECKSIG 操作碼有 VERIFY 後綴,而其他的則沒有?

隔離見證 Segregated Witness

隔離見證 Segregated Witness (segwit) 是比特幣共識規則和網路協議的升級,由BIP-9提出並作為軟分叉實施,於2017年8月1日在比特幣主網啟用。

在密碼學中,術語"見證"用於描述密碼謎題的解決方案。對比特幣來說,"見證"能夠滿足放在未支付交易輸出(UTXO)上的加密條件。

在比特幣的情況下,數位簽章是"見證"的一種類型,但更寬泛地來說,"見證"是能夠滿足UTXO所設置的條件,解鎖並花費UTXO的任何解決方案。術語"見證"是"解鎖腳本"或"scriptSig"的更一般的術語。

在segwit引入之前,交易中的每個輸入之後都是解鎖它的見證數據。見證數據作為每個輸入的一部分嵌入在交易中。術語 segregated_witness 或簡稱 segwit 僅僅意味著將特定輸出的簽名或解鎖腳本分離。考慮最簡單的形式,"單獨的scriptSig"或"單獨簽名"。

因此,隔離見證是比特幣的體系結構變化,旨在將見證數據從交易的 scriptSig(解鎖腳本)欄位移動到伴隨交易的單獨的 witness 資料結構中。客戶端可以選擇是否附帶見證數據請求交易。

在本節中,我們將看看隔離見證的好處,描述部署和實施此架構的機制,並演示如何在交易和地址中使用隔離見證。

隔離見證由以下BIP定義:

BIP-141

Segregated Witness 的主要定義。

BIP-143

版本0見證程序的交易簽名驗證

BIP-144

對等服務 - 新的網路訊息和序列化格式

BIP-145

隔離見證的getblocktemplate(用於挖礦)更新

BIP-173

原生 v0-16 見證輸出的 Base32 地址格式

為什麼要隔離見證?

隔離見證是一種體系結構變化,它對比特幣的可擴展性,安全性,經濟效益和性能有以下影響:

交易延展性 Transaction Malleability

通過將見證數據移動到交易外部,用作標識符的交易雜湊將不再包含見證數據。由於見證數據是交易中唯一可以由第三方修改的部分(請參閱 交易標識符 Transaction identifiers ),因此去除它也消除了交易延展性攻擊的機會。使用隔離見證,交易雜湊變得不可能由交易的創建者以外的任何人改變,這極大地改進了許多其他協議的實施,這些協議依賴於先進的比特幣交易建設,例如支付通道、鏈式交易和閃電網路。

腳本版本控制 Script Versioning

通過隔離見證腳本的進入,每個鎖定腳本前面都有一個 script_version 數字,類似於交易和區塊的版本號。腳本版本號的添加允許腳本語言以向後兼容的方式升級(即使用軟叉升級)來引入新的腳本操作符,語法或語義。以無中斷方式升級腳本語言的能力將大大加速比特幣的創新速度。

網路和儲存的可擴展性 Network and Storage Scaling

見證數據通常是交易總規模的主要貢獻者。更復雜的腳本,比如用於multisig或支付通道的腳本非常龐大。在某些情況下,這些腳本佔交易數據的大部分(超過75%)。將見證數據移到交易之外,提高了比特幣的可擴展性。節點可以在驗證簽名後裁剪見證數據,或者在進行簡單付款驗證時完全忽略見證數據。見證數據不需要傳輸到所有節點,也不需要被所有節點儲存在硬碟上。

簽名驗證優化 Signature Verification Optimization

隔離見證升級了簽名方法( CHECKSIG,CHECKMULTISIG 等)以降低演算法的計算複雜度。在隔離之前,用於生成簽名的演算法需要一些與交易大小成正比的雜湊操作。數據雜湊的計算複雜度相對於簽名操作是O(n2),在驗證簽名的所有節點上引入了大量的計算負擔。使用segwit時,演算法將複雜度降低到O(n)。

離線簽名改進 Offline Signing Improvement

隔離見證簽名包含了簽名的雜湊中每個輸入引用的值(金額)。以前,離線簽名設備(如硬體錢包)必須在簽署交易之前驗證每個輸入的數量。這通常是通過流式傳輸大量關於以引用為輸入的交易的數據來完成的。由於金額現在是已簽名的雜湊的一部分,因此離線設備不需要先前的交易。如果金額不匹配(由被入侵的系統篡改),簽名將無效。

隔離見證如何工作

隔離見證看起來改變了交易如何構建,是交易層面的特性,但事實並非如此。相反,隔離見證是對如何花費單個UTXO的改變,因此是每個輸出層面的特性。

交易可以花費隔離見證的輸出或傳統(內聯見證)的輸出,或同時花費兩者。因此,將交易稱為"隔離見證交易"沒有什麼意義。我們應該將具體的交易輸出稱為"隔離見證輸出"。

當交易花費UTXO時,它必須提供見證。在傳統的UTXO中,鎖定腳本要求在花費UTXO的交易的輸入部分提供 在線的 見證數據。然而,隔離見證UTXO指定了一個鎖定腳本,它可以被輸入之外的(隔離的)見證數據滿足。

軟分叉 (向後兼容)

隔離見證是輸出和交易架構方式的重大變化。這種改變通常需要每個比特幣節點和錢包同時改變以升級共識規則 —— 所謂的硬分叉。然而,隔離見證的引入具有較少的破壞性變化,是向後兼容的,被稱為軟分叉。這種類型的升級允許非升級軟體忽略更改並繼續運行而不會中斷。

隔離見證構建的輸出,使不能識別"見證"的舊系統仍然可以驗證它們。對於舊的錢包或節點,隔離見證輸出看起來像是任何人都可以花費的輸出。這樣的輸出可以用一個空的簽名來花費,交易內部沒有簽名(隔離的)並不會使交易失效。然而,較新的錢包和挖礦節點會看到隔離見證輸出,並期望在交易的見證數據中找到有效的見證。

隔離見證輸出和交易示例

讓我們來看一些示例交易,看它們將如何隨著隔離見證改變。首先看一下如何使用隔離見證程序來轉換Pay-to-Public-Key-Hash(P2PKH)。然後,看一下Pay-to-Script-Hash(P2SH)腳本的隔離見證等價物。最後,我們將看看如何將之前的隔離見證程序嵌入到P2SH腳本中。

Pay-to-Witness-Public-Key-Hash (P2WPKH)

[cup_of_coffee] 中,Alice創建了一筆交易,向Bob購買一杯咖啡。該交易創建了一個值為0.015 BTC的P2PKH輸出,該輸出可由Bob使用。輸出的腳本如下所示:

Example P2PKH output script
DUP HASH160 ab68025513c3dbd2f7b92a94e0581f5d50f654e7 EQUALVERIFY CHECKSIG

使用隔離見證,Alice將創建一個 Pay-to-Witness-Public-Key-Hash (P2WPKH) 腳本, 看起來如下:

Example P2WPKH output script
0 ab68025513c3dbd2f7b92a94e0581f5d50f654e7

如你所見,隔離見證輸出的鎖定腳本比傳統輸出簡單得多。它由推送到腳本計算堆疊的兩個值組成。對於老的(不支持隔離見證的 nonsegwit-aware )比特幣客戶端來說,這看起來像是任何人都可以花費的輸出,並且不需要簽名(或者更確切地說,可以使用空簽名)。對於一個更新的,支持segwit的客戶端,第一個數字(0)被解釋為版本號(witness version),第二部分(20位元組)相當於被稱為 witness program 的鎖定腳本。20位元組的見證程序就是公鑰的雜湊,就像在P2PKH腳本中一樣。

現在,我們來看看Bob用來花費這個輸出的相應的交易。對於原始腳本(nonsegwit),Bob的交易必須在交易輸入中包含簽名

Decoded transaction showing a P2PKH output being spent with a signature
[...]
"Vin" : [
"txid": "0627052b6f28912f2703066a912ea577f2ce4da4caa5a5fbd8a57286c345c2f2",
"vout": 0,
     	 "scriptSig": "<Bob’s scriptSig>",
]
[...]

但是,要花費隔離見證的輸出,交易在那個輸入上沒有簽名。相反,Bob的交易包含一個空的 scriptSig 和一個在交易之外的隔離見證。

Decoded transaction showing a P2WPKH output being spent with separate witness data
[...]
"Vin" : [
"txid": "0627052b6f28912f2703066a912ea577f2ce4da4caa5a5fbd8a57286c345c2f2",
"vout": 0,
     	 "scriptSig": "",
]
[...]
"witness": "<Bob’s witness data>"
[...]
錢包的P2WPKH構建

注意到P2WPKH只能由收款人創建,而不能由付款人從已知公鑰,P2PKH腳本或地址轉換,這一點非常重要。付款人無法知道收款人的錢包是否有能力構建隔離見證交易並花費P2WPKH輸出。

此外,P2WPKH輸出必須由 壓縮公鑰的雜湊構造。未壓縮的公鑰在segwit中是非標準的,並且可能會被未來的軟分支明確禁用。如果P2WPKH中使用未壓縮的公鑰雜湊,則它可能是不可靠的,你可能會失去資金。 P2WPKH輸出應該由收款人的錢包通過從其私鑰匯出的壓縮公鑰來創建。

Warning

P2WPKH應由收款人通過將壓縮公鑰轉換為P2WPKH雜湊來構造。你不應將P2PKH腳本,比特幣地址或未壓縮的公鑰轉換為P2WPKH見證腳本。

Pay-to-Witness-Script-Hash (P2WSH)

第二種見證程序對應於支付到腳本雜湊(P2SH)的腳本。我們在 支付到腳本雜湊 Pay-to-Script-Hash (P2SH) 中看到過這種類型的腳本。在這個例子中,Mohammed的公司使用P2SH來表示多重簽名腳本。對Mohammed的公司的付款用這種鎖定腳本編碼:

Example P2SH output script
HASH160 54c557e07dde5bb6cb791c7a540e0a4796f5e97e EQUAL

該P2SH腳本引用了 贖回腳本 redeem_script 的雜湊,該腳本定義了花費資金的 2-of-3 多重簽名要求。為了使用這種輸出,Mohammed的公司將在交易輸入中提供贖回腳本(其雜湊與P2SH輸出中的腳本雜湊匹配)以及滿足贖回腳本所需的簽名:

Decoded transaction showing a P2SH output being spent
[...]
"Vin" : [
"txid": "abcdef12345...",
"vout": 0,
     	 "scriptSig": "<SigA> <SigB> <2 PubA PubB PubC PubD PubE 5 CHECKMULTISIG>",
]

現在,讓我們看看整個示例如何升級到segwit。如果Mohammed的客戶使用兼容segwit的錢包,他們將創建一個付款,包含一個Pay-to-Witness-Script-Hash(P2WSH)輸出,看起來像這樣:

Example P2WSH output script
0 a9b7b38d972cabc7961dbfbcb841ad4508d133c47ba87457b4a0e8aae86dbb89

同樣,與P2WPKH的例子一樣,你可以看到隔離見證等效腳本更簡單,並且省略了你在P2SH腳本中看到的各種腳本操作數。相反,隔離見證程序由推送到堆疊的兩個值組成:見證版本(0)和贖回腳本的32位元組SHA256雜湊。

Tip

雖然P2SH使用20位元組 RIPEMD160( SHA256(script) ) 雜湊,P2WSH見證程序使用32位元組 SHA256(script)雜湊。這種雜湊演算法選擇上的差異是故意的,用於區分兩種類型的見證程序(P2WPKH和P2WSH)之間的雜湊長度,併為P2WSH提供更強的安全性(P2WSH中的128位安全性,對比P2SH中的80位安全性)。

Mohammed的公司可以通過提供正確的贖回腳本和足夠的簽名來滿足它,用於花費P2WSH的輸出。作為見證數據的一部分,贖回腳本和簽名都將作為消費交易的一部分進行隔離。在交易輸入中,Mohammed的錢包會放置一個空的 scriptSig :

Decoded transaction showing a P2WSH output being spent with separate witness data
[...]
"Vin" : [
"txid": "abcdef12345...",
"vout": 0,
     	 "scriptSig": "",
]
[...]
"witness": "<SigA> <SigB> <2 PubA PubB PubC PubD PubE 5 CHECKMULTISIG>"
[...]
P2WPKH 和 P2WSH 的區別

在前兩節中,我們演示了兩種類型的見證程序:Pay-to-Witness-Public-Key-Hash (P2WPKH)Pay-to-Witness-Script-Hash (P2WSH)。兩種見證程序都由一個單位元組版本號和一個較長的雜湊組成。它們看起來非常相似,但是卻有著不同的解釋:一個被解釋為一個公鑰的雜湊,它被簽名滿足,另一個被解釋為腳本的雜湊,被一個贖回腳本滿足。它們之間的關鍵區別在於雜湊的長度:

  • P2WPKH 中公鑰的雜湊是 20 位元組

  • P2WSH 中腳本的雜湊是 32 位元組

這是允許錢包區分兩種見證程序的一個區別。通過查看雜湊的長度,錢包可以確定它是什麼類型的見證程序,P2WPKH或P2WSH。

升級到隔離見證

從前面的例子可以看出,升級為隔離見證是一個兩步的過程。首先,錢包必須創建特殊的隔離型輸出。然後,這些輸出可以被知道如何構建隔離見證交易的錢包花費。在這些例子中,Alice的錢包支持segwit,能夠使用Segregated Witness腳本創建特殊輸出。 Bob 的錢包也是支持segwit的,能夠花費那些輸出。從這個例子中可能不明顯的是,在實踐中,Alice的錢包需要知道Bob使用了一個支持segwit的錢包並可以使用這些輸出。否則,如果Bob的錢包沒有升級,當Alice試圖向Bob進行segwit支付,那麼Bob的錢包將無法檢測到這些支付。

Tip

對於P2WPKH和P2WSH付款類型,付款人和收款人的錢包都需要升級才能使用segwit。此外,付款人的錢包需要知道收款人的錢包是否具有隔離見證功能。

隔離見證不會在整個網路中同時實施。而是向後兼容的升級,新老客戶可以共存。錢包開發人員將獨立升級錢包軟體以添加隔離見證功能。當付款人和收款人都支持隔離見證時,可以使用P2WPKH和P2WSH付款類型。傳統的P2PKH和P2SH將繼續為沒有升級的錢包工作。這留下了兩個重要的場景,下一節將討論這些:

  • 付款人的錢包不支持隔離見證的,向支持隔離見證的收款人錢包付款

  • 付款人的支持隔離見證的錢包通過地址識別和區分收款方是否支持隔離見證的能力

P2SH中嵌入的隔離見證

舉個例子,假設Alice的錢包沒有升級到segwit,但是Bob的錢包已升級並可以處理segwit交易。 Alice和Bob可以使用"舊"的非segwit交易。但是Bob可能想使用segwit,利用適用於隔離見證的折扣,降低交易費用。

在這種情況下,Bob的錢包可以構建一個內部包含segwit腳本的P2SH地址。Alice的錢包將其視為"正常"的P2SH地址,並且可以在不知道segwit的情況下付款。然後Bob的錢包可以通過segwit交易來花費這筆款項,充分利用segwit並降低交易費用。

兩種形式的見證腳本,P2WPKH 和 P2WSH,都可以嵌入到P2SH地址中。第一個被記作P2SH(P2WPKH),第二個被記作P2SH(P2WSH)。

Pay-to-Script-Hash 中的 Pay-to-Witness-Public-Key-Hash

我們將研究的第一種見證腳本是P2SH(P2WPKH)。這是一個Pay-to-Witness-Public-Key-Hash見證程序,嵌入在Pay-to-Script-Hash腳本中,以便它可以被不知道segwit的錢包使用。

Bob的錢包用Bob的公鑰構造了一個P2WPKH見證程序。這個見證程序之後被雜湊,並將結果編碼為P2SH腳本。這個P2SH腳本轉換為比特幣地址,其中一個以"3"開頭,正如我們在 支付到腳本雜湊 Pay-to-Script-Hash (P2SH) 部分看到的那樣。

Bob的錢包從我們之前看到的P2WPKH見證程序開始:

Bob’s P2WPKH witness program
0 ab68025513c3dbd2f7b92a94e0581f5d50f654e7

P2WPKH見證程序由見證版本和Bob的20位元組公鑰雜湊組成。

Bob的錢包然後對之前的見證程序進行雜湊,首先是SHA256,然後是RIPEMD160,產生另一個20位元組的雜湊值。

讓我們使用命令行中的 bx 命令來重現:

HASH160 of the P2WPKH witness program
echo \
'0 [ab68025513c3dbd2f7b92a94e0581f5d50f654e7]'\
 | bx script-encode | bx sha256 | bx ripemd160
3e0547268b3b19288b3adef9719ec8659f4b2b0b

接著,將贖回腳本的雜湊值轉換為比特幣地址。再次使用 bx:

P2SH address
echo \
'3e0547268b3b19288b3adef9719ec8659f4b2b0b' \
| bx address-encode -v 5
37Lx99uaGn5avKBxiW26HjedQE3LrDCZru

現在,Bob可以對客戶展示這個地址,讓他們為咖啡付費。Alice的錢包可以支付給 37Lx99uaGn5avKBxiW26HjedQE3LrDCZru ,就像支付給任何其他比特幣地址一樣。

為了向Bob付款,Alice的錢包會使用如下的P2HSH腳本鎖定輸出:

HASH160 3e0547268b3b19288b3adef9719ec8659f4b2b0b EQUAL

即使Alice的錢包不支持隔離見證,這筆付款也可以被Bob使用隔離見證交易消費:

Pay-to-Script-Hash 中的 Pay-to-Witness-Script-Hash

類似地,多重簽名腳本或其他複雜腳本的P2WSH見證程序也可以嵌入到P2SH腳本和地址中,使任何錢包都可以進行segwit兼容的支付。

正如我們在 Pay-to-Witness-Script-Hash (P2WSH) 中看到的,Mohammed的公司正在對多重簽名腳本使用隔離見證付款。為了使任何客戶都能向他的公司付款(無論他們的錢包是否升級到了支持segwit的版本),Mohammed的錢包可以在一個P2SH腳本中嵌入P2WSH見證程序。

首先,Mohammed的錢包用SHA256(僅此一次)將贖回腳本進行了雜湊。讓我們在命令行上使用 bx 來完成:

Mohammed’s wallet creates a P2WSH witness program
echo \
2 \ [04C16B8698A9ABF84250A7C3EA7EEDEF9897D1C8C6ADF47F06CF73370D74DCCA01CDCA79DCC5C395D7EEC6984D83F1F50C900A24DD47F569FD4193AF5DE762C587] \
[04A2192968D8655D6A935BEAF2CA23E3FB87A3495E7AF308EDF08DAC3C1FCBFC2C75B4B0F4D0B1B70CD2423657738C0C2B1D5CE65C97D78D0E34224858008E8B49] \
[047E63248B75DB7379BE9CDA8CE5751D16485F431E46117B9D0C1837C9D5737812F393DA7D4420D7E1A9162F0279CFC10F1E8E8F3020DECDBC3C0DD389D9977965] \
[0421D65CBD7149B255382ED7F78E946580657EE6FDA162A187543A9D85BAAA93A4AB3A8F044DADA618D087227440645ABE8A35DA8C5B73997AD343BE5C2AFD94A5] \
[043752580AFA1ECED3C68D446BCAB69AC0BA7DF50D56231BE0AABF1FDEEC78A6A45E394BA29A1EDF518C022DD618DA774D207D137AAB59E0B000EB7ED238F4D800] \
5 CHECKMULTISIG \
| bx script-encode | bx sha256
9592d601848d04b172905e0ddb0adde59f1590f1e553ffc81ddc4b0ed927dd73

接下來,雜湊後的贖回腳本轉換為P2WSH見證程序:

0 9592d601848d04b172905e0ddb0adde59f1590f1e553ffc81ddc4b0ed927dd73

然後,使用SHA256和RIPEMD160對見證程序本身進行雜湊處理,生成一個新的20位元組的雜湊,就像傳統的P2SH那樣,我們使用 bx 實驗:

The HASH160 of the P2WSH witness program
 echo \
'0 [9592d601848d04b172905e0ddb0adde59f1590f1e553ffc81ddc4b0ed927dd73]'\
 | bx script-encode | bx sha256 | bx ripemd160
86762607e8fe87c0c37740cddee880988b9455b2

再然後,錢包從這個雜湊值構建一個P2SH比特幣地址,使用 bx 實驗:

P2SH bitcoin address
echo \
'86762607e8fe87c0c37740cddee880988b9455b2'\
 | bx address-encode -v 5
3Dwz1MXhM6EfFoJChHCxh1jWHb8GQqRenG

現在,Mohammed的客戶不需要必須支持segwit就可以支付到這個地址。要向Mohammed付款,錢包將用以下P2SH腳本鎖定輸出:

P2SH script used to lock payments to Mohammed’s multisig
HASH160 86762607e8fe87c0c37740cddee880988b9455b2 EQUAL

然後,Mohammed的公司可以利用segwit的好處(包括較低的交易費用),構建segwit交易來花費這些款項。

隔離見證地址

即使是在segwit啟用後,大部分的錢包升級也需要一些時間。一開始,segwit將被嵌入P2SH,如我們在前一節中看到的那樣,來方便地兼容支持segwit和不支持segwit的錢包。

然而,一旦錢包廣泛支持segwit,就有必要將目擊者腳本直接編碼成為segwit的原生地址格式,而不是嵌入到P2SH中。

原生segwit地址格式定義在 BIP-173 中:

BIP-173

Base32 address format for native v0-16 witness outputs

BIP-173僅對見證腳本(P2WPKH和P2WSH)進行編碼。它與非segwit P2PKH或P2SH腳本不兼容。與傳統的比特幣地址的Base58編碼相比,BIP-173是Base32校驗和編碼。 BIP-173地址也稱為 bech32 地址,發音為 "beh-ch thirty two",暗指使用"BCH"錯誤檢測演算法和32字符編碼集。

BIP-173地址使用32個小寫字母的字母數字字元集,經過仔細選擇以減少誤讀或錯誤輸入。通過只選擇小寫字母集,bech32更容易閱讀,朗讀,並且在QR碼中的編碼效率提高了45%。

BCH錯誤檢測演算法比以前的校驗碼演算法(Base58Check)有了很大的改進,它不僅檢測,還能糾正錯誤。地址輸入介面(如表單中的文本框)可以檢測並突出顯示在檢測錯誤時最可能出現錯誤的字元。

根據BIP-173規範,這裡是一些 bech32 地址的示例:

Mainnet P2WPKH

bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4

Testnet P2WPKH

tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx

Mainnet P2WSH

bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3

Testnet P2WSH

tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7

如你所見,segwit bech32字串長達90個字元,由三個部分組成:

人類可讀的部分

"bc" 或 "tb" 標明主網( mainnet )還是測試網( testnet )。

分隔符

數字 "1", 不是32字元編碼集的一部分,只當做分隔符出現。

數據部分

至少6個字母數字字元,校驗碼編碼的見證腳本

此時,只有少數錢包接受或生成原生segwit bech32地址,但隨著segwit的廣泛使用,你將越來越多地看到這些地址。

交易標識符 Transaction identifiers

隔離見證的最大好處之一是它消除了第三方交易延展性。

在segwit之前,交易的簽名可以被第三方微妙地修改,改變它們的交易ID(雜湊),而不改變任何基本屬性(輸入、輸出、數量)。這為拒絕服務攻擊和攻擊編寫糟糕的錢包軟體創造了機會,這些錢包假定未經確認的交易的雜湊是不可變的。

通過引入隔離見證,交易有了兩個標識符 txid 和 wtxid。傳統的交易ID txid 是序列化交易的雙SHA256雜湊,沒有見證數據。交易的 wtxid 是具有見證數據的交易的新的序列化格式的雙SHA256雜湊。

傳統的 txid 的計算方式與nonsegwit交易完全相同。但是,由於segwit交易在每個輸入中都有空的 scriptSig,因此不存在可由第三方修改的交易部分。因此,在segwit交易中,即使交易未經確認,txid 也是第三方不可變的。

wtxid 就像一個"擴展"的ID,因為這個雜湊還包含了見證數據。如果交易不帶有見證數據傳輸,那麼 wtxid 和 txid 是相同的。注意,由於 wtxid 包含見證數據(簽名),並且由於見證數據可能具有延展性,所以應認為 wtxid 在交易確認之前具有延展性。只有當交易的輸入都是segwit輸入時,segwit 交易的 txid 才能被認為是不可變的。

Tip

隔離見證交易有兩個ID:txid 和 wtxid。 txid 是不包含見證數據的交易的雜湊,wtxid 是包含見證數據的雜湊。所有輸入都為 segwit 輸入的交易的 txid 不易受第三方交易延展性影響。

隔離見證的新簽名演算法

隔離見證修改了四種簽名驗證函數(CHECKSIG,CHECKSIGVERIFY,CHECKMULTISIG 和 CHECKMULTISIGVERIFY)的語義,改變了交易保證雜湊的計算方式。

比特幣交易中的簽名應用於commitment hash,這是根據交易數據計算的,鎖定數據的特定部分,表明簽名者對這些值的保證。例如,在簡單的 SIGHASH_ALL 類型簽名中,保證雜湊包括所有輸入和輸出。

不幸的是,計算保證雜湊的方式引入了驗證簽名的節點可能被迫執行大量雜湊計算的可能性。具體而言,雜湊操作相對於交易中的簽名操作的數量以 O(n2) 的複雜度增長。因此,攻擊者可以創建帶有大量簽名操作的交易,導致整個比特幣網路必須執行數百或數千次雜湊操作才能驗證交易。

Segwit提供了通過改變保證雜湊計算方式來解決這個問題的機會。對於segwit版本0見證程序,使用BIP-143中規定的改進的保證雜湊演算法進行簽名驗證。

新演算法實現了兩個重要目標。首先,雜湊操作的數量隨著簽名操作的數量逐漸以 O(n) 增長,減少了用過於複雜的交易創建拒絕服務攻擊的機會。其次,保證雜湊現在還將每個輸入的值(金額)作為雜湊的一部分,這意味著簽名者無需"獲取"並檢查輸入引用的前一個交易就可以保證特定的輸入值。對於離線設備(例如硬體錢包),這大大簡化了主機與硬體錢包之間的通信,消除了對以前的交易進行驗證的需要。硬體錢包可以接受不受信任的主機"所聲明的"輸入值,因為如果輸入值不正確則簽名無效,硬體錢包在簽名輸入前不需要驗證該值。

隔離見證的經濟效益

比特幣挖礦節點和完整節點會產生用於支持比特幣網路和區塊鏈的資源的成本。隨著比特幣交易量的增加,資源成本(CPU,網路頻寬,硬碟空間,內存)也不斷增加。礦工通過收取與每次交易的大小(位元組)成比例的費用來補償這些成本。非挖礦(Nonmining)完整節點沒有得到補償,蒙受了損失,因為他們需要運行一個權威的完全驗證的全索引節點,可能是因為他們使用節點來經營比特幣業務。

如果沒有交易費用,比特幣數據的增長可能會大幅增加。費用旨在通過基於市場的價格發現機制,將比特幣用戶的需求與交易對網路帶來的負擔相匹配。

基於交易規模的費用計算將交易中的所有數據視為成本相同的。但是從完整節點和礦工的角度來看,交易的某些部分承擔了更高的成本。添加到比特幣網路的每筆交易都會影響節點上四種資源的消耗:

硬盤空間

每筆交易都儲存在區塊鏈中,添加到區塊鏈的總大小上。區塊鏈儲存在硬碟上,但是可以通過"刪除"舊的交易來優化儲存。

CPU

每筆交易都必須被驗證,這需要CPU時間。

頻寬

每筆交易都在網路上至少傳輸一次(通過泛洪傳播),如果在區塊傳播協議中沒有進行任何優化,交易將作為區塊的一部分再次傳輸,從而對網路容量的影響加倍。

記憶體

驗證交易的節點將UTXO索引或整個UTXO集保存在內存中,以加快驗證。因為內存至少比硬碟貴一個數量級,所以UTXO集的增長不成比例地增加了運行節點的成本。

從列表中可以看出,並非交易的每個部分都對運行節點的成本或比特幣支持更多交易的能力產生同等影響。交易中最昂貴的部分是新創建的輸出,因為它們被添加到內存中的UTXO集合中。相比之下,簽名(又名見證數據)為增加了最小的網路負擔和節點運行成本,因為見證數據只被驗證一次,然後再也不會使用。此外,在收到新的交易並驗證見證數據之後,節點立即丟棄該見證數據。如果費用是根據交易規模計算的,而不區分這兩種數據,那麼市場化的費用激勵就不符合交易實際施加的成本。實際上,目前的費用結構實際上鼓勵了相反的行為,因為見證數據是交易的最大部分。

交易在其輸入中花費UTXO,並在輸出中創建新的UTXO。因此,一個輸入數量大於輸出數量的交易將導致UTXO集的減少,而一個輸出數量大於輸入數量的交易將導致UTXO集的增加。讓我們考慮輸入和輸出之間的差異,並稱之為"淨增UTXO"("Net-new-UTXO")。這是一個重要的指標,因為它告訴我們一個交易將對網路中最昂貴的資源(即內存裡的UTXO集)產生什麼影響。Net-new-UTXO為正的交易增加負擔,Net-new-UTXO為負的交易減少負擔。因此,我們希望鼓勵Net-new-UTXO為負或為0的交易。

讓我們看一個例子,說明在有無隔離見證的情況下,交易費用計算產生了哪些激勵。我們將看兩個不同的交易。交易A是有3個輸入2個輸出的交易,Net-new-UTXO為-1。交易B是2個輸入3個輸出的交易,Net-new-UTXO為1,意味著它增加了一個UTXO到UTXO集,給整個比特幣網路帶來了額外的成本。這兩筆交易都使用多重簽名(2-of-3)腳本來說明覆雜腳本如何增加隔離見證對費用的影響。假設交易費為每位元組30 satoshi,見證數據擁有75%的費用折扣:

Without Segregated Witness

Transaction A fee: 25,710 satoshi

Transaction B fee: 18,990 satoshi

With Segregated Witness

Transaction A fee: 8,130 satoshi

Transaction B fee: 12,045 satoshi

這兩種交易在實施隔離見證時都較為便宜。但是比較這兩筆交易的成本,我們發現在隔離見證之前,Net-new-UTXO為負的交易費用較高。在隔離見證後,交易費用與鼓勵減少新的UTXO產生的激勵相一致,不會無意地懲罰有許多輸入的交易。

因此,隔離見證對比特幣用戶支付的費用有兩個主要影響。首先,segwit通過見證數據折扣,和增加比特幣區塊鏈的能力,來降低交易的總體成本。其次,segwit對見證數據的折扣糾正了可能無意中導致UTXO集合中更加膨脹的激勵錯配。