上一章我們介紹了比特幣交易的基本元素,並研究了最常見的交易指令碼型別,即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 執行時,它應該消耗堆疊中的 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,其唯一目的是修正意外成為共識規則的錯誤。
支付到指令碼雜湊(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編碼的指令碼相同。
Locking Script |
2 PubKey1 PubKey2 PubKey3 PubKey4 PubKey5 5 CHECKMULTISIG |
Unlocking Script |
Sig1 Sig2 |
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功能的另一個重要部分是將指令碼雜湊編碼為地址的能力,如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將長指令碼的資料儲存負擔從輸出中(儲存在區塊鏈中的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交易也將被視為有效的並被接受。你可能會意外鎖定比特幣,之後無法花費。 |
比特幣的分散式時間戳帳簿,區塊鏈(blockchain),具有遠遠超出支付範圍的潛在用途。許多開發人員嘗試使用交易指令碼語言,利用系統的安全性和靈活性,應用於數位公證服務,股票證書和智慧合約等。將比特幣的指令碼語言用於這些目的的早期嘗試包括建立交易輸出,在區塊鏈上記錄資料;例如,記錄檔案的數位指紋,使得任何人都可以透過參考該交易作為該檔案在特定日期存在的證明。
使用比特幣區塊鏈來儲存與比特幣付款無關的資料是一個有爭議的話題。許多開發人員認為這種使用是濫用,並希望阻止它。其他人則認為這是區塊鏈技術強大功能的一個範例,並且希望鼓勵這種實驗。那些反對納入未付款資料的人爭辯說,它會導致“區塊鏈膨脹”,使那些執行完整比特幣節點的人承擔儲存區塊鏈無意承載的資料帶來的成本。此外,此類別交易建立了不能用於支付的,使用20位元組的目標比特幣地址的UTXO。由於該地址用於資料,因此它不對應於私鑰,產生的UTXO不會被花費;這是虛假的付款。因此,永遠不會花費的這些交易永遠不會從UTXO集中移除,並導致UTXO資料庫的大小永遠增加或“膨脹”。
在Bitcoin Core客戶端的0.9版本中,透過引入 RETURN 運算子達成了一個折衷方案。RETURN 允許開發人員將80個位元組的非付款資料新增到交易輸出中。但是,與使用“假”UTXO不同,RETURN 運算子會建立一個顯式的 可驗證不可消費 的輸出,該輸出不需要儲存在UTXO集合中。RETURN 輸出記錄在區塊鏈中,因此它們消耗磁碟空間並會導致區塊鏈大小的增加,但它們不儲存在UTXO集中,因此不會使UTXO記憶體池膨脹,完整節點頁不用承擔昂貴的記憶體負擔。
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 交易。 |
時間鎖是對交易或輸出的限制,只允許在某個時間點之後花費。比特幣從一開始就具有交易級別的時間鎖定功能。它由交易中的 nLocktime 欄位實現。2015年末和2016年中推出了兩個新的時間鎖功能,可提供UTXO級別的時間鎖定。這些是 CHECKLOCKTIMEVERIFY 和 CHECKSEQUENCEVERIFY。
時間鎖定對於推遲日期的交易非常有用,將資金鎖定在未來的日期。更重要的是,時間鎖將比特幣指令碼延伸到時間維度,為複雜的多步智慧合約打開了大門。
從一開始,比特幣就具有交易級別的時間鎖定功能。交易鎖定時間是交易級別的設定(交易資料結構中的一個欄位),用於定義交易有效的最早時間,並且可以在網路上中轉或新增到區塊鏈。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 的侷限性非常重要。唯一的保證是鮑勃在3個月之前不能贖回,而無法保證鮑勃將獲得資金。要達到這樣的保證,時間限制必須放在UTXO上,併成為鎖定指令碼的一部分,而不是交易的一部分。這是透過稱為 檢查鎖定時間驗證 Check Lock Time Verify (CLTV) 的下一種時間形式實現的。
2015年12月,一種新的時間鎖形式作為軟分叉升級引入了比特幣。根據BIP-65中的規範,一種名為 CHECKLOCKTIMEVERIFY(CLTV)的指令碼運算子新增到指令碼語言中。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):
-
棧為空;或者
-
棧頂元素小於0;或者
-
棧頂元素鎖定時間的型別(區塊高度或時間戳)與 nLocktime 欄位不同;或者
-
棧頂元素大於交易的 nLocktime 欄位;或者
-
輸入的 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, Relative lock-time using consensus-enforced sequence numbers 和 BIP-112, CHECKSEQUENCEVERIFY 中的規範實現的。
BIP-68和BIP-112於2016年5月作為共識規則的軟分叉升級啟用。
透過設定 nSequence 欄位,可以在交易的每個輸入上設定相對時間鎖。
nSequence 欄位的本意在於(但從未正確實施)允許修改 mempool 中的交易。在該用途中,包含 nSequence 值低於 232 - 1(0xFFFFFFFF)的輸入的交易指示尚未“完成”的交易。這樣的交易將保留在 mempool 中,直到它被另一個花費相同的輸入但具有更高的 nSequence 值的交易替代。一旦接收到輸入的 nSequence 值為 0xFFFFFFFF 的交易,它將被視為“完成的”並被開採。
nSequence 的原始含義從未正確實現,nSequence 的值通常在不使用時間鎖定的交易中被設定為0xFFFFFFFF。對於具有 nLocktime 或 CHECKLOCKTIMEVERIFY 的交易,必須將 nSequence 值設定為小於 231,才能使時間保護具有效果,如下所述
自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 定義。
基於 nSequence 值共識執行的相對時間鎖在BIP-68中定義。
與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 中詳細定義。
作為啟用相對時間鎖的一部分,時間鎖(絕對和相對)的“時間”計算方式有所變化。在比特幣中,實際時間(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 中被定義。
費用狙擊(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。
在大多數傳統(過程式)程式語言中,流程控制看起來是這樣的:
if (condition): code to run when condition is true else: code to run when condition is false code to run in either case
在類似比特幣指令碼的基於堆疊的語音中,邏輯條件放在 IF 之前,使其看起來是逆序的:
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 字尾表示如果所評估的條件不是 TRUE,指令碼將立即終止執行,並且交易被視為無效。
與 IF 語句提供不同的執行路徑不同,VERIFY 字尾用作 守護語句 guard clause, 只有滿足前面的條件時繼續執行。
例如,以下指令碼需要Bob的簽名和產生特定雜湊的原象(pre-image)(金鑰)。必須滿足這兩個條件才能解鎖:
HASH160 <expected hash> EQUALVERIFY <Bob's Pubkey> CHECKSIG
為了贖回這筆資金, Bob 必須建立提供原象(pre-image)和簽名的解鎖指令碼:
<Bob's Sig> <hash pre-image>
在不提供原象的情況下,Bob無法執行到檢查其簽名的指令碼部分。
這個指令碼可以用 IF 語句寫成:
HASH160 <expected hash> EQUAL IF <Bob's Pubkey> CHECKSIG ENDIF
Bob的解鎖指令碼是相同的:
<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設計的實現此目標的贖回指令碼:
01 IF
02 IF
03 2
04 ELSE
05 <30 days> CHECKSEQUENCEVERIFY DROP
06 <Abdul the Lawyer's Pubkey> CHECKSIGVERIFY
07 1
08 ENDIF
09 <Mohammed's Pubkey> <Saeed's Pubkey> <Zaira's Pubkey> 3 CHECKMULTISIG
10 ELSE
11 <90 days> CHECKSEQUENCEVERIFY DROP
12 <Abdul the Lawyer's Pubkey> CHECKSIG
13 ENDIF
Mohammed的指令碼使用巢狀的 IF...ELSE 流程控制語句實現了三個執行路徑。
在第一個執行路徑中,這個指令碼作為一個簡單的 2-of-3 多重簽名。此執行路徑由第3行和第9行組成。第3行將multisig的法定數設定為 2(2/3)。這個執行路徑可以透過在解鎖指令碼的末尾加上 TRUE TRUE 來選擇:
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 結束:
0 <Saeed's Sig> <Abdul's Sig> FALSE TRUE
Tip
|
為什麼 FALSE TRUE?因為這兩個值被推送到堆疊上,所以首先推入 FALSE ,然後再推入 TRUE。因此 TRUE 被第一個 IF 操作碼彈出。 |
最後,第三個執行路徑允許律師Abdul單獨花費資金,但只能在90天后。要選擇此執行路徑,解鎖指令碼必須以 FALSE 結尾:
<Abdul's Sig> FALSE
嘗試在紙上執行指令碼以檢視它在堆疊上的行為。
閱讀本範例時需要考慮幾件事情。看看你能否找到答案:
-
為什麼律師無法透過在解鎖指令碼上選擇 FALSE 執行第三條路徑來隨時贖回?
-
在UTXO開採之後的5天,35天和105天,分別可以使用的執行路徑數量?
-
如果律師失去了金鑰,資金是否會流失?如果91天過去了,你的答案會改變嗎?
-
合夥人如何每隔29或89天“重置”時鐘以防止律師獲得資金?
-
為什麼這個指令碼中的一些 CHECKSIG 操作碼有 VERIFY 字尾,而其他的則沒有?
隔離見證 Segregated Witness (segwit) 是比特幣共識規則和網路協議的升級,由BIP-9提出並作為軟分叉實施,於2017年8月1日在比特幣主網啟用。
在密碼學中,術語“見證”用於描述密碼謎題的解決方案。對比特幣來說,“見證”能夠滿足放在未支付交易輸出(UTXO)上的加密條件。
在比特幣的情況下,數位簽章是“見證”的一種型別,但更寬泛地來說,“見證”是能夠滿足UTXO所設定的條件,解鎖並花費UTXO的任何解決方案。術語“見證”是“解鎖指令碼”或“scriptSig”的更一般的術語。
在segwit引入之前,交易中的每個輸入之後都是解鎖它的見證資料。見證資料作為每個輸入的一部分嵌入在交易中。術語 segregated_witness 或簡稱 segwit 僅僅意味著將特定輸出的簽名或解鎖指令碼分離。考慮最簡單的形式,“單獨的scriptSig”或“單獨簽名”。
因此,隔離見證是比特幣的體系結構變化,旨在將見證資料從交易的 scriptSig(解鎖指令碼)欄位移動到伴隨交易的單獨的 witness 資料結構中。客戶端可以選擇是否附帶見證資料請求交易。
在本節中,我們將看看隔離見證的好處,描述部署和實施此架構的機制,並示範如何在交易和地址中使用隔離見證。
隔離見證由以下BIP定義:
隔離見證是一種體系結構變化,它對比特幣的可擴充套件性,安全性,經濟效益和效能有以下影響:
- 交易可鍛性 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指令碼中。
在 [cup_of_coffee] 中,Alice建立了一筆交易,向Bob購買一杯咖啡。該交易建立了一個值為0.015 BTC的P2PKH輸出,該輸出可由Bob使用。輸出的指令碼如下所示:
DUP HASH160 ab68025513c3dbd2f7b92a94e0581f5d50f654e7 EQUALVERIFY CHECKSIG
使用隔離見證,Alice將建立一個 Pay-to-Witness-Public-Key-Hash (P2WPKH) 指令碼, 看起來如下:
0 ab68025513c3dbd2f7b92a94e0581f5d50f654e7
如你所見,隔離見證輸出的鎖定指令碼比傳統輸出簡單得多。它由推送到指令碼計算堆疊的兩個值組成。對於老的(不支援隔離見證的 nonsegwit-aware )比特幣客戶端來說,這看起來像是任何人都可以花費的輸出,並且不需要簽名(或者更確切地說,可以使用空簽名)。對於一個更新的,支援segwit的客戶端,第一個數字(0)被解釋為版本號(witness version),第二部分(20位元組)相當於被稱為 witness program 的鎖定指令碼。20位元組的見證程式就是公鑰的雜湊,就像在P2PKH指令碼中一樣。
現在,我們來看看Bob用來花費這個輸出的相應的交易。對於原始指令碼(nonsegwit),Bob的交易必須在交易輸入中包含簽名
[...] “Vin” : [ "txid": "0627052b6f28912f2703066a912ea577f2ce4da4caa5a5fbd8a57286c345c2f2", "vout": 0, "scriptSig": “<Bob’s scriptSig>”, ] [...]
但是,要花費隔離見證的輸出,交易在那個輸入上沒有簽名。相反,Bob的交易包含一個空的 scriptSig 和一個在交易之外的隔離見證。
[...] “Vin” : [ "txid": "0627052b6f28912f2703066a912ea577f2ce4da4caa5a5fbd8a57286c345c2f2", "vout": 0, "scriptSig": “”, ] [...] “witness”: “<Bob’s witness data>” [...]
注意到P2WPKH只能由收款人建立,而不能由付款人從已知公鑰,P2PKH指令碼或地址轉換,這一點非常重要。付款人無法知道收款人的錢包是否有能力建構隔離見證交易並花費P2WPKH輸出。
此外,P2WPKH輸出必須由 _壓縮_公鑰的雜湊構造。未壓縮的公鑰在segwit中是非標準的,並且可能會被未來的軟分支明確禁用。如果P2WPKH中使用未壓縮的公鑰雜湊,則它可能是不可靠的,你可能會失去資金。P2WPKH輸出應該由收款人的錢包透過從其私鑰匯出的壓縮公鑰來建立。
Warning
|
P2WPKH應由收款人透過將壓縮公鑰轉換為P2WPKH雜湊來構造。你不應將P2PKH指令碼,比特幣地址或未壓縮的公鑰轉換為P2WPKH見證指令碼。 |
第二種見證程式對應於支付到指令碼雜湊(P2SH)的指令碼。我們在 支付到指令碼雜湊 Pay-to-Script-Hash (P2SH) 中看到過這種型別的指令碼。在這個例子中,Mohammed的公司使用P2SH來表示多重簽名指令碼。對Mohammed的公司的付款用這種鎖定指令碼編碼:
HASH160 54c557e07dde5bb6cb791c7a540e0a4796f5e97e EQUAL
該P2SH指令碼參考了 贖回指令碼 redeem_script 的雜湊,該指令碼定義了花費資金的 2-of-3 多重簽名要求。為了使用這種輸出,Mohammed的公司將在交易輸入中提供贖回指令碼(其雜湊與P2SH輸出中的指令碼雜湊匹配)以及滿足贖回指令碼所需的簽名:
[...] “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)輸出,看起來像這樣:
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 :
[...] “Vin” : [ "txid": "abcdef12345...", "vout": 0, "scriptSig": “”, ] [...] “witness”: “<SigA> <SigB> <2 PubA PubB PubC PubD PubE 5 CHECKMULTISIG>” [...]
在前兩節中,我們示範了兩種型別的見證程式:Pay-to-Witness-Public-Key-Hash (P2WPKH) 和 Pay-to-Witness-Script-Hash (P2WSH)。兩種見證程式都由一個單位元組版本號和一個較長的雜湊組成。它們看起來非常相似,但是卻有著不同的解釋:一個被解釋為一個公鑰的雜湊,它被簽名滿足,另一個被解釋為指令碼的雜湊,被一個贖回指令碼滿足。它們之間的關鍵區別在於雜湊的長度:
-
P2WPKH 中公鑰的雜湊是 20 位元組
-
P2WSH 中指令碼的雜湊是 32 位元組
這是允許錢包區分兩種見證程式的一個區別。透過檢視雜湊的長度,錢包可以確定它是什麼型別的見證程式,P2WPKH或P2WSH。
從前面的例子可以看出,升級為隔離見證是一個兩步的過程。首先,錢包必須建立特殊的隔離型輸出。然後,這些輸出可以被知道如何建構隔離見證交易的錢包花費。在這些例子中,Alice的錢包支援segwit,能夠使用Segregated Witness指令碼建立特殊輸出。鮑勃的錢包也是支援segwit的,能夠花費那些輸出。從這個例子中可能不明顯的是,在實踐中,Alice的錢包需要知道Bob使用了一個支援segwit的錢包並可以使用這些輸出。否則,如果Bob的錢包沒有升級,當Alice試圖向Bob進行segwit支付,那麼Bob的錢包將無法檢測到這些支付。
Tip
|
對於P2WPKH和P2WSH付款型別,付款人和收款人的錢包都需要升級才能使用segwit。此外,付款人的錢包需要知道收款人的錢包是否具有隔離見證功能。 |
隔離見證不會在整個網路中同時實施。而是向後相容的升級,新老客戶可以共存。錢包開發人員將獨立升級錢包軟體以新增隔離見證功能。當付款人和收款人都支援隔離見證時,可以使用P2WPKH和P2WSH付款型別。傳統的P2PKH和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)。
我們將研究的第一種見證指令碼是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見證程式開始:
0 ab68025513c3dbd2f7b92a94e0581f5d50f654e7
P2WPKH見證程式由見證版本和Bob的20位元組公鑰雜湊組成。
Bob的錢包然後對之前的見證程式進行雜湊,首先是SHA256,然後是RIPEMD160,產生另一個20位元組的雜湊值。
讓我們使用命令列中的 bx 命令來重現:
echo \ '0 [ab68025513c3dbd2f7b92a94e0581f5d50f654e7]'\ | bx script-encode | bx sha256 | bx ripemd160 3e0547268b3b19288b3adef9719ec8659f4b2b0b
接著,將贖回指令碼的雜湊值轉換為比特幣地址。再次使用 bx:
echo \ '3e0547268b3b19288b3adef9719ec8659f4b2b0b' \ | bx address-encode -v 5 37Lx99uaGn5avKBxiW26HjedQE3LrDCZru
現在,Bob可以對客戶展示這個地址,讓他們為咖啡付費。Alice的錢包可以支付給 37Lx99uaGn5avKBxiW26HjedQE3LrDCZru ,就像支付給任何其他比特幣地址一樣。
為了向Bob付款,Alice的錢包會使用如下的P2HSH指令碼鎖定輸出:
HASH160 3e0547268b3b19288b3adef9719ec8659f4b2b0b EQUAL
即使Alice的錢包不支援隔離見證,這筆付款也可以被Bob使用隔離見證交易消費:
類似地,多重簽名指令碼或其他複雜指令碼的P2WSH見證程式也可以嵌入到P2SH指令碼和地址中,使任何錢包都可以進行segwit相容的支付。
正如我們在 Pay-to-Witness-Script-Hash (P2WSH) 中看到的,Mohammed的公司正在對多重簽名指令碼使用隔離見證付款。為了使任何客戶都能向他的公司付款(無論他們的錢包是否升級到了支援segwit的版本),Mohammed的錢包可以在一個P2SH指令碼中嵌入P2WSH見證程式。
首先,Mohammed的錢包用SHA256(僅此一次)將贖回指令碼進行了雜湊。讓我們在命令列上使用 bx 來完成:
echo \ 2 \ [04C16B8698A9ABF84250A7C3EA7EEDEF9897D1C8C6ADF47F06CF73370D74DCCA01CDCA79DCC5C395D7EEC6984D83F1F50C900A24DD47F569FD4193AF5DE762C587] \ [04A2192968D8655D6A935BEAF2CA23E3FB87A3495E7AF308EDF08DAC3C1FCBFC2C75B4B0F4D0B1B70CD2423657738C0C2B1D5CE65C97D78D0E34224858008E8B49] \ [047E63248B75DB7379BE9CDA8CE5751D16485F431E46117B9D0C1837C9D5737812F393DA7D4420D7E1A9162F0279CFC10F1E8E8F3020DECDBC3C0DD389D9977965] \ [0421D65CBD7149B255382ED7F78E946580657EE6FDA162A187543A9D85BAAA93A4AB3A8F044DADA618D087227440645ABE8A35DA8C5B73997AD343BE5C2AFD94A5] \ [043752580AFA1ECED3C68D446BCAB69AC0BA7DF50D56231BE0AABF1FDEEC78A6A45E394BA29A1EDF518C022DD618DA774D207D137AAB59E0B000EB7ED238F4D800] \ 5 CHECKMULTISIG \ | bx script-encode | bx sha256 9592d601848d04b172905e0ddb0adde59f1590f1e553ffc81ddc4b0ed927dd73
接下來,雜湊後的贖回指令碼轉換為P2WSH見證程式:
0 9592d601848d04b172905e0ddb0adde59f1590f1e553ffc81ddc4b0ed927dd73
然後,使用SHA256和RIPEMD160對見證程式本身進行雜湊處理,產生一個新的20位元組的雜湊,就像傳統的P2SH那樣,我們使用 bx 實驗:
echo \ '0 [9592d601848d04b172905e0ddb0adde59f1590f1e553ffc81ddc4b0ed927dd73]'\ | bx script-encode | bx sha256 | bx ripemd160 86762607e8fe87c0c37740cddee880988b9455b2
再然後,錢包從這個雜湊值建構一個P2SH比特幣地址,使用 bx 實驗:
echo \ '86762607e8fe87c0c37740cddee880988b9455b2'\ | bx address-encode -v 5 3Dwz1MXhM6EfFoJChHCxh1jWHb8GQqRenG
現在,Mohammed的客戶不需要必須支援segwit就可以支付到這個地址。要向Mohammed付款,錢包將用以下P2SH指令碼鎖定輸出:
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 個小寫字母的英數字集 (lower-case-only alphanumeric character set),經過仔細選擇以減少誤讀或錯誤輸入。透過只選擇小寫字母集,bech32 更容易閱讀,朗讀,並且在 QRCode 中的編碼效率提高了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的廣泛使用,你將越來越多地看到這些地址。
隔離見證的最大好處之一是它消除了第三方交易可鍛性。
在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集合中更加膨脹的激勵錯配。