Skip to content

Commit 38d3e9e

Browse files
committed
[qa] Extend import-rescan.py to test imports on pruned nodes.
1 parent c28583d commit 38d3e9e

File tree

1 file changed

+43
-21
lines changed

1 file changed

+43
-21
lines changed

qa/rpc-tests/import-rescan.py

Lines changed: 43 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,8 @@
55
"""Test rescan behavior of importaddress, importpubkey, importprivkey, and
66
importmulti RPCs with different types of keys and rescan options.
77
8-
Test uses three connected nodes.
9-
108
In the first part of the test, node 0 creates an address for each type of
11-
import RPC call and sends BTC to it. Then nodes 1 and 2 import the addresses,
9+
import RPC call and sends BTC to it. Then other nodes import the addresses,
1210
and the test makes listtransactions and getbalance calls to confirm that the
1311
importing node either did or did not execute rescans picking up the send
1412
transactions.
@@ -19,6 +17,7 @@
1917
happened previously.
2018
"""
2119

20+
from test_framework.authproxy import JSONRPCException
2221
from test_framework.test_framework import BitcoinTestFramework
2322
from test_framework.util import (start_nodes, connect_nodes, sync_blocks, assert_equal, set_node_times)
2423
from decimal import Decimal
@@ -32,20 +31,24 @@
3231
Rescan = enum.Enum("Rescan", "no yes late_timestamp")
3332

3433

35-
class Variant(collections.namedtuple("Variant", "call data rescan")):
34+
class Variant(collections.namedtuple("Variant", "call data rescan prune")):
3635
"""Helper for importing one key and verifying scanned transactions."""
3736

3837
def do_import(self, timestamp):
3938
"""Call one key import RPC."""
4039

4140
if self.call == Call.single:
4241
if self.data == Data.address:
43-
response = self.node.importaddress(self.address["address"], self.label, self.rescan == Rescan.yes)
42+
response, error = try_rpc(self.node.importaddress, self.address["address"], self.label,
43+
self.rescan == Rescan.yes)
4444
elif self.data == Data.pub:
45-
response = self.node.importpubkey(self.address["pubkey"], self.label, self.rescan == Rescan.yes)
45+
response, error = try_rpc(self.node.importpubkey, self.address["pubkey"], self.label,
46+
self.rescan == Rescan.yes)
4647
elif self.data == Data.priv:
47-
response = self.node.importprivkey(self.key, self.label, self.rescan == Rescan.yes)
48+
response, error = try_rpc(self.node.importprivkey, self.key, self.label, self.rescan == Rescan.yes)
4849
assert_equal(response, None)
50+
assert_equal(error, {'message': 'Rescan is disabled in pruned mode',
51+
'code': -4} if self.expect_disabled else None)
4952
elif self.call == Call.multi:
5053
response = self.node.importmulti([{
5154
"scriptPubKey": {
@@ -85,16 +88,29 @@ def check(self, txid=None, amount=None, confirmations=None):
8588

8689

8790
# List of Variants for each way a key or address could be imported.
88-
IMPORT_VARIANTS = [Variant(*variants) for variants in itertools.product(Call, Data, Rescan)]
91+
IMPORT_VARIANTS = [Variant(*variants) for variants in itertools.product(Call, Data, Rescan, (False, True))]
92+
93+
# List of nodes to import keys to. Half the nodes will have pruning disabled,
94+
# half will have it enabled. Different nodes will be used for imports that are
95+
# expected to cause rescans, and imports that are not expected to cause
96+
# rescans, in order to prevent rescans during later imports picking up
97+
# transactions associated with earlier imports. This makes it easier to keep
98+
# track of expected balances and transactions.
99+
ImportNode = collections.namedtuple("ImportNode", "prune rescan")
100+
IMPORT_NODES = [ImportNode(*fields) for fields in itertools.product((False, True), repeat=2)]
89101

90102

91103
class ImportRescanTest(BitcoinTestFramework):
92104
def __init__(self):
93105
super().__init__()
94-
self.num_nodes = 3
106+
self.num_nodes = 1 + len(IMPORT_NODES)
95107

96108
def setup_network(self):
97109
extra_args = [["-debug=1"] for _ in range(self.num_nodes)]
110+
for i, import_node in enumerate(IMPORT_NODES, 1):
111+
if import_node.prune:
112+
extra_args[i] += ["-prune=1"]
113+
98114
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args)
99115
for i in range(1, self.num_nodes):
100116
connect_nodes(self.nodes[i], 0)
@@ -119,17 +135,13 @@ def run_test(self):
119135
sync_blocks(self.nodes)
120136

121137
# For each variation of wallet key import, invoke the import RPC and
122-
# check the results from getbalance and listtransactions. Import to
123-
# node 1 if rescanning is expected, and to node 2 if rescanning is not
124-
# expected. Node 2 is reserved for imports that do not cause rescans,
125-
# so later import calls don't inadvertently cause the wallet to pick up
126-
# transactions from earlier import calls where a rescan was not
127-
# expected (this would make it complicated to figure out expected
128-
# balances in the second part of the test.)
138+
# check the results from getbalance and listtransactions.
129139
for variant in IMPORT_VARIANTS:
130-
variant.node = self.nodes[1 if variant.rescan == Rescan.yes else 2]
140+
variant.expect_disabled = variant.rescan == Rescan.yes and variant.prune and variant.call == Call.single
141+
expect_rescan = variant.rescan == Rescan.yes and not variant.expect_disabled
142+
variant.node = self.nodes[1 + IMPORT_NODES.index(ImportNode(variant.prune, expect_rescan))]
131143
variant.do_import(timestamp)
132-
if variant.rescan == Rescan.yes:
144+
if expect_rescan:
133145
variant.expected_balance = variant.initial_amount
134146
variant.expected_txs = 1
135147
variant.check(variant.initial_txid, variant.initial_amount, 2)
@@ -151,9 +163,19 @@ def run_test(self):
151163

152164
# Check the latest results from getbalance and listtransactions.
153165
for variant in IMPORT_VARIANTS:
154-
variant.expected_balance += variant.sent_amount
155-
variant.expected_txs += 1
156-
variant.check(variant.sent_txid, variant.sent_amount, 1)
166+
if not variant.expect_disabled:
167+
variant.expected_balance += variant.sent_amount
168+
variant.expected_txs += 1
169+
variant.check(variant.sent_txid, variant.sent_amount, 1)
170+
else:
171+
variant.check()
172+
173+
174+
def try_rpc(func, *args, **kwargs):
175+
try:
176+
return func(*args, **kwargs), None
177+
except JSONRPCException as e:
178+
return None, e.error
157179

158180

159181
if __name__ == "__main__":

0 commit comments

Comments
 (0)