github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/lib/script/witness.go (about) 1 package script 2 3 import ( 4 "bytes" 5 "crypto/sha256" 6 "encoding/hex" 7 "fmt" 8 9 "github.com/piotrnar/gocoin/lib/btc" 10 ) 11 12 type witness_ctx struct { 13 stack scrStack 14 } 15 16 func (w *witness_ctx) IsNull() bool { 17 return w.stack.size() == 0 18 } 19 20 func (c *SigChecker) ExecuteWitnessScript(stack *scrStack, scriptPubKey []byte, flags uint32, sigversion int, execdata *btc.ScriptExecutionData) bool { 21 if sigversion == SIGVERSION_TAPSCRIPT { 22 var pc int 23 for pc < len(scriptPubKey) { 24 opcode, _, le, e := btc.GetOpcode(scriptPubKey[pc:]) 25 if e != nil { 26 // Note how this condition would not be reached if an unknown OP_SUCCESSx was found 27 if DBG_ERR { 28 fmt.Println("SCRIPT_ERR_BAD_OPCODE") 29 } 30 return false 31 } 32 // New opcodes will be listed here. May use a different sigversion to modify existing opcodes. 33 if IsOpSuccess(opcode) { 34 if (flags & VER_DIS_SUCCESS) != 0 { 35 if DBG_ERR { 36 fmt.Println("SCRIPT_ERR_DISCOURAGE_OP_SUCCESS") 37 } 38 return false 39 } 40 if DBG_ERR { 41 fmt.Println("IsOpSuccess - yes") 42 } 43 return true 44 } 45 pc += le 46 } 47 // Tapscript enforces initial stack size limits (altstack is empty here) 48 if stack.size() > MAX_STACK_SIZE { 49 if DBG_ERR { 50 fmt.Println("SCRIPT_ERR_STACK_SIZE") 51 } 52 return false 53 } 54 } 55 56 // Disallow stack item size > MAX_SCRIPT_ELEMENT_SIZE in witness stack 57 for i := 0; i < stack.size(); i++ { 58 if len(stack.at(i)) > btc.MAX_SCRIPT_ELEMENT_SIZE { 59 if DBG_ERR { 60 fmt.Println("SCRIPT_ERR_PUSH_SIZE") 61 } 62 return false 63 } 64 } 65 66 // Run the script interpreter. 67 //DBG_SCR = true 68 if !evalScript(scriptPubKey, stack, c, flags, sigversion, execdata) { 69 if DBG_ERR { 70 fmt.Println("eval script failed") 71 } 72 return false 73 } 74 75 // Scripts inside witness implicitly require cleanstack behaviour 76 if stack.size() != 1 { 77 if DBG_ERR { 78 fmt.Println("SCRIPT_ERR_EVAL_FALSE") 79 } 80 return false 81 } 82 83 if !stack.topBool(-1) { 84 if DBG_ERR { 85 fmt.Println("SCRIPT_ERR_EVAL_FALSE") 86 } 87 return false 88 } 89 return true 90 } 91 92 func (checker *SigChecker) VerifyWitnessProgram(witness *witness_ctx, witversion int, program []byte, flags uint32, is_p2sh bool) bool { 93 var stack scrStack 94 var scriptPubKey []byte 95 var execdata btc.ScriptExecutionData 96 97 if DBG_SCR { 98 fmt.Println("*****************VerifyWitnessProgram", len(checker.Tx.SegWit), witversion, flags, witness.stack.size(), len(program)) 99 } 100 101 if witversion == 0 { 102 if len(program) == 32 { 103 // Version 0 segregated witness program: SHA256(CScript) inside the program, CScript + inputs in witness 104 if witness.stack.size() == 0 { 105 if DBG_ERR { 106 fmt.Println("SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY") 107 } 108 return false 109 } 110 stack.copy_from(&witness.stack) 111 scriptPubKey = stack.pop() 112 sha := sha256.New() 113 sha.Write(scriptPubKey) 114 sum := sha.Sum(nil) 115 if !bytes.Equal(program, sum) { 116 if DBG_ERR { 117 fmt.Println("32-SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH") 118 fmt.Println(hex.EncodeToString(program)) 119 fmt.Println(hex.EncodeToString(sum)) 120 fmt.Println(hex.EncodeToString(scriptPubKey)) 121 } 122 return false 123 } 124 return checker.ExecuteWitnessScript(&stack, scriptPubKey, flags, SIGVERSION_WITNESS_V0, &execdata) 125 } else if len(program) == 20 { 126 // Special case for pay-to-pubkeyhash; signature + pubkey in witness 127 if witness.stack.size() != 2 { 128 if DBG_ERR { 129 fmt.Println("20-SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH", checker.Tx.Hash.String()) 130 } 131 return false 132 } 133 134 scriptPubKey = make([]byte, 25) 135 scriptPubKey[0] = 0x76 136 scriptPubKey[1] = 0xa9 137 scriptPubKey[2] = 0x14 138 copy(scriptPubKey[3:23], program) 139 scriptPubKey[23] = 0x88 140 scriptPubKey[24] = 0xac 141 stack.copy_from(&witness.stack) 142 return checker.ExecuteWitnessScript(&stack, scriptPubKey, flags, SIGVERSION_WITNESS_V0, &execdata) 143 } else { 144 if DBG_ERR { 145 fmt.Println("SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH") 146 } 147 return false 148 } 149 } else if witversion == 1 && len(program) == 32 && !is_p2sh { 150 // TAPROOT 151 if (flags & VER_TAPROOT) == 0 { 152 if DBG_SCR { 153 fmt.Println("VER_TAPROOT not set - verify script OK", checker.Tx.Hash.String(), checker.Idx) 154 } 155 return true 156 } 157 stack.copy_from(&witness.stack) 158 159 if stack.size() == 0 { 160 fmt.Println("SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY") 161 return false 162 } 163 164 execdata.M_annex_hash = nil 165 if stack.size() >= 2 { 166 dat := stack.top(-1) 167 if len(dat) > 0 && dat[0] == ANNEX_TAG { 168 // Drop annex (this is non-standard; see IsWitnessStandard) 169 annex := stack.pop() 170 sha := sha256.New() 171 btc.WriteVlen(sha, uint64(len(annex))) 172 sha.Write(annex) 173 execdata.M_annex_hash = sha.Sum(nil) 174 } 175 } 176 // execdata.M_annex_init - it doesn't seem like we need this 177 178 if stack.size() == 1 { 179 // Key path spending (stack size is 1 after removing optional annex) 180 return checker.CheckSchnorrSignature(stack.top(-1), program, SIGVERSION_TAPROOT, &execdata) 181 } else { 182 // Script path spending (stack size is >1 after removing optional annex) 183 control := stack.pop() 184 script_bytes := stack.pop() 185 //exec_script = script_bytes 186 if len(control) < TAPROOT_CONTROL_BASE_SIZE || len(control) > TAPROOT_CONTROL_MAX_SIZE || ((len(control)-TAPROOT_CONTROL_BASE_SIZE)%TAPROOT_CONTROL_NODE_SIZE) != 0 { 187 if DBG_ERR { 188 fmt.Println("SCRIPT_ERR_TAPROOT_WRONG_CONTROL_SIZE") 189 } 190 return false 191 } 192 193 if !VerifyTaprootCommitment(control, program, script_bytes, &execdata.M_tapleaf_hash) { 194 if DBG_ERR { 195 fmt.Println("VerifyTaprootCommitment: SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH") 196 } 197 return false 198 } 199 200 if (control[0] & TAPROOT_LEAF_MASK) == TAPROOT_LEAF_TAPSCRIPT { 201 // Tapscript (leaf version 0xc0) 202 execdata.M_validation_weight_left = int64(witness.stack.GetSerializeSize() + VALIDATION_WEIGHT_OFFSET) 203 execdata.M_validation_weight_left_init = true 204 return checker.ExecuteWitnessScript(&stack, script_bytes, flags, SIGVERSION_TAPSCRIPT, &execdata) 205 } 206 if (flags & VER_DIS_TAPVER) != 0 { 207 fmt.Println("SCRIPT_ERR_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION") 208 return false 209 } 210 return true 211 } 212 } else if (flags & VER_WITNESS_PROG) != 0 { 213 if DBG_ERR { 214 fmt.Println("SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM") 215 } 216 return false 217 } else { 218 // Higher version witness scripts return true for future softfork compatibility 219 return true 220 } 221 // There is intentionally no return statement here, to be able to use "control reaches end of non-void function" warnings to detect gaps in the logic above. 222 } 223 224 func lexicographical_compare(d1, d2 []byte) bool { 225 first1, first2 := 0, 0 226 last1, last2 := len(d1), len(d2) 227 for first1 != last1 { 228 if first2 == last2 || d2[first2] < d1[first1] { 229 return false 230 } 231 if d1[first1] < d2[first2] { 232 return true 233 } 234 first1++ 235 first2++ 236 } 237 return first2 != last2 238 } 239 240 // (control, program, script_bytes, execdata.M_tapleaf_hash) 241 func VerifyTaprootCommitment(control, program, script []byte, tapleaf_hash *[]byte) bool { 242 path_len := (len(control) - TAPROOT_CONTROL_BASE_SIZE) / TAPROOT_CONTROL_NODE_SIZE 243 //! The internal pubkey (x-only, so no Y coordinate parity). 244 p := control[1:TAPROOT_CONTROL_BASE_SIZE] 245 //! The output pubkey (taken from the scriptPubKey). 246 q := program //const XOnlyPubKey q{uint256(program)}; 247 // Compute the tapleaf hash. 248 249 sha := btc.Hasher(btc.HASHER_TAPLEAF) 250 sha.Write([]byte{control[0] & TAPROOT_LEAF_MASK}) 251 btc.WriteVlen(sha, uint64(len(script))) 252 sha.Write(script) 253 *tapleaf_hash = sha.Sum(nil) 254 255 // Compute the Merkle root from the leaf and the provided path. 256 k := *tapleaf_hash 257 for i := 0; i < path_len; i++ { 258 ss_branch := btc.Hasher(btc.HASHER_TAPBRANCH) 259 tmp := TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE*i 260 node := control[tmp : tmp+TAPROOT_CONTROL_NODE_SIZE] 261 262 if lexicographical_compare(k, node) { 263 ss_branch.Write(k) 264 ss_branch.Write(node) 265 } else { 266 ss_branch.Write(node) 267 ss_branch.Write(k) 268 } 269 k = ss_branch.Sum(nil) 270 } 271 272 // Compute the tweak from the Merkle root and the internal pubkey. 273 sha = btc.Hasher(btc.HASHER_TAPTWEAK) 274 sha.Write(p) 275 sha.Write(k) 276 k = sha.Sum(nil) 277 // Verify that the output pubkey matches the tweaked internal pubkey, after correcting for parity. 278 return btc.CheckPayToContract(q, p, k, (control[0]&1) != 0) 279 }