Schnorr-Signatures
你如何計算主根 sighash?
我在以下範例中手動重新創建主根 sighash 時遇到問題。
我使用btcdeb傳入一個輸入交易(帶有 P2TR 輸出),以及一個從中花費的有效交易。在日誌中,它顯示了 sighash 消息的組成部分,但是當我將它們連接起來並使用 sha256 對其進行散列時,我沒有得到相同的 sighash。我錯過了什麼?
另外,什麼是
epoch
?我嘗試過使用和不使用這個值,但都沒有奏效。$ btcdeb --txin='02000000000101c7eb56c02553b5fe29c16b458960ba21be0ef1fb020972fde15324b1f725f0480000000000fdffffff027c9c181e010000002251204a0cd40b4984a107334966b266bdc8134bcba9f23f039ed9ddc3f99d6c99c03fa048ed0b000000002251204f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa024730440220077e71f9276149475ecb27e68374712f43e0af6f61ca0e0c9a74483c07bd08d902206f0befdcbc047b0a9a59eff49e011f81644ba0e179a6fa4a90bb19f7765d165c012103d31501fa2dd221b0d40eead3961afd65bdfa986f2577327d1400fa82691cc69965000000' --tx='02000000000101d0a3e1a6c730355abefa376f9b5d3ca9325b153475becc8402e3b4a98d393ed20100000000ffffffff0280d1f00800000000160014fc7250a211deddc70ee5a2738de5f07817351cef80f0fa0200000000160014531260aa2a199e228c537dfa42c82bea2c7c1f4d01404d22f94e5d1787eecf712b14b0c8fb57242633616d50ca5b77f31dad9ad43d0a6786b1e5467dc20494a08e5862821834675f91f897f0e568b417430b07ba100b00000000' btcdeb 0.4.22 -- type `btcdeb -h` for start up options LOG: sighash signing segwit taproot notice: btcdeb has gotten quieter; use --verbose if necessary (this message is temporary) input tx index = 0; tx input vout = 1; value = 200100000 got witness stack of size 1 34 bytes (v0=P2WSH, v1=taproot/tapscript) valid script - generating prevout hash from 1 ins [+] COutPoint(d23e398da9, 1) note: there is a for-clarity preamble (use --verbose for details) 2 op script loaded. type `help` for usage information script | stack -----------------------------------------------------------------+------------------------------------------------------------------- 4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa | 4d22f94e5d1787eecf712b14b0c8fb57242633616d50ca5b77f31dad9ad43d0... OP_CHECKSIG | #0000 4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa btcdeb> step <> PUSH stack 4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa script | stack -----------------------------------------------------------------+------------------------------------------------------------------- OP_CHECKSIG | 4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa | 4d22f94e5d1787eecf712b14b0c8fb57242633616d50ca5b77f31dad9ad43d0... #0001 OP_CHECKSIG btcdeb> step EvalChecksig() sigversion=2 GenericTransactionSignatureChecker::CheckSchnorrSignature(64 len sig, 32 len pubkey, sigversion=2) sig = 4d22f94e5d1787eecf712b14b0c8fb57242633616d50ca5b77f31dad9ad43d0a6786b1e5467dc20494a08e5862821834675f91f897f0e568b417430b07ba100b pub key = 4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa SignatureHashSchnorr(in_pos=0, hash_type=00) - taproot sighash << epoch #001 00 << hash type #001 00 << tx_to.nVersion #004 02000000 << tx_to.nLockTime #004 00000000 input type != anyone can pay: << cache prevouts single hash #032 e186179664b38e58723ba1ac8d5915f97023f6afc2f2be94459fe9868cf953eb << cache spent amounts single hash #032 646b3ee05dcdae994f2d99d94a878d1ad40c3f4b536b76a6a730b4e28b29c385 << cache spent scripts single hash #032 e00e0a53937928e143c165df361096d6f607d8fe35aa63f4adfe2e73b56cd324 << cache sequences single hash #032 ad95131bc0b799c0b1af477fb14fcf26a6a9f76079e48bf090acb7e8367bfd0e output type == sighash_all << cache outputs single hash #032 5ac2b429009184588bf78341f4692a02303a6a3110c2f1a914f3dba9cea091cb << spend type #001 00 << in_pos #004 00000000 - schnorr sighash = 1eaaf5f3228e6aa087cf9620ac86f8bc5458261b074b7cb6d78c20f358d6fc1f pubkey.VerifySchnorrSignature(sig=4d22f94e5d1787eecf712b14b0c8fb57242633616d50ca5b77f31dad9ad43d0a6786b1e5467dc20494a08e5862821834675f91f897f0e568b417430b07ba100b, sighash=1eaaf5f3228e6aa087cf9620ac86f8bc5458261b074b7cb6d78c20f358d6fc1f): result: success
這是我的 python 腳本,用於嘗試創建相同的 sighash:
import hashlib sig_msg = ( "00" # epoch + "00" # hash_type + "02000000" # nVersion + "00000000" # nLockTime + "e186179664b38e58723ba1ac8d5915f97023f6afc2f2be94459fe9868cf953eb" # sha_prevouts + "646b3ee05dcdae994f2d99d94a878d1ad40c3f4b536b76a6a730b4e28b29c385" # sha_amounts + "e00e0a53937928e143c165df361096d6f607d8fe35aa63f4adfe2e73b56cd324" # sha_scriptpubkeys + "ad95131bc0b799c0b1af477fb14fcf26a6a9f76079e48bf090acb7e8367bfd0e" # sha_sequences + "5ac2b429009184588bf78341f4692a02303a6a3110c2f1a914f3dba9cea091cb" # sha_outputs + "00" # spend_type + "00000000" # input_index ) sig_msg = bytes.fromhex(sig_msg) hashlib.sha256(sig_msg).digest().hex() # returns 4c1f7ef4d927d34de60599d334fe26cfb03090c5672e01019bf5da54b9cb5446
我相信您錯過了 BIP341/BIP342 中使用的雜湊函式不是直接 SHA256,而是標記雜湊“hash TapSighash ()”這一事實。
來自 BIP:
雜湊標籤(x) 表示法引用 SHA256(SHA256(tag) || SHA256(tag) || x)。
因此,您必須使用 SHA256(“TapSighash”) 兩次為您的消息添加前綴。
根據 Pieter Wuille 的回答,BIP340 的正確雜湊函式應該是:
import hashlib def tagged_hash(tag: str, msg: bytes) -> bytes: tag_hash = hashlib.sha256(tag.encode()).digest() return hashlib.sha256(tag_hash + tag_hash + msg).digest() sig_msg = ( "00" # epoch + "00" # hash_type + "02000000" # nVersion + "00000000" # nLockTime + "e186179664b38e58723ba1ac8d5915f97023f6afc2f2be94459fe9868cf953eb" # sha_prevouts + "646b3ee05dcdae994f2d99d94a878d1ad40c3f4b536b76a6a730b4e28b29c385" # sha_amounts + "e00e0a53937928e143c165df361096d6f607d8fe35aa63f4adfe2e73b56cd324" # sha_scriptpubkeys + "ad95131bc0b799c0b1af477fb14fcf26a6a9f76079e48bf090acb7e8367bfd0e" # sha_sequences + "5ac2b429009184588bf78341f4692a02303a6a3110c2f1a914f3dba9cea091cb" # sha_outputs + "00" # spend_type + "00000000" # input_index ) sig_msg = bytes.fromhex(sig_msg) tagged_hash("TapSighash", sig_msg) # returns 1ffcd658f3208cd7b67c4b071b265854bcf886ac2096cf87a06a8e22f3f5aa1e # which is the same as btcdeb (little endian)
此外,可以在 BIP341 中找到對 sighash 時期的解釋。
為什麼 hashTapSighash 的輸入以 0x00 為前綴?此前綴稱為 sighash epoch,並允許在未來的簽名算法中重用 hashTapSighash 標記的雜湊,從而對雜湊的執行方式進行侵入性更改(與用於增量擴展的 ext_flag 機制相反)。另一種方法是讓他們使用不同的標籤,但支持越來越多的標籤可能變得不受歡迎。