Skip to content

Commit fd0041a

Browse files
committed
Use BIP173 addresses in segwit.py test
1 parent e278f12 commit fd0041a

File tree

3 files changed

+162
-13
lines changed

3 files changed

+162
-13
lines changed

test/functional/segwit.py

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from test_framework.test_framework import BitcoinTestFramework
88
from test_framework.util import *
99
from test_framework.mininode import sha256, CTransaction, CTxIn, COutPoint, CTxOut, COIN, ToHex, FromHex
10-
from test_framework.address import script_to_p2sh, key_to_p2pkh
10+
from test_framework.address import script_to_p2sh, key_to_p2pkh, key_to_p2sh_p2wpkh, key_to_p2wpkh, script_to_p2sh_p2wsh, script_to_p2wsh, program_to_witness
1111
from test_framework.script import CScript, OP_HASH160, OP_CHECKSIG, OP_0, hash160, OP_EQUAL, OP_DUP, OP_EQUALVERIFY, OP_1, OP_2, OP_CHECKMULTISIG, OP_TRUE
1212
from io import BytesIO
1313

@@ -33,23 +33,23 @@ def witness_script(use_p2wsh, pubkey):
3333

3434
# Return a transaction (in hex) that spends the given utxo to a segwit output,
3535
# optionally wrapping the segwit output using P2SH.
36-
def create_witnessprogram(use_p2wsh, utxo, pubkey, encode_p2sh, amount):
37-
pkscript = hex_str_to_bytes(witness_script(use_p2wsh, pubkey))
38-
if (encode_p2sh):
39-
p2sh_hash = hash160(pkscript)
40-
pkscript = CScript([OP_HASH160, p2sh_hash, OP_EQUAL])
41-
tx = CTransaction()
42-
tx.vin.append(CTxIn(COutPoint(int(utxo["txid"], 16), utxo["vout"]), b""))
43-
tx.vout.append(CTxOut(int(amount*COIN), pkscript))
44-
return ToHex(tx)
36+
def create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount):
37+
if use_p2wsh:
38+
program = CScript([OP_1, hex_str_to_bytes(pubkey), OP_1, OP_CHECKMULTISIG])
39+
addr = script_to_p2sh_p2wsh(program) if encode_p2sh else script_to_p2wsh(program)
40+
else:
41+
addr = key_to_p2sh_p2wpkh(pubkey) if encode_p2sh else key_to_p2wpkh(pubkey)
42+
if not encode_p2sh:
43+
assert_equal(node.validateaddress(addr)['scriptPubKey'], witness_script(use_p2wsh, pubkey))
44+
return node.createrawtransaction([utxo], {addr: amount})
4545

