script_util.py raw
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_equal(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_equal(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_equal(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_equal(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_equal(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])))
224