Skip to content

Commit b224a47

Browse files
committed
Add address_types test
Improvements and cleanups by John Newbery
1 parent 7ee54fd commit b224a47

File tree

2 files changed

+200
-0
lines changed

2 files changed

+200
-0
lines changed

test/functional/address_types.py

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) 2017 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+
"""Test that the wallet can send and receive using all combinations of address types.
6+
7+
There are 4 nodes-under-test:
8+
- node0 uses legacy addresses
9+
- node1 uses p2sh/segwit addresses
10+
- node2 uses p2sh/segwit addresses and bech32 addresses for change
11+
- node3 uses bech32 addresses
12+
13+
node4 exists to generate new blocks.
14+
15+
The script is a series of tests, iterating over the 4 nodes. In each iteration
16+
of the test, one node sends:
17+
- 10/101th of its balance to itself (using getrawchangeaddress for single key addresses)
18+
- 20/101th to the next node
19+
- 30/101th to the node after that
20+
- 40/101th to the remaining node
21+
- 1/101th remains as fee+change
22+
23+
Iterate over each node for single key addresses, and then over each node for
24+
multisig addresses. In a second iteration, the same is done, but with explicit address_type
25+
parameters passed to getnewaddress and getrawchangeaddress. Node0 and node3 send to p2sh,
26+
node 1 sends to bech32, and node2 sends to legacy. As every node sends coins after receiving,
27+
this also verifies that spending coins sent to all these address types works."""
28+
29+
from decimal import Decimal
30+
import itertools
31+
32+
from test_framework.test_framework import BitcoinTestFramework
33+
from test_framework.util import assert_equal, assert_greater_than, connect_nodes_bi, sync_blocks, sync_mempools
34+
35+
class AddressTypeTest(BitcoinTestFramework):
36+
def set_test_params(self):
37+
self.num_nodes = 5
38+
self.extra_args = [["-addresstype=legacy"], ["-addresstype=p2sh-segwit"], ["-addresstype=p2sh-segwit", "-changetype=bech32"], ["-addresstype=bech32"], []]
39+
40+
def setup_network(self):
41+
self.setup_nodes()
42+
43+
# Fully mesh-connect nodes for faster mempool sync
44+
for i, j in itertools.product(range(self.num_nodes), repeat=2):
45+
if i > j:
46+
connect_nodes_bi(self.nodes, i, j)
47+
self.sync_all()
48+
49+
def get_balances(self, confirmed=True):
50+
"""Return a list of confirmed or unconfirmed balances."""
51+
if confirmed:
52+
return [self.nodes[i].getbalance() for i in range(4)]
53+
else:
54+
return [self.nodes[i].getunconfirmedbalance() for i in range(4)]
55+
56+
def test_address(self, node, address, multisig, typ):
57+
"""Run sanity checks on an address."""
58+
info = self.nodes[node].validateaddress(address)
59+
assert(info['isvalid'])
60+
if not multisig and typ == 'legacy':
61+
# P2PKH
62+
assert(not info['isscript'])
63+
assert(not info['iswitness'])
64+
assert('pubkey' in info)
65+
elif not multisig and typ == 'p2sh-segwit':
66+
# P2SH-P2WPKH
67+
assert(info['isscript'])
68+
assert(not info['iswitness'])
69+
assert_equal(info['script'], 'witness_v0_keyhash')
70+
assert('pubkey' in info)
71+
elif not multisig and typ == 'bech32':
72+
# P2WPKH
73+
assert(not info['isscript'])
74+
assert(info['iswitness'])
75+
assert_equal(info['witness_version'], 0)
76+
assert_equal(len(info['witness_program']), 40)
77+
assert('pubkey' in info)
78+
elif typ == 'legacy':
79+
# P2SH-multisig
80+
assert(info['isscript'])
81+
assert_equal(info['script'], 'multisig')
82+
assert(not info['iswitness'])
83+
assert('pubkeys' in info)
84+
elif typ == 'p2sh-segwit':
85+
# P2SH-P2WSH-multisig
86+
assert(info['isscript'])
87+
assert_equal(info['script'], 'witness_v0_scripthash')
88+
assert(not info['iswitness'])
89+
assert(info['embedded']['isscript'])
90+
assert_equal(info['embedded']['script'], 'multisig')
91+
assert(info['embedded']['iswitness'])
92+
assert_equal(info['embedded']['witness_version'], 0)
93+
assert_equal(len(info['embedded']['witness_program']), 64)
94+
assert('pubkeys' in info['embedded'])
95+
elif typ == 'bech32':
96+
# P2WSH-multisig
97+
assert(info['isscript'])
98+
assert_equal(info['script'], 'multisig')
99+
assert(info['iswitness'])
100+
assert_equal(info['witness_version'], 0)
101+
assert_equal(len(info['witness_program']), 64)
102+
assert('pubkeys' in info)
103+
else:
104+
# Unknown type
105+
assert(False)
106+
107+
def run_test(self):
108+
# Mine 101 blocks on node4 to bring nodes out of IBD and make sure that
109+
# no coinbases are maturing for the nodes-under-test during the test
110+
self.nodes[4].generate(101)
111+
sync_blocks(self.nodes)
112+
113+
uncompressed_1 = "0496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858ee"
114+
uncompressed_2 = "047211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073dee6c89064984f03385237d92167c13e236446b417ab79a0fcae412ae3316b77"
115+
compressed_1 = "0296b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52"
116+
compressed_2 = "037211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073"
117+
118+
# addmultisigaddress with at least 1 uncompressed key should return a legacy address.
119+
for node in range(4):
120+
self.test_address(node, self.nodes[node].addmultisigaddress(2, [uncompressed_1, uncompressed_2]), True, 'legacy')
121+
self.test_address(node, self.nodes[node].addmultisigaddress(2, [compressed_1, uncompressed_2]), True, 'legacy')
122+
self.test_address(node, self.nodes[node].addmultisigaddress(2, [uncompressed_1, compressed_2]), True, 'legacy')
123+
# addmultisigaddress with all compressed keys should return the appropriate address type (even when the keys are not ours).
124+
self.test_address(0, self.nodes[0].addmultisigaddress(2, [compressed_1, compressed_2]), True, 'legacy')
125+
self.test_address(1, self.nodes[1].addmultisigaddress(2, [compressed_1, compressed_2]), True, 'p2sh-segwit')
126+
self.test_address(2, self.nodes[2].addmultisigaddress(2, [compressed_1, compressed_2]), True, 'p2sh-segwit')
127+
self.test_address(3, self.nodes[3].addmultisigaddress(2, [compressed_1, compressed_2]), True, 'bech32')
128+
129+
for explicit_type, multisig, from_node in itertools.product([False, True], [False, True], range(4)):
130+
address_type = None
131+
if explicit_type and not multisig:
132+
if from_node == 1:
133+
address_type = 'bech32'
134+
elif from_node == 0 or from_node == 3:
135+
address_type = 'p2sh-segwit'
136+
else:
137+
address_type = 'legacy'
138+
self.log.info("Sending from node {} ({}) with{} multisig using {}".format(from_node, self.extra_args[from_node], "" if multisig else "out", "default" if address_type is None else address_type))
139+
old_balances = self.get_balances()
140+
self.log.debug("Old balances are {}".format(old_balances))
141+
to_send = (old_balances[from_node] / 101).quantize(Decimal("0.00000001"))
142+
sends = {}
143+
144+
self.log.debug("Prepare sends")
145+
for n, to_node in enumerate(range(from_node, from_node + 4)):
146+
to_node %= 4
147+
change = False
148+
if not multisig:
149+
if from_node == to_node:
150+
# When sending non-multisig to self, use getrawchangeaddress
151+
address = self.nodes[to_node].getrawchangeaddress(address_type=address_type)
152+
change = True
153+
else:
154+
address = self.nodes[to_node].getnewaddress(address_type=address_type)
155+
else:
156+
addr1 = self.nodes[to_node].getnewaddress()
157+
addr2 = self.nodes[to_node].getnewaddress()
158+
address = self.nodes[to_node].addmultisigaddress(2, [addr1, addr2])
159+
160+
# Do some sanity checking on the created address
161+
if address_type is not None:
162+
typ = address_type
163+
elif to_node == 0:
164+
typ = 'legacy'
165+
elif to_node == 1 or (to_node == 2 and not change):
166+
typ = 'p2sh-segwit'
167+
else:
168+
typ = 'bech32'
169+
self.test_address(to_node, address, multisig, typ)
170+
171+
# Output entry
172+
sends[address] = to_send * 10 * (1 + n)
173+
174+
self.log.debug("Sending: {}".format(sends))
175+
self.nodes[from_node].sendmany("", sends)
176+
sync_mempools(self.nodes)
177+
178+
unconf_balances = self.get_balances(False)
179+
self.log.debug("Check unconfirmed balances: {}".format(unconf_balances))
180+
assert_equal(unconf_balances[from_node], 0)
181+
for n, to_node in enumerate(range(from_node + 1, from_node + 4)):
182+
to_node %= 4
183+
assert_equal(unconf_balances[to_node], to_send * 10 * (2 + n))
184+
185+
# node4 collects fee and block subsidy to keep accounting simple
186+
self.nodes[4].generate(1)
187+
sync_blocks(self.nodes)
188+
189+
new_balances = self.get_balances()
190+
self.log.debug("Check new balances: {}".format(new_balances))
191+
# We don't know what fee was set, so we can only check bounds on the balance of the sending node
192+
assert_greater_than(new_balances[from_node], to_send * 10)
193+
assert_greater_than(to_send * 11, new_balances[from_node])
194+
for n, to_node in enumerate(range(from_node + 1, from_node + 4)):
195+
to_node %= 4
196+
assert_equal(new_balances[to_node], old_balances[to_node] + to_send * 10 * (2 + n))
197+
198+
if __name__ == '__main__':
199+
AddressTypeTest().main()

test/functional/test_runner.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
'abandonconflict.py',
7979
'bip68-112-113-p2p.py',
8080
'rawtransactions.py',
81+
'address_types.py',
8182
'reindex.py',
8283
# vv Tests less than 30s vv
8384
'keypool-topup.py',

0 commit comments

Comments
 (0)