wallet_importdescriptors.py raw

   1  #!/usr/bin/env python3
   2  # Copyright (c) 2019-present The Bitcoin Core 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 importdescriptors RPC.
   6  
   7  Test importdescriptors by generating keys on node0, importing the corresponding
   8  descriptors on node1 and then testing the address info for the different address
   9  variants.
  10  
  11  - `get_generate_key()` is called to generate keys and return the privkeys,
  12    pubkeys and all variants of scriptPubKey and address.
  13  - `test_importdesc()` is called to send an importdescriptors 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  import concurrent.futures
  19  import threading
  20  import time
  21  
  22  from test_framework.address import key_to_p2sh_p2wpkh, key_to_p2wpkh, script_to_p2wsh
  23  from test_framework.blocktools import COINBASE_MATURITY
  24  from test_framework.test_framework import BitcoinTestFramework
  25  from test_framework.descriptors import descsum_create
  26  from test_framework.extendedkey import ExtendedPrivateKey
  27  from test_framework.script import SEQUENCE_LOCKTIME_TYPE_FLAG
  28  from test_framework.script_util import keys_to_multisig_script
  29  from test_framework.util import (
  30      assert_equal,
  31      assert_raises_rpc_error,
  32      JSONRPCException,
  33  )
  34  from test_framework.wallet_util import (
  35      get_generate_key,
  36      test_address,
  37  )
  38  
  39  class ImportDescriptorsTest(BitcoinTestFramework):
  40      def set_test_params(self):
  41          self.num_nodes = 2
  42          # whitelist peers to speed up tx relay / mempool sync
  43          self.noban_tx_relay = True
  44          self.extra_args = [["-addresstype=legacy"],
  45                             ["-addresstype=bech32", "-keypool=5"]
  46                            ]
  47          self.setup_clean_chain = True
  48          self.wallet_names = []
  49  
  50      def skip_test_if_missing_module(self):
  51          self.skip_if_no_wallet()
  52  
  53      def test_importdesc(self, req, success, error_code=None, error_message=None, warnings=None, wallet=None):
  54          """Run importdescriptors and assert success"""
  55          if warnings is None:
  56              warnings = []
  57          wrpc = self.nodes[1].get_wallet_rpc('w1')
  58          if wallet is not None:
  59              wrpc = wallet
  60  
  61          result = wrpc.importdescriptors([req])
  62          observed_warnings = []
  63          if 'warnings' in result[0]:
  64              observed_warnings = result[0]['warnings']
  65          assert_equal("\n".join(sorted(warnings)), "\n".join(sorted(observed_warnings)))
  66          self.log.debug(result)
  67          assert_equal(result[0]['success'], success)
  68          if error_code is not None:
  69              assert_equal(result[0]['error']['code'], error_code)
  70              assert_equal(result[0]['error']['message'], error_message)
  71  
  72      def test_import_unused_key(self):
  73          self.log.info("Test import of unused(KEY)")
  74          self.nodes[0].createwallet(wallet_name="import_unused", blank=True)
  75          wallet = self.nodes[0].get_wallet_rpc("import_unused")
  76  
  77          assert_equal(len(wallet.gethdkeys()), 0)
  78  
  79          extended_key = ExtendedPrivateKey.generate()
  80          xprv = extended_key.to_string()
  81          xpub = extended_key.pubkey().to_string()
  82          self.test_importdesc({"desc":descsum_create(f"unused({xpub})"),
  83                                "timestamp": "now"},
  84                                success=False,
  85                                error_code=-4,
  86                                error_message='Cannot import descriptor without private keys to a wallet with private keys enabled',
  87                                wallet=wallet)
  88          self.test_importdesc({"timestamp": "now", "desc": descsum_create(f"unused({xprv})")},
  89                               success=True,
  90                               wallet=wallet)
  91          hdkeys = wallet.gethdkeys()
  92          assert_equal(len(hdkeys), 1)
  93          assert_equal(hdkeys[0]["xpub"], xpub)
  94          wallet.unloadwallet()
  95  
  96      def test_import_unused_key_existing(self):
  97          self.log.info("Test import of unused(KEY) with existing KEY")
  98          self.nodes[0].createwallet(wallet_name="import_existing_unused")
  99          wallet = self.nodes[0].get_wallet_rpc("import_existing_unused")
 100  
 101          hdkeys = wallet.gethdkeys(private=True)
 102          assert_equal(len(hdkeys), 1)
 103          xprv = hdkeys[0]["xprv"]
 104  
 105          self.test_importdesc({"timestamp": "now", "desc": descsum_create(f"unused({xprv})")},
 106                               success=False,
 107                               error_code=-4,
 108                               error_message="Cannot import an unused() descriptor when its private key is already in the wallet",
 109                               wallet=wallet)
 110          wallet.unloadwallet()
 111  
 112      def test_import_unused_noprivs(self):
 113          self.log.info("Test import of unused(KEY) to wallet without privkeys")
 114          self.nodes[0].createwallet(wallet_name="import_unused_noprivs", disable_private_keys=True)
 115          wallet = self.nodes[0].get_wallet_rpc("import_unused_noprivs")
 116  
 117          xpub = ExtendedPrivateKey.generate().pubkey().to_string()
 118          self.test_importdesc({"timestamp": "now", "desc": descsum_create(f"unused({xpub})")},
 119                               success=False,
 120                               error_code=-4,
 121                               error_message="Cannot import unused() to wallet without private keys enabled",
 122                               wallet=wallet)
 123          wallet.unloadwallet()
 124  
 125      def test_rescan_fails_import(self):
 126          xpriv = ExtendedPrivateKey.generate().to_string()
 127  
 128          self.log.info("Test importdescriptors fails when wallet is already rescanning")
 129          wallet_name = "rescan_wallet"
 130          self.nodes[0].createwallet(wallet_name=wallet_name, blank=True)
 131          other_desc = descsum_create("pkh(" + get_generate_key().privkey + ")")
 132  
 133          w_import = self.nodes[0].create_new_rpc_connection(mode="AUTHPROXY") / f"wallet/{wallet_name}"
 134  
 135          with concurrent.futures.ThreadPoolExecutor(max_workers=2) as thread:
 136              w_rescan = self.nodes[0].create_new_rpc_connection(mode="AUTHPROXY") / f"wallet/{wallet_name}"
 137              w_conflict = self.nodes[0].create_new_rpc_connection(mode="AUTHPROXY") / f"wallet/{wallet_name}"
 138              # Use an xprv with timestamp=0 and a large key-range to trigger a slow full rescan that stays in-flight
 139              slow_desc = [{"desc": descsum_create("pkh(" + xpriv + "/0h/*h)"),
 140              "timestamp": 0, "range": [0, 10000]}]
 141              conflicting_desc = [{"desc": descsum_create("pkh(" + xpriv + "/1h/*h)"),
 142              "timestamp": 0, "range": [0, 10000]}]
 143              num_relevant_blocks = 1000
 144              self.generatetoaddress(self.nodes[0], num_relevant_blocks, self.nodes[0].deriveaddresses(slow_desc[0]['desc'], [0, 0])[0])
 145              self.generatetoaddress(self.nodes[0], num_relevant_blocks, self.nodes[0].deriveaddresses(conflicting_desc[0]['desc'], [0, 0])[0])
 146  
 147              start = threading.Barrier(3)
 148  
 149              def import_after_barrier(wallet, descriptors):
 150                  start.wait(timeout=10)
 151                  return wallet.importdescriptors(descriptors)
 152  
 153              imports = [
 154                  thread.submit(import_after_barrier, w_rescan, slow_desc),
 155                  thread.submit(import_after_barrier, w_conflict, conflicting_desc),
 156              ]
 157              start.wait(timeout=10)
 158  
 159              # One importdescriptor call must hold WalletRescanReserver while the other fails immediately.
 160              num_errors = 0
 161              num_success = 0
 162              for future in concurrent.futures.as_completed(imports, timeout=30 * self.options.timeout_factor):
 163                  try:
 164                      assert_equal(future.result(), [{'success': True}])
 165                      num_success += 1
 166                  except JSONRPCException as e:
 167                      assert_equal(e.error["code"], -4)
 168                      assert_equal(e.error["message"], "Wallet is currently rescanning. Abort existing rescan or wait.")
 169                      num_errors += 1
 170  
 171              assert_equal(num_success, 1)
 172              assert_equal(num_errors, 1)
 173  
 174          # After the rescan finishes, any importdescriptors should succeed.
 175          result = w_import.importdescriptors([{"desc": other_desc, "timestamp": "now"}])
 176          assert_equal(result[0]['success'], True)
 177  
 178          self.log.info("Aborting an importdescriptors rescan should fail the RPC call")
 179          wallet_name = "abort_import_wallet"
 180          self.nodes[0].createwallet(wallet_name, blank=True)
 181  
 182          with concurrent.futures.ThreadPoolExecutor(max_workers=1) as thread:
 183              w_import = self.nodes[0].create_new_rpc_connection(mode="AUTHPROXY") / f"wallet/{wallet_name}"
 184              abort_rpc = self.nodes[0].create_new_rpc_connection(mode="AUTHPROXY") / f"wallet/{wallet_name}"
 185              descriptor = [{"desc": descsum_create("pkh(" + xpriv + "/2h/*h)"),
 186              "timestamp": 0, "range": [0, 4000]}]
 187  
 188              importing = thread.submit(w_import.importdescriptors, descriptor)
 189  
 190              # Keep trying because an abort before ScanForWalletTransactions starts
 191              # is reset when the scan loop begins.
 192              abort_succeeded = False
 193              abort_deadline = time.time() + 30 * self.options.timeout_factor
 194              while not importing.done() and time.time() < abort_deadline:
 195                  abort_succeeded = abort_rpc.abortrescan() or abort_succeeded
 196  
 197              assert_equal(abort_succeeded, True)
 198              try:
 199                  importing.result(timeout=30 * self.options.timeout_factor)
 200                  raise AssertionError("importdescriptors unexpectedly succeeded")
 201              except JSONRPCException as e:
 202                  assert_equal(e.error["code"], -1)
 203                  assert_equal(e.error["message"], "Rescan aborted by user.")
 204  
 205      def run_test(self):
 206          self.log.info('Setting up wallets')
 207          self.nodes[0].createwallet(wallet_name='w0', disable_private_keys=False)
 208          w0 = self.nodes[0].get_wallet_rpc('w0')
 209  
 210          self.nodes[1].createwallet(wallet_name='w1', disable_private_keys=True, blank=True)
 211          w1 = self.nodes[1].get_wallet_rpc('w1')
 212          assert_equal(w1.getwalletinfo()['keypoolsize'], 0)
 213  
 214          self.nodes[1].createwallet(wallet_name="wpriv", disable_private_keys=False, blank=True)
 215          wpriv = self.nodes[1].get_wallet_rpc("wpriv")
 216          assert_equal(wpriv.getwalletinfo()['keypoolsize'], 0)
 217  
 218          self.log.info('Mining coins')
 219          self.generatetoaddress(self.nodes[0], COINBASE_MATURITY + 1, w0.getnewaddress())
 220  
 221          # RPC importdescriptors -----------------------------------------------
 222  
 223          # # Test import fails if no descriptor present
 224          self.log.info("Import should fail if a descriptor is not provided")
 225          self.test_importdesc({"timestamp": "now"},
 226                               success=False,
 227                               error_code=-8,
 228                               error_message='Descriptor not found.')
 229  
 230          # # Test importing of a P2PKH descriptor
 231          key = get_generate_key()
 232          self.log.info("Should import a p2pkh descriptor")
 233          import_request = {"desc": descsum_create("pkh(" + key.pubkey + ")"),
 234                   "timestamp": "now",
 235                   "label": "Descriptor import test"}
 236          self.test_importdesc(import_request, success=True)
 237          test_address(w1,
 238                       key.p2pkh_addr,
 239                       solvable=True,
 240                       ismine=True,
 241                       labels=["Descriptor import test"])
 242          assert_equal(w1.getwalletinfo()['keypoolsize'], 0)
 243  
 244          self.log.info("Test can import same descriptor with public key twice")
 245          self.test_importdesc(import_request, success=True)
 246  
 247          self.log.info("Test can update descriptor label")
 248          self.test_importdesc({**import_request, "label": "Updated label"}, success=True)
 249          test_address(w1, key.p2pkh_addr, solvable=True, ismine=True, labels=["Updated label"])
 250  
 251          self.log.info("Internal addresses cannot have labels")
 252          self.test_importdesc({**import_request, "internal": True},
 253                               success=False,
 254                               error_code=-8,
 255                               error_message="Internal addresses should not have a label")
 256  
 257          self.log.info("External non-ranged addresses can have labels")
 258          self.test_importdesc({**import_request, "internal": False}, success=True)
 259  
 260          self.log.info("Internal addresses should be detected as such")
 261          key = get_generate_key()
 262          self.test_importdesc({"desc": descsum_create("pkh(" + key.pubkey + ")"),
 263                                "timestamp": "now",
 264                                "internal": True},
 265                               success=True)
 266          info = w1.getaddressinfo(key.p2pkh_addr)
 267          assert_equal(info["ismine"], True)
 268          assert_equal(info["ischange"], True)
 269  
 270          self.log.info("Should not import a descriptor with an invalid public key due to whitespace")
 271          self.test_importdesc({"desc": descsum_create("pkh( " + key.pubkey + ")"),
 272                                      "timestamp": "now",
 273                                      "internal": True},
 274                                      error_code=-5,
 275                                      error_message=f"pkh(): Key ' {key.pubkey}' is invalid due to whitespace",
 276                                      success=False)
 277          self.test_importdesc({"desc": descsum_create("pkh(" + key.pubkey + " )"),
 278                                      "timestamp": "now",
 279                                      "internal": True},
 280                                      error_code=-5,
 281                                      error_message=f"pkh(): Key '{key.pubkey} ' is invalid due to whitespace",
 282                                      success=False)
 283  
 284          # # Test importing of a P2SH-P2WPKH descriptor
 285          key = get_generate_key()
 286          self.log.info("Should not import a p2sh-p2wpkh descriptor without checksum")
 287          self.test_importdesc({"desc": "sh(wpkh(" + key.pubkey + "))",
 288                                "timestamp": "now"
 289                                },
 290                               success=False,
 291                               error_code=-5,
 292                               error_message="Missing checksum")
 293  
 294          self.log.info("Should not import a p2sh-p2wpkh descriptor that has range specified")
 295          self.test_importdesc({"desc": descsum_create("sh(wpkh(" + key.pubkey + "))"),
 296                                 "timestamp": "now",
 297                                 "range": 1,
 298                                },
 299                                success=False,
 300                                error_code=-8,
 301                                error_message="Range should not be specified for an un-ranged descriptor")
 302  
 303          self.log.info("Should not import a p2sh-p2wpkh descriptor and have it set to active")
 304          self.test_importdesc({"desc": descsum_create("sh(wpkh(" + key.pubkey + "))"),
 305                                 "timestamp": "now",
 306                                 "active": True,
 307                                },
 308                                success=False,
 309                                error_code=-8,
 310                                error_message="Active descriptors must be ranged")
 311  
 312          self.log.info("Should import a (non-active) p2sh-p2wpkh descriptor")
 313          self.test_importdesc({"desc": descsum_create("sh(wpkh(" + key.pubkey + "))"),
 314                                 "timestamp": "now",
 315                                 "active": False,
 316                                },
 317                                success=True)
 318          assert_equal(w1.getwalletinfo()['keypoolsize'], 0)
 319  
 320          test_address(w1,
 321                       key.p2sh_p2wpkh_addr,
 322                       ismine=True,
 323                       solvable=True)
 324  
 325          # Check persistence of data and that loading works correctly
 326          w1.unloadwallet()
 327          self.nodes[1].loadwallet('w1')
 328          test_address(w1,
 329                       key.p2sh_p2wpkh_addr,
 330                       ismine=True,
 331                       solvable=True)
 332  
 333          # # Test importing of a multisig descriptor
 334          key1 = get_generate_key()
 335          key2 = get_generate_key()
 336          self.log.info("Should import a 1-of-2 bare multisig from descriptor")
 337          self.test_importdesc({"desc": descsum_create("multi(1," + key1.pubkey + "," + key2.pubkey + ")"),
 338                                "timestamp": "now"},
 339                               success=True)
 340          self.log.info("Should not treat individual keys from the imported bare multisig as watchonly")
 341          test_address(w1,
 342                       key1.p2pkh_addr,
 343                       ismine=False)
 344  
 345          # # Test ranged descriptors
 346          extended_key = ExtendedPrivateKey.generate()
 347          xpriv = extended_key.to_string()
 348          xpub = extended_key.pubkey().to_string()
 349          pubkeys = [extended_key.derive_path(f"m/0'/0'/{i}'").pubkey().pubkey.get_bytes() for i in range(2)]
 350          addresses = [key_to_p2sh_p2wpkh(pubkey) for pubkey in pubkeys] # hdkeypath=m/0'/0'/0' and 1'
 351          addresses += [key_to_p2wpkh(pubkey) for pubkey in pubkeys] # wpkh subscripts corresponding to the above addresses
 352          desc = "sh(wpkh(" + xpub + "/0/0/*" + "))"
 353  
 354          self.log.info("Ranged descriptors cannot have labels")
 355          self.test_importdesc({"desc":descsum_create(desc),
 356                                "timestamp": "now",
 357                                "range": [0, 100],
 358                                "label": "test"},
 359                                success=False,
 360                                error_code=-8,
 361                                error_message='Ranged descriptors should not have a label')
 362  
 363          self.log.info("Ranged descriptors cannot have labels - even if range not provided by user and only implied by asterisk (*)")
 364          self.test_importdesc({"desc":descsum_create("wpkh(" + xpub + "/100/0/*)"),
 365                                "timestamp": "now",
 366                                "label": "test",
 367                                "active": True},
 368                                success=False,
 369                                warnings=['Range not given, using default keypool range'],
 370                                error_code=-8,
 371                                error_message='Ranged descriptors should not have a label')
 372  
 373          self.log.info("Private keys required for private keys enabled wallet")
 374          self.test_importdesc({"desc":descsum_create(desc),
 375                                "timestamp": "now",
 376                                "range": [0, 100]},
 377                                success=False,
 378                                error_code=-4,
 379                                error_message='Cannot import descriptor without private keys to a wallet with private keys enabled',
 380                                wallet=wpriv)
 381  
 382          self.log.info("Ranged descriptor import should warn without a specified range")
 383          self.test_importdesc({"desc": descsum_create(desc),
 384                                 "timestamp": "now"},
 385                                success=True,
 386                                warnings=['Range not given, using default keypool range'])
 387          assert_equal(w1.getwalletinfo()['keypoolsize'], 0)
 388  
 389          # # Test importing of a ranged descriptor with xpriv
 390          self.log.info("Should not import a ranged descriptor that includes xpriv into a watch-only wallet")
 391          desc = "sh(wpkh(" + xpriv + "/0'/0'/*'" + "))"
 392          self.test_importdesc({"desc": descsum_create(desc),
 393                                "timestamp": "now",
 394                                "range": 1},
 395                               success=False,
 396                               error_code=-4,
 397                               error_message='Cannot import private keys to a wallet with private keys disabled')
 398  
 399          self.log.info("Should not import a descriptor with hardened derivations when private keys are disabled")
 400          self.test_importdesc({"desc": descsum_create("wpkh(" + xpub + "/1h/*)"),
 401                                "timestamp": "now",
 402                                "range": 1},
 403                               success=False,
 404                               error_code=-4,
 405                               error_message='Cannot expand descriptor. Probably because of hardened derivations without private keys provided')
 406  
 407          for address in addresses:
 408              test_address(w1,
 409                           address,
 410                           ismine=False,
 411                           solvable=False)
 412  
 413          self.test_importdesc({"desc": descsum_create(desc), "timestamp": "now", "range": -1},
 414                                success=False, error_code=-8, error_message='End of range is too high')
 415  
 416          self.test_importdesc({"desc": descsum_create(desc), "timestamp": "now", "range": [-1, 10]},
 417                                success=False, error_code=-8, error_message='Range should be greater or equal than 0')
 418  
 419          self.test_importdesc({"desc": descsum_create(desc), "timestamp": "now", "range": [(2 << 31 + 1) - 1000000, (2 << 31 + 1)]},
 420                                success=False, error_code=-8, error_message='End of range is too high')
 421  
 422          self.test_importdesc({"desc": descsum_create(desc), "timestamp": "now", "range": [2, 1]},
 423                                success=False, error_code=-8, error_message='Range specified as [begin,end] must not have begin after end')
 424  
 425          self.test_importdesc({"desc": descsum_create(desc), "timestamp": "now", "range": [0, 1000001]},
 426                                success=False, error_code=-8, error_message='Range is too large')
 427  
 428          self.log.info("Verify we can only extend descriptor's range")
 429          range_request = {"desc": descsum_create(desc), "timestamp": "now", "range": [5, 10], 'active': True}
 430          self.test_importdesc(range_request, wallet=wpriv, success=True)
 431          assert_equal(wpriv.getwalletinfo()['keypoolsize'], 6)
 432          self.test_importdesc({**range_request, "range": [0, 10]}, wallet=wpriv, success=True)
 433          assert_equal(wpriv.getwalletinfo()['keypoolsize'], 11)
 434          self.test_importdesc({**range_request, "range": [0, 20]}, wallet=wpriv, success=True)
 435          assert_equal(wpriv.getwalletinfo()['keypoolsize'], 21)
 436          # Can keep range the same
 437          self.test_importdesc({**range_request, "range": [0, 20]}, wallet=wpriv, success=True)
 438          assert_equal(wpriv.getwalletinfo()['keypoolsize'], 21)
 439  
 440          self.test_importdesc({**range_request, "range": [5, 10]}, wallet=wpriv, success=False,
 441                               error_code=-4, error_message=f"Could not add descriptor '{range_request['desc']}': new range must include current range = [0,20]")
 442          self.test_importdesc({**range_request, "range": [0, 10]}, wallet=wpriv, success=False,
 443                               error_code=-4, error_message=f"Could not add descriptor '{range_request['desc']}': new range must include current range = [0,20]")
 444          self.test_importdesc({**range_request, "range": [5, 20]}, wallet=wpriv, success=False,
 445                               error_code=-4, error_message=f"Could not add descriptor '{range_request['desc']}': new range must include current range = [0,20]")
 446          assert_equal(wpriv.getwalletinfo()['keypoolsize'], 21)
 447  
 448          self.log.info("Check we can change descriptor internal flag")
 449          self.test_importdesc({**range_request, "range": [0, 20], "internal": True}, wallet=wpriv, success=True)
 450          assert_equal(wpriv.getwalletinfo()['keypoolsize'], 0)
 451          assert_raises_rpc_error(-4, 'This wallet has no available keys', wpriv.getnewaddress, '', 'p2sh-segwit')
 452          assert_equal(wpriv.getwalletinfo()['keypoolsize_hd_internal'], 21)
 453          wpriv.getrawchangeaddress('p2sh-segwit')
 454  
 455          self.test_importdesc({**range_request, "range": [0, 20], "internal": False}, wallet=wpriv, success=True)
 456          assert_equal(wpriv.getwalletinfo()['keypoolsize'], 21)
 457          wpriv.getnewaddress('', 'p2sh-segwit')
 458          assert_equal(wpriv.getwalletinfo()['keypoolsize_hd_internal'], 0)
 459          assert_raises_rpc_error(-4, 'This wallet has no available keys', wpriv.getrawchangeaddress, 'p2sh-segwit')
 460  
 461          # Make sure ranged imports import keys in order
 462          w1 = self.nodes[1].get_wallet_rpc('w1')
 463          self.log.info('Key ranges should be imported in order')
 464          xpub = "tpubDAXcJ7s7ZwicqjprRaEWdPoHKrCS215qxGYxpusRLLmJuT69ZSicuGdSfyvyKpvUNYBW1s2U3NSrT6vrCYB9e6nZUEvrqnwXPF8ArTCRXMY"
 465          addresses = [
 466              'bcrt1qtmp74ayg7p24uslctssvjm06q5phz4yrxucgnv', # m/0'/0'/0
 467              'bcrt1q8vprchan07gzagd5e6v9wd7azyucksq2xc76k8', # m/0'/0'/1
 468              'bcrt1qtuqdtha7zmqgcrr26n2rqxztv5y8rafjp9lulu', # m/0'/0'/2
 469              'bcrt1qau64272ymawq26t90md6an0ps99qkrse58m640', # m/0'/0'/3
 470              'bcrt1qsg97266hrh6cpmutqen8s4s962aryy77jp0fg0', # m/0'/0'/4
 471          ]
 472  
 473          self.test_importdesc({'desc': descsum_create('wpkh([80002067/0h/0h]' + xpub + '/*)'),
 474                                'active': True,
 475                                'range' : [0, 2],
 476                                'timestamp': 'now'
 477                               },
 478                               success=True)
 479          self.test_importdesc({'desc': descsum_create('sh(wpkh([abcdef12/0h/0h]' + xpub + '/*))'),
 480                                'active': True,
 481                                'range' : [0, 2],
 482                                'timestamp': 'now'
 483                               },
 484                               success=True)
 485          self.test_importdesc({'desc': descsum_create('pkh([12345678/0h/0h]' + xpub + '/*)'),
 486                                'active': True,
 487                                'range' : [0, 2],
 488                                'timestamp': 'now'
 489                               },
 490                               success=True)
 491  
 492          assert_equal(w1.getwalletinfo()['keypoolsize'], 5 * 3)
 493          for i, expected_addr in enumerate(addresses):
 494              received_addr = w1.getnewaddress('', 'bech32')
 495              assert_raises_rpc_error(-4, 'This wallet has no available keys', w1.getrawchangeaddress, 'bech32')
 496              assert_equal(received_addr, expected_addr)
 497              bech32_addr_info = w1.getaddressinfo(received_addr)
 498              assert_equal(bech32_addr_info['desc'][:23], 'wpkh([80002067/0h/0h/{}]'.format(i))
 499  
 500              shwpkh_addr = w1.getnewaddress('', 'p2sh-segwit')
 501              shwpkh_addr_info = w1.getaddressinfo(shwpkh_addr)
 502              assert_equal(shwpkh_addr_info['desc'][:26], 'sh(wpkh([abcdef12/0h/0h/{}]'.format(i))
 503  
 504              pkh_addr = w1.getnewaddress('', 'legacy')
 505              pkh_addr_info = w1.getaddressinfo(pkh_addr)
 506              assert_equal(pkh_addr_info['desc'][:22], 'pkh([12345678/0h/0h/{}]'.format(i))
 507  
 508              assert_equal(w1.getwalletinfo()['keypoolsize'], 4 * 3) # After retrieving a key, we don't refill the keypool again, so it's one less for each address type
 509          w1.keypoolrefill()
 510          assert_equal(w1.getwalletinfo()['keypoolsize'], 5 * 3)
 511  
 512          self.log.info("Check we can change next_index")
 513          # go back and forth with next_index
 514          for i in [4, 0, 2, 1, 3]:
 515              self.test_importdesc({'desc': descsum_create('wpkh([80002067/0h/0h]' + xpub + '/*)'),
 516                                    'active': True,
 517                                    'range': [0, 9],
 518                                    'next_index': i,
 519                                    'timestamp': 'now'
 520                                    },
 521                                   success=True)
 522              assert_equal(w1.getnewaddress('', 'bech32'), addresses[i])
 523  
 524          # Check active=False default
 525          self.log.info('Check imported descriptors are not active by default')
 526          self.test_importdesc({'desc': descsum_create('pkh([12345678/1h]' + xpub + '/*)'),
 527                                'range' : [0, 2],
 528                                'timestamp': 'now',
 529                                'internal': True
 530                               },
 531                               success=True)
 532          assert_raises_rpc_error(-4, 'This wallet has no available keys', w1.getrawchangeaddress, 'legacy')
 533  
 534          self.log.info('Check can activate inactive descriptor')
 535          self.test_importdesc({'desc': descsum_create('pkh([12345678]' + xpub + '/*)'),
 536                                'range': [0, 5],
 537                                'active': True,
 538                                'timestamp': 'now',
 539                                'internal': True
 540                                },
 541                               success=True)
 542          address = w1.getrawchangeaddress('legacy')
 543          assert_equal(address, "mpA2Wh9dvZT7yfELq1UnrUmAoc5qCkMetg")
 544  
 545          self.log.info('Check can deactivate active descriptor')
 546          self.test_importdesc({'desc': descsum_create('pkh([12345678]' + xpub + '/*)'),
 547                                'range': [0, 5],
 548                                'active': False,
 549                                'timestamp': 'now',
 550                                'internal': True
 551                                },
 552                               success=True)
 553          assert_raises_rpc_error(-4, 'This wallet has no available keys', w1.getrawchangeaddress, 'legacy')
 554  
 555          self.log.info('Verify activation state is persistent')
 556          w1.unloadwallet()
 557          self.nodes[1].loadwallet('w1')
 558          assert_raises_rpc_error(-4, 'This wallet has no available keys', w1.getrawchangeaddress, 'legacy')
 559  
 560          # # Test importing a descriptor containing a WIF private key
 561          wif_priv = "cTe1f5rdT8A8DFgVWTjyPwACsDPJM9ff4QngFxUixCSvvbg1x6sh"
 562          address = "2MuhcG52uHPknxDgmGPsV18jSHFBnnRgjPg"
 563          desc = "sh(wpkh(" + wif_priv + "))"
 564          self.log.info("Should import a descriptor with a WIF private key as spendable")
 565          self.test_importdesc({"desc": descsum_create(desc),
 566                                 "timestamp": "now"},
 567                                success=True,
 568                                wallet=wpriv)
 569  
 570          self.log.info('Test can import same descriptor with private key twice')
 571          self.test_importdesc({"desc": descsum_create(desc), "timestamp": "now"}, success=True, wallet=wpriv)
 572  
 573          test_address(wpriv,
 574                       address,
 575                       solvable=True,
 576                       ismine=True)
 577          txid = w0.sendtoaddress(address, 49.99995540)
 578          self.generatetoaddress(self.nodes[0], 6, w0.getnewaddress())
 579          tx = wpriv.createrawtransaction([{"txid": txid, "vout": 0}], {w0.getnewaddress(): 49.999})
 580          signed_tx = wpriv.signrawtransactionwithwallet(tx)
 581          w1.sendrawtransaction(signed_tx['hex'])
 582  
 583          # Make sure that we can use import and use multisig as addresses
 584          self.log.info('Test that multisigs can be imported, signed for, and getnewaddress\'d')
 585          self.nodes[1].createwallet(wallet_name="wmulti_priv", disable_private_keys=False, blank=True)
 586          wmulti_priv = self.nodes[1].get_wallet_rpc("wmulti_priv")
 587          assert_equal(wmulti_priv.getwalletinfo()['keypoolsize'], 0)
 588  
 589          derivation_path = "84h/0h/0h"
 590          change_derivation_path = "84h/1h/0h"
 591          extended_key_1 = ExtendedPrivateKey.generate()
 592          xprv1 = extended_key_1.to_string()
 593          xprv1_fingerprint = extended_key_1._fingerprint().hex()
 594          acc_xpub1_key = extended_key_1.derive_path(derivation_path).pubkey()
 595          acc_xpub1 = acc_xpub1_key.to_string()
 596          chg_xpub1_key = extended_key_1.derive_path(change_derivation_path).pubkey()
 597          chg_xpub1 = chg_xpub1_key.to_string()
 598  
 599          extended_key_2 = ExtendedPrivateKey.generate()
 600          xprv2 = extended_key_2.to_string()
 601          xprv2_fingerprint = extended_key_2._fingerprint().hex()
 602          acc_xprv2 = extended_key_2.derive_path(derivation_path).to_string()
 603          acc_xpub2_key = extended_key_2.derive_path(derivation_path).pubkey()
 604          acc_xpub2 = acc_xpub2_key.to_string()
 605          chg_xpub2_key = extended_key_2.derive_path(change_derivation_path).pubkey()
 606          chg_xpub2 = chg_xpub2_key.to_string()
 607  
 608          extended_key_3 = ExtendedPrivateKey.generate()
 609          xprv3 = extended_key_3.to_string()
 610          xprv3_fingerprint = extended_key_3._fingerprint().hex()
 611          acc_xpub3_key = extended_key_3.derive_path(derivation_path).pubkey()
 612          acc_xpub3 = acc_xpub3_key.to_string()
 613          chg_xpub3_key = extended_key_3.derive_path(change_derivation_path).pubkey()
 614          chg_xpub3 = chg_xpub3_key.to_string()
 615  
 616          self.test_importdesc({"desc": descsum_create(f"wsh(multi(2,{xprv1}/{derivation_path}/*,{xprv2}/{derivation_path}/*,{xprv3}/{derivation_path}/*))"),
 617                              "active": True,
 618                              "range": 1000,
 619                              "next_index": 0,
 620                              "timestamp": "now"},
 621                              success=True,
 622                              wallet=wmulti_priv)
 623          self.test_importdesc({"desc": descsum_create(f"wsh(multi(2,{xprv1}/{change_derivation_path}/*,{xprv2}/{change_derivation_path}/*,{xprv3}/{change_derivation_path}/*))"),
 624                              "active": True,
 625                              "internal" : True,
 626                              "range": 1000,
 627                              "next_index": 0,
 628                              "timestamp": "now"},
 629                              success=True,
 630                              wallet=wmulti_priv)
 631  
 632          assert_equal(wmulti_priv.getwalletinfo()['keypoolsize'], 1001) # Range end (1000) is inclusive, so 1001 addresses generated
 633  
 634          addr = wmulti_priv.getnewaddress('', 'bech32') # uses receive 0
 635          expected_addr = script_to_p2wsh(keys_to_multisig_script([k.derive_path("m/0").pubkey.get_bytes() for k in [acc_xpub1_key, acc_xpub2_key, acc_xpub3_key]], k=2))
 636          assert_equal(addr, expected_addr) # Derived at m/84'/0'/0'/0
 637  
 638          change_addr = wmulti_priv.getrawchangeaddress('bech32') # uses change 0
 639          expected_change_addr = script_to_p2wsh(keys_to_multisig_script([k.derive_path("m/0").pubkey.get_bytes() for k in [chg_xpub1_key, chg_xpub2_key, chg_xpub3_key]], k=2))
 640          assert_equal(change_addr, expected_change_addr)  # Derived at m/84'/1'/0'/0
 641          assert_equal(wmulti_priv.getwalletinfo()['keypoolsize'], 1000)
 642  
 643          txid = w0.sendtoaddress(addr, 10)
 644          self.generate(self.nodes[0], 6)
 645          send_txid = wmulti_priv.sendtoaddress(w0.getnewaddress(), 8) # uses change 1
 646          decoded = wmulti_priv.gettransaction(txid=send_txid, verbose=True)['decoded']
 647          assert_equal(len(decoded['vin'][0]['txinwitness']), 4)
 648          self.sync_all()
 649  
 650          self.nodes[1].createwallet(wallet_name="wmulti_pub", disable_private_keys=True, blank=True)
 651          wmulti_pub = self.nodes[1].get_wallet_rpc("wmulti_pub")
 652          assert_equal(wmulti_pub.getwalletinfo()['keypoolsize'], 0)
 653  
 654          self.test_importdesc({"desc": descsum_create(f"wsh(multi(2,[{xprv1_fingerprint}/{derivation_path}]{acc_xpub1}/*,[{xprv2_fingerprint}/{derivation_path}]{acc_xpub2}/*,[{xprv3_fingerprint}/{derivation_path}]{acc_xpub3}/*))"),
 655                              "active": True,
 656                              "range": 1000,
 657                              "next_index": 0,
 658                              "timestamp": "now"},
 659                              success=True,
 660                              wallet=wmulti_pub)
 661          self.test_importdesc({"desc": descsum_create(f"wsh(multi(2,[{xprv1_fingerprint}/{change_derivation_path}]{chg_xpub1}/*,[{xprv2_fingerprint}/{change_derivation_path}]{chg_xpub2}/*,[{xprv3_fingerprint}/{change_derivation_path}]{chg_xpub3}/*))"),
 662                              "active": True,
 663                              "internal" : True,
 664                              "range": 1000,
 665                              "next_index": 0,
 666                              "timestamp": "now"},
 667                              success=True,
 668                              wallet=wmulti_pub)
 669  
 670          assert_equal(wmulti_pub.getwalletinfo()['keypoolsize'], 1000) # The first one was already consumed by previous import and is detected as used
 671          addr = wmulti_pub.getnewaddress('', 'bech32') # uses receive 1
 672          expected_addr = script_to_p2wsh(keys_to_multisig_script([k.derive_path("m/1").pubkey.get_bytes() for k in [acc_xpub1_key, acc_xpub2_key, acc_xpub3_key]], k=2))
 673          assert_equal(addr, expected_addr) # Derived at m/84'/0'/0'/1
 674          assert_equal(wmulti_pub.getaddressinfo(addr)["desc"].count(f"{derivation_path}/1"), 3) # Derived at m/84h/0h/0h/1 for all three keys
 675          change_addr = wmulti_pub.getrawchangeaddress('bech32') # uses change 2
 676          expected_change_addr = script_to_p2wsh(keys_to_multisig_script([k.derive_path("m/2").pubkey.get_bytes() for k in [chg_xpub1_key, chg_xpub2_key, chg_xpub3_key]], k=2))
 677          assert_equal(change_addr, expected_change_addr)  # Derived at m/84'/1'/0'/2
 678          assert_equal(wmulti_pub.getaddressinfo(change_addr)["desc"].count(f"{change_derivation_path}/2"), 3) # Derived at m/84h/1h/0h/1 for all three keys
 679          assert send_txid in self.nodes[0].getrawmempool(True)
 680          assert send_txid in (x['txid'] for x in wmulti_pub.listunspent(0))
 681          assert_equal(wmulti_pub.getwalletinfo()['keypoolsize'], 999)
 682  
 683          # generate some utxos for next tests
 684          utxo = self.create_outpoints(w0, outputs=[{addr: 10}])[0]
 685  
 686          addr2 = wmulti_pub.getnewaddress('', 'bech32')
 687          utxo2 = self.create_outpoints(w0, outputs=[{addr2: 10}])[0]
 688  
 689          self.generate(self.nodes[0], 6)
 690          assert_equal(wmulti_pub.getbalance(), wmulti_priv.getbalance())
 691  
 692          # Make sure that descriptor wallets containing multiple xpubs in a single descriptor load correctly
 693          wmulti_pub.unloadwallet()
 694          self.nodes[1].loadwallet('wmulti_pub')
 695  
 696          self.log.info("Multisig with distributed keys")
 697          self.nodes[1].createwallet(wallet_name="wmulti_priv1")
 698          wmulti_priv1 = self.nodes[1].get_wallet_rpc("wmulti_priv1")
 699          res = wmulti_priv1.importdescriptors([
 700          {
 701              "desc": descsum_create(f"wsh(multi(2,{xprv1}/{derivation_path}/*,[{xprv2_fingerprint}/{derivation_path}]{acc_xpub2}/*,[{xprv3_fingerprint}/{derivation_path}]{acc_xpub3}/*))"),
 702              "active": True,
 703              "range": 1000,
 704              "next_index": 0,
 705              "timestamp": "now"
 706          },
 707          {
 708              "desc": descsum_create(f"wsh(multi(2,{xprv1}/{change_derivation_path}/*,[{xprv2_fingerprint}/{change_derivation_path}]{chg_xpub2}/*,[{xprv3_fingerprint}/{change_derivation_path}]{chg_xpub3}/*))"),
 709              "active": True,
 710              "internal" : True,
 711              "range": 1000,
 712              "next_index": 0,
 713              "timestamp": "now"
 714          }])
 715          assert_equal(res[0]['success'], True)
 716          assert_equal(res[0]['warnings'][0], 'Not all private keys provided. Some wallet functionality may return unexpected errors')
 717          assert_equal(res[1]['success'], True)
 718          assert_equal(res[1]['warnings'][0], 'Not all private keys provided. Some wallet functionality may return unexpected errors')
 719  
 720          self.nodes[1].createwallet(wallet_name='wmulti_priv2', blank=True)
 721          wmulti_priv2 = self.nodes[1].get_wallet_rpc('wmulti_priv2')
 722          res = wmulti_priv2.importdescriptors([
 723          {
 724              "desc": descsum_create(f"wsh(multi(2,[{xprv1_fingerprint}/{derivation_path}]{acc_xpub1}/*,{xprv2}/{derivation_path}/*,[{xprv3_fingerprint}/{derivation_path}]{acc_xpub3}/*))"),
 725              "active": True,
 726              "range": 1000,
 727              "next_index": 0,
 728              "timestamp": "now"
 729          },
 730          {
 731              "desc": descsum_create(f"wsh(multi(2,[{xprv1_fingerprint}/{change_derivation_path}]{chg_xpub1}/*,{xprv2}/{change_derivation_path}/*,[{xprv3_fingerprint}/{change_derivation_path}]{chg_xpub3}/*))"),
 732              "active": True,
 733              "internal" : True,
 734              "range": 1000,
 735              "next_index": 0,
 736              "timestamp": "now"
 737          }])
 738          assert_equal(res[0]['success'], True)
 739          assert_equal(res[0]['warnings'][0], 'Not all private keys provided. Some wallet functionality may return unexpected errors')
 740          assert_equal(res[1]['success'], True)
 741          assert_equal(res[1]['warnings'][0], 'Not all private keys provided. Some wallet functionality may return unexpected errors')
 742  
 743          rawtx = self.nodes[1].createrawtransaction([utxo], {w0.getnewaddress(): 9.999})
 744          tx_signed_1 = wmulti_priv1.signrawtransactionwithwallet(rawtx)
 745          assert_equal(tx_signed_1['complete'], False)
 746          tx_signed_2 = wmulti_priv2.signrawtransactionwithwallet(tx_signed_1['hex'])
 747          assert_equal(tx_signed_2['complete'], True)
 748          self.nodes[1].sendrawtransaction(tx_signed_2['hex'])
 749  
 750          self.log.info("We can create and use a huge multisig under P2WSH")
 751          self.nodes[1].createwallet(wallet_name='wmulti_priv_big', blank=True)
 752          wmulti_priv_big = self.nodes[1].get_wallet_rpc('wmulti_priv_big')
 753          xprv = ExtendedPrivateKey.generate().to_string()
 754          xkey = f"{xprv}/*"
 755          xkey_int = f"{xprv}/1/*"
 756          res = wmulti_priv_big.importdescriptors([
 757          {
 758              "desc": descsum_create(f"wsh(sortedmulti(20,{(xkey + ',') * 19}{xkey}))"),
 759              "active": True,
 760              "range": 1000,
 761              "next_index": 0,
 762              "timestamp": "now"
 763          },
 764          {
 765              "desc": descsum_create(f"wsh(sortedmulti(20,{(xkey_int + ',') * 19}{xkey_int}))"),
 766              "active": True,
 767              "internal": True,
 768              "range": 1000,
 769              "next_index": 0,
 770              "timestamp": "now"
 771          }])
 772          assert_equal(res[0]['success'], True)
 773          assert_equal(res[1]['success'], True)
 774  
 775          addr = wmulti_priv_big.getnewaddress()
 776          w0.sendtoaddress(addr, 10)
 777          self.generate(self.nodes[0], 1)
 778          # It is standard and would relay.
 779          txid = wmulti_priv_big.sendtoaddress(w0.getnewaddress(), 9.999)
 780          decoded = wmulti_priv_big.gettransaction(txid=txid, verbose=True)['decoded']
 781          # 20 sigs + dummy + witness script
 782          assert_equal(len(decoded['vin'][0]['txinwitness']), 22)
 783  
 784  
 785          self.log.info("Under P2SH, multisig are standard with up to 15 "
 786                        "compressed keys")
 787          self.nodes[1].createwallet(wallet_name='multi_priv_big_legacy',
 788                                     blank=True)
 789          multi_priv_big = self.nodes[1].get_wallet_rpc('multi_priv_big_legacy')
 790          res = multi_priv_big.importdescriptors([
 791          {
 792              "desc": descsum_create(f"sh(multi(15,{(xkey + ',') * 14}{xkey}))"),
 793              "active": True,
 794              "range": 1000,
 795              "next_index": 0,
 796              "timestamp": "now"
 797          },
 798          {
 799              "desc": descsum_create(f"sh(multi(15,{(xkey_int + ',') * 14}{xkey_int}))"),
 800              "active": True,
 801              "internal": True,
 802              "range": 1000,
 803              "next_index": 0,
 804              "timestamp": "now"
 805          }])
 806          assert_equal(res[0]['success'], True)
 807          assert_equal(res[1]['success'], True)
 808  
 809          addr = multi_priv_big.getnewaddress("", "legacy")
 810          w0.sendtoaddress(addr, 10)
 811          self.generate(self.nodes[0], 6)
 812          # It is standard and would relay.
 813          txid = multi_priv_big.sendtoaddress(w0.getnewaddress(), 10, "", "", True)
 814          decoded = multi_priv_big.gettransaction(txid=txid, verbose=True)['decoded']
 815  
 816          self.log.info("Amending multisig with new private keys")
 817          self.nodes[1].createwallet(wallet_name="wmulti_priv3")
 818          wmulti_priv3 = self.nodes[1].get_wallet_rpc("wmulti_priv3")
 819          res = wmulti_priv3.importdescriptors([
 820              {
 821                  "desc": descsum_create(f"wsh(multi(2,{xprv1}/{derivation_path}/*,[{xprv2_fingerprint}/{derivation_path}]{acc_xpub2}/*,[{xprv3_fingerprint}/{derivation_path}]{acc_xpub3}/*))"),
 822                  "active": True,
 823                  "range": 1000,
 824                  "next_index": 0,
 825                  "timestamp": "now"
 826              }])
 827          assert_equal(res[0]['success'], True)
 828          res = wmulti_priv3.importdescriptors([
 829              {
 830                  "desc": descsum_create(f"wsh(multi(2,{xprv1}/{derivation_path}/*,[{xprv2_fingerprint}/{derivation_path}]{acc_xprv2}/*,[{xprv3_fingerprint}/{derivation_path}]{acc_xpub3}/*))"),
 831                  "active": True,
 832                  "range": 1000,
 833                  "next_index": 0,
 834                  "timestamp": "now"
 835              }])
 836          assert_equal(res[0]['success'], True)
 837  
 838          rawtx = self.nodes[1].createrawtransaction([utxo2], {w0.getnewaddress(): 9.999})
 839          tx = wmulti_priv3.signrawtransactionwithwallet(rawtx)
 840          assert_equal(tx['complete'], True)
 841          self.nodes[1].sendrawtransaction(tx['hex'])
 842  
 843          self.log.info("Combo descriptors cannot be active")
 844          self.test_importdesc({"desc": descsum_create(f"combo({ExtendedPrivateKey.generate().pubkey().to_string()}/*)"),
 845                                "active": True,
 846                                "range": 1,
 847                                "timestamp": "now"},
 848                                success=False,
 849                                error_code=-4,
 850                                error_message="Combo descriptors cannot be set to active")
 851  
 852          self.log.info("Descriptors with no type cannot be active")
 853          self.test_importdesc({"desc": descsum_create(f"pk({ExtendedPrivateKey.generate().pubkey().to_string()}/*)"),
 854                                "active": True,
 855                                "range": 1,
 856                                "timestamp": "now"},
 857                                success=True,
 858                                warnings=["Unknown output type, cannot set descriptor to active."])
 859  
 860          self.log.info("Test importing a descriptor to an encrypted wallet")
 861  
 862          descriptor = {"desc": descsum_create("pkh(" + xpriv + "/1h/*h)"),
 863                                "timestamp": "now",
 864                                "active": True,
 865                                "range": [0,4000],
 866                                "next_index": 4000}
 867  
 868          self.nodes[0].createwallet("temp_wallet", blank=True)
 869          temp_wallet = self.nodes[0].get_wallet_rpc("temp_wallet")
 870          temp_wallet.importdescriptors([descriptor])
 871          self.generatetoaddress(self.nodes[0], COINBASE_MATURITY + 1, temp_wallet.getnewaddress())
 872          self.generatetoaddress(self.nodes[0], COINBASE_MATURITY + 1, temp_wallet.getnewaddress())
 873  
 874          self.nodes[0].createwallet("encrypted_wallet", blank=True, passphrase="passphrase")
 875          encrypted_wallet = self.nodes[0].get_wallet_rpc("encrypted_wallet")
 876  
 877          self.log.info("Wallet must be unlocked to import a descriptor")
 878          assert_raises_rpc_error(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.",
 879              encrypted_wallet.importdescriptors, [descriptor])
 880  
 881          descriptor["timestamp"] = 0
 882          descriptor["next_index"] = 0
 883  
 884          encrypted_wallet.walletpassphrase("passphrase", 99999)
 885          with concurrent.futures.ThreadPoolExecutor(max_workers=1) as thread:
 886              with self.nodes[0].assert_debug_log(expected_msgs=["Rescan started from block 0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206... (slow variant inspecting all blocks)"], timeout=10):
 887                  importing = thread.submit(encrypted_wallet.importdescriptors, requests=[descriptor])
 888  
 889              # Set the passphrase timeout to 1 to test that the wallet remains unlocked during the rescan
 890              self.nodes[0].cli("-rpcwallet=encrypted_wallet").walletpassphrase("passphrase", 1)
 891  
 892              try:
 893                  self.nodes[0].cli("-rpcwallet=encrypted_wallet").walletlock()
 894              except JSONRPCException as e:
 895                  assert_equal(e.error["code"], -4)
 896                  assert "Error: the wallet is currently being used to rescan the blockchain for related transactions. Please call `abortrescan` before locking the wallet." in e.error["message"]
 897  
 898              try:
 899                  self.nodes[0].cli("-rpcwallet=encrypted_wallet").walletpassphrasechange("passphrase", "newpassphrase")
 900              except JSONRPCException as e:
 901                  assert_equal(e.error["code"], -4)
 902                  assert "Error: the wallet is currently being used to rescan the blockchain for related transactions. Please call `abortrescan` before changing the passphrase." in e.error["message"]
 903  
 904              assert_equal(importing.result(), [{"success": True}])
 905  
 906          assert_equal(temp_wallet.getbalance(), encrypted_wallet.getbalance())
 907  
 908          self.log.info("Multipath descriptors")
 909          self.nodes[1].createwallet(wallet_name="multipath", blank=True)
 910          w_multipath = self.nodes[1].get_wallet_rpc("multipath")
 911          self.nodes[1].createwallet(wallet_name="multipath_split", blank=True)
 912          w_multisplit = self.nodes[1].get_wallet_rpc("multipath_split")
 913          timestamp = int(time.time())
 914  
 915          self.test_importdesc({"desc": descsum_create(f"wpkh({xpriv}/<10;20>/0/*)"),
 916                                "active": True,
 917                                "range": 10,
 918                                "timestamp": "now",
 919                                "label": "some label"},
 920                                success=False,
 921                                error_code=-8,
 922                                error_message="Multipath descriptors should not have a label",
 923                                wallet=w_multipath)
 924          self.test_importdesc({"desc": descsum_create(f"wpkh({xpriv}/<10;20>/0/*)"),
 925                                "active": True,
 926                                "range": 10,
 927                                "timestamp": timestamp,
 928                                "internal": True},
 929                                success=False,
 930                                error_code=-5,
 931                                error_message="Cannot have multipath descriptor while also specifying \'internal\'",
 932                                wallet=w_multipath)
 933  
 934          self.test_importdesc({"desc": descsum_create(f"wpkh({xpriv}/<10;20>/0/*)"),
 935                                "active": True,
 936                                "range": 10,
 937                                "timestamp": timestamp},
 938                                success=True,
 939                                wallet=w_multipath)
 940  
 941          self.test_importdesc({"desc": descsum_create(f"wpkh({xpriv}/10/0/*)"),
 942                                "active": True,
 943                                "range": 10,
 944                                "timestamp": timestamp},
 945                                success=True,
 946                                wallet=w_multisplit)
 947          self.test_importdesc({"desc": descsum_create(f"wpkh({xpriv}/20/0/*)"),
 948                                "active": True,
 949                                "range": 10,
 950                                "internal": True,
 951                                "timestamp": timestamp},
 952                                success=True,
 953                                wallet=w_multisplit)
 954          for _ in range(0, 10):
 955              assert_equal(w_multipath.getnewaddress(address_type="bech32"), w_multisplit.getnewaddress(address_type="bech32"))
 956              assert_equal(w_multipath.getrawchangeaddress(address_type="bech32"), w_multisplit.getrawchangeaddress(address_type="bech32"))
 957          assert_equal(sorted(w_multipath.listdescriptors()["descriptors"], key=lambda x: x["desc"]), sorted(w_multisplit.listdescriptors()["descriptors"], key=lambda x: x["desc"]))
 958  
 959          self.log.info("Test older() safety")
 960  
 961          for flag in [0, SEQUENCE_LOCKTIME_TYPE_FLAG]:
 962              self.log.debug("Importing a safe value always works")
 963              safe_value = (65535 | flag)
 964              self.test_importdesc(
 965                  {
 966                      'desc': descsum_create(f"wsh(and_v(v:pk([12345678/0h/0h]{xpub}/*),older({safe_value})))"),
 967                      'active': True,
 968                      'range': [0, 2],
 969                      'timestamp': 'now'
 970                  },
 971                  success=True
 972              )
 973  
 974              self.log.debug("Importing an unsafe value results in a warning")
 975              unsafe_value = safe_value + 1
 976              desc = descsum_create(f"wsh(and_v(v:pk([12345678/0h/0h]{xpub}/*),older({unsafe_value})))")
 977              expected_warning = (
 978                  f"time-based relative locktime: older({unsafe_value}) > (65535 * 512) seconds is unsafe"
 979                  if flag == SEQUENCE_LOCKTIME_TYPE_FLAG
 980                  else f"height-based relative locktime: older({unsafe_value}) > 65535 blocks is unsafe"
 981              )
 982              self.test_importdesc(
 983                  {
 984                      'desc': desc,
 985                      'active': True,
 986                      'range': [0, 2],
 987                      'timestamp': 'now'
 988                  },
 989                  success=True,
 990                  warnings=[expected_warning],
 991              )
 992  
 993  
 994          self.test_import_unused_key()
 995          self.test_import_unused_key_existing()
 996          self.test_import_unused_noprivs()
 997          self.test_rescan_fails_import()
 998  
 999  if __name__ == '__main__':
1000      ImportDescriptorsTest(__file__).main()
1001