55"""Test rescan behavior of importaddress, importpubkey, importprivkey, and
66importmulti RPCs with different types of keys and rescan options.
77
8- Test uses three connected nodes.
9-
108In 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,
1210and the test makes listtransactions and getbalance calls to confirm that the
1311importing node either did or did not execute rescans picking up the send
1412transactions.
1917happened previously.
2018"""
2119
20+ from test_framework .authproxy import JSONRPCException
2221from test_framework .test_framework import BitcoinTestFramework
2322from test_framework .util import (start_nodes , connect_nodes , sync_blocks , assert_equal , set_node_times )
2423from decimal import Decimal
3231Rescan = 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
91103class 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
159181if __name__ == "__main__" :
0 commit comments