<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # 第七章 高級交易和腳本 ## 7.1 介紹 在上一章中,我們介紹了比特幣交易的基本要素,并且了解了最常見的交易腳本類型,即P2PKH腳本。在本章中,我們將介紹更高級的腳本,以及如何使用它來構建復雜條件的交易。 首先,我們將了解*多重簽名multisignature*腳本。接下來,我們將查看第二種最常見的交易腳本*支付腳本哈希Pay-to-Script-Hash*,它開啟了復雜腳本的整個世界。然后,查看新的腳本操作符,能夠通過*時間鎖timelocks*給比特幣添加時間維度。最后,我們將研究*隔離見證Segregated Witness*,這是對交易結構的架構性更改。 ## 7.2 多重簽名 多重簽名腳本設置了一個條件,腳本中記錄了N個公鑰,必須至少提供其中的M個簽名才能解鎖資金。這也稱為M/N方案,其中N是密鑰的總數,M是驗證必須的簽名數。例如,2/3的多重簽名是三個公鑰被列為潛在簽名人,其中至少兩個必須用于簽名才能創建有效的使用資金的交易。 目前,*標準*多重簽名腳本限制在最多3個公鑰,這意味著可以執行從1/1到3/3或這個范圍內的任意組合的多重簽名。在本書出版時,可能會取消對3個公鑰的限制,建議檢查issstandard()函數,查看網絡當前接受的值。注意,3個公鑰限制只應用于標準多重簽名腳本,不適用于P2SH腳本包裝的多重簽名腳本。P2SH多重簽名腳本限制為15個密鑰,允許最多15/15多重簽名。我們將在[【7.3 P2SH(Pay-to-Script-Hash)】](https://github.com/tianmingyun/MasterBitcoin2CN/blob/master/ch07.md#73-p2shpay-to-script-hash)學習P2SH。 設置M/N多重簽名條件的鎖定腳本的一般形式是: ``` M <Public Key 1> <Public Key 2> ... <Public Key N> N CHECKMULTISIG ``` M是花費輸出所需的簽名的數量的底限,N是列出的公鑰的總數。 設置2/3多重簽名條件的鎖定腳本如下所示: ``` 2 <Public Key A> <Public Key B> <Public Key C> 3 CHECKMULTISIG ``` 上述鎖定腳本可被含有一對簽名和公鑰的解鎖腳本滿足: ``` <Signature B> <Signature C> ``` 或者由3個公鑰中任意2個對應的私鑰產生的簽名組合。 這兩個腳本一起形成下面的組合驗證腳本: ``` <Signature B> <Signature C> 2 <Public Key A> <Public Key B> <Public Key C> 3 CHECKMULTISIG ``` 執行時,僅當解鎖腳本與鎖定腳本設置的條件匹配時,此組合腳本的評估結果才為TRUE。上述例子中設置條件就是:解鎖腳本是否含有3個公鑰中的任意2個相對應的私鑰的有效簽名。 **CHECKMULTISIG執行中的bug** CHECKMULTISIG的執行中出現了一個bug,需要做一些輕微的變通。 就是當CHECKMULTISIG執行時,它應該消耗堆棧上的M + N + 2個項目作為參數。 然而,由于該bug,CHECKMULTISIG會彈出一個額外的值或超出預期一個值。 我們使用前面的驗證示例更詳細地看一下: ``` <Signature B> <Signature C> 2 <Public Key A> <Public Key B> <Public Key C> 3 CHECKMULTISIG ``` 首先,CHECKMULTISIG彈出最上面的項目,這是N(在這個例子中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> ``` 從現在開始,如果你看到一個多簽解鎖腳本,你應該期望開頭就看到有一個額外的0,其目的是解決一個bug,卻意外地成為共識規則。 ## 7.3 P2SH(Pay-to-Script-Hash) 支付腳本哈希P2SH是2012年推出的一種功能強大的新型交易,它大大簡化了復雜交易腳本的使用。為了解釋P2SH的必要性,讓我們看一個實際的例子。 在[【第1章 比特幣介紹】](https://github.com/tianmingyun/MasterBitcoin2CN/blob/master/ch01.md)中,我們曾介紹過迪拜的電子產品進口商Mohammed。他的公司賬目廣泛采用比特幣的多重簽名功能。多重簽名腳本是比特幣高級腳本最為常見的一種用途之一,是一種非常強大的功能。Mohammed的公司對所有客戶付款,會計術語稱為“應收賬款”,即AR,都使用多重簽名腳本。基于多重簽名方案,客戶支付的任何款項都會被鎖定,必須至少兩個簽名才能解鎖,一個來自Mohammed,另一個來自其合伙人或擁有備份密鑰的律師。這樣的多重簽名機制能提升公司治理管控,同時也能有效防范盜竊、挪用和丟失。 最終的腳本非常長: ``` 2 <Mohammed's Public Key> <Partner1 Public Key> <Partner2 Public Key> <Partner3 Public Key> <Attorney Public Key> 5 OP_C HECKMULTISIG ``` 雖然多重簽名十分強大,但使用起來還是多有不便。Mohammed必須在客戶付款前將上面的腳本發送給每一位客戶,而每一位客戶也必須使用專用的能創建自定義交易腳本的比特幣錢包軟件,每位客戶還得學會如何利用自定義腳本來創建交易。此外,由于腳本可能包含特別長的公鑰,最終的交易腳本可能是最初交易腳本長度的5倍之多。超大的交易還將給客戶造成費用負擔。最后,這樣一個大交易腳本將一直記錄在所有節點內存的UTXO集中,直到該筆資金被使用。所有這些都使得這種復雜鎖定腳本在實踐中變得困難重重。 P2SH正是為了解決這一實際難題而被引入的,它使復雜腳本的使用能與直接向比特幣地址支付一樣簡單。使用P2SH支付,復雜的鎖定腳本被其電子指紋(加密哈希)所取代。當隨后出現的一筆交易試圖花費這個UTXO時,除了解鎖腳本外,它還必須包含與哈希匹配的腳本。簡單地說,P2SH意味著“支付給匹配這個哈希的腳本,這個腳本將在以后花費這個輸出時呈現。” 在P2SH交易中,鎖定腳本被哈希值取代,稱為*兌換腳本redeem script*,因為它在兌換時提交給系統,而不是作為鎖定腳本。表7-1顯示了不帶P2SH的腳本,表7-2顯示用P2SH編碼的相同腳本。 表7-1 不含P2SH的復雜腳本 Locking Script|2 PubKey1 PubKey2 PubKey3 PubKey4 PubKey5 5 CHECKMULTISIG -|- Unlocking Script|Sig1 Sig2 表7-2 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個字節的加密哈希所取代,首先采用SH256哈希算法,再對結果運用RIPEMD160算法。 命令行界面使用libbitcoin-explorer (bx)產生下面的腳本哈希: ``` echo \ 2 \ [04C16B8698A9ABF84250A7C3EA7EEDEF9897D1C8C6ADF47F06CF73370D74DCCA01CDCA79DCC5C395D7EEC6984D83F1F50C900A24DD47F569FD4193AF5DE762C587] \ [04A2192968D8655D6A935BEAF2CA23E3FB87A3495E7AF308EDF08DAC3C1FCBFC2C75B4B0F4D0B1B70CD2423657738C0C2B1D5CE65C97D78D0E34224858008E8B49] \ [047E63248B75DB7379BE9CDA8CE5751D16485F431E46117B9D0C1837C9D5737812F393DA7D4420D7E1A9162F0279CFC10F1E8E8F3020DECDBC3C0DD389D9977965] \ [0421D65CBD7149B255382ED7F78E946580657EE6FDA162A187543A9D85BAAA93A4AB3A8F044DADA618D087227440645ABE8A35DA8C5B73997AD343BE5C2AFD94A5] \ [043752580AFA1ECED3C68D446BCAB69AC0BA7DF50D56231BE0AABF1FDEEC78A6A45E394BA29A1EDF518C022DD618DA774D207D137AAB59E0B000EB7ED238F4D800] \ 5 CHECKMULTISIG \ | bx script-encode | bx sha256 | bx ripemd160 54c557e07dde5bb6cb791c7a540e0a4796f5e97e ``` 上面的一系列命令首先將Mohammed的多簽兌換腳本編碼為一個序列化的十六進制編碼比特幣腳本。下面的bx命令使用RIPEMD160再次哈希,生成最終的腳本哈希: Mohammed的兌換腳本20字節哈希: ``` 54c557e07dde5bb6cb791c7a540e0a4796f5e97e ``` P2SH交易把輸出鎖定在這個哈希,而不是那個特別長的兌換腳本。使用的鎖定腳本為: ``` HASH160 54c557e07dde5bb6cb791c7a540e0a4796f5e97e EQUAL ``` 正如你所看到的,這個腳本簡短多了。P2SH交易等同于“支付給包含該哈希的腳本”,而不是“支付給5個多重簽名腳本”。客戶在向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的鎖定腳本中。 ### 7.3.1 P2SH地址 P2SH的另一重要特征是它能將腳本哈希編碼為一個地址,正如BIP-13中所定義的。P2SH地址是采用Base58Check對20個字節哈希的腳本進行編碼,就像比特幣地址是公鑰20字節哈希的Base58Check編碼一樣。由于P2SH地址采用5作為前綴,這導致基于Base58編碼的地址以“3”開頭。 例如,Mohammed的復雜腳本,Base58Check編碼后的P2SH地址為“39RF6JqABiHdYHkfChV6USGMe6Nsr66Gzw”。我們可以通過bx命令確認: ``` echo \ '54c557e07dde5bb6cb791c7a540e0a4796f5e97e'\ | bx address-encode -v 5 39RF6JqABiHdYHkfChV6USGMe6Nsr66Gzw ``` 現在,Mohammed可以把這個地址發送給他的客戶,他們可以采用任意比特幣錢包進行簡單支付,就像是比特幣地址一樣。前綴“3”暗示客戶這是一種特殊類型的地址,一種對應于腳本而不是公鑰的地址,但它作為付款方式與比特幣地址完全相同。 P2SH地址隱藏了所有的復雜性,因此,運用其進行支付的人根本看不到腳本。 ### 7.3.2 P2SH的優點 與在鎖定輸出中直接使用復雜腳本相比,P2SH特性提供了以下好處: - 在交易輸出中,復雜腳本由簡短電子指紋取代,使得交易代碼變短。 - 腳本被編譯為地址,支付發送者及其比特幣錢包都不需要復雜工程就可以執行P2SH。 - P2SH將構建腳本的負擔轉移至接收方,而非發送者。 - P2SH將長腳本數據的存儲負擔從輸出方(既存儲在區塊鏈,又存儲在UTXO集),轉移至輸入方(只存儲在區塊鏈中)。 - P2SH將長腳本數據的存儲負擔從當前(支付時)轉移至未來(花費時)。 - P2SH將長腳本的交易費成本從發送方轉移至接收方,接收方必須包含長的兌換腳本才能使用該筆資金。 ### 7.3.3 兌換腳本和驗證 在0.9.2版Bitcoin Core客戶端之前,P2SH通過IsStandard(),僅限于標準類型的比特幣交易腳本。這也意味著花費交易中的兌換腳本只能是標準化的P2PK、P2PKH或者多重簽名。 0.9.2版的Bitcoin Core客戶端,P2SH交易能包含任意有效的腳本,這使得P2SH標準更為靈活,可以用于多種新的或復雜類型的交易進行實驗。 請記住不能將P2SH植入P2SH兌換腳本,因為P2SH規范不是遞歸的。雖然在技術上可以將RETURN[【7.4 數據記錄輸出(RETURN操作符)】](https://github.com/tianmingyun/MasterBitcoin2CN/blob/master/ch07.md#74-數據記錄輸出return操作符)包含在兌換腳本中,規則中也未阻止此這樣操作,但這沒有實際用途,因為在驗證期間執行RETURN將導致交易被標記為無效。 需要注意的是,因為在嘗試使用P2SH輸出之前,兌換腳本不會呈現給網絡,因此,如果使用無效兌換腳本的哈希鎖定輸出,則不管如何都會進行處理。該UTXO將會被成功鎖定,但是你將不能使用該筆資金,包含兌換腳本的花費交易也不被接受,因為該腳本是無效的。這樣就會產生風險,你把比特幣鎖定在永不能花費的P2SH中。比特幣網絡本身會接受這個P2SH鎖定腳本,即便它對應的是無效的兌換腳本,因為腳本哈希沒有給出它所表示的腳本的含義。 >**注釋** P2SH鎖定腳本包含一個兌換腳本哈希,其中不包括該兌換腳本本身的任何線索。即便在兌換腳本無效的情況下,P2SH交易也會被認為有效并被接受。你可能會意外地鎖死比特幣,以后再也無法使用它。 ## 7.4 數據記錄輸出(RETURN操作符) 比特幣的分布式和時間戳賬本,即區塊鏈技術,其潛在用途將大大超越支付領域。許多開發者試圖充分發揮交易腳本語言的安全性和彈性優勢,將其運用于數字公證服務、股票證書和智能合約等領域。使用比特幣的腳本語言來實現這些目的的早期嘗試,包括創建交易輸出,把數據記錄在區塊鏈上,例如,以這樣的方式記錄文件的數字指紋,任何人可以通過引用該交易來建立該文件特定日期的存在證明。 運用比特幣的區塊鏈技術存儲與比特幣支付不相關數據的做法是一個有爭議的話題。許多開發者認為其有濫用的嫌疑,因而試圖予以阻止。另一些開發者則將之視為區塊鏈技術強大功能的有力證明,認為應該給予大力支持。那些反對包含非支付數據的人辯稱這將導致“區塊鏈膨脹”,增加運行的全節點的磁盤存儲成本,承擔了區塊鏈不應該攜帶的數據。而且,此類交易創建了不能花費的UTXO,使用目標比特幣地址作為20字節的自由格式字段。因為比特幣地址只是被當作數據使用,并不對應于私鑰,所以會導致UTXO*永遠*不能用于交易,因而是偽支付。這些交易永遠不會被花費,所以永遠不會從UTXO集中刪除,會導致UTXO數據庫的大小永遠增加“膨脹”。 在0.9版的Bitcoin Core客戶端上,通過采用RETURN 操作符最終實現了妥協。RETURN 允許開發者在交易輸出上增加80字節的非支付數據。然后,與偽UTXO不同,RETURN 創造了一種明確的*可驗證不可消費型*輸出,此類數據無需存儲于UTXO集。RETURN輸出被記錄在區塊鏈上,它們會消耗磁盤空間,也會導致區塊鏈規模的增加,但它們不存儲在UTXO集中,因此也不會使得UTXO內存池膨脹,更不會增加全節點昂貴的內存代價。 RETURN 腳本的樣式: ``` RETURN <data> ``` “data”部分被限制為80字節,且多表示為哈希值,如同SHA256算法輸出一樣(不過值是32字節)。許多應用都在其前面加上前綴以方便識別。例如, 這家網站[Proof of Existence](http://proofofexistence.com/) 的數字公證服務使用8字節前綴DOCTIOND,16進制ASCII編碼為44×4F 43 50 50 4F 4F 46。 請記住,并不存在對應于RETURN 的解鎖腳本,也就不能花費RETURN的輸出。RETURN的關鍵點就是鎖定的輸出不能花費,因此它不需要被保存在UTXO集中供未來消費,RETURN是*可驗證但是不可花費的*。 RETURN 常為一個金額為0比特幣的輸出, 因為分配到該輸出的比特幣都會永久消失。假如一筆 RETURN 被作為一筆交易的輸入,腳本驗證引擎將會阻止驗證腳本的執行,將標記交易為無效。執行RETURN本質上導致腳本“返回”FALSE并停止執行。如果你不小心將 RETURN 的輸出作為另一筆交易的輸入,則該交易是無效的。 一筆標準交易(通過了 isStandard() 函數檢驗的)只能有一個 RETURN 輸出。但是單個RETURN 輸出能與任意類型的輸出交易進行組合。 Bitcoin Core 0.10版本添加了兩個新的命令行選項。 選項datacarrier控制RETURN交易的傳播和挖礦,默認設置為“1”以允許它們。 選項datacarriersize采用一個數字參數,指定RETURN腳本的最大大小(以字節為單位),默認為83字節,允許最多80個字節的RETURN數據加上一個字節的RETURN操作碼和兩個字節的PUSHDATA操作碼。 >**注釋** RETURN最初提出的時候,限制為80字節,但發布時,限制被減少到40字節。 2015年2月,在Bitcoin Core的0.10版本中,限制提高到80字節。 節點可以選擇不傳播或不挖礦RETURN,或者只傳播和挖礦包含少于80字節數據的RETURN。 ## 7.5 時間鎖(Timelocks) 時間鎖是對交易或輸出的限制,只允許在一個時間點之后才能消費。比特幣從一開始就有一個交易級時間鎖定功能,它由交易中的nLocktime字段實現。在2015年底和2016年中期推出了兩個新的時間鎖功能,提供UTXO級別的時間鎖功能,這就是CHECKLOCKTIMEVERIFY和CHECKSEQUENCEVERIFY。 時間鎖對于延期交易和將資金鎖定到將來某個日期很有用。更重要的是,時間鎖將比特幣腳本擴展到時間的維度,為復雜的多步驟智能合約打開了大門。 ### 7.5.1 交易鎖定時間(nLocktime) 比特幣從一開始就有一個交易級的時間鎖功能。交易鎖定時間是交易級設置(交易數據結構中的一個字段),它定義了交易有效,可以在網絡上傳播或添加到區塊鏈的最早時間。鎖定時間也稱為nLocktime,是來自于Bitcoin Core代碼庫中使用的變量名稱。在大多數交易中將其設置為零,表示立即傳播和執行。如果nLocktime不為零,低于5億,則將其解釋為區塊高度,這意味著交易在指定的區塊高度之前無效,并且不被傳播,也不被包含在區塊鏈中。如果大于或等于5億,它被解釋為Unix紀元時間戳(自1-1-1970之后的秒數),并且交易在指定時間之前無效。指定未來區塊或時間的nLocktime交易必須由發起系統持有,并且只有在有效后才被發送到比特幣網絡。如果交易在指定的nLocktime之前傳輸到網絡,那么第一個節點就會拒絕該交易,并且不會傳播到其他節點。使用nLocktime等同于一張延期支票。 #### 7.5.1.1 交易時間鎖限制 nLocktime就是一個限制,雖然將來有可能花費這些輸出,但是到指定時間為止,還不能說不能花費它們。我們用下面的例子解釋一下。 Alice簽署了一筆交易,支付給Bob的地址,并將交易nLocktime設定為未來的3個月。Alice把這筆交易發送給Bob擱置起來。有了這個交易,Alice和Bob知道: - 在3個月過去之前,Bob不能完成交易進行兌換。 - Bob可以在3個月后接受交易。 然而: - Alice可以創建另一筆交易,不設置時間鎖,花費上面兩倍的輸入。 這樣,Alice就可以在3個月過去之前花費相同的UTXO。 - Bob不能保證Alice不會這樣做。 了解交易nLocktime的限制很重要。 唯一的保證是Bob在3個月過去之前無法兌換它,卻無法保證Bob最終是否可以得到資金。 為了實現這樣的保證,時間鎖限制必須放在UTXO上,成為鎖定腳本的一部分,而不是交易的一部分。 這是通過另一種形式的時間鎖來實現的,稱為檢查鎖定時間驗證(CLTV)。 ### 7.5.2 檢查鎖定時間驗證Check Lock Time Verify (CLTV) 2015年12月,通過比特幣軟分叉升級引入了一種新形式的時間鎖。根據BIP-65中的規范,腳本語言中添加了一個名為*CHECKLOCKTIMEVERIFY CLTV*的新腳本操作符。 CLTV是每個輸出的時間鎖,而不是像nLocktime一樣是每個交易的時間鎖。這使得在應用時間鎖的方式上具有更大的靈活性。 簡單地說,通過在輸出的兌換腳本中添加CLTV操作碼,限制了輸出,因此只能在指定的時間過后花費。 >**提示** 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個月以后,交易將是一個包含兌換腳本的P2SH交易: ``` <now + 3 months> CHECKLOCKTIMEVERIFY DROP DUP HASH160 <Bob's Public Key Hash> EQUALVERIFY CHECKSIG ``` 其中<now + months>是從交易開始被挖礦時間起估計3個月的區塊高度或時間值:當前塊高度+12,960(塊)或當前Unix紀元時間+7,760,000(秒)。現在,不要擔心CHECKLOCKTIMEVERIFY之后的DROP操作碼,下面很快就會解釋。 當Bob嘗試花費這個UTXO時,他構建了一個引用UTXO作為輸入的交易。他在該輸入的解鎖腳本中使用了他的簽名和公鑰,并將交易nLocktime設置為等于或大于Alice設置的CHECKLOCKTIMEVERIFY 時間鎖。然后,Bob把這筆交易廣播到比特幣網絡上。 Bob的交易評估如下。如果Alice設置的CHECKLOCKTIMEVERIFY參數小于或等于支出交易的nLocktime,腳本執行將繼續(就好像執行“無操作”或NOP操作碼一樣)。否則,腳本執行停止,并且該交易被視為無效。 更確切地說,CHECKLOCKTIMEVERIFY失敗并停止執行,標記交易無效(來自:BIP-65): 1. 堆棧是空的要么 2. 堆棧中的頂部項小于0;要么 3. 頂層堆棧項和nLocktime字段的鎖定時間類型(高度或者時間戳)不相同;要么 4. 頂層堆棧項大于交易的nLocktime字段;要么 5. 輸入的nSequence字段為0xffffffff。 >**注釋** CLTV和nLocktime描述時間鎖必須使用相同的格式,無論是區塊高度還是自Unix紀元以來經過的秒數。 最重要的是,在一起使用時,nLocktime的格式必須與輸出中的CLTV格式相匹配,它們必須是區塊高度或秒數時間。 執行后,如果滿足CLTV的要求,則它前面的時間參數將作為堆棧上的頂部項保留,并且可能需要隨DROP一起刪除,以便正確執行后續腳本操作碼。 經常在腳本中看到CHECKLOCKTIMEVERIFY后面跟著DROP就是這個原因。 通過將nLocktime與CLTV結合使用,[【7.5.1.1交易時間鎖限制】](https://github.com/tianmingyun/MasterBitcoin2CN/blob/master/ch07.md#7511交易時間鎖限制)中描述的情況就發生了變化。 Alice就不能再花這筆錢了(因為它被Bob的密鑰鎖定了),Bob也不能在3個月的鎖定期終止前花掉它。 通過將時間鎖功能直接引入到腳本語言中,CLTV允許我們開發一些非常有趣的復雜腳本。 該標準在BIP-65(CHECKLOCKTIMEVERIFY)中定義([附錄BIP-65](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki))。 ### 7.5.3 相對時間鎖 nLocktime和CLTV都是*絕對時間鎖absolute timelocks*,它們指定絕對時間點。接下來我們研究的兩個時間鎖功能,是*相對時間鎖relative timelocks*,它們設置的花費輸出的條件為從區塊鏈中的輸出被確認開始所經過的時間。 相對時間鎖是有用的,因為它們允許兩個或多個相互依賴的交易鏈被下鏈處理,同時對一個交易施加時間限制,該時間限制依賴于其前一個交易從被確認開始經過的時間。換句話說,只有這個UTXO被記錄在區塊鏈,時鐘才開始計數。這個功能在雙向狀態通道和閃電網絡中特別有用,我們將在后面章節[【12.6支付通道和狀態通道】](https://github.com/tianmingyun/MasterBitcoin2CN/blob/master/ch12.md#126支付通道和狀態通道)中看到。 相對時間鎖和絕對時間鎖一樣,都是通過交易級特性和腳本級操作碼實現的。交易級相對時間鎖是通過設置作為共識規則的nSequence的值實現的,它是每個交易輸入中都有設置的交易字段。腳本級相對時間鎖使用CHECKSEQUENCEVERIFY(CSV)操作碼實現。 相對時間鎖是根據[【BIP-68】](https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki)與[【BIP-112】](https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki)的規范共同實現的,其中BIP-68通過與相對時間鎖運用一致性增強的數字序列實現,BIP-112中是運用到了CHECKSEQUENCEVERIFY這個操作碼實現。 BIP-68和BIP-112是在2016年5月作為軟分叉升級時被激活的一個共識規則。 ### 7.5.4 nSequence相對時間鎖 相對時間鎖可以設置在每個交易輸入中,方法是設置每個輸入中的nSequence字段。 #### 7.5.4.1 nSequence的本義 nSequence字段的最初設計是想在內存中修改交易(但是從未運用過)。這種情況下,一筆交易的輸入包含的nSequence值低于2<sup>32</sup>-1(0xffffffff),就表示該交易尚未“完全完成”。這樣的交易將一直保留在內存池中,直到被花費相同輸入,具有更高nSequence值的的另一筆交易代替。一旦收到一筆交易,其nSequence值為0xFFFFFFFF,那么它就被視為“完成”并交給礦工挖礦。 nSequence的最初設計從未被正確實現,在不使用時間鎖的交易中,nSequence的值通常設置為0xFFFFFFFF。對于具有nLocktime或CHECKLOCKTIMEVERIFY的交易,nSequence值必須設置為小于2<sup>31</sup>,以使時間鎖保護有效,如下面所述。 #### 7.5.4.2 nSequence作為共識執行的相對時間鎖 隨著BIP-68的激活,新的共識規則適用于輸入中的nSequence值小于2<sup>31</sup>的任何交易( 1<<31位未設置為1)。從編程角度,如果沒有設置最高位(1<<31位 )為1,意味著它是一個表示“相對鎖定時間”的標志。否則( 設置了1<<31位為1),nSequence值就被保留用于其他用途,例如啟用CHECKLOCKTIMEVERIFY,nLocktime,Opt-In-Replace-By-Fee以及其他未來的新開發功能。 交易的輸入中的nSequence值小于2<sup>31</sup>,就表示具有相對時間鎖。這種交易中的輸入只有相對鎖定時間到期后才能有效。例如,一筆交易的輸入的nSequence相對時間鎖是30個區塊,那么只有當輸入引用的UTXO被挖出后再經過30個區塊之后,該交易才有效。由于nSequence是每個輸入中的字段,因此交易可能包含任何數量的時間鎖定輸入,這其中的每個輸入都必須滿足時間限制交易才能有效。交易中的輸入可以是時間鎖定輸入(nSequence <2<sup>31</sup>),也可以是沒有相對時間鎖定(nSequence> = 2<sup>31</sup>)的輸入。 nSequence值以塊或秒為單位,但與nLocktime中使用的格式略有不同。類型(type)標志用于區分計數塊和計數時間(以秒為單位)。類型標志設置在第23個最低有效位(即值1 << 22)。如果設置了類型標志,則nSequence值將被解釋為512秒的倍數。如果未設置類型標志,則nSequence值被解釋為區塊數。 當將nSequence解釋為相對時間鎖時,只考慮16個最低有效位。一旦對標志(位32和23)求值,nSequence值通常用16位掩碼(例如nSequence或者0x0000FFFF)進行“屏蔽”。 下圖顯示由BIP-68定義的nSequence值的二進制結構。 ![BIP-68 definition of nSequence encoding](https://github.com/bitcoinbook/bitcoinbook/raw/develop/images/mbc2_0701.png) 圖7-1 BIP-68 中nSequence編碼的定義(出處: BIP-68) BIP-68規定了基于nSequence值的共識執行的相對時間鎖。該標準參見[BIP-68, Relative lock-time using consensus-enforced sequence numbers。](https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki). ### 7.5.5 帶CSV的相對時間鎖 就像CLTV和nLocktime一樣,在腳本中有一個腳本操作碼使用nSequence值作為相對時間鎖。該操作碼是CHECKSEQUENCEVERIFY,通常簡稱為CSV。 在UTXO的兌換腳本中執行時,CSV操作碼僅允許花費交易中nSequence值大于或等于CSV參數的輸入。實質上,這限制了UTXO的消耗,直到從該UTXO被挖出之后過了一定數量的區塊或秒數。 與CLTV一樣,CSV中的值必須與相應nSequence值中的格式相匹配。如果CSV指定的是區塊數,那么nSequence也是區塊數。如果CSV以秒為單位,那么nSequence的參數也是秒數。 當創建和簽署多個(鏈接的)交易,但不想對外廣播,想讓它們保持“鏈下”時,使用CSV的相對時間戳特別有用。在父交易按相對時間鎖中指定的時間進行傳播、被挖礦和老化之前,子交易是不能使用的。有一個應用案例可以在[【12.6支付通道和狀態通道】](https://github.com/tianmingyun/MasterBitcoin2CN/blob/master/ch12.md#126支付通道和狀態通道)和[【12.7可路由的支付通道(閃電網絡)】](https://github.com/tianmingyun/MasterBitcoin2CN/blob/master/ch12.md#127可路由的支付通道閃電網絡)章節中看到。 CSV 細節參見 [BIP-112, CHECKSEQUENCEVERIFY](https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki). ### 7.5.6 中位時間過去 作為激活相對時間鎖的一部分,時間鎖(絕對和相對)的“時間”計算方式也發生了變化。在比特幣中,現實時間(wall time)和共識時間之間存在微妙但非常顯著的差異。比特幣是一個去中心化網絡,這意味著每個參與者都有自己的時間視角。網絡上的事件并非在任何地方都是瞬間發生的。網絡延遲必須是每個節點必須考慮的重要因素。最終,所有內容都被同步,以創建一個共同的分類帳。比特幣每10分鐘會對于*過去*存在的分類賬狀態達成一個新的共識。 區塊頭中的時間戳由礦工設定。共識規則允許一定的余地來解決去中心化節點之間時鐘精度的問題。然而,這給礦商創造了一個不好的激勵,他們在一個區塊內謊報時間,通過納入尚未到期的時間鎖交易來賺取額外費用。有關詳細信息,請參閱以下部分。 為了杜絕礦工說謊,加強時間鎖的安全性,與相對時間鎖同時激活的還有一個BIP。這就是BIP-113,它定義了一個稱為*中位時間過去Median-Time-Past*的新的共識測量機制。 中位時間過去是通過讀取最后11個塊的時間戳并求出中位值來計算的。這個中位時間值就變成了共識時間,并被用于所有的時間鎖計算。通過結合11個區塊,沒有一個礦工會為了獲得尚未到期的時間戳的交易中的交易費,去影響時間戳。 中位時間過去改變了nLocktime,CLTV,nSequence和CSV的時間計算的實現。由中位時間過去計算的共識時間總是比現實時間晚大約一個小時。如果要創建時間鎖交易,評估nLocktime,nSequence,CLTV和CSV中編碼所需時間值時,應該考慮在這個因素。 中位時間過去參見[【BIP-113】。](https://github.com/bitcoin/bips/blob/master/bip-0113.mediawiki) ### 7.5.7 針對費用狙擊(Fee Sniping)的時間鎖 費用狙擊是一種理論攻擊情形,礦工試圖從將來的區塊挑選手續費較高的交易重寫過去的塊,實現“狙擊”更高費用的交易,以最大限度地提高盈利能力。 例如,假設存在的最高塊是塊#100,000。有些礦工現在不是試圖對#100,001區塊進行挖礦來延長區塊鏈,而是試圖重新挖礦#100,000區塊。這些礦工選擇在自己的候選塊#100,000中包括任何有效的交易(還未挖出)。他們不必使用相同的交易來重挖區塊。事實上,他們會更傾向于選擇在其中添加最有利可圖(每kB最高交易費)的交易。他們會納入“舊”塊#100,000中的任何交易,還有他們當前內存池的任何交易。當他們重新創建塊#100,000時,本質上就把“現在”的交易拉出來重寫到“過去”中。 今天,這種攻擊還不是非常有利可圖,因為挖礦獎勵遠遠高于每個區塊中的總費用。但在未來的某個時候,交易費將是獎勵的大部分(甚至是獎勵的整體),那時候這種情況就會很難避免了。 為了防止“費用狙擊”,Bitcoin Core創建交易時,默認情況下,使用nLocktime將它們限制為“下一個區塊”。在剛才的場景中,Bitcoin Core 會將其創建的任何交易的nLocktime設置為100001。在正常情況下,這個nLocktime不起作用-無論如何,這些交易只能包含在塊100001中,就是下一個塊。 但是在區塊鏈分叉攻擊時,由于所有這些交易都將被時間鎖阻止在#100,001,所以礦工們無法從內存池中提取高收費交易,因為所有這些交易都將被時間鎖定到區塊100001中。他們只能在當時有效的交易中重新挖區塊#100,000,實質上不會獲得新的費用。 為了實現這一點,Bitcoin Core將所有新交易的nLocktime設置為<當前區塊號+ 1>,并將所有輸入上的nSequence設置為0xFFFFFFFE以啟用nLocktime。 ## 7.6 流程控制腳本(條件語句 ) 比特幣腳本的一個更強大的功能是流程控制,也稱為條件語句。您可能熟悉各種編程語言中的類似IF…THEN…ELSE的流程控制。比特幣條件語句看起來有點不同,但本質上是相同的構造。 基本上,比特幣條件操作碼允許我們構造一個具有兩種解鎖方式的贖回腳本,具體取決于對邏輯條件求值的真/假結果。例如,如果x為真,則兌換腳本為A,否則ELSE兌換腳本為B。 此外,比特幣條件表達式可以無限期地“嵌套”,這意味著一個條件語句可以包含另外一個條件語句,其中又會包含別的條件語句等等 。比特幣腳本流程控制可用于構造非常復雜的腳本,可以有數百甚至數千個可能的執行路徑。嵌套沒有限制,但共識規則對腳本的最大字節數有限制。 比特幣使用IF,ELSE,ENDIF和NOTIF操作碼實現流程控制。此外,條件表達式可以包含布爾運算符,如BOOLAND,BOOLOR和NOT。 乍看之下,您可能會發現比特幣的流程控制腳本令人困惑。那是因為比特幣腳本是一種堆棧語言。正如當1+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之前,看起來像是“逆向”的,如下所示: Bitcoin腳本流程控制 ``` condition IF code to run when condition is true ELSE code to run when condition is false ENDIF code to run in either case ``` 閱讀Bitcoin腳本時,請記住,條件語句在IF操作碼的*前面*。 ### 7.6.1 VERIFY操作碼條件語句 比特幣腳本中的另一種條件形式是操作碼以VERIFY結尾。 VERIFY后綴表示如果評估的條件不為TRUE,腳本的執行將立即終止,并且該交易被視為無效。 與提供可選執行路徑的IF子句不同,VERIFY后綴充當*保護子句*,只有在滿足前提條件時才會繼續執行。 例如,以下腳本需要Bob的簽名和產生特定哈希的原像(密鑰)。 這兩個條件必須都滿足才能解鎖: 有EQUALVERIFY保護子句的兌換腳本。 ``` HASH160 <expected hash> EQUALVERIFY <Bob's Pubkey> CHECKSIG ``` 為了兌換成功,Bob必須構建一個解鎖腳本,提供有效的原像和簽名: 滿足上述兌換腳本的解鎖腳本 ``` <Bob's Sig> <hash pre-image> ``` 沒有原像,Bob無法繼續執行到檢查其簽名的腳本部分。 該腳本可以用IF編寫: 具有IF保護語句的兌換腳本 ``` HASH160 <expected hash> EQUAL IF <Bob's Pubkey> CHECKSIG ENDIF ``` Bob的解鎖腳本是一樣的: 滿足上述兌換腳本的解鎖腳本以 ``` <Bob's Sig> <hash pre-image> ``` 使用IF的腳本與使用VERIFY后綴的操作碼作用相同;,它們都可以作為保護語句。 但是,VERIFY的構造更有效率,使用兩個較少的操作碼。 那么,我們什么時候使用VERIFY,什么時候使用IF? 如果我們想要做的是附加一個前提條件(保護語句),那么VERIFY后綴更好。 然而,如果有不止一個執行路徑(流程控制),那么IF ... ELSE流程控制語句更合適。 >**提示** 諸如EQUAL之類的操作碼會將結果(TRUE / FALSE)推送到堆棧上,留下它用于后續操作碼的執行。 相比之下,操作碼EQUALVERIFY后綴不會在堆棧上留下任何東西。 以VERIFY結尾的操作碼都不會將結果留在堆棧上。 ### 7.6.2 在腳本中使用流程控制 比特幣腳本中流程控制的常見的用途是構建一個提供多個執行路徑的兌換腳本,每個執行路徑都是兌換UTXO的不同方式。 我們來看一個簡單的例子,兩個簽名人,Alice和Bob,兩人中任何一個都可以兌換。 使用多重簽名,表示為1/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。 花費時,構建一個解鎖腳本,通過在每個流程控制點的堆棧上放置相應的TRUE和FALSE值來指引執行路徑。 ## 7.7 復雜的腳本示例 在本節中,我們將本章中的許多概念合并成一個例子。 我們的例子使用了迪拜一家公司所有者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/3多重簽名。該執行路徑由第3行和第9行組成。第3行將多重簽名的法定人數設置為2(2/3)。 通過在解鎖腳本的末尾設置TRUE TRUE來選擇該腳本: 第一個執行路徑的解鎖腳本(2/3 多簽) ``` 0 <Mohammed's Sig> <Zaira's Sig> TRUE TRUE ``` >**提示** 此解鎖腳本開頭的0是因為CHECKMULTISIG中的一個錯誤,會從堆棧中多彈出一個額外的值。 CHECKMULTISIG會忽略這個額外的值,但它必須存在,否則腳本執行將失敗。 推送0(通常)是解決bug的方法,如[【7.2 多重簽名CHECKMULTISIG執行中的bug】](https://github.com/tianmingyun/MasterBitcoin2CN/blob/master/ch07.md#72-多重簽名)所述。 第二個執行路徑只能在UTXO創建30天后才能使用。 此時,它需要Abdul(律師)和三個合伙人之一(1/3)的簽名。 這是通過第7行實現的,該行將多簽的法定人數設置為1。要選擇此執行路徑,解鎖腳本將以FALSE TRUE結束: 第二個執行路徑的解鎖腳本(律師 + 1/3) ``` 0 <Saeed's Sig> <Abdul's Sig> FALSE TRUE ``` >**提示** 為什么先FALSE后TRUE? 反了嗎?是這兩個值被推到堆棧的順序,先推FALSE,后推 TRUE。 因此,第一個IF操作碼首先彈出的是TRUE。 最后,第三個執行路徑允許律師單獨花費資金,但只能在90天之后。 要選擇此執行路徑,解鎖腳本必須以FALSE結束: 第三個執行路徑的解鎖腳本(僅適用于律師) ``` <Abdul's Sig> FALSE ``` 在紙上運行腳本來查看它在堆棧上的行為。 閱讀這個例子還需要考慮幾件事情。 看看你能不能找到答案? - 為什么律師不能通過在解鎖腳本上選擇FALSE,隨時兌換第三條執行路徑? - 在UTXO挖出后,5天、35天和105天分別可以使用多少條執行路徑? - 如果律師失去密鑰,資金是否丟失? 如果91天過去了,你的答案是否會改變? - 合伙人如何每隔29天或89天“重置”時鐘,以防止律師獲取資金? - 為什么這個腳本中的一些CHECKSIG操作碼有VERIFY后綴,而其他的沒有? ## 7.8 隔離見證 隔離見證(segwit)是對比特幣共識規則和網絡協議的升級,是作為BIP-9軟分叉被提議并實施的,并于2017年8月1日在比特幣的主網上激活。 在密碼學中,術語“見證”用于描述解決密碼難題的方案。在比特幣術語中,見證滿足放置在未花費交易輸出(UTXO)上的加密條件。 在比特幣環境中,數字簽名是見證的*一種類型*,但更廣義來說,見證是指能夠滿足對UTXO施加的條件并解鎖該UTXO以供消費的任何解決方案。術語“見證”是“解鎖腳本”或“scriptSig”的更一般的術語。 在引入隔離見證之前,交易中的每一個輸入后面緊跟著的就是解鎖它的見證數據。見證數據作為每個輸入的一部分嵌入到交易中。術語*隔離見證*(簡稱*segwit*)只是指把特定輸出的簽名或解鎖腳本隔離開。“單獨的scriptSig”或“單獨的簽名”就是它最簡單的形式。 因此,隔離見證是對比特幣的一種架構更改,旨在將見證數據從交易的scriptSig(解鎖腳本)字段移動到伴隨交易的獨立的*見證*數據結構中。客戶端要求的交易數據可以包括見證數據,也可以不包括。 在本節中,我們將討論隔離見證的一些好處,描述用于部署和實現此體系結構更改的機制,并演示在交易和地址中如何使用隔離見證。 隔離見證由以下BIP定義: [**BIP-141**](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki) 隔離見證的主要定義。 [**BIP-143**](https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki) 0版本見證程序的交易簽名驗證 [**BIP-144**](https://github.com/bitcoin/bips/blob/master/bip-0144.mediawiki) 對等服務-新的網絡消息和序列化格式 [**BIP-145**](https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki) 隔離見證的getblocktemplate更新(用于挖礦) [**BIP-173**](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki) 原生v0-16見證輸出的Base32地址格式 ### 7.8.1 為什么要用隔離見證 隔離見證是一種架構性更改,對比特幣的可擴展性、安全性、經濟激勵和性能有多方面影響: ***交易延展性*** 將見證數據從交易中移出,用作標識符的交易哈希就不再包括見證數據。由于見證數據是交易中唯一可由第三方修改的部分,請參閱[【7.8.5.5 交易標識符】](https://github.com/tianmingyun/MasterBitcoin2CN/blob/master/ch07.md#7855-交易標識符),刪除它也就消除了交易延展性攻擊的機會。有了隔離見證,交易哈希就不能被除了交易的創建者之外的任何人更改,這大大促進了許多依賴于高級比特幣交易構造的其他協議的實現,如支付通道、鏈式交易和閃電網絡。 ***腳本版本控制*** 隨著隔離見證腳本的引入,每個鎖定腳本前面都有一個*腳本版本號*,類似于交易和區塊的版本號。添加腳本版本號允許以后向兼容的方式(例如使用軟分叉升級)升級腳本語言,引入新的腳本操作對象、語法或語義。以無中斷方式升級腳本語言的能力將大大加快比特幣的創新速度。 ***網絡和存儲擴展*** 見證數據通常對交易的總大小有很大貢獻。更復雜的腳本,如用于多簽或支付通道的腳本體積會非常大。在某些情況下,這些腳本會占到交易數據的大多數(超過75%)。通過將見證數據從交易中移出,隔離見證提高了比特幣的可擴展性。節點可以在驗證簽名后刪減見證數據,或者在進行簡化的支付驗證時完全忽略見證數據。見證數據不再需要發送到所有節點,也不需要被所有節點存儲在磁盤上。 ***簽名驗證優化*** 隔離見證升級簽名函數(CHECKSIG,CHECKONSIGG等),以減少算法的計算復雜度。在引入隔離見證之前,用于生成簽名的算法需要大量與交易大小成比例的哈希操作。相對于簽名操作的數量數據哈希計算復雜度增加到O(n<sup>2</sup>),給驗證簽名的所有節點帶來了巨大的計算負擔。有了隔離見證,算法的復雜度降低到O(n)。 ***離線簽名改進*** 隔離見證簽名包含由簽名的哈希中的每個輸入引用的值(金額)。以前,離線簽名設備(如硬件錢包)必須在簽名交易之前驗證每個輸入的金額。這通常是通過流式傳輸大量先前作為輸入引用的交易的數據來實現的。由于金額現在是已簽名的提交哈希的一部分,因此離線設備不需要以前的交易。如果金額不匹配(由被入侵的在線系統篡改),簽名將是無效的。 ### 7.8.1 隔離見證如何工作 乍一看,隔離見證似乎只是對交易構造方式的改變,屬于交易級特性,但事實并非如此。其實,隔離見證是對單獨的UTXO花費方式的更改,因此可以說是每個輸出層面的特性。 交易可以花費使用了隔離見證的輸出或傳統(內嵌見證)輸出,或兩者都有。因此,將交易稱為“隔離見證交易”沒有多大意義。相反,應該將特定的交易輸出稱為“隔離見證輸出”。 當交易使用UTXO時,必須提供一個見證。在傳統的UTXO中,鎖定腳本要求見證數據*內嵌*在花費UTXO的交易的輸入部分。但是,隔離見證UTXO指定了一個鎖定腳本,該腳本可以用輸入之外的見證數據(隔離)滿足。 ### 7.8.2 軟分叉(后向兼容) 隔離見證是對輸出和交易架構方式的重大變革。通常情況下,這種變革需要改變共識規則,要同時更換每個比特幣節點和錢包,也就是硬分叉。但是,隔離見證引入的時候采用的是一個更少破壞性的變革,是后向兼容的,稱為軟分叉。這種類型的升級允許未升級的軟件忽略更改并繼續運行,而不會造成任何中斷。 隔離見證輸出被構造成未升級的舊系統仍然可以驗證它們。對于舊的錢包或節點,隔離見證輸出看起來和其他*任何可以花費的輸出*沒什么區別。這樣的輸出可以使用空簽名花費,因此交易內部沒有簽名(它是隔離的)這一事實不會使交易無效。但是,新的錢包和挖礦節點會看到隔離見證輸出,并希望在交易的見證數據中找到有效的見證。 ### 7.8.3 隔離見證輸出和交易示例 接下來看一些示例交易,看看它們在隔離見證下會發生什么變化。我們首先研究如何使用隔離見證程序改造Pay-to-Public-Key-Hash(P2PKH)支付。然后,再來研究隔離見證同樣如何作用于Pay-to-Script-Hash(P2SH)腳本。最后,研究如何將前面兩個隔離見證程序嵌入到P2SH腳本中。 #### 7.8.3.1 Pay-to-Witness-Public-Key-Hash (P2WPKH) 在[【2.1.2 買一杯咖啡】](https://github.com/tianmingyun/MasterBitcoin2CN/blob/master/ch02.md#212買一杯咖啡)中,Alice創建一筆交易,付給Bob一杯咖啡的費用。該筆交易構建了一個價值0.015BTC的 P2PKH 輸出(Bob可用來花費),該輸出腳本看起來像這樣: P2PKH 輸出腳本示例: ``` DUP HASH160 ab68025513c3dbd2f7b92a94e0581f5d50f654e7 EQUALVERIFY CHECKSIG ``` 如果通過隔離見證,Alice將會創建一個支付給見證公鑰哈希Pay-to-Witness-Public-Key-Hash(P2WPKH)腳本,看起來是這樣的: P2WPKH 輸出腳本示例: ``` 0 ab68025513c3dbd2f7b92a94e0581f5d50f654e7 ``` 正如你所見,隔離見證輸出的鎖定腳本比傳統輸出簡單得多。它包含兩個值,會被推送到腳本計算堆棧中。對于一個傳統(非隔離見證)比特幣客戶端來說,這兩個推送值看起來像是一個任何人都能花費的輸出,而不需要簽名(或者更確切的說,能被空的簽名使用)。而對一個更新的、隔離見證客戶端來說,第一個數字(0)被解釋為一個版本號(見證版本),第二部分(20字節)相當于一個鎖定腳本,被稱為*見證程序 witness program*。這20字節的見證程序就像是 P2PKH 腳本中的公鑰哈希值一樣。 現在,再看看Bob用來去花費這個輸出對應的交易。對于原始腳本(非隔離見證),Bob的交易必須在交易輸入中包含簽名: 以下被解碼的交易,顯示了使用簽名花費的 P2PKH 輸出: ``` [...] “Vin” : [ "txid": "0627052b6f28912f2703066a912ea577f2ce4da4caa5a5fbd8a57286c345c2f2", "vout": 0, "scriptSig": “<Bob’s scriptSig>”, ] [...] ``` 但是,要花費隔離見證輸出,這個交易輸入中沒有簽名。相反,Bob的交易只有空的 scriptSig 和交易本身之外的隔離見證: 以下被解碼的交易,顯示了使用隔離見證數據花費的 P2WPKH 輸出: ``` [...] “Vin” : [ "txid": "0627052b6f28912f2703066a912ea577f2ce4da4caa5a5fbd8a57286c345c2f2","vout": 0, "scriptSig": “”, ] [...] “witness”: “<Bob’s witness data>” [...] ``` #### 7.8.3.2 錢包的P2WPKH 構造 尤其值得注意的是,P2WPKH 應該只能由收款人(接收方)創建,而不是由發送者從已知的公鑰、P2PKH 腳本或地址進行轉換。發送方無從知道接收者的錢包是否具有能力構建隔離見證交易,并消費 P2WPKH 輸出。 另外,P2WPKH 輸出必須從*壓縮公鑰*的哈希值中創建。未壓縮公鑰在隔離見證中是非標準的,可能會被將來的軟分叉明確禁用。如果在 P2WPKH 中使用的哈希值來自未壓縮公鑰,那么它可能是不可消費的,資金就可能丟失。P2WPKH 輸出應該由收款人的錢包,通過私鑰派生的壓縮公鑰來創建。 >**警告** P2WPKH 應該由收款人(接收者)通過將壓縮公鑰轉換成P2WPKH哈希值進行創建。絕對不要將P2PKH腳本、比特幣地址或未壓縮公鑰轉換成P2WPKH見證腳本。 #### 7.8.3.3 Pay-to-Witness-Script-Hash (P2WSH) 第二種類型的驗證程序對應“支付給腳本哈希”( Pay-to-Script-Hash , P2SH)腳本。我們在[【7.3 P2SH(Pay-to-Script-Hash)】](https://github.com/tianmingyun/MasterBitcoin2CN/blob/master/ch07.md#73-p2shpay-to-script-hash)中見過這類腳本。例子中,Mohammed的公司使用P2SH 來表示一個多重簽名腳本。對Mohammed’s的公司的支付被編碼成一個這樣的鎖定腳本: P2SH 輸出腳本示例: ``` HASH160 54c557e07dde5bb6cb791c7a540e0a4796f5e97e EQUAL ``` 這個P2SH腳本引用了一個兌換腳本的哈希值,該腳本定義了一個花費資金的“2/3”的多重簽名要求。為了花費該輸出,Mohammed的公司需提供兌現腳本(其哈希值與P2SH輸出中的腳本哈希值匹配)和滿足兌現腳本所必需的簽名,所有這些都在交易輸入中: 下面的解碼交易顯示要被花費的P2SH輸出: ``` [...] “Vin” : [ "txid": "abcdef12345...", "vout": 0, "scriptSig": “<SigA> <SigB> <2 PubA PubB PubC PubD PubE 5 CHECKMULTISIG>”, ] ``` 現在,讓我們看看整個示例如何升級成為隔離見證。如果Mohammed的客戶使用的是兼容隔離見證的錢包,他們就能創建一個P2WSH輸出進行付款,如下: P2WSH 輸出腳本示例: ``` 0 9592d601848d04b172905e0ddb0adde59f1590f1e553ffc81ddc4b0ed927dd73 ``` 再一次,就像P2WPKH的例子一樣,你可以看到,隔離見證等效腳本要簡單得多,省略了P2SH腳本中的各種腳本操作符。而且,隔離見證程序僅包含兩個推送到堆棧的值:一個見證版本(0)和一個32字節的兌換腳本的哈希值。 > **提示** P2SH使用結果為20字節的RIPEMD160(SHA256(script)) 哈希算法,P2WSH見證程序使用了結果為32字節的SHA256(腳本)哈希算法。在選擇哈希算法時,這一差異是有意為之,用于通過哈希值長度來區分兩種類型的見證程序(P2WPKH and P2WSH),并為P2WSH(128bit安全,P2SH是80bit安全)提供更強的安全性。 Mohammed的公司可以通過提供正確的兌換腳本和足夠的簽名滿足花費P2WSH輸出。兌換腳本和簽名都將作為見證數據的一部分被隔離在消費交易*之外*。在交易輸入內部,Mohammed的錢包會放一個空的scriptSig: 下面的解碼交易顯示了帶有隔離見證數據的P2WSH輸出: ``` [...] “Vin” : [ "txid": "abcdef12345...","vout": 0, "scriptSig": “”, ] [...] “witness”: “<SigA> <SigB> <2 PubA PubB PubC PubD PubE 5 CHECKMULTISIG>” [...] ``` #### 7.8.3.4 P2WPKH和P2WSH的區別 在前面的兩節中,我們展示了兩種類型的見證程序:支付給見證公鑰哈希 (P2WPKH)和 支付給見證腳本哈希(P2WSH) 。這兩種見證程序都有一個字節版本號和跟隨其后的更長的一個哈希值組成。它們看起來非常相似,但解釋卻完全不同:一個被解釋為一個公鑰哈希值,被簽名所滿足,另一個被解釋為腳本哈希值,被兌換腳本所滿足。他們之間的關鍵區別是哈希值的長度: - P2WPKH中的公鑰哈希值是20字節。 - P2WSH中的腳本哈希值是32字節。 正是這個區別使得錢包可以區分這兩種類型的見證程序。通過查看哈希值的長度,錢包可以確定是哪種類型的見證程序,P2WPKH 或者 P2WSH。 ### 7.8.4 隔離見證升級 正如我們前面看到的例子,隔離見證的升級需要經過兩步過程。首先,錢包必須創建特殊的隔離見證輸出。然后,這些輸出可以被知道如何構建隔離見證交易的錢包花費。在這些例子中,Alice的錢包是支持隔離見證的,能夠使用隔離見證腳本創建特殊輸出。Bob的錢包也是支持隔離見證的,能夠花費這些輸出。這個例子中隱含的是,實際上,Alice的錢包需要*知道*Bob使用的是支持隔離見證的錢包,也能花費這些輸出。否則,如果Bob的錢包沒有升級,Alice試圖對Bob創建隔離見證付款,那么Bob的錢包將無法檢測到這些付款。 >**提示** 對于P2WPKH和P2WSH付款類型,付款和收款錢包都需要升級才能使用隔離見證。此外,付款的錢包還需要知道收款的錢包已經兼容隔離見證。 隔離見證不會在整個網絡中同時實施。相反,隔離見證被實施為向后兼容的升級,這時*新老客戶端*都有。錢包開發人員會各自升級自己的錢包軟件,添加隔離見證功能。當付款錢包和收款錢包都支持隔離見證時,使用P2WPKH和P2WSH付款類型。傳統的P2PKH和P2SH將繼續為未升級的錢包工作。這留下了兩個重要的場景,下一節將討論這個情況: - 付款錢包不支持隔離見證,如何支付給能處理隔離見證的收款錢包。 - 付款錢包支持隔離識別,如何根據地址識別和區分收款錢包是否具有隔離見證功能。 #### 7.8.4.1 在P2SH中嵌入隔離見證 舉個例子,假設Alice的錢包沒有升級隔離見證,但Bob的錢包已經升級,可以處理隔離見證交易。Alice和Bob可以使用“舊”的非隔離見證交易。但是Bob可能想使用隔離見證,享受適用于見證數據的折扣,降低交易費用。 在這種情況下,Bob的錢包會構建一個包含隔離見證腳本的P2SH地址。Alice的錢包認為這是一個“正常的”P2SH地址,并可以在未識別隔離見證的情況下付款。然后,Bob的錢包可以通過隔離見證交易來花費這筆款項,這樣充分利用了隔離見證交易并降低交易費用。 兩種形式的見證腳本P2WPKH和P2WSH都可以嵌入到P2SH地址中。第一個是P2SH(P2WPKH),第二個是P2SH(P2WSH)。 #### 7.8.4.2 P2SH 中的 P2WPKH 我們將檢查的第一種見證腳本是P2SH(P2WPKH)。這是一個支付到見證公鑰哈希的見證程序,嵌入在P2SH腳本中,所以可以被不支持隔離見證的錢包使用。 Bob的錢包用Bob的公鑰構造了一個P2WPKH見證程序。然后這個見證程序被哈希,這個哈希值被編碼成P2SH腳本。P2SH腳本被轉換成以“3”開頭的比特幣地址,正如[【7.3 P2SH(Pay-to-Script-Hash)】](https://github.com/tianmingyun/MasterBitcoin2CN/blob/master/ch07.md#73-p2shpay-to-script-hash)部分看到的。 Bob的錢包開始使用的就是之前看到的P2WPKH 見證程序: Bob的P2WPKH見證程序: `0 ab68025513c3dbd2f7b92a94e0581f5d50f654e7` P2WPKH見證程序由見證版本和Bob的20字節公鑰哈希組成。 Bob的錢包然后哈希之前的見證程序,先用SHA256,然后用RIPEMD160,產生另一個20字節的哈希值。 我們使用bx命令重現: P2WPKH見證程序HASH160后的值 ``` echo \ '0 [ab68025513c3dbd2f7b92a94e0581f5d50f654e7]'\ | bx script-encode | bx sha256 | bx ripemd160 3e0547268b3b19288b3adef9719ec8659f4b2b0b ``` 接下來,兌換腳本哈希被轉換成比特幣地址。再看一下bx命令結果: P2SH地址 ``` echo \ '3e0547268b3b19288b3adef9719ec8659f4b2b0b' \ | bx address-encode -v 5 37Lx99uaGn5avKBxiW26HjedQE3LrDCZru ``` 現在,Bob可以把這個地址展示給顧客,方便他們付款。Alice的錢包可以支付給37Lx99uaGn5avKBxiW26HjedQE3LrDCZru,就像任何其他比特幣地址一樣。 為了給Bob付款,Alice的錢包會用P2SH腳本鎖定輸出: `HASH160 3e0547268b3b19288b3adef9719ec8659f4b2b0b EQUAL` 即使Alice的錢包不支持隔離見證,Bob也可以使用隔離見證交易花費它創建的付款。 #### 7.8.4.3 P2SH 中的 P2WSH 同樣,多重簽名腳本或其他復雜腳本的P2WSH見證程序也可以嵌入到P2SH腳本和地址中,使得任何錢包都可以實現兼容隔離見證的支付。 正如我們在[【7.8.4.3 Pay-to-Witness-Script-Hash (P2WSH)】](https://github.com/tianmingyun/MasterBitcoin2CN/blob/master/ch07.md#7843-pay-to-witness-script-hash-p2wsh)中看到的,Mohammed的公司正在使用隔離見證腳本付款到多重簽名腳本中。為了讓任何一個客戶都有可能向他的公司付款,不管他們的錢包是否已經升級支持隔離見證,Mohammed的錢包都可以將P2WSH見證程序嵌入到P2SH腳本中。 首先,Mohammed的錢包用SHA256(僅一次)對兌換腳本進行哈希運算。我們使用bx命令: Mohammed的錢包創建了P2WSH見證程序 ``` echo \ 2 \ [04C16B8698A9ABF84250A7C3EA7EEDEF9897D1C8C6ADF47F06CF73370D74DCCA01CDCA79DCC5C395D7EEC6984D83F1F50C900A24DD47F569FD4193AF5DE762C587] \ [04A2192968D8655D6A935BEAF2CA23E3FB87A3495E7AF308EDF08DAC3C1FCBFC2C75B4B0F4D0B1B70CD2423657738C0C2B1D5CE65C97D78D0E34224858008E8B49] \ [047E63248B75DB7379BE9CDA8CE5751D16485F431E46117B9D0C1837C9D5737812F393DA7D4420D7E1A9162F0279CFC10F1E8E8F3020DECDBC3C0DD389D9977965] \ [0421D65CBD7149B255382ED7F78E946580657EE6FDA162A187543A9D85BAAA93A4AB3A8F044DADA618D087227440645ABE8A35DA8C5B73997AD343BE5C2AFD94A5] \ [043752580AFA1ECED3C68D446BCAB69AC0BA7DF50D56231BE0AABF1FDEEC78A6A45E394BA29A1EDF518C022DD618DA774D207D137AAB59E0B000EB7ED238F4D800] \ 5 CHECKMULTISIG \ | bx script-encode | bx sha256 9592d601848d04b172905e0ddb0adde59f1590f1e553ffc81ddc4b0ed927dd73 ``` 接下來,哈希過的兌換腳本轉換為P2WSH見證程序: ``` 0 9592d601848d04b172905e0ddb0adde59f1590f1e553ffc81ddc4b0ed927dd73 ``` 然后,使用SHA256和RIPEMD160對見證程序本身進行哈希,生成一個新的20字節哈希,就像傳統P2SH中使用的那樣。還是使用bx命令: P2WSH見證程序的HASH160結果 ``` echo \ '0 [9592d601848d04b172905e0ddb0adde59f1590f1e553ffc81ddc4b0ed927dd73]'\ | bx script-encode | bx sha256 | bx ripemd160 86762607e8fe87c0c37740cddee880988b9455b2 ``` 接下來,錢包從這個哈希構造一個P2SH比特幣地址。同樣,我們使用bx命令計算: P2SH比特幣地址 ``` echo \ '86762607e8fe87c0c37740cddee880988b9455b2'\ | bx address-encode -v 5 3Dwz1MXhM6EfFoJChHCxh1jWHb8GQqRenG ``` 現在,Mohammed的客戶無需支持隔離見證就可以向這個地址付款。要向Mohammed發送付款,錢包將使用以下P2SH腳本鎖定輸出: 用于鎖定對Mohammed多簽的付款的P2SH腳本 ``` HASH160 86762607e8fe87c0c37740cddee880988b9455b2 EQUAL ``` 然后,Mohammed的公司就可以利用隔離見證的特點,包括更低的交易費用,構建隔離見證交易來消費這些款項。 #### 7.8.4.4 隔離見證地址 即使在隔離見證激活后,大多數錢包也需要一段時間才能升級。首先,正如我們在上一節中看到的,隔離見證將嵌入到P2SH中,以簡化支持與不支持隔離見證錢包之間的兼容性。 然而,一旦錢包廣泛支持隔離見證,直接使用隔離見證設計的原生地址格式,對見證腳本進行編碼就很有意義,而不再是將其嵌入P2SH。 原生隔離見證地址格式在BIP-173中定義: [【BIP-173】](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki) 原生v0-16見證輸出的Base32地址格式 BIP-173只編碼見證(P2WPKH和P2WSH)腳本。它與非隔離見證的 P2PKH或P2SH腳本不兼容。與“傳統”比特幣地址的Base58編碼相比,BIP-173是校驗和Base32編碼。BIP-173地址也稱為bech32地址,暗示使用了“BCH”錯誤檢測算法和32字符編碼集。 BIP-173地址使用32個小寫字母數字字符集,經過了精心選擇,減少因誤讀或敲錯產生的錯誤。選擇一個小寫字符集,bech32就更易于閱讀、提起,并且二維碼編碼效率提高了45%。 BCH錯誤檢測算法是對以前的校驗和算法(從Base58Check)的一個巨大改進,不僅允許檢測,而且允許糾正錯誤。在檢測錯誤時,地址輸入界面(如表單中的文本字段)可以檢測并突出顯示最可能敲錯的字符。 根據BIP-173規范,以下是bech32地址的一些示例: ***主網 P2WPKH:*** bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 ***測試網 P2WPKH:*** tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx ***主網 P2WSH:*** bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3 ***測試網 P2WSH:*** tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7 就像上面示例中所見,隔離見證bech32字符串最長為90個字符,由三部分組成: ***人類可讀部分:*** 前綴“bc”和“tb”表示是主網還是測試網 ***分隔符:*** 數字“1”,不是32字符編碼集的一部分,只是作為分隔符出現在這個位置 ***數據部分:*** 最少6個字母數字字符,經過校驗和編碼的見證腳本 目前,只有少數錢包接受或產生原生隔離見證bech32地址,但隨著隔離見證使用率的增加,這些將會越來越多。 #### 7.8.4.5 交易標識符 隔離見證的最大好處之一就是消除了第三方交易延展性。 在隔離見證之前,交易可以通過第三方巧妙地修改其簽名,在不改變任何基本屬性(輸入,輸出,金額)的情況下更改其交易ID(哈希)。這為拒絕服務DOS攻擊,以及對有缺陷的錢包軟件的攻擊創造了機會,這些軟件假定未經證實的交易哈希是不可變的。 通過引入隔離見證,交易有兩個標識符txid和wtxid。傳統的txid是序列化交易的雙SHA256哈希,沒有見證數據。交易的wtxid是具有見證數據的交易的新序列化格式的雙SHA256哈希。 傳統txid的計算方式與非隔離見證交易完全相同。但是,由于隔離見證交易在每個輸入中都有空的scriptSig,不存在可由第三方修改的交易部分。因此,在隔離交易中,即使交易未經確認,txid也是不能被第三方修改的。 wtxid就像一個“擴展的”ID,因為該哈希也包含了見證數據。如果交易在沒有見證數據情況下傳輸,則wtxid和txid是相同的。注意,由于wtxid包含見證數據(簽名),并且由于見證數據可能具有延展性,所以在交易確認之前,wtxid被認為是可延展的。只有隔離見證的txid可以被第三方認為是不可變的,并且只有當交易的所有輸入都是隔離見證輸入時才是不變的。 >**提示** 隔離見證交易有兩個ID:txid和wtxid。txid是沒有見證數據的交易的哈希,wtxid是包含見證數據的哈希。所有輸入都是隔離見證輸入的交易,不受第三方交易延展性影響。 ### 7.8.5 隔離見證新的簽名算法 隔離見證修改了四個簽名驗證函數(CHECKSIG,CHECKSIGVERIFY,CHECKMULTISIG和CHECKMULTISIGVERIFY)的語義,改變了交易承諾哈希的計算方式。 比特幣交易中的簽名應用于一個*承諾哈希 commitment hash*,該哈希值由交易數據計算得出,鎖定了數據中表示簽名者承諾這些金額的特定部分。例如,在簡單的SIGHASH_ALL類型簽名中,承諾哈希包括所有的輸入和輸出。 不幸的是,計算承諾哈希的方法導致了這樣一種可能,即驗證簽名的節點可能會被迫執行大量哈希計算。具體地說,相對于交易中的簽名操作的數量,其哈希操作數量增加了O(n<sup>2</sup>)復雜度。因此,攻擊者可以創建具有大量簽名操作的交易,從而導致整個比特幣網絡必須執行數百或數千個哈希操作來驗證該交易。 隔離見證帶來機會,通過改變承諾哈希計算方式來解決這個問題。對于隔離見證版本0見證程序,使用BIP-143中規定的改進的承諾哈希算法進行簽名驗證。 新算法實現了兩個重要目標。首先,哈希操作的數量與簽名操作的數量相比,平緩增長O(n)復雜度,從而減少了使用過于復雜的交易創建拒絕服務攻擊的機會。其次,承諾哈希現在還包括作為承諾的一部分的每個輸入的值(金額)。這意味著簽名者可以承諾特定的輸入值,而無需“獲取”和檢查輸入引用的前一個交易。在離線設備(如硬件錢包)的情況下,這極大地簡化了主機與硬件錢包之間的通信,消除了對以前的交易流進行驗證的需要。硬件錢包可以接受不可信主機“聲明的”的輸入值。由于簽名是無效的,如果輸入值不正確,硬件錢包在簽名輸入之前不需要驗證該值。 ### 7.8.6 隔離見證的經濟激勵 比特幣挖礦節點和全節點支持比特幣網絡和區塊鏈,會產生資源成本。隨著比特幣交易量的增加,資源成本(CPU,網絡帶寬,磁盤空間,內存)也在增加。礦工通過收取與每次交易的大小(字節)成比例的費獲得補償。不挖礦的全節點不能得到補償,因為需要運行權威的完全驗證全索引節點,他們也會有這些成本,他們會使用該節點來運營比特幣業務。 如果沒有交易費用,比特幣數據可能會大幅增加。費用旨在通過基于市場的價格發現機制,使比特幣用戶的需求與交易對網絡帶來的負擔保持一致。 基于交易占據空間大小的費用計算方式認為交易中的所有數據其成本是相同的。但是從全節點和礦工的角度來看,交易的某些部分的成本要高得多。加入比特幣網絡的每筆交易都會影響節點上四種資源的消耗: - 磁盤空間 每個交易都存儲在區塊鏈中,從而增加區塊鏈的總大小。區塊鏈存儲在磁盤上,但可以通過“修剪pruning”舊交易來優化存儲。 - CPU 每個交易都必須經過驗證,這需要CPU時間。 - 帶寬 每筆交易至少通過網絡傳輸一次(通過泛洪傳播)。如果在區塊傳播協議中沒有任何優化,交易將作為區塊的一部分再次傳輸,對網絡容量的影響會加倍。 - 內存 驗證交易的節點將UTXO索引或整個UTXO集保留在內存中,以加快驗證。由于內存比磁盤至少貴一個數量級,所以UTXO集的增長不成比例增加了節點的運行成本。 從列表中可以看出,并不是交易的每個部分都對運行節點的成本或者比特幣擴展以支持更多交易的能力產生同等的影響。交易中最昂貴的部分是新創建的輸出,因為它們被添加到內存中的UTXO集。相比之下,簽名(又名見證數據)對網絡增加的負擔較小,運行節點的成本增加也不多,因為見證數據只被驗證一次,之后又不再使用。此外,在收到新的交易并驗證見證數據之后,節點可以立即丟棄該見證數據。如果按照交易規模計算費用,而不區分這兩種數據,那么市場上的收費激勵就不符合交易實際成本。事實上,目前的費用結構實際上鼓勵了相反的行為,因為見證數據是交易的最大部分。 收費產生的激勵之所以重要,是因為它們會影響錢包的行為。所有的錢包都必須執行一些策略來組合交易,這些策略要考慮到許多因素,比如隱私(減少地址重復使用),碎片化(大量找零)以及費用等。如果費用壓倒性地促使錢包在交易中使用盡可能少的投入,這可能導致選擇UTXO和改變地址策略,從而不經意地膨脹UTXO集。 交易在其輸入中消耗UTXO,并用它們的輸出創建新的UTXO。因此,輸入比輸出多的交易將導致UTXO集合的減少,而輸出多于輸入的交易將導致UTXO集合的增加。讓我們考慮輸入和輸出之間的*差異*,并稱之為“凈增UTXO(Net-new-UTXO)”。這是一個重要的指標,因為它告訴我們交易將對最昂貴的全網資源(內存中的UTXO集)產生什么影響。凈增UTXO為正值的交易增加了這一負擔。凈增UTXO為負值的交易就可以減輕負擔。因此,因此,我們希望鼓勵負凈增UTXO或零凈增UTXO持平的交易。 讓我們來看一個例子,說明有無隔離見證,交易費用計算產生了哪些激勵措施。我們來看兩個不同的交易。交易A是3輸入,2輸出的交易,凈增UTXO為-1,意味著它消耗的UTXO比它創建的多了一個,將UTXO減1。交易B是2輸入3輸出的交易,凈增UTXO為1,意味著它向UTXO集增加一個UTXO,增加整個比特幣網絡的成本。這兩個交易都使用多重簽名(2/3)腳本來演示復雜腳本如何增加隔離見證對費用的影響。我們假設交易費為每字節30 satoshi,見證數據的折扣費用為75%: - 未使用隔離見證 交易A費用:25,710 satoshi 交易B費用:18,990 satoshi - 使用隔離見證 交易A手續費:8,130 satoshi 交易B手續費:12,045 satoshi 實施了隔離見證實施,這兩筆交易的費都減少了。但通過比較這兩個交易的成本,我們發現,在隔離見證之前,凈增UTXO為負的交易的費用更高。在隔離見證之后,交易費用與減少新增UTXO的動機就保持一致,而不是無意中懲罰包含較多輸入的交易。 因此,隔離見證對比特幣用戶支付的費用有兩個主要的影響。首先,隔離見證通過折扣見證數據和增加比特幣區塊鏈的能力來降低交易的總體成本。其次,隔離見證對見證數據的折扣糾正了可能無意中在UTXO集合中造成更多膨脹的激勵偏差。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看