4646
# Create a transaction spending a given utxo to a segwit output corresponding
4747
# to the given pubkey: use_p2wsh determines whether to use P2WPKH or P2WSH;
4848
# encode_p2sh determines whether to wrap in P2SH.
4949
# sign=True will have the given node sign the transaction.
5050
# insert_redeem_script will be added to the scriptSig, if given.
5151
def send_to_witness(use_p2wsh, node, utxo, pubkey, encode_p2sh, amount, sign=True, insert_redeem_script=""):
52-
tx_to_witness = create_witnessprogram(use_p2wsh, utxo, pubkey, encode_p2sh, amount)
52+
tx_to_witness = create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount)
5353
if (sign):
5454
signed = node.signrawtransaction(tx_to_witness)
5555
assert("errors" not in signed or len(["errors"]) == 0)
@@ -133,8 +133,15 @@ def run_test(self):
133133
newaddress = self.nodes[i].getnewaddress()
134134
self.pubkey.append(self.nodes[i].validateaddress(newaddress)["pubkey"])
135135
multiaddress = self.nodes[i].addmultisigaddress(1, [self.pubkey[-1]])
136-
self.nodes[i].addwitnessaddress(newaddress)
137-
self.nodes[i].addwitnessaddress(multiaddress)
136+
multiscript = CScript([OP_1, hex_str_to_bytes(self.pubkey[-1]), OP_1, OP_CHECKMULTISIG])
137+
p2sh_addr = self.nodes[i].addwitnessaddress(newaddress, True)
138+
bip173_addr = self.nodes[i].addwitnessaddress(newaddress, False)
139+
p2sh_ms_addr = self.nodes[i].addwitnessaddress(multiaddress, True)
140+
bip173_ms_addr = self.nodes[i].addwitnessaddress(multiaddress, False)
141+
assert_equal(p2sh_addr, key_to_p2sh_p2wpkh(self.pubkey[-1]))
142+
assert_equal(bip173_addr, key_to_p2wpkh(self.pubkey[-1]))
143+
assert_equal(p2sh_ms_addr, script_to_p2sh_p2wsh(multiscript))
144+
assert_equal(bip173_ms_addr, script_to_p2wsh(multiscript))
138145
p2sh_ids.append([])
139146
wit_ids.append([])
140147
for v in range(2):
@@ -558,6 +565,13 @@ def run_test(self):
558565
solvable_txid.append(self.mine_and_test_listunspent(solvable_after_addwitnessaddress, 1))
559566
self.mine_and_test_listunspent(unseen_anytime, 0)
560567

568+
# Check that createrawtransaction/decoderawtransaction with non-v0 Bech32 works
569+
v1_addr = program_to_witness(1, [3,5])
570+
v1_tx = self.nodes[0].createrawtransaction([getutxo(spendable_txid[0])],{v1_addr: 1})
571+
v1_decoded = self.nodes[1].decoderawtransaction(v1_tx)
572+
assert_equal(v1_decoded['vout'][0]['scriptPubKey']['addresses'][0], v1_addr)
573+
assert_equal(v1_decoded['vout'][0]['scriptPubKey']['hex'], "51020305")
574+
561575
# Check that spendable outputs are really spendable
562576
self.create_and_mine_tx_from_txids(spendable_txid)
563577

test/functional/test_framework/address.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
from .script import hash256, hash160, sha256, CScript, OP_0
88
from .util import bytes_to_hex_str, hex_str_to_bytes
99

10+
from . import segwit_addr
11+
1012
chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
1113

1214
def byte_to_base58(b, version):
@@ -44,6 +46,32 @@ def script_to_p2sh(script, main = False):
4446
script = check_script(script)
4547
return scripthash_to_p2sh(hash160(script), main)
4648

