Schnorr-Signatures

你如何計算主根 sighash?

  • September 22, 2022

我在以下範例中手動重新創建主根 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 機制相反)。另一種方法是讓他們使用不同的標籤,但支持越來越多的標籤可能變得不受歡迎。

引用自:https://bitcoin.stackexchange.com/questions/115328