20
20
"""
21
21
22
22
from test_framework .test_framework import BitcoinTestFramework
23
+ from test_framework .address import AddressType
23
24
from test_framework .util import (
24
25
connect_nodes ,
25
26
assert_equal ,
26
27
set_node_times ,
27
28
)
28
29
29
30
import collections
31
+ from decimal import Decimal
30
32
import enum
31
33
import itertools
34
+ import random
32
35
33
36
Call = enum .Enum ("Call" , "single multiaddress multiscript" )
34
37
Data = enum .Enum ("Data" , "address pub priv" )
35
38
Rescan = enum .Enum ("Rescan" , "no yes late_timestamp" )
36
39
37
40
38
- class Variant (collections .namedtuple ("Variant" , "call data rescan prune" )):
41
+ class Variant (collections .namedtuple ("Variant" , "call data address_type rescan prune" )):
39
42
"""Helper for importing one key and verifying scanned transactions."""
40
43
41
44
def do_import (self , timestamp ):
42
45
"""Call one key import RPC."""
43
46
rescan = self .rescan == Rescan .yes
44
47
48
+ assert_equal (self .address ["solvable" ], True )
49
+ assert_equal (self .address ["isscript" ], self .address_type == AddressType .p2sh_segwit )
50
+ assert_equal (self .address ["iswitness" ], self .address_type == AddressType .bech32 )
51
+ if self .address ["isscript" ]:
52
+ assert_equal (self .address ["embedded" ]["isscript" ], False )
53
+ assert_equal (self .address ["embedded" ]["iswitness" ], True )
54
+
45
55
if self .call == Call .single :
46
56
if self .data == Data .address :
47
57
response = self .node .importaddress (address = self .address ["address" ], label = self .label , rescan = rescan )
@@ -52,7 +62,7 @@ def do_import(self, timestamp):
52
62
assert_equal (response , None )
53
63
54
64
elif self .call in (Call .multiaddress , Call .multiscript ):
55
- response = self . node . importmulti ([ {
65
+ request = {
56
66
"scriptPubKey" : {
57
67
"address" : self .address ["address" ]
58
68
} if self .call == Call .multiaddress else self .address ["scriptPubKey" ],
@@ -61,13 +71,21 @@ def do_import(self, timestamp):
61
71
"keys" : [self .key ] if self .data == Data .priv else [],
62
72
"label" : self .label ,
63
73
"watchonly" : self .data != Data .priv
64
- }], {"rescan" : self .rescan in (Rescan .yes , Rescan .late_timestamp )})
74
+ }
75
+ if self .address_type == AddressType .p2sh_segwit and self .data != Data .address :
76
+ # We need solving data when providing a pubkey or privkey as data
77
+ request .update ({"redeemscript" : self .address ['embedded' ]['scriptPubKey' ]})
78
+ response = self .node .importmulti (
79
+ requests = [request ],
80
+ options = {"rescan" : self .rescan in (Rescan .yes , Rescan .late_timestamp )},
81
+ )
65
82
assert_equal (response , [{"success" : True }])
66
83
67
- def check (self , txid = None , amount = None , confirmations = None ):
84
+ def check (self , txid = None , amount = None , confirmation_height = None ):
68
85
"""Verify that listtransactions/listreceivedbyaddress return expected values."""
69
86
70
87
txs = self .node .listtransactions (label = self .label , count = 10000 , include_watchonly = True )
88
+ current_height = self .node .getblockcount ()
71
89
assert_equal (len (txs ), self .expected_txs )
72
90
73
91
addresses = self .node .listreceivedbyaddress (minconf = 0 , include_watchonly = True , address_filter = self .address ['address' ])
@@ -82,13 +100,13 @@ def check(self, txid=None, amount=None, confirmations=None):
82
100
assert_equal (tx ["category" ], "receive" )
83
101
assert_equal (tx ["label" ], self .label )
84
102
assert_equal (tx ["txid" ], txid )
85
- assert_equal (tx ["confirmations" ], confirmations )
103
+ assert_equal (tx ["confirmations" ], 1 + current_height - confirmation_height )
86
104
assert_equal ("trusted" not in tx , True )
87
105
88
106
address , = [ad for ad in addresses if txid in ad ["txids" ]]
89
107
assert_equal (address ["address" ], self .address ["address" ])
90
108
assert_equal (address ["amount" ], self .expected_balance )
91
- assert_equal (address ["confirmations" ], confirmations )
109
+ assert_equal (address ["confirmations" ], 1 + current_height - confirmation_height )
92
110
# Verify the transaction is correctly marked watchonly depending on
93
111
# whether the transaction pays to an imported public key or
94
112
# imported private key. The test setup ensures that transaction
@@ -102,7 +120,7 @@ def check(self, txid=None, amount=None, confirmations=None):
102
120
103
121
104
122
# List of Variants for each way a key or address could be imported.
105
- IMPORT_VARIANTS = [Variant (* variants ) for variants in itertools .product (Call , Data , Rescan , (False , True ))]
123
+ IMPORT_VARIANTS = [Variant (* variants ) for variants in itertools .product (Call , Data , AddressType , Rescan , (False , True ))]
106
124
107
125
# List of nodes to import keys to. Half the nodes will have pruning disabled,
108
126
# half will have it enabled. Different nodes will be used for imports that are
@@ -116,6 +134,13 @@ def check(self, txid=None, amount=None, confirmations=None):
116
134
# Rescans start at the earliest block up to 2 hours before the key timestamp.
117
135
TIMESTAMP_WINDOW = 2 * 60 * 60
118
136
137
+ AMOUNT_DUST = 0.00000546
138
+
139
+
140
+ def get_rand_amount ():
141
+ r = random .uniform (AMOUNT_DUST , 1 )
142
+ return Decimal (str (round (r , 8 )))
143
+
119
144
120
145
class ImportRescanTest (BitcoinTestFramework ):
121
146
def set_test_params (self ):
@@ -125,12 +150,12 @@ def skip_test_if_missing_module(self):
125
150
self .skip_if_no_wallet ()
126
151
127
152
def setup_network (self ):
128
- extra_args = [["-addresstype=legacy" ] for _ in range ( self .num_nodes )]
153
+ self . extra_args = [[]] * self .num_nodes
129
154
for i , import_node in enumerate (IMPORT_NODES , 2 ):
130
155
if import_node .prune :
131
- extra_args [i ] += ["-prune=1" ]
156
+ self . extra_args [i ] += ["-prune=1" ]
132
157
133
- self .add_nodes (self .num_nodes , extra_args = extra_args )
158
+ self .add_nodes (self .num_nodes , extra_args = self . extra_args )
134
159
135
160
# Import keys with pruning disabled
136
161
self .start_nodes (extra_args = [[]] * self .num_nodes )
@@ -147,17 +172,23 @@ def run_test(self):
147
172
# each possible type of wallet import RPC.
148
173
for i , variant in enumerate (IMPORT_VARIANTS ):
149
174
variant .label = "label {} {}" .format (i , variant )
150
- variant .address = self .nodes [1 ].getaddressinfo (self .nodes [1 ].getnewaddress (variant .label ))
175
+ variant .address = self .nodes [1 ].getaddressinfo (self .nodes [1 ].getnewaddress (
176
+ label = variant .label ,
177
+ address_type = variant .address_type .value ,
178
+ ))
151
179
variant .key = self .nodes [1 ].dumpprivkey (variant .address ["address" ])
152
- variant .initial_amount = 1 - ( i + 1 ) / 64
180
+ variant .initial_amount = get_rand_amount ()
153
181
variant .initial_txid = self .nodes [0 ].sendtoaddress (variant .address ["address" ], variant .initial_amount )
182
+ self .nodes [0 ].generate (1 ) # Generate one block for each send
183
+ variant .confirmation_height = self .nodes [0 ].getblockcount ()
184
+ variant .timestamp = self .nodes [0 ].getblockheader (self .nodes [0 ].getbestblockhash ())["time" ]
154
185
155
- # Generate a block containing the initial transactions, then another
156
- # block further in the future (past the rescan window).
157
- self .nodes [0 ].generate (1 )
186
+ # Generate a block further in the future (past the rescan window).
158
187
assert_equal (self .nodes [0 ].getrawmempool (), [])
159
- timestamp = self .nodes [0 ].getblockheader (self .nodes [0 ].getbestblockhash ())["time" ]
160
- set_node_times (self .nodes , timestamp + TIMESTAMP_WINDOW + 1 )
188
+ set_node_times (
189
+ self .nodes ,
190
+ self .nodes [0 ].getblockheader (self .nodes [0 ].getbestblockhash ())["time" ] + TIMESTAMP_WINDOW + 1 ,
191
+ )
161
192
self .nodes [0 ].generate (1 )
162
193
self .sync_all ()
163
194
@@ -167,23 +198,23 @@ def run_test(self):
167
198
self .log .info ('Run import for variant {}' .format (variant ))
168
199
expect_rescan = variant .rescan == Rescan .yes
169
200
variant .node = self .nodes [2 + IMPORT_NODES .index (ImportNode (variant .prune , expect_rescan ))]
170
- variant .do_import (timestamp )
201
+ variant .do_import (variant . timestamp )
171
202
if expect_rescan :
172
203
variant .expected_balance = variant .initial_amount
173
204
variant .expected_txs = 1
174
- variant .check (variant .initial_txid , variant .initial_amount , 2 )
205
+ variant .check (variant .initial_txid , variant .initial_amount , variant . confirmation_height )
175
206
else :
176
207
variant .expected_balance = 0
177
208
variant .expected_txs = 0
178
209
variant .check ()
179
210
180
211
# Create new transactions sending to each address.
181
212
for i , variant in enumerate (IMPORT_VARIANTS ):
182
- variant .sent_amount = 1 - ( 2 * i + 1 ) / 128
213
+ variant .sent_amount = get_rand_amount ()
183
214
variant .sent_txid = self .nodes [0 ].sendtoaddress (variant .address ["address" ], variant .sent_amount )
215
+ self .nodes [0 ].generate (1 ) # Generate one block for each send
216
+ variant .confirmation_height = self .nodes [0 ].getblockcount ()
184
217
185
- # Generate a block containing the new transactions.
186
- self .nodes [0 ].generate (1 )
187
218
assert_equal (self .nodes [0 ].getrawmempool (), [])
188
219
self .sync_all ()
189
220
@@ -192,7 +223,7 @@ def run_test(self):
192
223
self .log .info ('Run check for variant {}' .format (variant ))
193
224
variant .expected_balance += variant .sent_amount
194
225
variant .expected_txs += 1
195
- variant .check (variant .sent_txid , variant .sent_amount , 1 )
226
+ variant .check (variant .sent_txid , variant .sent_amount , variant . confirmation_height )
196
227
197
228
if __name__ == "__main__" :
198
229
ImportRescanTest ().main ()
0 commit comments