wallet_importdescriptors.py raw

   1  #!/usr/bin/env python3
   2  # Copyright (c) 2019-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 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 time
  20  
  21  from test_framework.authproxy import JSONRPCException
  22  from test_framework.blocktools import COINBASE_MATURITY
  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_raises_rpc_error,
  28  )
  29  from test_framework.wallet_util import (
  30      get_generate_key,
  31      test_address,
  32  )
  33  
  34  class ImportDescriptorsTest(LimenkaTestFramework):
  35      def add_options(self, parser):
  36          self.add_wallet_options(parser, legacy=False)
  37  
  38      def set_test_params(self):
  39          self.num_nodes = 2
  40          # whitelist peers to speed up tx relay / mempool sync
  41          self.noban_tx_relay = True
  42          self.extra_args = [["-addresstype=legacy"],
  43                             ["-addresstype=bech32", "-keypool=5"]
  44                            ]
  45          self.setup_clean_chain = True
  46          self.wallet_names = []
  47  
  48      def skip_test_if_missing_module(self):
  49          self.skip_if_no_wallet()
  50          self.skip_if_no_sqlite()
  51  
  52      def test_importdesc(self, req, success, error_code=None, error_message=None, warnings=None, wallet=None):
  53          """Run importdescriptors and assert success"""
  54          if warnings is None:
  55              warnings = []
  56          wrpc = self.nodes[1].get_wallet_rpc('w1')
  57          if wallet is not None:
  58              wrpc = wallet
  59  
  60          result = wrpc.importdescriptors([req])
  61          observed_warnings = []
  62          if 'warnings' in result[0]:
  63              observed_warnings = result[0]['warnings']
  64          assert_equal("\n".join(sorted(warnings)), "\n".join(sorted(observed_warnings)))
  65          assert_equal(result[0]['success'], success)
  66          if error_code is not None:
  67              assert_equal(result[0]['error']['code'], error_code)
  68              assert_equal(result[0]['error']['message'], error_message)
  69  
  70      def run_test(self):
  71          self.log.info('Setting up wallets')
  72          self.nodes[0].createwallet(wallet_name='w0', disable_private_keys=False, descriptors=True)
  73          w0 = self.nodes[0].get_wallet_rpc('w0')
  74  
  75          self.nodes[1].createwallet(wallet_name='w1', disable_private_keys=True, blank=True, descriptors=True)
  76          w1 = self.nodes[1].get_wallet_rpc('w1')
  77          assert_equal(w1.getwalletinfo()['keypoolsize'], 0)
  78  
  79          self.nodes[1].createwallet(wallet_name="wpriv", disable_private_keys=False, blank=True, descriptors=True)
  80          wpriv = self.nodes[1].get_wallet_rpc("wpriv")
  81          assert_equal(wpriv.getwalletinfo()['keypoolsize'], 0)
  82  
  83          self.log.info('Mining coins')
  84          self.generatetoaddress(self.nodes[0], COINBASE_MATURITY + 1, w0.getnewaddress())
  85  
  86          # RPC importdescriptors -----------------------------------------------
  87  
  88          # # Test import fails if no descriptor present
  89          self.log.info("Import should fail if a descriptor is not provided")
  90          self.test_importdesc({"timestamp": "now"},
  91                               success=False,
  92                               error_code=-8,
  93                               error_message='Descriptor not found.')
  94  
  95          # # Test importing of a P2PKH descriptor
  96          key = get_generate_key()
  97          self.log.info("Should import a p2pkh descriptor")
  98          import_request = {"desc": descsum_create("pkh(" + key.pubkey + ")"),
  99                   "timestamp": "now",
 100                   "label": "Descriptor import test"}
 101          self.test_importdesc(import_request, success=True)
 102          test_address(w1,
 103                       key.p2pkh_addr,
 104                       solvable=True,
 105                       ismine=True,
 106                       labels=["Descriptor import test"])
 107          assert_equal(w1.getwalletinfo()['keypoolsize'], 0)
 108  
 109          self.log.info("Test can import same descriptor with public key twice")
 110          self.test_importdesc(import_request, success=True)
 111  
 112          self.log.info("Test can update descriptor label")
 113          self.test_importdesc({**import_request, "label": "Updated label"}, success=True)
 114          test_address(w1, key.p2pkh_addr, solvable=True, ismine=True, labels=["Updated label"])
 115  
 116          self.log.info("Internal addresses cannot have labels")
 117          self.test_importdesc({**import_request, "internal": True},
 118                               success=False,
 119                               error_code=-8,
 120                               error_message="Internal addresses should not have a label")
 121  
 122          self.log.info("External non-ranged addresses can have labels")
 123          self.test_importdesc({**import_request, "internal": False}, success=True)
 124  
 125          self.log.info("Internal addresses should be detected as such")
 126          key = get_generate_key()
 127          self.test_importdesc({"desc": descsum_create("pkh(" + key.pubkey + ")"),
 128                                "timestamp": "now",
 129                                "internal": True},
 130                               success=True)
 131          info = w1.getaddressinfo(key.p2pkh_addr)
 132          assert_equal(info["ismine"], True)
 133          assert_equal(info["ischange"], True)
 134  
 135          # # Test importing of a P2SH-P2WPKH descriptor
 136          key = get_generate_key()
 137          self.log.info("Should not import a p2sh-p2wpkh descriptor without checksum")
 138          self.test_importdesc({"desc": "sh(wpkh(" + key.pubkey + "))",
 139                                "timestamp": "now"
 140                                },
 141                               success=False,
 142                               error_code=-5,
 143                               error_message="Missing checksum")
 144  
 145          self.log.info("Should not import a p2sh-p2wpkh descriptor that has range specified")
 146          self.test_importdesc({"desc": descsum_create("sh(wpkh(" + key.pubkey + "))"),
 147                                 "timestamp": "now",
 148                                 "range": 1,
 149                                },
 150                                success=False,
 151                                error_code=-8,
 152                                error_message="Range should not be specified for an un-ranged descriptor")
 153  
 154          self.log.info("Should not import a p2sh-p2wpkh descriptor and have it set to active")
 155          self.test_importdesc({"desc": descsum_create("sh(wpkh(" + key.pubkey + "))"),
 156                                 "timestamp": "now",
 157                                 "active": True,
 158                                },
 159                                success=False,
 160                                error_code=-8,
 161                                error_message="Active descriptors must be ranged")
 162  
 163          self.log.info("Should import a (non-active) p2sh-p2wpkh descriptor")
 164          self.test_importdesc({"desc": descsum_create("sh(wpkh(" + key.pubkey + "))"),
 165                                 "timestamp": "now",
 166                                 "active": False,
 167                                },
 168                                success=True)
 169          assert_equal(w1.getwalletinfo()['keypoolsize'], 0)
 170  
 171          test_address(w1,
 172                       key.p2sh_p2wpkh_addr,
 173                       ismine=True,
 174                       solvable=True)
 175  
 176          # Check persistence of data and that loading works correctly
 177          w1.unloadwallet()
 178          self.nodes[1].loadwallet('w1')
 179          test_address(w1,
 180                       key.p2sh_p2wpkh_addr,
 181                       ismine=True,
 182                       solvable=True)
 183  
 184          # # Test importing of a multisig descriptor
 185          key1 = get_generate_key()
 186          key2 = get_generate_key()
 187          self.log.info("Should import a 1-of-2 bare multisig from descriptor")
 188          self.test_importdesc({"desc": descsum_create("multi(1," + key1.pubkey + "," + key2.pubkey + ")"),
 189                                "timestamp": "now"},
 190                               success=True)
 191          self.log.info("Should not treat individual keys from the imported bare multisig as watchonly")
 192          test_address(w1,
 193                       key1.p2pkh_addr,
 194                       ismine=False)
 195  
 196          # # Test ranged descriptors
 197          xpriv = "tprv8ZgxMBicQKsPeuVhWwi6wuMQGfPKi9Li5GtX35jVNknACgqe3CY4g5xgkfDDJcmtF7o1QnxWDRYw4H5P26PXq7sbcUkEqeR4fg3Kxp2tigg"
 198          xpub = "tpubD6NzVbkrYhZ4YNXVQbNhMK1WqguFsUXceaVJKbmno2aZ3B6QfbMeraaYvnBSGpV3vxLyTTK9DYT1yoEck4XUScMzXoQ2U2oSmE2JyMedq3H"
 199          addresses = ["2N7yv4p8G8yEaPddJxY41kPihnWvs39qCMf", "2MsHxyb2JS3pAySeNUsJ7mNnurtpeenDzLA"] # hdkeypath=m/0'/0'/0' and 1'
 200          addresses += ["bcrt1qrd3n235cj2czsfmsuvqqpr3lu6lg0ju7scl8gn", "bcrt1qfqeppuvj0ww98r6qghmdkj70tv8qpchehegrg8"] # wpkh subscripts corresponding to the above addresses
 201          desc = "sh(wpkh(" + xpub + "/0/0/*" + "))"
 202  
 203          self.log.info("Ranged descriptors cannot have labels")
 204          self.test_importdesc({"desc":descsum_create(desc),
 205                                "timestamp": "now",
 206                                "range": [0, 100],
 207                                "label": "test"},
 208                                success=False,
 209                                error_code=-8,
 210                                error_message='Ranged descriptors should not have a label')
 211  
 212          self.log.info("Ranged descriptors cannot have labels - even if range not provided by user and only implied by asterisk (*)")
 213          self.test_importdesc({"desc":descsum_create("wpkh(" + xpub + "/100/0/*)"),
 214                                "timestamp": "now",
 215                                "label": "test",
 216                                "active": True},
 217                                success=False,
 218                                warnings=['Range not given, using default keypool range'],
 219                                error_code=-8,
 220                                error_message='Ranged descriptors should not have a label')
 221  
 222          self.log.info("Private keys required for private keys enabled wallet")
 223          self.test_importdesc({"desc":descsum_create(desc),
 224                                "timestamp": "now",
 225                                "range": [0, 100]},
 226                                success=False,
 227                                error_code=-4,
 228                                error_message='Cannot import descriptor without private keys to a wallet with private keys enabled',
 229                                wallet=wpriv)
 230  
 231          self.log.info("Ranged descriptor import should warn without a specified range")
 232          self.test_importdesc({"desc": descsum_create(desc),
 233                                 "timestamp": "now"},
 234                                success=True,
 235                                warnings=['Range not given, using default keypool range'])
 236          assert_equal(w1.getwalletinfo()['keypoolsize'], 0)
 237  
 238          # # Test importing of a ranged descriptor with xpriv
 239          self.log.info("Should not import a ranged descriptor that includes xpriv into a watch-only wallet")
 240          desc = "sh(wpkh(" + xpriv + "/0'/0'/*'" + "))"
 241          self.test_importdesc({"desc": descsum_create(desc),
 242                                "timestamp": "now",
 243                                "range": 1},
 244                               success=False,
 245                               error_code=-4,
 246                               error_message='Cannot import private keys to a wallet with private keys disabled')
 247  
 248          self.log.info("Should not import a descriptor with hardened derivations when private keys are disabled")
 249          self.test_importdesc({"desc": descsum_create("wpkh(" + xpub + "/1h/*)"),
 250                                "timestamp": "now",
 251                                "range": 1},
 252                               success=False,
 253                               error_code=-4,
 254                               error_message='Cannot expand descriptor. Probably because of hardened derivations without private keys provided')
 255  
 256          for address in addresses:
 257              test_address(w1,
 258                           address,
 259                           ismine=False,
 260                           solvable=False)
 261  
 262          self.test_importdesc({"desc": descsum_create(desc), "timestamp": "now", "range": -1},
 263                                success=False, error_code=-8, error_message='End of range is too high')
 264  
 265          self.test_importdesc({"desc": descsum_create(desc), "timestamp": "now", "range": [-1, 10]},
 266                                success=False, error_code=-8, error_message='Range should be greater or equal than 0')
 267  
 268          self.test_importdesc({"desc": descsum_create(desc), "timestamp": "now", "range": [(2 << 31 + 1) - 1000000, (2 << 31 + 1)]},
 269                                success=False, error_code=-8, error_message='End of range is too high')
 270  
 271          self.test_importdesc({"desc": descsum_create(desc), "timestamp": "now", "range": [2, 1]},
 272                                success=False, error_code=-8, error_message='Range specified as [begin,end] must not have begin after end')
 273  
 274          self.test_importdesc({"desc": descsum_create(desc), "timestamp": "now", "range": [0, 1000001]},
 275                                success=False, error_code=-8, error_message='Range is too large')
 276  
 277          self.log.info("Verify we can only extend descriptor's range")
 278          range_request = {"desc": descsum_create(desc), "timestamp": "now", "range": [5, 10], 'active': True}
 279          self.test_importdesc(range_request, wallet=wpriv, success=True)
 280          assert_equal(wpriv.getwalletinfo()['keypoolsize'], 6)
 281          self.test_importdesc({**range_request, "range": [0, 10]}, wallet=wpriv, success=True)
 282          assert_equal(wpriv.getwalletinfo()['keypoolsize'], 11)
 283          self.test_importdesc({**range_request, "range": [0, 20]}, wallet=wpriv, success=True)
 284          assert_equal(wpriv.getwalletinfo()['keypoolsize'], 21)
 285          # Can keep range the same
 286          self.test_importdesc({**range_request, "range": [0, 20]}, wallet=wpriv, success=True)
 287          assert_equal(wpriv.getwalletinfo()['keypoolsize'], 21)
 288  
 289          self.test_importdesc({**range_request, "range": [5, 10]}, wallet=wpriv, success=False,
 290                               error_code=-8, error_message='new range must include current range = [0,20]')
 291          self.test_importdesc({**range_request, "range": [0, 10]}, wallet=wpriv, success=False,
 292                               error_code=-8, error_message='new range must include current range = [0,20]')
 293          self.test_importdesc({**range_request, "range": [5, 20]}, wallet=wpriv, success=False,
 294                               error_code=-8, error_message='new range must include current range = [0,20]')
 295          assert_equal(wpriv.getwalletinfo()['keypoolsize'], 21)
 296  
 297          self.log.info("Check we can change descriptor internal flag")
 298          self.test_importdesc({**range_request, "range": [0, 20], "internal": True}, wallet=wpriv, success=True)
 299          assert_equal(wpriv.getwalletinfo()['keypoolsize'], 0)
 300          assert_raises_rpc_error(-4, 'This wallet has no available keys', wpriv.getnewaddress, '', 'p2sh-segwit')
 301          assert_equal(wpriv.getwalletinfo()['keypoolsize_hd_internal'], 21)
 302          wpriv.getrawchangeaddress('p2sh-segwit')
 303  
 304          self.test_importdesc({**range_request, "range": [0, 20], "internal": False}, wallet=wpriv, success=True)
 305          assert_equal(wpriv.getwalletinfo()['keypoolsize'], 21)
 306          wpriv.getnewaddress('', 'p2sh-segwit')
 307          assert_equal(wpriv.getwalletinfo()['keypoolsize_hd_internal'], 0)
 308          assert_raises_rpc_error(-4, 'This wallet has no available keys', wpriv.getrawchangeaddress, 'p2sh-segwit')
 309  
 310          # Make sure ranged imports import keys in order
 311          w1 = self.nodes[1].get_wallet_rpc('w1')
 312          self.log.info('Key ranges should be imported in order')
 313          xpub = "tpubDAXcJ7s7ZwicqjprRaEWdPoHKrCS215qxGYxpusRLLmJuT69ZSicuGdSfyvyKpvUNYBW1s2U3NSrT6vrCYB9e6nZUEvrqnwXPF8ArTCRXMY"
 314          addresses = [
 315              'bcrt1qtmp74ayg7p24uslctssvjm06q5phz4yrxucgnv', # m/0'/0'/0
 316              'bcrt1q8vprchan07gzagd5e6v9wd7azyucksq2xc76k8', # m/0'/0'/1
 317              'bcrt1qtuqdtha7zmqgcrr26n2rqxztv5y8rafjp9lulu', # m/0'/0'/2
 318              'bcrt1qau64272ymawq26t90md6an0ps99qkrse58m640', # m/0'/0'/3
 319              'bcrt1qsg97266hrh6cpmutqen8s4s962aryy77jp0fg0', # m/0'/0'/4
 320          ]
 321  
 322          self.test_importdesc({'desc': descsum_create('wpkh([80002067/0h/0h]' + xpub + '/*)'),
 323                                'active': True,
 324                                'range' : [0, 2],
 325                                'timestamp': 'now'
 326                               },
 327                               success=True)
 328          self.test_importdesc({'desc': descsum_create('sh(wpkh([abcdef12/0h/0h]' + xpub + '/*))'),
 329                                'active': True,
 330                                'range' : [0, 2],
 331                                'timestamp': 'now'
 332                               },
 333                               success=True)
 334          self.test_importdesc({'desc': descsum_create('pkh([12345678/0h/0h]' + xpub + '/*)'),
 335                                'active': True,
 336                                'range' : [0, 2],
 337                                'timestamp': 'now'
 338                               },
 339                               success=True)
 340  
 341          assert_equal(w1.getwalletinfo()['keypoolsize'], 5 * 3)
 342          for i, expected_addr in enumerate(addresses):
 343              received_addr = w1.getnewaddress('', 'bech32')
 344              assert_raises_rpc_error(-4, 'This wallet has no available keys', w1.getrawchangeaddress, 'bech32')
 345              assert_equal(received_addr, expected_addr)
 346              bech32_addr_info = w1.getaddressinfo(received_addr)
 347              assert_equal(bech32_addr_info['desc'][:23], 'wpkh([80002067/0h/0h/{}]'.format(i))
 348  
 349              shwpkh_addr = w1.getnewaddress('', 'p2sh-segwit')
 350              shwpkh_addr_info = w1.getaddressinfo(shwpkh_addr)
 351              assert_equal(shwpkh_addr_info['desc'][:26], 'sh(wpkh([abcdef12/0h/0h/{}]'.format(i))
 352  
 353              pkh_addr = w1.getnewaddress('', 'legacy')
 354              pkh_addr_info = w1.getaddressinfo(pkh_addr)
 355              assert_equal(pkh_addr_info['desc'][:22], 'pkh([12345678/0h/0h/{}]'.format(i))
 356  
 357              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
 358          w1.keypoolrefill()
 359          assert_equal(w1.getwalletinfo()['keypoolsize'], 5 * 3)
 360  
 361          self.log.info("Check we can change next_index")
 362          # go back and forth with next_index
 363          for i in [4, 0, 2, 1, 3]:
 364              self.test_importdesc({'desc': descsum_create('wpkh([80002067/0h/0h]' + xpub + '/*)'),
 365                                    'active': True,
 366                                    'range': [0, 9],
 367                                    'next_index': i,
 368                                    'timestamp': 'now'
 369                                    },
 370                                   success=True)
 371              assert_equal(w1.getnewaddress('', 'bech32'), addresses[i])
 372  
 373          # Check active=False default
 374          self.log.info('Check imported descriptors are not active by default')
 375          self.test_importdesc({'desc': descsum_create('pkh([12345678/1h]' + xpub + '/*)'),
 376                                'range' : [0, 2],
 377                                'timestamp': 'now',
 378                                'internal': True
 379                               },
 380                               success=True)
 381          assert_raises_rpc_error(-4, 'This wallet has no available keys', w1.getrawchangeaddress, 'legacy')
 382  
 383          self.log.info('Check can activate inactive descriptor')
 384          self.test_importdesc({'desc': descsum_create('pkh([12345678]' + xpub + '/*)'),
 385                                'range': [0, 5],
 386                                'active': True,
 387                                'timestamp': 'now',
 388                                'internal': True
 389                                },
 390                               success=True)
 391          address = w1.getrawchangeaddress('legacy')
 392          assert_equal(address, "mpA2Wh9dvZT7yfELq1UnrUmAoc5qCkMetg")
 393  
 394          self.log.info('Check can deactivate active descriptor')
 395          self.test_importdesc({'desc': descsum_create('pkh([12345678]' + xpub + '/*)'),
 396                                'range': [0, 5],
 397                                'active': False,
 398                                'timestamp': 'now',
 399                                'internal': True
 400                                },
 401                               success=True)
 402          assert_raises_rpc_error(-4, 'This wallet has no available keys', w1.getrawchangeaddress, 'legacy')
 403  
 404          self.log.info('Verify activation state is persistent')
 405          w1.unloadwallet()
 406          self.nodes[1].loadwallet('w1')
 407          assert_raises_rpc_error(-4, 'This wallet has no available keys', w1.getrawchangeaddress, 'legacy')
 408  
 409          # # Test importing a descriptor containing a WIF private key
 410          wif_priv = "cTe1f5rdT8A8DFgVWTjyPwACsDPJM9ff4QngFxUixCSvvbg1x6sh"
 411          address = "2MuhcG52uHPknxDgmGPsV18jSHFBnnRgjPg"
 412          desc = "sh(wpkh(" + wif_priv + "))"
 413          self.log.info("Should import a descriptor with a WIF private key as spendable")
 414          self.test_importdesc({"desc": descsum_create(desc),
 415                                 "timestamp": "now"},
 416                                success=True,
 417                                wallet=wpriv)
 418  
 419          self.log.info('Test can import same descriptor with private key twice')
 420          self.test_importdesc({"desc": descsum_create(desc), "timestamp": "now"}, success=True, wallet=wpriv)
 421  
 422          test_address(wpriv,
 423                       address,
 424                       solvable=True,
 425                       ismine=True)
 426          txid = w0.sendtoaddress(address, 49.99995540)
 427          self.generatetoaddress(self.nodes[0], 6, w0.getnewaddress())
 428          tx = wpriv.createrawtransaction([{"txid": txid, "vout": 0}], {w0.getnewaddress(): 49.999})
 429          signed_tx = wpriv.signrawtransactionwithwallet(tx)
 430          w1.sendrawtransaction(signed_tx['hex'])
 431  
 432          # Make sure that we can use import and use multisig as addresses
 433          self.log.info('Test that multisigs can be imported, signed for, and getnewaddress\'d')
 434          self.nodes[1].createwallet(wallet_name="wmulti_priv", disable_private_keys=False, blank=True, descriptors=True)
 435          wmulti_priv = self.nodes[1].get_wallet_rpc("wmulti_priv")
 436          assert_equal(wmulti_priv.getwalletinfo()['keypoolsize'], 0)
 437  
 438          xprv1 = 'tprv8ZgxMBicQKsPevADjDCWsa6DfhkVXicu8NQUzfibwX2MexVwW4tCec5mXdCW8kJwkzBRRmAay1KZya4WsehVvjTGVW6JLqiqd8DdZ4xSg52'
 439          acc_xpub1 = 'tpubDCJtdt5dgJpdhW4MtaVYDhG4T4tF6jcLR1PxL43q9pq1mxvXgMS9Mzw1HnXG15vxUGQJMMSqCQHMTy3F1eW5VkgVroWzchsPD5BUojrcWs8'  # /84'/0'/0'
 440          chg_xpub1 = 'tpubDCXqdwWZcszwqYJSnZp8eARkxGJfHAk23KDxbztV4BbschfaTfYLTcSkSJ3TN64dRqwa1rnFUScsYormKkGqNbbPwkorQimVevXjxzUV9Gf'  # /84'/1'/0'
 441          xprv2 = 'tprv8ZgxMBicQKsPdSNWUhDiwTScDr6JfkZuLshTRwzvZGnMSnGikV6jxpmdDkC3YRc4T3GD6Nvg9uv6hQg73RVv1EiTXDZwxVbsLugVHU8B1aq'
 442          acc_xprv2 = 'tprv8gVCsmRAxVSxyUpsL13Y7ZEWBFPWbgS5E2MmFVNGuANrknvmmn2vWnmHvU8AwEFYzR2ji6EeZLSCLVacsYkvor3Pcb5JY5FGcevqTwYvdYx'
 443          acc_xpub2 = 'tpubDDBF2BTR6s8drwrfDei8WxtckGuSm1cyoKxYY1QaKSBFbHBYQArWhHPA6eJrzZej6nfHGLSURYSLHr7GuYch8aY5n61tGqgn8b4cXrMuoPH'
 444          chg_xpub2 = 'tpubDCYfZY2ceyHzYzMMVPt9MNeiqtQ2T7Uyp9QSFwYXh8Vi9iJFYXcuphJaGXfF3jUQJi5Y3GMNXvM11gaL4txzZgNGK22BFAwMXynnzv4z2Jh'
 445          xprv3 = 'tprv8ZgxMBicQKsPeonDt8Ka2mrQmHa61hQ5FQCsvWBTpSNzBFgM58cV2EuXNAHF14VawVpznnme3SuTbA62sGriwWyKifJmXntfNeK7zeqMCj1'
 446          acc_xpub3 = 'tpubDCsWoW1kuQB9kG5MXewHqkbjPtqPueRnXju7uM2NK7y3JYb2ajAZ9EiuZXNNuE4661RAfriBWhL8UsnAPpk8zrKKnZw1Ug7X4oHgMdZiU4E'
 447          chg_xpub3 = 'tpubDC6UGqnsQStngYuGD4MKsMy7eD1Yg9NTJfPdvjdG2JE5oZ7EsSL3WHg4Gsw2pR5K39ZwJ46M1wZayhedVdQtMGaUhq5S23PH6fnENK3V1sb'
 448  
 449          self.test_importdesc({"desc":"wsh(multi(2," + xprv1 + "/84h/0h/0h/*," + xprv2 + "/84h/0h/0h/*," + xprv3 + "/84h/0h/0h/*))#m2sr93jn",
 450                              "active": True,
 451                              "range": 1000,
 452                              "next_index": 0,
 453                              "timestamp": "now"},
 454                              success=True,
 455                              wallet=wmulti_priv)
 456          self.test_importdesc({"desc":"wsh(multi(2," + xprv1 + "/84h/1h/0h/*," + xprv2 + "/84h/1h/0h/*," + xprv3 + "/84h/1h/0h/*))#q3sztvx5",
 457                              "active": True,
 458                              "internal" : True,
 459                              "range": 1000,
 460                              "next_index": 0,
 461                              "timestamp": "now"},
 462                              success=True,
 463                              wallet=wmulti_priv)
 464  
 465          assert_equal(wmulti_priv.getwalletinfo()['keypoolsize'], 1001) # Range end (1000) is inclusive, so 1001 addresses generated
 466          addr = wmulti_priv.getnewaddress('', 'bech32') # uses receive 0
 467          assert_equal(addr, 'bcrt1qdt0qy5p7dzhxzmegnn4ulzhard33s2809arjqgjndx87rv5vd0fq2czhy8') # Derived at m/84'/0'/0'/0
 468          change_addr = wmulti_priv.getrawchangeaddress('bech32') # uses change 0
 469          assert_equal(change_addr, 'bcrt1qt9uhe3a9hnq7vajl7a094z4s3crm9ttf8zw3f5v9gr2nyd7e3lnsy44n8e') # Derived at m/84'/1'/0'/0
 470          assert_equal(wmulti_priv.getwalletinfo()['keypoolsize'], 1000)
 471          txid = w0.sendtoaddress(addr, 10)
 472          self.generate(self.nodes[0], 6)
 473          send_txid = wmulti_priv.sendtoaddress(w0.getnewaddress(), 8) # uses change 1
 474          decoded = wmulti_priv.gettransaction(txid=send_txid, verbose=True)['decoded']
 475          assert_equal(len(decoded['vin'][0]['txinwitness']), 4)
 476          self.sync_all()
 477  
 478          self.nodes[1].createwallet(wallet_name="wmulti_pub", disable_private_keys=True, blank=True, descriptors=True)
 479          wmulti_pub = self.nodes[1].get_wallet_rpc("wmulti_pub")
 480          assert_equal(wmulti_pub.getwalletinfo()['keypoolsize'], 0)
 481  
 482          self.test_importdesc({"desc":"wsh(multi(2,[7b2d0242/84h/0h/0h]" + acc_xpub1 + "/*,[59b09cd6/84h/0h/0h]" + acc_xpub2 + "/*,[e81a0532/84h/0h/0h]" + acc_xpub3 +"/*))#tsry0s5e",
 483                              "active": True,
 484                              "range": 1000,
 485                              "next_index": 0,
 486                              "timestamp": "now"},
 487                              success=True,
 488                              wallet=wmulti_pub)
 489          self.test_importdesc({"desc":"wsh(multi(2,[7b2d0242/84h/1h/0h]" + chg_xpub1 + "/*,[59b09cd6/84h/1h/0h]" + chg_xpub2 + "/*,[e81a0532/84h/1h/0h]" + chg_xpub3 + "/*))#c08a2rzv",
 490                              "active": True,
 491                              "internal" : True,
 492                              "range": 1000,
 493                              "next_index": 0,
 494                              "timestamp": "now"},
 495                              success=True,
 496                              wallet=wmulti_pub)
 497  
 498          assert_equal(wmulti_pub.getwalletinfo()['keypoolsize'], 1000) # The first one was already consumed by previous import and is detected as used
 499          addr = wmulti_pub.getnewaddress('', 'bech32') # uses receive 1
 500          assert_equal(addr, 'bcrt1qp8s25ckjl7gr6x2q3dx3tn2pytwp05upkjztk6ey857tt50r5aeqn6mvr9') # Derived at m/84'/0'/0'/1
 501          change_addr = wmulti_pub.getrawchangeaddress('bech32') # uses change 2
 502          assert_equal(change_addr, 'bcrt1qp6j3jw8yetefte7kw6v5pc89rkgakzy98p6gf7ayslaveaxqyjusnw580c') # Derived at m/84'/1'/0'/2
 503          assert send_txid in self.nodes[0].getrawmempool(True)
 504          assert send_txid in (x['txid'] for x in wmulti_pub.listunspent(0))
 505          assert_equal(wmulti_pub.getwalletinfo()['keypoolsize'], 999)
 506  
 507          # generate some utxos for next tests
 508          utxo = self.create_outpoints(w0, outputs=[{addr: 10}])[0]
 509  
 510          addr2 = wmulti_pub.getnewaddress('', 'bech32')
 511          utxo2 = self.create_outpoints(w0, outputs=[{addr2: 10}])[0]
 512  
 513          self.generate(self.nodes[0], 6)
 514          assert_equal(wmulti_pub.getbalance(), wmulti_priv.getbalance())
 515  
 516          # Make sure that descriptor wallets containing multiple xpubs in a single descriptor load correctly
 517          wmulti_pub.unloadwallet()
 518          self.nodes[1].loadwallet('wmulti_pub')
 519  
 520          self.log.info("Multisig with distributed keys")
 521          self.nodes[1].createwallet(wallet_name="wmulti_priv1", descriptors=True)
 522          wmulti_priv1 = self.nodes[1].get_wallet_rpc("wmulti_priv1")
 523          res = wmulti_priv1.importdescriptors([
 524          {
 525              "desc": descsum_create("wsh(multi(2," + xprv1 + "/84h/0h/0h/*,[59b09cd6/84h/0h/0h]" + acc_xpub2 + "/*,[e81a0532/84h/0h/0h]" + acc_xpub3 + "/*))"),
 526              "active": True,
 527              "range": 1000,
 528              "next_index": 0,
 529              "timestamp": "now"
 530          },
 531          {
 532              "desc": descsum_create("wsh(multi(2," + xprv1 + "/84h/1h/0h/*,[59b09cd6/84h/1h/0h]" + chg_xpub2 + "/*,[e81a0532/84h/1h/0h]" + chg_xpub3 + "/*))"),
 533              "active": True,
 534              "internal" : True,
 535              "range": 1000,
 536              "next_index": 0,
 537              "timestamp": "now"
 538          }])
 539          assert_equal(res[0]['success'], True)
 540          assert_equal(res[0]['warnings'][0], 'Not all private keys provided. Some wallet functionality may return unexpected errors')
 541          assert_equal(res[1]['success'], True)
 542          assert_equal(res[1]['warnings'][0], 'Not all private keys provided. Some wallet functionality may return unexpected errors')
 543  
 544          self.nodes[1].createwallet(wallet_name='wmulti_priv2', blank=True, descriptors=True)
 545          wmulti_priv2 = self.nodes[1].get_wallet_rpc('wmulti_priv2')
 546          res = wmulti_priv2.importdescriptors([
 547          {
 548              "desc": descsum_create("wsh(multi(2,[7b2d0242/84h/0h/0h]" + acc_xpub1 + "/*," + xprv2 + "/84h/0h/0h/*,[e81a0532/84h/0h/0h]" + acc_xpub3 + "/*))"),
 549              "active": True,
 550              "range": 1000,
 551              "next_index": 0,
 552              "timestamp": "now"
 553          },
 554          {
 555              "desc": descsum_create("wsh(multi(2,[7b2d0242/84h/1h/0h]" + chg_xpub1 + "/*," + xprv2 + "/84h/1h/0h/*,[e81a0532/84h/1h/0h]" + chg_xpub3 + "/*))"),
 556              "active": True,
 557              "internal" : True,
 558              "range": 1000,
 559              "next_index": 0,
 560              "timestamp": "now"
 561          }])
 562          assert_equal(res[0]['success'], True)
 563          assert_equal(res[0]['warnings'][0], 'Not all private keys provided. Some wallet functionality may return unexpected errors')
 564          assert_equal(res[1]['success'], True)
 565          assert_equal(res[1]['warnings'][0], 'Not all private keys provided. Some wallet functionality may return unexpected errors')
 566  
 567          rawtx = self.nodes[1].createrawtransaction([utxo], {w0.getnewaddress(): 9.999})
 568          tx_signed_1 = wmulti_priv1.signrawtransactionwithwallet(rawtx)
 569          assert_equal(tx_signed_1['complete'], False)
 570          tx_signed_2 = wmulti_priv2.signrawtransactionwithwallet(tx_signed_1['hex'])
 571          assert_equal(tx_signed_2['complete'], True)
 572          self.nodes[1].sendrawtransaction(tx_signed_2['hex'])
 573  
 574          self.log.info("We can create and use a huge multisig under P2WSH")
 575          self.nodes[1].createwallet(wallet_name='wmulti_priv_big', blank=True, descriptors=True)
 576          wmulti_priv_big = self.nodes[1].get_wallet_rpc('wmulti_priv_big')
 577          xkey = "tprv8ZgxMBicQKsPeZSeYx7VXDDTs3XrTcmZQpRLbAeSQFCQGgKwR4gKpcxHaKdoTNHniv4EPDJNdzA3KxRrrBHcAgth8fU5X4oCndkkxk39iAt/*"
 578          xkey_int = "tprv8ZgxMBicQKsPeZSeYx7VXDDTs3XrTcmZQpRLbAeSQFCQGgKwR4gKpcxHaKdoTNHniv4EPDJNdzA3KxRrrBHcAgth8fU5X4oCndkkxk39iAt/1/*"
 579          res = wmulti_priv_big.importdescriptors([
 580          {
 581              "desc": descsum_create(f"wsh(sortedmulti(20,{(xkey + ',') * 19}{xkey}))"),
 582              "active": True,
 583              "range": 1000,
 584              "next_index": 0,
 585              "timestamp": "now"
 586          },
 587          {
 588              "desc": descsum_create(f"wsh(sortedmulti(20,{(xkey_int + ',') * 19}{xkey_int}))"),
 589              "active": True,
 590              "internal": True,
 591              "range": 1000,
 592              "next_index": 0,
 593              "timestamp": "now"
 594          }])
 595          assert_equal(res[0]['success'], True)
 596          assert_equal(res[1]['success'], True)
 597  
 598          addr = wmulti_priv_big.getnewaddress()
 599          w0.sendtoaddress(addr, 10)
 600          self.generate(self.nodes[0], 1)
 601          # It is standard and would relay.
 602          txid = wmulti_priv_big.sendtoaddress(w0.getnewaddress(), 9.999)
 603          decoded = wmulti_priv_big.gettransaction(txid=txid, verbose=True)['decoded']
 604          # 20 sigs + dummy + witness script
 605          assert_equal(len(decoded['vin'][0]['txinwitness']), 22)
 606  
 607  
 608          self.log.info("Under P2SH, multisig are standard with up to 15 "
 609                        "compressed keys")
 610          self.nodes[1].createwallet(wallet_name='multi_priv_big_legacy',
 611                                     blank=True, descriptors=True)
 612          multi_priv_big = self.nodes[1].get_wallet_rpc('multi_priv_big_legacy')
 613          res = multi_priv_big.importdescriptors([
 614          {
 615              "desc": descsum_create(f"sh(multi(15,{(xkey + ',') * 14}{xkey}))"),
 616              "active": True,
 617              "range": 1000,
 618              "next_index": 0,
 619              "timestamp": "now"
 620          },
 621          {
 622              "desc": descsum_create(f"sh(multi(15,{(xkey_int + ',') * 14}{xkey_int}))"),
 623              "active": True,
 624              "internal": True,
 625              "range": 1000,
 626              "next_index": 0,
 627              "timestamp": "now"
 628          }])
 629          assert_equal(res[0]['success'], True)
 630          assert_equal(res[1]['success'], True)
 631  
 632          addr = multi_priv_big.getnewaddress("", "legacy")
 633          w0.sendtoaddress(addr, 10)
 634          self.generate(self.nodes[0], 6)
 635          # It is standard and would relay.
 636          txid = multi_priv_big.sendtoaddress(w0.getnewaddress(), 10, "", "", True)
 637          decoded = multi_priv_big.gettransaction(txid=txid, verbose=True)['decoded']
 638  
 639          self.log.info("Amending multisig with new private keys")
 640          self.nodes[1].createwallet(wallet_name="wmulti_priv3", descriptors=True)
 641          wmulti_priv3 = self.nodes[1].get_wallet_rpc("wmulti_priv3")
 642          res = wmulti_priv3.importdescriptors([
 643              {
 644                  "desc": descsum_create("wsh(multi(2," + xprv1 + "/84h/0h/0h/*,[59b09cd6/84h/0h/0h]" + acc_xpub2 + "/*,[e81a0532/84h/0h/0h]" + acc_xpub3 + "/*))"),
 645                  "active": True,
 646                  "range": 1000,
 647                  "next_index": 0,
 648                  "timestamp": "now"
 649              }])
 650          assert_equal(res[0]['success'], True)
 651          res = wmulti_priv3.importdescriptors([
 652              {
 653                  "desc": descsum_create("wsh(multi(2," + xprv1 + "/84h/0h/0h/*,[59b09cd6/84h/0h/0h]" + acc_xprv2 + "/*,[e81a0532/84h/0h/0h]" + acc_xpub3 + "/*))"),
 654                  "active": True,
 655                  "range": 1000,
 656                  "next_index": 0,
 657                  "timestamp": "now"
 658              }])
 659          assert_equal(res[0]['success'], True)
 660  
 661          rawtx = self.nodes[1].createrawtransaction([utxo2], {w0.getnewaddress(): 9.999})
 662          tx = wmulti_priv3.signrawtransactionwithwallet(rawtx)
 663          assert_equal(tx['complete'], True)
 664          self.nodes[1].sendrawtransaction(tx['hex'])
 665  
 666          self.log.info("Combo descriptors cannot be active")
 667          self.test_importdesc({"desc": descsum_create("combo(tpubDCJtdt5dgJpdhW4MtaVYDhG4T4tF6jcLR1PxL43q9pq1mxvXgMS9Mzw1HnXG15vxUGQJMMSqCQHMTy3F1eW5VkgVroWzchsPD5BUojrcWs8/*)"),
 668                                "active": True,
 669                                "range": 1,
 670                                "timestamp": "now"},
 671                                success=False,
 672                                error_code=-4,
 673                                error_message="Combo descriptors cannot be set to active")
 674  
 675          self.log.info("Descriptors with no type cannot be active")
 676          self.test_importdesc({"desc": descsum_create("pk(tpubDCJtdt5dgJpdhW4MtaVYDhG4T4tF6jcLR1PxL43q9pq1mxvXgMS9Mzw1HnXG15vxUGQJMMSqCQHMTy3F1eW5VkgVroWzchsPD5BUojrcWs8/*)"),
 677                                "active": True,
 678                                "range": 1,
 679                                "timestamp": "now"},
 680                                success=True,
 681                                warnings=["Unknown output type, cannot set descriptor to active."])
 682  
 683          self.log.info("Test importing a descriptor to an encrypted wallet")
 684  
 685          descriptor = {"desc": descsum_create("pkh(" + xpriv + "/1h/*h)"),
 686                                "timestamp": "now",
 687                                "active": True,
 688                                "range": [0,4000],
 689                                "next_index": 4000}
 690  
 691          self.nodes[0].createwallet("temp_wallet", blank=True, descriptors=True)
 692          temp_wallet = self.nodes[0].get_wallet_rpc("temp_wallet")
 693          temp_wallet.importdescriptors([descriptor])
 694          self.generatetoaddress(self.nodes[0], COINBASE_MATURITY + 1, temp_wallet.getnewaddress())
 695          self.generatetoaddress(self.nodes[0], COINBASE_MATURITY + 1, temp_wallet.getnewaddress())
 696  
 697          self.nodes[0].createwallet("encrypted_wallet", blank=True, descriptors=True, passphrase="passphrase")
 698          encrypted_wallet = self.nodes[0].get_wallet_rpc("encrypted_wallet")
 699  
 700          descriptor["timestamp"] = 0
 701          descriptor["next_index"] = 0
 702  
 703          encrypted_wallet.walletpassphrase("passphrase", 99999)
 704          with concurrent.futures.ThreadPoolExecutor(max_workers=1) as thread:
 705              with self.nodes[0].assert_debug_log(expected_msgs=["Rescan started from block 0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206... (slow variant inspecting all blocks)"], timeout=10):
 706                  importing = thread.submit(encrypted_wallet.importdescriptors, requests=[descriptor])
 707  
 708              # Set the passphrase timeout to 1 to test that the wallet remains unlocked during the rescan
 709              self.nodes[0].cli("-rpcwallet=encrypted_wallet").walletpassphrase("passphrase", 1)
 710  
 711              try:
 712                  self.nodes[0].cli("-rpcwallet=encrypted_wallet").walletlock()
 713              except JSONRPCException as e:
 714                  assert e.error["code"] == -4 and "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"]
 715  
 716              try:
 717                  self.nodes[0].cli("-rpcwallet=encrypted_wallet").walletpassphrasechange("passphrase", "newpassphrase")
 718              except JSONRPCException as e:
 719                  assert e.error["code"] == -4 and "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"]
 720  
 721              assert_equal(importing.result(), [{"success": True}])
 722  
 723          assert_equal(temp_wallet.getbalance(), encrypted_wallet.getbalance())
 724  
 725          self.log.info("Multipath descriptors")
 726          self.nodes[1].createwallet(wallet_name="multipath", descriptors=True, blank=True)
 727          w_multipath = self.nodes[1].get_wallet_rpc("multipath")
 728          self.nodes[1].createwallet(wallet_name="multipath_split", descriptors=True, blank=True)
 729          w_multisplit = self.nodes[1].get_wallet_rpc("multipath_split")
 730          timestamp = int(time.time())
 731  
 732          self.test_importdesc({"desc": descsum_create(f"wpkh({xpriv}/<10;20>/0/*)"),
 733                                "active": True,
 734                                "range": 10,
 735                                "timestamp": "now",
 736                                "label": "some label"},
 737                                success=False,
 738                                error_code=-8,
 739                                error_message="Multipath descriptors should not have a label",
 740                                wallet=w_multipath)
 741          self.test_importdesc({"desc": descsum_create(f"wpkh({xpriv}/<10;20>/0/*)"),
 742                                "active": True,
 743                                "range": 10,
 744                                "timestamp": timestamp,
 745                                "internal": True},
 746                                success=False,
 747                                error_code=-5,
 748                                error_message="Cannot have multipath descriptor while also specifying \'internal\'",
 749                                wallet=w_multipath)
 750  
 751          self.test_importdesc({"desc": descsum_create(f"wpkh({xpriv}/<10;20>/0/*)"),
 752                                "active": True,
 753                                "range": 10,
 754                                "timestamp": timestamp},
 755                                success=True,
 756                                wallet=w_multipath)
 757  
 758          self.test_importdesc({"desc": descsum_create(f"wpkh({xpriv}/10/0/*)"),
 759                                "active": True,
 760                                "range": 10,
 761                                "timestamp": timestamp},
 762                                success=True,
 763                                wallet=w_multisplit)
 764          self.test_importdesc({"desc": descsum_create(f"wpkh({xpriv}/20/0/*)"),
 765                                "active": True,
 766                                "range": 10,
 767                                "internal": True,
 768                                "timestamp": timestamp},
 769                                success=True,
 770                                wallet=w_multisplit)
 771          for _ in range(0, 10):
 772              assert_equal(w_multipath.getnewaddress(address_type="bech32"), w_multisplit.getnewaddress(address_type="bech32"))
 773              assert_equal(w_multipath.getrawchangeaddress(address_type="bech32"), w_multisplit.getrawchangeaddress(address_type="bech32"))
 774          assert_equal(sorted(w_multipath.listdescriptors()["descriptors"], key=lambda x: x["desc"]), sorted(w_multisplit.listdescriptors()["descriptors"], key=lambda x: x["desc"]))
 775  
 776  if __name__ == '__main__':
 777      ImportDescriptorsTest(__file__).main()
 778