5
5
"""Test rescan behavior of importaddress, importpubkey, importprivkey, and
6
6
importmulti RPCs with different types of keys and rescan options.
7
7
8
- Test uses three connected nodes.
9
-
10
8
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,
12
10
and the test makes listtransactions and getbalance calls to confirm that the
13
11
importing node either did or did not execute rescans picking up the send
14
12
transactions.
19
17
happened previously.
20
18
"""
21
19
20
+ from test_framework .authproxy import JSONRPCException
22
21
from test_framework .test_framework import BitcoinTestFramework
23
22
from test_framework .util import (start_nodes , connect_nodes , sync_blocks , assert_equal , set_node_times )
24
23
from decimal import Decimal
32
31
Rescan = enum .Enum ("Rescan" , "no yes late_timestamp" )
33
32
34
33
35
- class Variant (collections .namedtuple ("Variant" , "call data rescan" )):
34
+ class Variant (collections .namedtuple ("Variant" , "call data rescan prune " )):
36
35
"""Helper for importing one key and verifying scanned transactions."""
37
36
38
37
def do_import (self , timestamp ):
39
38
"""Call one key import RPC."""
40
39
41
40
if self .call == Call .single :
42
41
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 )
44
44
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 )
46
47
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 )
48
49
assert_equal (response , None )
50
+ assert_equal (error , {'message' : 'Rescan is disabled in pruned mode' ,
51
+ 'code' : - 4 } if self .expect_disabled else None )
49
52
elif self .call == Call .multi :
50
53
response = self .node .importmulti ([{
51
54
"scriptPubKey" : {
@@ -85,16 +88,29 @@ def check(self, txid=None, amount=None, confirmations=None):
85
88
86
89
87
90
# 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 )]
89
101
90
102
91
103
class ImportRescanTest (BitcoinTestFramework ):
92
104
def __init__ (self ):
93
105
super ().__init__ ()
94
- self .num_nodes = 3
106
+ self .num_nodes = 1 + len ( IMPORT_NODES )
95
107
96
108
def setup_network (self ):
97
109
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
+
98
114
self .nodes = start_nodes (self .num_nodes , self .options .tmpdir , extra_args )
99
115
for i in range (1 , self .num_nodes ):
100
116
connect_nodes (self .nodes [i ], 0 )
@@ -119,17 +135,13 @@ def run_test(self):
119
135
sync_blocks (self .nodes )
120
136
121
137
# 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.
129
139
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 ))]
131
143
variant .do_import (timestamp )
132
- if variant . rescan == Rescan . yes :
144
+ if expect_rescan :
133
145
variant .expected_balance = variant .initial_amount
134
146
variant .expected_txs = 1
135
147
variant .check (variant .initial_txid , variant .initial_amount , 2 )
@@ -151,9 +163,19 @@ def run_test(self):
151
163
152
164
# Check the latest results from getbalance and listtransactions.
153
165
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
157
179
158
180
159
181
if __name__ == "__main__" :
0 commit comments