wallet_importmulti.py raw
1 #!/usr/bin/env python3
2 # Copyright (c) 2014-2022 The Limenka 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 the importmulti RPC.
6
7 Test importmulti by generating keys on node0, importing the scriptPubKeys and
8 addresses on node1 and then testing the address info for the different address
9 variants.
10
11 - `get_key()` and `get_multisig()` are called to generate keys on node0 and
12 return the privkeys, pubkeys and all variants of scriptPubKey and address.
13 - `test_importmulti()` is called to send an importmulti call to node1, test
14 success, and (if unsuccessful) test the error code and error message returned.
15 - `test_address()` is called to call getaddressinfo for an address on node1
16 and test the values returned."""
17
18 from test_framework.blocktools import COINBASE_MATURITY
19 from test_framework.script import (
20 CScript,
21 OP_NOP,
22 )
23 from test_framework.test_framework import LimenkaTestFramework
24 from test_framework.descriptors import descsum_create
25 from test_framework.util import (
26 assert_equal,
27 assert_greater_than,
28 assert_raises_rpc_error,
29 )
30 from test_framework.wallet_util import (
31 get_key,
32 get_multisig,
33 test_address,
34 )
35
36
37 class ImportMultiTest(LimenkaTestFramework):
38 def add_options(self, parser):
39 self.add_wallet_options(parser, descriptors=False)
40
41 def set_test_params(self):
42 self.num_nodes = 2
43 self.extra_args = [["-addresstype=legacy"], ["-addresstype=legacy"]]
44 self.setup_clean_chain = True
45
46 def skip_test_if_missing_module(self):
47 self.skip_if_no_wallet()
48
49 def setup_network(self):
50 self.setup_nodes()
51
52 def test_importmulti(self, req, success, error_code=None, error_message=None, warnings=None):
53 """Run importmulti and assert success"""
54 if warnings is None:
55 warnings = []
56 result = self.nodes[1].importmulti([req])
57 observed_warnings = []
58 if 'warnings' in result[0]:
59 observed_warnings = result[0]['warnings']
60 assert_equal("\n".join(sorted(warnings)), "\n".join(sorted(observed_warnings)))
61 assert_equal(result[0]['success'], success)
62 if error_code is not None:
63 assert_equal(result[0]['error']['code'], error_code)
64 assert_equal(result[0]['error']['message'], error_message)
65
66 def run_test(self):
67 self.log.info("Mining blocks...")
68 self.generate(self.nodes[0], 1, sync_fun=self.no_op)
69 self.generate(self.nodes[1], 1, sync_fun=self.no_op)
70 timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime']
71
72 node0_address1 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
73
74 # Check only one address
75 assert_equal(node0_address1['ismine'], True)
76
77 # Node 1 sync test
78 assert_equal(self.nodes[1].getblockcount(), 1)
79
80 # Address Test - before import
81 address_info = self.nodes[1].getaddressinfo(node0_address1['address'])
82 assert_equal(address_info['iswatchonly'], False)
83 assert_equal(address_info['ismine'], False)
84
85 # RPC importmulti -----------------------------------------------
86
87 # Limenka Address (implicit non-internal)
88 self.log.info("Should import an address")
89 key = get_key(self.nodes[0])
90 self.test_importmulti({"scriptPubKey": {"address": key.p2pkh_addr},
91 "timestamp": "now"},
92 success=True)
93 test_address(self.nodes[1],
94 key.p2pkh_addr,
95 iswatchonly=True,
96 ismine=False,
97 timestamp=timestamp,
98 ischange=False)
99 watchonly_address = key.p2pkh_addr
100 watchonly_timestamp = timestamp
101
102 self.log.info("Should not import an invalid address")
103 self.test_importmulti({"scriptPubKey": {"address": "not valid address"},
104 "timestamp": "now"},
105 success=False,
106 error_code=-5,
107 error_message='Invalid address \"not valid address\"')
108
109 # ScriptPubKey + internal
110 self.log.info("Should import a scriptPubKey with internal flag")
111 key = get_key(self.nodes[0])
112 self.test_importmulti({"scriptPubKey": key.p2pkh_script,
113 "timestamp": "now",
114 "internal": True},
115 success=True)
116 test_address(self.nodes[1],
117 key.p2pkh_addr,
118 iswatchonly=True,
119 ismine=False,
120 timestamp=timestamp,
121 ischange=True)
122
123 # ScriptPubKey + internal + label
124 self.log.info("Should not allow a label to be specified when internal is true")
125 key = get_key(self.nodes[0])
126 self.test_importmulti({"scriptPubKey": key.p2pkh_script,
127 "timestamp": "now",
128 "internal": True,
129 "label": "Unsuccessful labelling for internal addresses"},
130 success=False,
131 error_code=-8,
132 error_message='Internal addresses should not have a label')
133
134 # Nonstandard scriptPubKey + !internal
135 self.log.info("Should not import a nonstandard scriptPubKey without internal flag")
136 nonstandardScriptPubKey = key.p2pkh_script + CScript([OP_NOP]).hex()
137 key = get_key(self.nodes[0])
138 self.test_importmulti({"scriptPubKey": nonstandardScriptPubKey,
139 "timestamp": "now"},
140 success=False,
141 error_code=-8,
142 error_message='Internal must be set to true for nonstandard scriptPubKey imports.')
143 test_address(self.nodes[1],
144 key.p2pkh_addr,
145 iswatchonly=False,
146 ismine=False,
147 timestamp=None)
148
149 # Address + Public key + !Internal(explicit)
150 self.log.info("Should import an address with public key")
151 key = get_key(self.nodes[0])
152 self.test_importmulti({"scriptPubKey": {"address": key.p2pkh_addr},
153 "timestamp": "now",
154 "pubkeys": [key.pubkey],
155 "internal": False},
156 success=True,
157 warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
158 test_address(self.nodes[1],
159 key.p2pkh_addr,
160 iswatchonly=True,
161 ismine=False,
162 timestamp=timestamp)
163
164 # ScriptPubKey + Public key + internal
165 self.log.info("Should import a scriptPubKey with internal and with public key")
166 key = get_key(self.nodes[0])
167 self.test_importmulti({"scriptPubKey": key.p2pkh_script,
168 "timestamp": "now",
169 "pubkeys": [key.pubkey],
170 "internal": True},
171 success=True,
172 warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
173 test_address(self.nodes[1],
174 key.p2pkh_addr,
175 iswatchonly=True,
176 ismine=False,
177 timestamp=timestamp)
178
179 # Nonstandard scriptPubKey + Public key + !internal
180 self.log.info("Should not import a nonstandard scriptPubKey without internal and with public key")
181 key = get_key(self.nodes[0])
182 self.test_importmulti({"scriptPubKey": nonstandardScriptPubKey,
183 "timestamp": "now",
184 "pubkeys": [key.pubkey]},
185 success=False,
186 error_code=-8,
187 error_message='Internal must be set to true for nonstandard scriptPubKey imports.')
188 test_address(self.nodes[1],
189 key.p2pkh_addr,
190 iswatchonly=False,
191 ismine=False,
192 timestamp=None)
193
194 # Address + Private key + !watchonly
195 self.log.info("Should import an address with private key")
196 key = get_key(self.nodes[0])
197 self.test_importmulti({"scriptPubKey": {"address": key.p2pkh_addr},
198 "timestamp": "now",
199 "keys": [key.privkey]},
200 success=True)
201 test_address(self.nodes[1],
202 key.p2pkh_addr,
203 iswatchonly=False,
204 ismine=True,
205 timestamp=timestamp)
206
207 self.log.info("Should not import an address with private key if is already imported")
208 self.test_importmulti({"scriptPubKey": {"address": key.p2pkh_addr},
209 "timestamp": "now",
210 "keys": [key.privkey]},
211 success=False,
212 error_code=-4,
213 error_message='The wallet already contains the private key for this address or script ("' + key.p2pkh_script + '")')
214
215 # Address + Private key + watchonly
216 self.log.info("Should import an address with private key and with watchonly")
217 key = get_key(self.nodes[0])
218 self.test_importmulti({"scriptPubKey": {"address": key.p2pkh_addr},
219 "timestamp": "now",
220 "keys": [key.privkey],
221 "watchonly": True},
222 success=True,
223 warnings=["All private keys are provided, outputs will be considered spendable. If this is intentional, do not specify the watchonly flag."])
224 test_address(self.nodes[1],
225 key.p2pkh_addr,
226 iswatchonly=False,
227 ismine=True,
228 timestamp=timestamp)
229
230 # ScriptPubKey + Private key + internal
231 self.log.info("Should import a scriptPubKey with internal and with private key")
232 key = get_key(self.nodes[0])
233 self.test_importmulti({"scriptPubKey": key.p2pkh_script,
234 "timestamp": "now",
235 "keys": [key.privkey],
236 "internal": True},
237 success=True)
238 test_address(self.nodes[1],
239 key.p2pkh_addr,
240 iswatchonly=False,
241 ismine=True,
242 timestamp=timestamp)
243
244 # Nonstandard scriptPubKey + Private key + !internal
245 self.log.info("Should not import a nonstandard scriptPubKey without internal and with private key")
246 key = get_key(self.nodes[0])
247 self.test_importmulti({"scriptPubKey": nonstandardScriptPubKey,
248 "timestamp": "now",
249 "keys": [key.privkey]},
250 success=False,
251 error_code=-8,
252 error_message='Internal must be set to true for nonstandard scriptPubKey imports.')
253 test_address(self.nodes[1],
254 key.p2pkh_addr,
255 iswatchonly=False,
256 ismine=False,
257 timestamp=None)
258
259 # P2SH address
260 multisig = get_multisig(self.nodes[0])
261 self.generate(self.nodes[1], COINBASE_MATURITY, sync_fun=self.no_op)
262 self.nodes[1].sendtoaddress(multisig.p2sh_addr, 10.00)
263 self.generate(self.nodes[1], 1, sync_fun=self.no_op)
264 timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime']
265
266 self.log.info("Should import a p2sh")
267 self.test_importmulti({"scriptPubKey": {"address": multisig.p2sh_addr},
268 "timestamp": "now"},
269 success=True)
270 test_address(self.nodes[1],
271 multisig.p2sh_addr,
272 isscript=True,
273 iswatchonly=True,
274 timestamp=timestamp)
275 p2shunspent = self.nodes[1].listunspent(0, 999999, [multisig.p2sh_addr])[0]
276 assert_equal(p2shunspent['spendable'], False)
277 assert_equal(p2shunspent['solvable'], False)
278
279 # P2SH + Redeem script
280 multisig = get_multisig(self.nodes[0])
281 self.generate(self.nodes[1], COINBASE_MATURITY, sync_fun=self.no_op)
282 self.nodes[1].sendtoaddress(multisig.p2sh_addr, 10.00)
283 self.generate(self.nodes[1], 1, sync_fun=self.no_op)
284 timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime']
285
286 self.log.info("Should import a p2sh with respective redeem script")
287 self.test_importmulti({"scriptPubKey": {"address": multisig.p2sh_addr},
288 "timestamp": "now",
289 "redeemscript": multisig.redeem_script},
290 success=True,
291 warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
292 test_address(self.nodes[1],
293 multisig.p2sh_addr, timestamp=timestamp, iswatchonly=True, ismine=False, solvable=True)
294
295 p2shunspent = self.nodes[1].listunspent(0, 999999, [multisig.p2sh_addr])[0]
296 assert_equal(p2shunspent['spendable'], False)
297 assert_equal(p2shunspent['solvable'], True)
298
299 # P2SH + Redeem script + Private Keys + !Watchonly
300 multisig = get_multisig(self.nodes[0])
301 self.generate(self.nodes[1], COINBASE_MATURITY, sync_fun=self.no_op)
302 self.nodes[1].sendtoaddress(multisig.p2sh_addr, 10.00)
303 self.generate(self.nodes[1], 1, sync_fun=self.no_op)
304 timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime']
305
306 self.log.info("Should import a p2sh with respective redeem script and private keys")
307 self.test_importmulti({"scriptPubKey": {"address": multisig.p2sh_addr},
308 "timestamp": "now",
309 "redeemscript": multisig.redeem_script,
310 "keys": multisig.privkeys[0:2]},
311 success=True,
312 warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
313 test_address(self.nodes[1],
314 multisig.p2sh_addr,
315 timestamp=timestamp,
316 ismine=False,
317 iswatchonly=True,
318 solvable=True)
319
320 p2shunspent = self.nodes[1].listunspent(0, 999999, [multisig.p2sh_addr])[0]
321 assert_equal(p2shunspent['spendable'], False)
322 assert_equal(p2shunspent['solvable'], True)
323
324 # P2SH + Redeem script + Private Keys + Watchonly
325 multisig = get_multisig(self.nodes[0])
326 self.generate(self.nodes[1], COINBASE_MATURITY, sync_fun=self.no_op)
327 self.nodes[1].sendtoaddress(multisig.p2sh_addr, 10.00)
328 self.generate(self.nodes[1], 1, sync_fun=self.no_op)
329 timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime']
330
331 self.log.info("Should import a p2sh with respective redeem script and private keys")
332 self.test_importmulti({"scriptPubKey": {"address": multisig.p2sh_addr},
333 "timestamp": "now",
334 "redeemscript": multisig.redeem_script,
335 "keys": multisig.privkeys[0:2],
336 "watchonly": True},
337 success=True)
338 test_address(self.nodes[1],
339 multisig.p2sh_addr,
340 iswatchonly=True,
341 ismine=False,
342 solvable=True,
343 timestamp=timestamp)
344
345 # Address + Public key + !Internal + Wrong pubkey
346 self.log.info("Should not import an address with the wrong public key as non-solvable")
347 key = get_key(self.nodes[0])
348 wrong_key = get_key(self.nodes[0]).pubkey
349 self.test_importmulti({"scriptPubKey": {"address": key.p2pkh_addr},
350 "timestamp": "now",
351 "pubkeys": [wrong_key]},
352 success=True,
353 warnings=["Importing as non-solvable: some required keys are missing. If this is intentional, don't provide any keys, pubkeys, witnessscript, or redeemscript.", "Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
354 test_address(self.nodes[1],
355 key.p2pkh_addr,
356 iswatchonly=True,
357 ismine=False,
358 solvable=False,
359 timestamp=timestamp)
360
361 # ScriptPubKey + Public key + internal + Wrong pubkey
362 self.log.info("Should import a scriptPubKey with internal and with a wrong public key as non-solvable")
363 key = get_key(self.nodes[0])
364 wrong_key = get_key(self.nodes[0]).pubkey
365 self.test_importmulti({"scriptPubKey": key.p2pkh_script,
366 "timestamp": "now",
367 "pubkeys": [wrong_key],
368 "internal": True},
369 success=True,
370 warnings=["Importing as non-solvable: some required keys are missing. If this is intentional, don't provide any keys, pubkeys, witnessscript, or redeemscript.", "Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
371 test_address(self.nodes[1],
372 key.p2pkh_addr,
373 iswatchonly=True,
374 ismine=False,
375 solvable=False,
376 timestamp=timestamp)
377
378 # Address + Private key + !watchonly + Wrong private key
379 self.log.info("Should import an address with a wrong private key as non-solvable")
380 key = get_key(self.nodes[0])
381 wrong_privkey = get_key(self.nodes[0]).privkey
382 self.test_importmulti({"scriptPubKey": {"address": key.p2pkh_addr},
383 "timestamp": "now",
384 "keys": [wrong_privkey]},
385 success=True,
386 warnings=["Importing as non-solvable: some required keys are missing. If this is intentional, don't provide any keys, pubkeys, witnessscript, or redeemscript.", "Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
387 test_address(self.nodes[1],
388 key.p2pkh_addr,
389 iswatchonly=True,
390 ismine=False,
391 solvable=False,
392 timestamp=timestamp)
393
394 # ScriptPubKey + Private key + internal + Wrong private key
395 self.log.info("Should import a scriptPubKey with internal and with a wrong private key as non-solvable")
396 key = get_key(self.nodes[0])
397 wrong_privkey = get_key(self.nodes[0]).privkey
398 self.test_importmulti({"scriptPubKey": key.p2pkh_script,
399 "timestamp": "now",
400 "keys": [wrong_privkey],
401 "internal": True},
402 success=True,
403 warnings=["Importing as non-solvable: some required keys are missing. If this is intentional, don't provide any keys, pubkeys, witnessscript, or redeemscript.", "Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
404 test_address(self.nodes[1],
405 key.p2pkh_addr,
406 iswatchonly=True,
407 ismine=False,
408 solvable=False,
409 timestamp=timestamp)
410
411 # Importing existing watch only address with new timestamp should replace saved timestamp.
412 assert_greater_than(timestamp, watchonly_timestamp)
413 self.log.info("Should replace previously saved watch only timestamp.")
414 self.test_importmulti({"scriptPubKey": {"address": watchonly_address},
415 "timestamp": "now"},
416 success=True)
417 test_address(self.nodes[1],
418 watchonly_address,
419 iswatchonly=True,
420 ismine=False,
421 timestamp=timestamp)
422 watchonly_timestamp = timestamp
423
424 # restart nodes to check for proper serialization/deserialization of watch only address
425 self.stop_nodes()
426 self.start_nodes()
427 test_address(self.nodes[1],
428 watchonly_address,
429 iswatchonly=True,
430 ismine=False,
431 timestamp=watchonly_timestamp)
432
433 # Bad or missing timestamps
434 self.log.info("Should throw on invalid or missing timestamp values")
435 assert_raises_rpc_error(-3, 'Missing required timestamp field for key',
436 self.nodes[1].importmulti, [{"scriptPubKey": key.p2pkh_script}])
437 assert_raises_rpc_error(-3, 'Expected number or "now" timestamp value for key. got type string',
438 self.nodes[1].importmulti, [{
439 "scriptPubKey": key.p2pkh_script,
440 "timestamp": ""
441 }])
442
443 # Import P2WPKH address as watch only
444 self.log.info("Should import a P2WPKH address as watch only")
445 key = get_key(self.nodes[0])
446 self.test_importmulti({"scriptPubKey": {"address": key.p2wpkh_addr},
447 "timestamp": "now"},
448 success=True)
449 test_address(self.nodes[1],
450 key.p2wpkh_addr,
451 iswatchonly=True,
452 solvable=False)
453
454 # Import P2WPKH address with public key but no private key
455 self.log.info("Should import a P2WPKH address and public key as solvable but not spendable")
456 key = get_key(self.nodes[0])
457 self.test_importmulti({"scriptPubKey": {"address": key.p2wpkh_addr},
458 "timestamp": "now",
459 "pubkeys": [key.pubkey]},
460 success=True,
461 warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
462 test_address(self.nodes[1],
463 key.p2wpkh_addr,
464 ismine=False,
465 solvable=True)
466
467 # Import P2WPKH address with key and check it is spendable
468 self.log.info("Should import a P2WPKH address with key")
469 key = get_key(self.nodes[0])
470 self.test_importmulti({"scriptPubKey": {"address": key.p2wpkh_addr},
471 "timestamp": "now",
472 "keys": [key.privkey]},
473 success=True)
474 test_address(self.nodes[1],
475 key.p2wpkh_addr,
476 iswatchonly=False,
477 ismine=True)
478
479 # P2WSH multisig address without scripts or keys
480 multisig = get_multisig(self.nodes[0])
481 self.log.info("Should import a p2wsh multisig as watch only without respective redeem script and private keys")
482 self.test_importmulti({"scriptPubKey": {"address": multisig.p2wsh_addr},
483 "timestamp": "now"},
484 success=True)
485 test_address(self.nodes[1],
486 multisig.p2sh_addr,
487 solvable=False)
488
489 # Same P2WSH multisig address as above, but now with witnessscript + private keys
490 self.log.info("Should import a p2wsh with respective witness script and private keys")
491 self.test_importmulti({"scriptPubKey": {"address": multisig.p2wsh_addr},
492 "timestamp": "now",
493 "witnessscript": multisig.redeem_script,
494 "keys": multisig.privkeys},
495 success=True)
496 test_address(self.nodes[1],
497 multisig.p2sh_addr,
498 solvable=True,
499 ismine=True,
500 sigsrequired=2)
501
502 # P2SH-P2WPKH address with no redeemscript or public or private key
503 key = get_key(self.nodes[0])
504 self.log.info("Should import a p2sh-p2wpkh without redeem script or keys")
505 self.test_importmulti({"scriptPubKey": {"address": key.p2sh_p2wpkh_addr},
506 "timestamp": "now"},
507 success=True)
508 test_address(self.nodes[1],
509 key.p2sh_p2wpkh_addr,
510 solvable=False,
511 ismine=False)
512
513 # P2SH-P2WPKH address + redeemscript + public key with no private key
514 self.log.info("Should import a p2sh-p2wpkh with respective redeem script and pubkey as solvable")
515 self.test_importmulti({"scriptPubKey": {"address": key.p2sh_p2wpkh_addr},
516 "timestamp": "now",
517 "redeemscript": key.p2sh_p2wpkh_redeem_script,
518 "pubkeys": [key.pubkey]},
519 success=True,
520 warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
521 test_address(self.nodes[1],
522 key.p2sh_p2wpkh_addr,
523 solvable=True,
524 ismine=False)
525
526 # P2SH-P2WPKH address + redeemscript + private key
527 key = get_key(self.nodes[0])
528 self.log.info("Should import a p2sh-p2wpkh with respective redeem script and private keys")
529 self.test_importmulti({"scriptPubKey": {"address": key.p2sh_p2wpkh_addr},
530 "timestamp": "now",
531 "redeemscript": key.p2sh_p2wpkh_redeem_script,
532 "keys": [key.privkey]},
533 success=True)
534 test_address(self.nodes[1],
535 key.p2sh_p2wpkh_addr,
536 solvable=True,
537 ismine=True)
538
539 # P2SH-P2WSH multisig + redeemscript with no private key
540 multisig = get_multisig(self.nodes[0])
541 self.log.info("Should import a p2sh-p2wsh with respective redeem script but no private key")
542 self.test_importmulti({"scriptPubKey": {"address": multisig.p2sh_p2wsh_addr},
543 "timestamp": "now",
544 "redeemscript": multisig.p2wsh_script,
545 "witnessscript": multisig.redeem_script},
546 success=True,
547 warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
548 test_address(self.nodes[1],
549 multisig.p2sh_p2wsh_addr,
550 solvable=True,
551 ismine=False)
552
553 # Test importing of a P2SH-P2WPKH address via descriptor + private key
554 key = get_key(self.nodes[0])
555 self.log.info("Should not import a p2sh-p2wpkh address from descriptor without checksum and private key")
556 self.test_importmulti({"desc": "sh(wpkh(" + key.pubkey + "))",
557 "timestamp": "now",
558 "label": "Unsuccessful P2SH-P2WPKH descriptor import",
559 "keys": [key.privkey]},
560 success=False,
561 error_code=-5,
562 error_message="Missing checksum")
563
564 # Test importing of a P2SH-P2WPKH address via descriptor + private key
565 key = get_key(self.nodes[0])
566 p2sh_p2wpkh_label = "Successful P2SH-P2WPKH descriptor import"
567 self.log.info("Should import a p2sh-p2wpkh address from descriptor and private key")
568 self.test_importmulti({"desc": descsum_create("sh(wpkh(" + key.pubkey + "))"),
569 "timestamp": "now",
570 "label": p2sh_p2wpkh_label,
571 "keys": [key.privkey]},
572 success=True)
573 test_address(self.nodes[1],
574 key.p2sh_p2wpkh_addr,
575 solvable=True,
576 ismine=True,
577 labels=[p2sh_p2wpkh_label])
578
579 # Test ranged descriptor fails if range is not specified
580 xpriv = "tprv8ZgxMBicQKsPeuVhWwi6wuMQGfPKi9Li5GtX35jVNknACgqe3CY4g5xgkfDDJcmtF7o1QnxWDRYw4H5P26PXq7sbcUkEqeR4fg3Kxp2tigg"
581 addresses = ["2N7yv4p8G8yEaPddJxY41kPihnWvs39qCMf", "2MsHxyb2JS3pAySeNUsJ7mNnurtpeenDzLA"] # hdkeypath=m/0'/0'/0' and 1'
582 addresses += ["bcrt1qrd3n235cj2czsfmsuvqqpr3lu6lg0ju7scl8gn", "bcrt1qfqeppuvj0ww98r6qghmdkj70tv8qpchehegrg8"] # wpkh subscripts corresponding to the above addresses
583 desc = "sh(wpkh(" + xpriv + "/0'/0'/*'" + "))"
584 self.log.info("Ranged descriptor import should fail without a specified range")
585 self.test_importmulti({"desc": descsum_create(desc),
586 "timestamp": "now"},
587 success=False,
588 error_code=-8,
589 error_message='Descriptor is ranged, please specify the range')
590
591 # Test importing of a ranged descriptor with xpriv
592 self.log.info("Should import the ranged descriptor with specified range as solvable")
593 self.test_importmulti({"desc": descsum_create(desc),
594 "timestamp": "now",
595 "range": 1},
596 success=True)
597 for address in addresses:
598 test_address(self.nodes[1],
599 address,
600 solvable=True,
601 ismine=True)
602
603 self.test_importmulti({"desc": descsum_create(desc), "timestamp": "now", "range": -1},
604 success=False, error_code=-8, error_message='End of range is too high')
605
606 self.test_importmulti({"desc": descsum_create(desc), "timestamp": "now", "range": [-1, 10]},
607 success=False, error_code=-8, error_message='Range should be greater or equal than 0')
608
609 self.test_importmulti({"desc": descsum_create(desc), "timestamp": "now", "range": [(2 << 31 + 1) - 1000000, (2 << 31 + 1)]},
610 success=False, error_code=-8, error_message='End of range is too high')
611
612 self.test_importmulti({"desc": descsum_create(desc), "timestamp": "now", "range": [2, 1]},
613 success=False, error_code=-8, error_message='Range specified as [begin,end] must not have begin after end')
614
615 self.test_importmulti({"desc": descsum_create(desc), "timestamp": "now", "range": [0, 1000001]},
616 success=False, error_code=-8, error_message='Range is too large')
617
618 # Test importing a descriptor containing a WIF private key
619 wif_priv = "cTe1f5rdT8A8DFgVWTjyPwACsDPJM9ff4QngFxUixCSvvbg1x6sh"
620 address = "2MuhcG52uHPknxDgmGPsV18jSHFBnnRgjPg"
621 desc = "sh(wpkh(" + wif_priv + "))"
622 self.log.info("Should import a descriptor with a WIF private key as spendable")
623 self.test_importmulti({"desc": descsum_create(desc),
624 "timestamp": "now"},
625 success=True)
626 test_address(self.nodes[1],
627 address,
628 solvable=True,
629 ismine=True)
630
631 # dump the private key to ensure it matches what was imported
632 privkey = self.nodes[1].dumpprivkey(address)
633 assert_equal(privkey, wif_priv)
634
635 # Test importing of a P2PKH address via descriptor
636 key = get_key(self.nodes[0])
637 p2pkh_label = "P2PKH descriptor import"
638 self.log.info("Should import a p2pkh address from descriptor")
639 self.test_importmulti({"desc": descsum_create("pkh(" + key.pubkey + ")"),
640 "timestamp": "now",
641 "label": p2pkh_label},
642 True,
643 warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
644 test_address(self.nodes[1],
645 key.p2pkh_addr,
646 solvable=True,
647 ismine=False,
648 labels=[p2pkh_label])
649
650 # Test import fails if both desc and scriptPubKey are provided
651 key = get_key(self.nodes[0])
652 self.log.info("Import should fail if both scriptPubKey and desc are provided")
653 self.test_importmulti({"desc": descsum_create("pkh(" + key.pubkey + ")"),
654 "scriptPubKey": {"address": key.p2pkh_addr},
655 "timestamp": "now"},
656 success=False,
657 error_code=-8,
658 error_message='Both a descriptor and a scriptPubKey should not be provided.')
659
660 # Test import fails if neither desc nor scriptPubKey are present
661 key = get_key(self.nodes[0])
662 self.log.info("Import should fail if neither a descriptor nor a scriptPubKey are provided")
663 self.test_importmulti({"timestamp": "now"},
664 success=False,
665 error_code=-8,
666 error_message='Either a descriptor or scriptPubKey must be provided.')
667
668 # Test importing of a multisig via descriptor
669 key1 = get_key(self.nodes[0])
670 key2 = get_key(self.nodes[0])
671 self.log.info("Should import a 1-of-2 bare multisig from descriptor")
672 self.test_importmulti({"desc": descsum_create("multi(1," + key1.pubkey + "," + key2.pubkey + ")"),
673 "timestamp": "now"},
674 success=True,
675 warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
676 self.log.info("Should not treat individual keys from the imported bare multisig as watchonly")
677 test_address(self.nodes[1],
678 key1.p2pkh_addr,
679 ismine=False,
680 iswatchonly=False)
681
682 # Import pubkeys with key origin info
683 self.log.info("Addresses should have hd keypath and master key id after import with key origin")
684 pub_addr = self.nodes[1].getnewaddress()
685 pub_addr = self.nodes[1].getnewaddress(address_type="bech32")
686 info = self.nodes[1].getaddressinfo(pub_addr)
687 pub = info['pubkey']
688 pub_keypath = info['hdkeypath']
689 pub_fpr = info['hdmasterfingerprint']
690 result = self.nodes[0].importmulti(
691 [{
692 'desc' : descsum_create("wpkh([" + pub_fpr + pub_keypath[1:] +"]" + pub + ")"),
693 "timestamp": "now",
694 }]
695 )
696 assert result[0]['success']
697 pub_import_info = self.nodes[0].getaddressinfo(pub_addr)
698 assert_equal(pub_import_info['hdmasterfingerprint'], pub_fpr)
699 assert_equal(pub_import_info['pubkey'], pub)
700 assert_equal(pub_import_info['hdkeypath'], pub_keypath)
701
702 # Import privkeys with key origin info
703 priv_addr = self.nodes[1].getnewaddress(address_type="bech32")
704 info = self.nodes[1].getaddressinfo(priv_addr)
705 priv = self.nodes[1].dumpprivkey(priv_addr)
706 priv_keypath = info['hdkeypath']
707 priv_fpr = info['hdmasterfingerprint']
708 result = self.nodes[0].importmulti(
709 [{
710 'desc' : descsum_create("wpkh([" + priv_fpr + priv_keypath[1:] + "]" + priv + ")"),
711 "timestamp": "now",
712 }]
713 )
714 assert result[0]['success']
715 priv_import_info = self.nodes[0].getaddressinfo(priv_addr)
716 assert_equal(priv_import_info['hdmasterfingerprint'], priv_fpr)
717 assert_equal(priv_import_info['hdkeypath'], priv_keypath)
718
719 # Make sure the key origin info are still there after a restart
720 self.stop_nodes()
721 self.start_nodes()
722 import_info = self.nodes[0].getaddressinfo(pub_addr)
723 assert_equal(import_info['hdmasterfingerprint'], pub_fpr)
724 assert_equal(import_info['hdkeypath'], pub_keypath)
725 import_info = self.nodes[0].getaddressinfo(priv_addr)
726 assert_equal(import_info['hdmasterfingerprint'], priv_fpr)
727 assert_equal(import_info['hdkeypath'], priv_keypath)
728
729 # Check legacy import does not import key origin info
730 self.log.info("Legacy imports don't have key origin info")
731 pub_addr = self.nodes[1].getnewaddress()
732 info = self.nodes[1].getaddressinfo(pub_addr)
733 pub = info['pubkey']
734 result = self.nodes[0].importmulti(
735 [{
736 'scriptPubKey': {'address': pub_addr},
737 'pubkeys': [pub],
738 "timestamp": "now",
739 }]
740 )
741 assert result[0]['success']
742 pub_import_info = self.nodes[0].getaddressinfo(pub_addr)
743 assert_equal(pub_import_info['pubkey'], pub)
744 assert 'hdmasterfingerprint' not in pub_import_info
745 assert 'hdkeypath' not in pub_import_info
746
747 # Bech32m addresses and descriptors cannot be imported
748 self.log.info("Bech32m addresses and descriptors cannot be imported")
749 self.test_importmulti(
750 {
751 "scriptPubKey": {"address": "bcrt1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqc8gma6"},
752 "timestamp": "now",
753 },
754 success=False,
755 error_code=-5,
756 error_message="Bech32m addresses cannot be imported into legacy wallets",
757 )
758 self.test_importmulti(
759 {
760 "desc": descsum_create("tr({})".format(pub)),
761 "timestamp": "now",
762 },
763 success=False,
764 error_code=-5,
765 error_message="Bech32m descriptors cannot be imported into legacy wallets",
766 )
767
768 # Import some public keys to the keypool of a no privkey wallet
769 self.log.info("Adding pubkey to keypool of disableprivkey wallet")
770 self.nodes[1].createwallet(wallet_name="noprivkeys", disable_private_keys=True)
771 wrpc = self.nodes[1].get_wallet_rpc("noprivkeys")
772
773 addr1 = self.nodes[0].getnewaddress(address_type="bech32")
774 addr2 = self.nodes[0].getnewaddress(address_type="bech32")
775 pub1 = self.nodes[0].getaddressinfo(addr1)['pubkey']
776 pub2 = self.nodes[0].getaddressinfo(addr2)['pubkey']
777 result = wrpc.importmulti(
778 [{
779 'desc': descsum_create('wpkh(' + pub1 + ')'),
780 'keypool': True,
781 "timestamp": "now",
782 },
783 {
784 'desc': descsum_create('wpkh(' + pub2 + ')'),
785 'keypool': True,
786 "timestamp": "now",
787 }]
788 )
789 assert result[0]['success']
790 assert result[1]['success']
791 assert_equal(wrpc.getwalletinfo()["keypoolsize"], 2)
792 newaddr1 = wrpc.getnewaddress(address_type="bech32")
793 assert_equal(addr1, newaddr1)
794 newaddr2 = wrpc.getnewaddress(address_type="bech32")
795 assert_equal(addr2, newaddr2)
796
797 # Import some public keys to the internal keypool of a no privkey wallet
798 self.log.info("Adding pubkey to internal keypool of disableprivkey wallet")
799 addr1 = self.nodes[0].getnewaddress(address_type="bech32")
800 addr2 = self.nodes[0].getnewaddress(address_type="bech32")
801 pub1 = self.nodes[0].getaddressinfo(addr1)['pubkey']
802 pub2 = self.nodes[0].getaddressinfo(addr2)['pubkey']
803 result = wrpc.importmulti(
804 [{
805 'desc': descsum_create('wpkh(' + pub1 + ')'),
806 'keypool': True,
807 'internal': True,
808 "timestamp": "now",
809 },
810 {
811 'desc': descsum_create('wpkh(' + pub2 + ')'),
812 'keypool': True,
813 'internal': True,
814 "timestamp": "now",
815 }]
816 )
817 assert result[0]['success']
818 assert result[1]['success']
819 assert_equal(wrpc.getwalletinfo()["keypoolsize_hd_internal"], 2)
820 newaddr1 = wrpc.getrawchangeaddress(address_type="bech32")
821 assert_equal(addr1, newaddr1)
822 newaddr2 = wrpc.getrawchangeaddress(address_type="bech32")
823 assert_equal(addr2, newaddr2)
824
825 # Import a multisig and make sure the keys don't go into the keypool
826 self.log.info('Imported scripts with pubkeys should not have their pubkeys go into the keypool')
827 addr1 = self.nodes[0].getnewaddress(address_type="bech32")
828 addr2 = self.nodes[0].getnewaddress(address_type="bech32")
829 pub1 = self.nodes[0].getaddressinfo(addr1)['pubkey']
830 pub2 = self.nodes[0].getaddressinfo(addr2)['pubkey']
831 result = wrpc.importmulti(
832 [{
833 'desc': descsum_create('wsh(multi(2,' + pub1 + ',' + pub2 + '))'),
834 'keypool': True,
835 "timestamp": "now",
836 }]
837 )
838 assert result[0]['success']
839 assert_equal(wrpc.getwalletinfo()["keypoolsize"], 0)
840
841 # Cannot import those pubkeys to keypool of wallet with privkeys
842 self.log.info("Pubkeys cannot be added to the keypool of a wallet with private keys")
843 wrpc = self.nodes[1].get_wallet_rpc(self.default_wallet_name)
844 assert wrpc.getwalletinfo()['private_keys_enabled']
845 result = wrpc.importmulti(
846 [{
847 'desc': descsum_create('wpkh(' + pub1 + ')'),
848 'keypool': True,
849 "timestamp": "now",
850 }]
851 )
852 assert_equal(result[0]['error']['code'], -8)
853 assert_equal(result[0]['error']['message'], "Keys can only be imported to the keypool when private keys are disabled")
854
855 # Make sure ranged imports import keys in order
856 self.log.info('Key ranges should be imported in order')
857 wrpc = self.nodes[1].get_wallet_rpc("noprivkeys")
858 assert_equal(wrpc.getwalletinfo()["keypoolsize"], 0)
859 assert_equal(wrpc.getwalletinfo()["private_keys_enabled"], False)
860 xpub = "tpubDAXcJ7s7ZwicqjprRaEWdPoHKrCS215qxGYxpusRLLmJuT69ZSicuGdSfyvyKpvUNYBW1s2U3NSrT6vrCYB9e6nZUEvrqnwXPF8ArTCRXMY"
861 addresses = [
862 'bcrt1qtmp74ayg7p24uslctssvjm06q5phz4yrxucgnv', # m/0'/0'/0
863 'bcrt1q8vprchan07gzagd5e6v9wd7azyucksq2xc76k8', # m/0'/0'/1
864 'bcrt1qtuqdtha7zmqgcrr26n2rqxztv5y8rafjp9lulu', # m/0'/0'/2
865 'bcrt1qau64272ymawq26t90md6an0ps99qkrse58m640', # m/0'/0'/3
866 'bcrt1qsg97266hrh6cpmutqen8s4s962aryy77jp0fg0', # m/0'/0'/4
867 ]
868 result = wrpc.importmulti(
869 [{
870 'desc': descsum_create('wpkh([80002067/0h/0h]' + xpub + '/*)'),
871 'keypool': True,
872 'timestamp': 'now',
873 'range' : [0, 4],
874 }]
875 )
876 for i in range(0, 5):
877 addr = wrpc.getnewaddress('', 'bech32')
878 assert_equal(addr, addresses[i])
879
880 # Create wallet with passphrase
881 self.log.info('Test watchonly imports on a wallet with a passphrase, without unlocking')
882 self.nodes[1].createwallet(wallet_name='w1', blank=True, passphrase='pass')
883 wrpc = self.nodes[1].get_wallet_rpc('w1')
884 assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first.",
885 wrpc.importmulti, [{
886 'desc': descsum_create('wpkh(' + pub1 + ')'),
887 "timestamp": "now",
888 }])
889
890 result = wrpc.importmulti(
891 [{
892 'desc': descsum_create('wpkh(' + pub1 + ')'),
893 "timestamp": "now",
894 "watchonly": True,
895 }]
896 )
897 assert result[0]['success']
898
899 self.log.info("Multipath descriptors")
900 self.nodes[1].createwallet(wallet_name="multipath", blank=True, disable_private_keys=True)
901 w_multipath = self.nodes[1].get_wallet_rpc("multipath")
902 self.nodes[1].createwallet(wallet_name="multipath_split", blank=True, disable_private_keys=True)
903 w_multisplit = self.nodes[1].get_wallet_rpc("multipath_split")
904
905 res = w_multipath.importmulti([{"desc": descsum_create(f"wpkh({xpub}/<10;20>/0/*)"),
906 "keypool": True,
907 "range": 10,
908 "timestamp": "now",
909 "internal": True}])
910 assert_equal(res[0]["success"], False)
911 assert_equal(res[0]["error"]["code"], -5)
912 assert_equal(res[0]["error"]["message"], "Cannot have multipath descriptor while also specifying 'internal'")
913
914 res = w_multipath.importmulti([{"desc": descsum_create(f"wpkh({xpub}/<10;20>/0/*)"),
915 "keypool": True,
916 "range": 10,
917 "timestamp": "now"}])
918 assert_equal(res[0]["success"], True)
919
920 res = w_multisplit.importmulti([{"desc": descsum_create(f"wpkh({xpub}/10/0/*)"),
921 "keypool": True,
922 "range": 10,
923 "timestamp": "now"}])
924 assert_equal(res[0]["success"], True)
925 res = w_multisplit.importmulti([{"desc": descsum_create(f"wpkh({xpub}/20/0/*)"),
926 "keypool": True,
927 "range": 10,
928 "internal": True,
929 "timestamp": timestamp}])
930 assert_equal(res[0]["success"], True)
931
932 for _ in range(0, 9):
933 assert_equal(w_multipath.getnewaddress(address_type="bech32"), w_multisplit.getnewaddress(address_type="bech32"))
934 assert_equal(w_multipath.getrawchangeaddress(address_type="bech32"), w_multisplit.getrawchangeaddress(address_type="bech32"))
935
936
937 if __name__ == '__main__':
938 ImportMultiTest(__file__).main()
939