github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/lib/btc/taproot.go (about) 1 package btc 2 3 import ( 4 "crypto/sha256" 5 "encoding/binary" 6 ) 7 8 type ScriptExecutionData struct { 9 //! The tapleaf hash. 10 M_tapleaf_hash []byte 11 12 //! Whether m_codeseparator_pos is initialized. 13 M_codeseparator_pos_init bool 14 //! Opcode position of the last executed OP_CODESEPARATOR (or 0xFFFFFFFF if none executed). 15 M_codeseparator_pos uint32 16 17 //! Hash of the annex data. 18 M_annex_hash []byte 19 20 //! Whether m_validation_weight_left is initialized. 21 M_validation_weight_left_init bool 22 //! How much validation weight is left (decremented for every successful non-empty signature check). 23 M_validation_weight_left int64 24 } 25 26 // TaprootSigHash implements taproot's sighash algorithm 27 // script - if true uses TAPSCRIPT mode (not TAPROOT) 28 func (tx *Tx) TaprootSigHash(execdata *ScriptExecutionData, in_pos int, hash_type byte, script bool) []byte { 29 var ext_flag, key_version byte 30 31 if script { 32 ext_flag = 1 33 } 34 35 tx.hash_lock.Lock() 36 defer tx.hash_lock.Unlock() 37 38 sha := Hasher(HASHER_TAPSIGHASH) 39 40 sha.Write([]byte{0}) // EPOCH 41 42 // Hash type 43 var output_type byte 44 if hash_type == SIGHASH_DEFAULT { 45 output_type = SIGHASH_ALL 46 } else { 47 output_type = (hash_type & SIGHASH_OUTPUT_MASK) // Default (no sighash byte) is equivalent to SIGHASH_ALL 48 } 49 50 input_type := hash_type & SIGHASH_INPUT_MASK 51 if !(hash_type <= 0x03 || (hash_type >= 0x81 && hash_type <= 0x83)) { 52 return make([]byte, 32) 53 } 54 sha.Write([]byte{hash_type}) 55 56 // Transaction level data 57 binary.Write(sha, binary.LittleEndian, tx.Version) 58 binary.Write(sha, binary.LittleEndian, tx.Lock_time) 59 60 if input_type != SIGHASH_ANYONECANPAY { 61 if tx.m_prevouts_single_hash == nil { 62 sh := sha256.New() 63 for _, ti := range tx.TxIn { 64 sh.Write(ti.Input.Hash[:]) 65 binary.Write(sh, binary.LittleEndian, ti.Input.Vout) 66 } 67 tx.m_prevouts_single_hash = sh.Sum(nil) 68 69 sh.Reset() 70 for i := range tx.TxIn { 71 binary.Write(sh, binary.LittleEndian, tx.Spent_outputs[i].Value) 72 } 73 tx.m_spent_amounts_single_hash = sh.Sum(nil) 74 75 sh.Reset() 76 for i := range tx.TxIn { 77 WriteVlen(sh, uint64(len(tx.Spent_outputs[i].Pk_script))) 78 sh.Write(tx.Spent_outputs[i].Pk_script) 79 } 80 tx.m_spent_scripts_single_hash = sh.Sum(nil) 81 82 sh.Reset() 83 for _, vin := range tx.TxIn { 84 binary.Write(sh, binary.LittleEndian, vin.Sequence) 85 } 86 tx.m_sequences_single_hash = sh.Sum(nil) 87 } 88 89 sha.Write(tx.m_prevouts_single_hash) 90 sha.Write(tx.m_spent_amounts_single_hash) 91 sha.Write(tx.m_spent_scripts_single_hash) 92 sha.Write(tx.m_sequences_single_hash) 93 } 94 95 if output_type == SIGHASH_ALL { 96 if tx.m_outputs_single_hash == nil { 97 sh := sha256.New() 98 for _, vout := range tx.TxOut { 99 binary.Write(sh, binary.LittleEndian, vout.Value) 100 WriteVlen(sh, uint64(len(vout.Pk_script))) 101 sh.Write(vout.Pk_script) 102 } 103 tx.m_outputs_single_hash = sh.Sum(nil) 104 } 105 106 sha.Write(tx.m_outputs_single_hash) 107 } 108 109 // Data about the input/prevout being spent 110 have_annex := execdata.M_annex_hash != nil 111 spend_type := (ext_flag << 1) 112 if have_annex { 113 spend_type++ 114 } 115 sha.Write([]byte{spend_type}) 116 117 if input_type == SIGHASH_ANYONECANPAY { 118 sha.Write(tx.TxIn[in_pos].Input.Hash[:]) 119 binary.Write(sha, binary.LittleEndian, tx.TxIn[in_pos].Input.Vout) 120 binary.Write(sha, binary.LittleEndian, tx.Spent_outputs[in_pos].Value) 121 WriteVlen(sha, uint64(len(tx.Spent_outputs[in_pos].Pk_script))) 122 sha.Write(tx.Spent_outputs[in_pos].Pk_script) 123 binary.Write(sha, binary.LittleEndian, tx.TxIn[in_pos].Sequence) 124 } else { 125 binary.Write(sha, binary.LittleEndian, uint32(in_pos)) 126 } 127 if have_annex { 128 sha.Write(execdata.M_annex_hash) 129 } 130 131 // Data about the output (if only one). 132 if output_type == SIGHASH_SINGLE { 133 if in_pos >= len(tx.TxOut) { 134 return make([]byte, 32) 135 } 136 sh := sha256.New() 137 binary.Write(sh, binary.LittleEndian, tx.TxOut[in_pos].Value) 138 WriteVlen(sh, uint64(len(tx.TxOut[in_pos].Pk_script))) 139 sh.Write(tx.TxOut[in_pos].Pk_script) 140 sha.Write(sh.Sum(nil)) 141 } 142 143 // Additional data for BIP 342 signatures 144 if script { 145 sha.Write(execdata.M_tapleaf_hash) 146 sha.Write([]byte{key_version}) 147 binary.Write(sha, binary.LittleEndian, execdata.M_codeseparator_pos) 148 } 149 150 return sha.Sum(nil) 151 }