49+
def key_to_p2sh_p2wpkh(key, main = False):
50+
key = check_key(key)
51+
p2shscript = CScript([OP_0, hash160(key)])
52+
return script_to_p2sh(p2shscript, main)
53+
54+
def program_to_witness(version, program, main = False):
55+
if (type(program) is str):
56+
program = hex_str_to_bytes(program)
57+
assert 0 <= version <= 16
58+
assert 2 <= len(program) <= 40
59+
assert version > 0 or len(program) in [20, 32]
60+
return segwit_addr.encode("bc" if main else "bcrt", version, program)
61+
62+
def script_to_p2wsh(script, main = False):
63+
script = check_script(script)
64+
return program_to_witness(0, sha256(script), main)
65+
66+
def key_to_p2wpkh(key, main = False):
67+
key = check_key(key)
68+
return program_to_witness(0, hash160(key), main)
69+
70+
def script_to_p2sh_p2wsh(script, main = False):
71+
script = check_script(script)
72+
p2shscript = CScript([OP_0, sha256(script)])
73+
return script_to_p2sh(p2shscript, main)
74+
4775
def check_key(key):
4876
if (type(key) is str):
4977
key = hex_str_to_bytes(key) # Assuming this is hex string
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) 2017 Pieter Wuille
3+
# Distributed under the MIT software license, see the accompanying
4+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
"""Reference implementation for Bech32 and segwit addresses."""
6+
7+
8+
CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
9+
10+
11+
def bech32_polymod(values):
12+
"""Internal function that computes the Bech32 checksum."""
13+
generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]
14+
chk = 1
15+
for value in values:
16+
top = chk >> 25
17+
chk = (chk & 0x1ffffff) << 5 ^ value
18+
for i in range(5):
19+
chk ^= generator[i] if ((top >> i) & 1) else 0
20+
return chk
21+
22+
23+
def bech32_hrp_expand(hrp):
24+
"""Expand the HRP into values for checksum computation."""
25+
return [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 31 for x in hrp]
26+
27+
28+
def bech32_verify_checksum(hrp, data):
29+
"""Verify a checksum given HRP and converted data characters."""
30+
return bech32_polymod(bech32_hrp_expand(hrp) + data) == 1
31+
32+
33+
def bech32_create_checksum(hrp, data):
34+
"""Compute the checksum values given HRP and data."""
35+
values = bech32_hrp_expand(hrp) + data
36+
polymod = bech32_polymod(values + [0, 0, 0, 0, 0, 0]) ^ 1
37+
return [(polymod >> 5 * (5 - i)) & 31 for i in range(6)]
38+
39+
40+
def bech32_encode(hrp, data):
41+
"""Compute a Bech32 string given HRP and data values."""
42+
combined = data + bech32_create_checksum(hrp, data)
43+
return hrp + '1' + ''.join([CHARSET[d] for d in combined])
44+
45+
46+
def bech32_decode(bech):
47+
"""Validate a Bech32 string, and determine HRP and data."""
48+
if ((any(ord(x) < 33 or ord(x) > 126 for x in bech)) or
49+
(bech.lower() != bech and bech.upper() != bech)):
50+
return (None, None)
51+
bech = bech.lower()
52+
pos = bech.rfind('1')
53+
if pos < 1 or pos + 7 > len(bech) or len(bech) > 90:
54+
return (None, None)
55+
if not all(x in CHARSET for x in bech[pos+1:]):
56+
return (None, None)
57+
hrp = bech[:pos]
58+
data = [CHARSET.find(x) for x in bech[pos+1:]]
59+
if not bech32_verify_checksum(hrp, data):
60+
return (None, None)
61+
return (hrp, data[:-6])
62+
63+
64+
def convertbits(data, frombits, tobits, pad=True):
65+
"""General power-of-2 base conversion."""
66+
acc = 0
67+
bits = 0
68+
ret = []
69+
maxv = (1 << tobits) - 1
70+
max_acc = (1 << (frombits + tobits - 1)) - 1
71+
for value in data:
72+
if value < 0 or (value >> frombits):
73+
return None
74+
acc = ((acc << frombits) | value) & max_acc
75+
bits += frombits
76+
while bits >= tobits:
77+
bits -= tobits
78+
ret.append((acc >> bits) & maxv)
79+
if pad:
80+
if bits:
81+
ret.append((acc << (tobits - bits)) & maxv)
82+
elif bits >= frombits or ((acc << (tobits - bits)) & maxv):
83+
return None
84+
return ret
85+
86+
87+
def decode(hrp, addr):
88+
"""Decode a segwit address."""
89+
hrpgot, data = bech32_decode(addr)
90+
if hrpgot != hrp:
91+
return (None, None)
92+
decoded = convertbits(data[1:], 5, 8, False)
93+
if decoded is None or len(decoded) < 2 or len(decoded) > 40:
94+
return (None, None)
95+
if data[0] > 16:
96+
return (None, None)
97+
if data[0] == 0 and len(decoded) != 20 and len(decoded) != 32:
98+
return (None, None)
99+
return (data[0], decoded)
100+
101+
102+
def encode(hrp, witver, witprog):
103+
"""Encode a segwit address."""
104+
ret = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5))
105+
if decode(hrp, ret) == (None, None):
106+
return None
107+
return ret

0 commit comments

Comments
 (0)