Skip to content

Commit bb610d1

Browse files
authored
WIP: fix p2sh redeemscript parsing (#124)
Co-authored-by: Michael Flaxman Co-authored-by: Jimmy Song
1 parent 7a7b3e0 commit bb610d1

File tree

3 files changed

+33
-5
lines changed

3 files changed

+33
-5
lines changed

buidl/script.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,13 @@ def __add__(self, other):
5454
return Script(self.commands + other.commands)
5555

5656
@classmethod
57-
def parse(cls, stream):
58-
# get the length of the entire field
59-
raw = read_varstr(stream)
57+
def parse(cls, stream=None, raw=None):
58+
if stream and raw:
59+
raise ValueError("provide exactly one of stream/raw, not both")
60+
if raw is None:
61+
if stream is None:
62+
raise ValueError("provide one of stream/raw")
63+
raw = read_varstr(stream)
6064
length = len(raw)
6165
s = BytesIO(raw)
6266
# initialize the commands array
@@ -101,10 +105,16 @@ def parse(cls, stream):
101105
commands.append(op_code)
102106
obj = cls(commands)
103107
if count != length:
108+
# Would throw error, but Bitcoin Core will read the number of bytes that are there (not what was promised)
104109
print(f"mismatch between length and consumed bytes {count} vs {length}")
105110
obj.raw = raw
106111
return obj
107112

113+
@classmethod
114+
def parse_hex(cls, hex_str):
115+
"""Helper method to make it easier to parse a hex string without converting to bytes"""
116+
return cls.parse(raw=bytes.fromhex(hex_str))
117+
108118
def raw_serialize(self):
109119
if self.raw:
110120
return self.raw

buidl/test/test_script.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def test_address(self):
6363

6464

6565
class RedeemScriptTest(TestCase):
66-
def test_redeem_script(self):
66+
def test_nonstandard_redeemscript(self):
6767
hex_redeem_script = "4752210223136797cb0d7596cb5bd476102fe3aface2a06338e1afabffacf8c3cab4883c210385c865e61e275ba6fda4a3167180fc5a6b607150ff18797ee44737cd0d34507b52ae"
6868
stream = BytesIO(bytes.fromhex(hex_redeem_script))
6969
redeem_script = RedeemScript.parse(stream)
@@ -75,6 +75,24 @@ def test_redeem_script(self):
7575
self.assertEqual(redeem_script.address(network="testnet"), want)
7676
self.assertEqual(redeem_script.address(network="signet"), want)
7777

78+
def test_standard_redeemscript(self):
79+
# This was tested against bitcoin core v0.22
80+
# https://github.com/buidl-bitcoin/buidl-python/issues/123
81+
redeem_script_hex = "522102be8d4de672b4d6149616962a7d193b702e608a1ed65aaa44f432ff9dd902252f21036ec4565fb304b0fc8e2cdd56e477816ab703e06d52f87279bf0cbdb9fa4941b221038fc14c8dd5a15828bd4fb0e206e443e3ac6e3e3782fbf6f0a1ff969a9ec8f28f53ae"
82+
rs_obj = RedeemScript.parse_hex(redeem_script_hex)
83+
self.assertEqual(
84+
"2NAaGqgaZicBUXuA2iZc6ssxLEsS4sZwdwz", rs_obj.address("testnet")
85+
)
86+
self.assertEqual((2, 3), rs_obj.get_quorum())
87+
self.assertTrue(rs_obj.is_p2sh_multisig())
88+
pubkeys_wanted_hex = (
89+
"02be8d4de672b4d6149616962a7d193b702e608a1ed65aaa44f432ff9dd902252f",
90+
"036ec4565fb304b0fc8e2cdd56e477816ab703e06d52f87279bf0cbdb9fa4941b2",
91+
"038fc14c8dd5a15828bd4fb0e206e443e3ac6e3e3782fbf6f0a1ff969a9ec8f28f",
92+
)
93+
for cnt, pubkey_bytes in enumerate(rs_obj.signing_pubkeys()):
94+
self.assertEqual(pubkey_bytes.hex(), pubkeys_wanted_hex[cnt])
95+
7896
def test_create_p2sh_multisig(self):
7997
BASE_PATH = "m/45h/0"
8098

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
setup(
88
name="buidl",
9-
version="0.2.34",
9+
version="0.2.35",
1010
author="Example Author",
1111
author_email="[email protected]",
1212
description="An easy-to-use and fully featured bitcoin library written in pure python (no dependencies).",

0 commit comments

Comments
 (0)