script_util.py raw
1 #!/usr/bin/env python3
2 # Copyright (c) 2019-2022 The Limenka 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 test_framework.script import (
9 CScript,
10 OP_0,
11 OP_1,
12 OP_15,
13 OP_16,
14 OP_CHECKMULTISIG,
15 OP_CHECKSIG,
16 OP_DUP,
17 OP_EQUAL,
18 OP_EQUALVERIFY,
19 OP_HASH160,
20 OP_RETURN,
21 hash160,
22 sha256,
23 )
24
25 # Maximum number of potentially executed legacy signature operations in validating a transaction.
26 MAX_STD_LEGACY_SIGOPS = 2_500
27
28 # Maximum number of sigops per standard P2SH redeemScript.
29 MAX_STD_P2SH_SIGOPS = 15
30
31 # To prevent a "tx-size-small" policy rule error, a transaction has to have a
32 # non-witness size of at least 65 bytes (MIN_STANDARD_TX_NONWITNESS_SIZE in
33 # src/policy/policy.h). Considering a Tx with the smallest possible single
34 # input (blank, empty scriptSig), and with an output omitting the scriptPubKey,
35 # we get to a minimum size of 60 bytes:
36 #
37 # Tx Skeleton: 4 [Version] + 1 [InCount] + 1 [OutCount] + 4 [LockTime] = 10 bytes
38 # Blank Input: 32 [PrevTxHash] + 4 [Index] + 1 [scriptSigLen] + 4 [SeqNo] = 41 bytes
39 # Output: 8 [Amount] + 1 [scriptPubKeyLen] = 9 bytes
40 #
41 # Hence, the scriptPubKey of the single output has to have a size of at
42 # least 5 bytes.
43 MIN_STANDARD_TX_NONWITNESS_SIZE = 65
44 MIN_PADDING = MIN_STANDARD_TX_NONWITNESS_SIZE - 10 - 41 - 9
45 assert MIN_PADDING == 5
46
47 # This script cannot be spent, allowing dust output values under
48 # standardness checks
49 DUMMY_MIN_OP_RETURN_SCRIPT = CScript([OP_RETURN] + ([OP_0] * (MIN_PADDING - 1)))
50 assert len(DUMMY_MIN_OP_RETURN_SCRIPT) == MIN_PADDING
51
52 PAY_TO_ANCHOR = CScript([OP_1, bytes.fromhex("4e73")])
53 ANCHOR_ADDRESS = "bcrt1pfeesnyr2tx"
54
55 def key_to_p2pk_script(key):
56 key = check_key(key)
57 return CScript([key, OP_CHECKSIG])
58
59
60 def keys_to_multisig_script(keys, *, k=None):
61 n = len(keys)
62 if k is None: # n-of-n multisig by default
63 k = n
64 assert k <= n
65 checked_keys = [check_key(key) for key in keys]
66 return CScript([k] + checked_keys + [n, OP_CHECKMULTISIG])
67
68
69 def keyhash_to_p2pkh_script(hash):
70 assert len(hash) == 20
71 return CScript([OP_DUP, OP_HASH160, hash, OP_EQUALVERIFY, OP_CHECKSIG])
72
73
74 def scripthash_to_p2sh_script(hash):
75 assert len(hash) == 20
76 return CScript([OP_HASH160, hash, OP_EQUAL])
77
78
79 def key_to_p2pkh_script(key):
80 key = check_key(key)
81 return keyhash_to_p2pkh_script(hash160(key))
82
83
84 def script_to_p2sh_script(script):
85 script = check_script(script)
86 return scripthash_to_p2sh_script(hash160(script))
87
88
89 def key_to_p2sh_p2wpkh_script(key):
90 key = check_key(key)
91 p2shscript = CScript([OP_0, hash160(key)])
92 return script_to_p2sh_script(p2shscript)
93
94
95 def program_to_witness_script(version, program):
96 if isinstance(program, str):
97 program = bytes.fromhex(program)
98 assert 0 <= version <= 16
99 assert 2 <= len(program) <= 40
100 assert version > 0 or len(program) in [20, 32]
101 return CScript([version, program])
102
103
104 def script_to_p2wsh_script(script):
105 script = check_script(script)
106 return program_to_witness_script(0, sha256(script))
107
108
109 def key_to_p2wpkh_script(key):
110 key = check_key(key)
111 return program_to_witness_script(0, hash160(key))
112
113
114 def script_to_p2sh_p2wsh_script(script):
115 script = check_script(script)
116 p2shscript = CScript([OP_0, sha256(script)])
117 return script_to_p2sh_script(p2shscript)
118
119
120 def output_key_to_p2tr_script(key):
121 assert len(key) == 32
122 return program_to_witness_script(1, key)
123
124
125 def check_key(key):
126 if isinstance(key, str):
127 key = bytes.fromhex(key) # Assuming this is hex string
128 if isinstance(key, bytes) and (len(key) == 33 or len(key) == 65):
129 return key
130 assert False
131
132
133 def check_script(script):
134 if isinstance(script, str):
135 script = bytes.fromhex(script) # Assuming this is hex string
136 if isinstance(script, bytes) or isinstance(script, CScript):
137 return script
138 assert False
139
140
141 class TestFrameworkScriptUtil(unittest.TestCase):
142 def test_multisig(self):
143 fake_pubkey = bytes([0]*33)
144 # check correct encoding of P2MS script with n,k <= 16
145 normal_ms_script = keys_to_multisig_script([fake_pubkey]*16, k=15)
146 self.assertEqual(len(normal_ms_script), 1 + 16*34 + 1 + 1)
147 self.assertTrue(normal_ms_script.startswith(bytes([OP_15])))
148 self.assertTrue(normal_ms_script.endswith(bytes([OP_16, OP_CHECKMULTISIG])))
149
150 # check correct encoding of P2MS script with n,k > 16
151 max_ms_script = keys_to_multisig_script([fake_pubkey]*20, k=19)
152 self.assertEqual(len(max_ms_script), 2 + 20*34 + 2 + 1)
153 self.assertTrue(max_ms_script.startswith(bytes([1, 19]))) # using OP_PUSH1
154 self.assertTrue(max_ms_script.endswith(bytes([1, 20, OP_CHECKMULTISIG])))
155