script_util.py
1 #!/usr/bin/env python3 2 # Copyright (c) 2019-present The Bitcoin Core developers 3 # Distributed under the MIT software license, see the accompanying 4 # file COPYING or http://www.opensource.org/licenses/mit-license.php. 5 """Useful Script constants and utils.""" 6 import unittest 7 8 from copy import deepcopy 9 10 from test_framework.messages import ( 11 COutPoint, 12 CTransaction, 13 CTxIn, 14 CTxInWitness, 15 CTxOut, 16 ser_compact_size, 17 sha256, 18 ) 19 from test_framework.script import ( 20 CScript, 21 OP_0, 22 OP_1, 23 OP_15, 24 OP_16, 25 OP_CHECKMULTISIG, 26 OP_CHECKSIG, 27 OP_DUP, 28 OP_ELSE, 29 OP_ENDIF, 30 OP_EQUAL, 31 OP_EQUALVERIFY, 32 OP_HASH160, 33 OP_IF, 34 OP_RETURN, 35 OP_TRUE, 36 hash160, 37 ) 38 39 from test_framework.util import ( 40 assert_greater_than_or_equal, 41 assert_equal, 42 ) 43 44 # Maximum number of potentially executed legacy signature operations in validating a transaction. 45 MAX_STD_LEGACY_SIGOPS = 2_500 46 47 # Maximum number of sigops per standard P2SH redeemScript. 48 MAX_STD_P2SH_SIGOPS = 15 49 50 # To prevent a "tx-size-small" policy rule error, a transaction has to have a 51 # non-witness size of at least 65 bytes (MIN_STANDARD_TX_NONWITNESS_SIZE in 52 # src/policy/policy.h). Considering a Tx with the smallest possible single 53 # input (blank, empty scriptSig), and with an output omitting the scriptPubKey, 54 # we get to a minimum size of 60 bytes: 55 # 56 # Tx Skeleton: 4 [Version] + 1 [InCount] + 1 [OutCount] + 4 [LockTime] = 10 bytes 57 # Blank Input: 32 [PrevTxHash] + 4 [Index] + 1 [scriptSigLen] + 4 [SeqNo] = 41 bytes 58 # Output: 8 [Amount] + 1 [scriptPubKeyLen] = 9 bytes 59 # 60 # Hence, the scriptPubKey of the single output has to have a size of at 61 # least 5 bytes. 62 MIN_STANDARD_TX_NONWITNESS_SIZE = 65 63 MIN_PADDING = MIN_STANDARD_TX_NONWITNESS_SIZE - 10 - 41 - 9 64 assert MIN_PADDING == 5 65 66 # This script cannot be spent, allowing dust output values under 67 # standardness checks 68 DUMMY_MIN_OP_RETURN_SCRIPT = CScript([OP_RETURN] + ([OP_0] * (MIN_PADDING - 1))) 69 assert len(DUMMY_MIN_OP_RETURN_SCRIPT) == MIN_PADDING 70 71 PAY_TO_ANCHOR = CScript([OP_1, bytes.fromhex("4e73")]) 72 ANCHOR_ADDRESS = "bcrt1pfeesnyr2tx" 73 74 def key_to_p2pk_script(key): 75 key = check_key(key) 76 return CScript([key, OP_CHECKSIG]) 77 78 79 def keys_to_multisig_script(keys, *, k=None): 80 n = len(keys) 81 if k is None: # n-of-n multisig by default 82 k = n 83 assert k <= n 84 checked_keys = [check_key(key) for key in keys] 85 return CScript([k] + checked_keys + [n, OP_CHECKMULTISIG]) 86 87 88 def keyhash_to_p2pkh_script(hash): 89 assert len(hash) == 20 90 return CScript([OP_DUP, OP_HASH160, hash, OP_EQUALVERIFY, OP_CHECKSIG]) 91 92 93 def scripthash_to_p2sh_script(hash): 94 assert len(hash) == 20 95 return CScript([OP_HASH160, hash, OP_EQUAL]) 96 97 98 def key_to_p2pkh_script(key): 99 key = check_key(key) 100 return keyhash_to_p2pkh_script(hash160(key)) 101 102 103 def script_to_p2sh_script(script): 104 script = check_script(script) 105 return scripthash_to_p2sh_script(hash160(script)) 106 107 108 def key_to_p2sh_p2wpkh_script(key): 109 key = check_key(key) 110 p2shscript = CScript([OP_0, hash160(key)]) 111 return script_to_p2sh_script(p2shscript) 112 113 114 def program_to_witness_script(version, program): 115 if isinstance(program, str): 116 program = bytes.fromhex(program) 117 assert 0 <= version <= 16 118 assert 2 <= len(program) <= 40 119 assert version > 0 or len(program) in [20, 32] 120 return CScript([version, program]) 121 122 123 def script_to_p2wsh_script(script): 124 script = check_script(script) 125 return program_to_witness_script(0, sha256(script)) 126 127 128 def key_to_p2wpkh_script(key): 129 key = check_key(key) 130 return program_to_witness_script(0, hash160(key)) 131 132 133 def script_to_p2sh_p2wsh_script(script): 134 script = check_script(script) 135 p2shscript = CScript([OP_0, sha256(script)]) 136 return script_to_p2sh_script(p2shscript) 137 138 def bulk_vout(tx, target_vsize): 139 if target_vsize < tx.get_vsize(): 140 raise RuntimeError(f"target_vsize {target_vsize} is less than transaction virtual size {tx.get_vsize()}") 141 # determine number of needed padding bytes 142 dummy_vbytes = target_vsize - tx.get_vsize() 143 # compensate for the increase of the compact-size encoded script length 144 # (note that the length encoding of the unpadded output script needs one byte) 145 dummy_vbytes -= len(ser_compact_size(dummy_vbytes)) - 1 146 tx.vout[-1].scriptPubKey = CScript([OP_RETURN] + [OP_1] * dummy_vbytes) 147 assert_equal(tx.get_vsize(), target_vsize) 148 149 def output_key_to_p2tr_script(key): 150 assert len(key) == 32 151 return program_to_witness_script(1, key) 152 153 154 def check_key(key): 155 if isinstance(key, str): 156 key = bytes.fromhex(key) # Assuming this is hex string 157 if isinstance(key, bytes) and (len(key) == 33 or len(key) == 65): 158 return key 159 assert False 160 161 162 def check_script(script): 163 if isinstance(script, str): 164 script = bytes.fromhex(script) # Assuming this is hex string 165 if isinstance(script, bytes) or isinstance(script, CScript): 166 return script 167 assert False 168 169 170 def build_malleated_tx_package(*, parent: CTransaction, rebalance_parent_output_amount, child_amount): 171 """ 172 Returns a transaction package with valid witness: 173 - Parent transaction whose last output contains a script that has two spending conditions 174 - Two malleated child transactions with same txid but different wtxids because of different witnesses 175 176 Args: 177 parent: Transaction with modifiable outputs. Either unsigned (sign after 178 calling this function) or anyone-can-spend (e.g., MiniWallet's OP_TRUE). 179 """ 180 hashlock = hash160(b'Preimage') 181 witness_script = CScript([OP_IF, OP_HASH160, hashlock, OP_EQUAL, OP_ELSE, OP_TRUE, OP_ENDIF]) 182 witness_program = sha256(witness_script) 183 script_pubkey = CScript([OP_0, witness_program]) 184 185 # Append to the transaction the vout containing the script supporting 2 spending conditions 186 assert_greater_than_or_equal(len(parent.vout), 1) 187 last_output = parent.vout[len(parent.vout) - 1] 188 assert_greater_than_or_equal(last_output.nValue, rebalance_parent_output_amount) 189 last_output.nValue -= rebalance_parent_output_amount 190 parent.vout.append(CTxOut(rebalance_parent_output_amount, script_pubkey)) 191 192 193 # Create 2 valid children that differ only in witness data. 194 # 1. Create a new transaction with witness solving first branch 195 child_witness_script = CScript([OP_TRUE]) 196 child_witness_program = sha256(child_witness_script) 197 child_script_pubkey = CScript([OP_0, child_witness_program]) 198 child_one = CTransaction() 199 200 child_one.vin.append(CTxIn(COutPoint(int(parent.txid_hex, 16), len(parent.vout) - 1), b"")) 201 child_one.vout.append(CTxOut(child_amount, child_script_pubkey)) 202 child_one.wit.vtxinwit.append(CTxInWitness()) 203 child_one.wit.vtxinwit[0].scriptWitness.stack = [b'Preimage', b'\x01', witness_script] 204 # 2. Create another identical transaction with witness solving second branch 205 child_two = deepcopy(child_one) 206 child_two.wit.vtxinwit[0].scriptWitness.stack = [b'', witness_script] 207 return parent, child_one, child_two 208 209 210 class TestFrameworkScriptUtil(unittest.TestCase): 211 def test_multisig(self): 212 fake_pubkey = bytes([0]*33) 213 # check correct encoding of P2MS script with n,k <= 16 214 normal_ms_script = keys_to_multisig_script([fake_pubkey]*16, k=15) 215 self.assertEqual(len(normal_ms_script), 1 + 16*34 + 1 + 1) 216 self.assertTrue(normal_ms_script.startswith(bytes([OP_15]))) 217 self.assertTrue(normal_ms_script.endswith(bytes([OP_16, OP_CHECKMULTISIG]))) 218 219 # check correct encoding of P2MS script with n,k > 16 220 max_ms_script = keys_to_multisig_script([fake_pubkey]*20, k=19) 221 self.assertEqual(len(max_ms_script), 2 + 20*34 + 2 + 1) 222 self.assertTrue(max_ms_script.startswith(bytes([1, 19]))) # using OP_PUSH1 223 self.assertTrue(max_ms_script.endswith(bytes([1, 20, OP_CHECKMULTISIG])))