From 223588b1bbc63dc57098bbd0baa48635e0cc0b82 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Tue, 16 Jul 2019 15:33:35 -0400 Subject: [PATCH] Add a --descriptors option to various tests Adds a --descriptors option globally to the test framework. This will make the test create and use descriptor wallets. However some tests may not work with this. Some tests are modified to work with --descriptors and run with that option in test_runer: * wallet_basic.py * wallet_encryption.py * wallet_keypool.py * wallet_keypool_topup.py * wallet_labels.py * wallet_avoidreuse.py --- test/functional/rpc_createmultisig.py | 41 +++++-- test/functional/rpc_psbt.py | 42 +++++-- test/functional/test_framework/key.py | 13 +++ .../test_framework/test_framework.py | 18 ++- test/functional/test_framework/test_node.py | 28 ++++- test/functional/test_runner.py | 9 ++ test/functional/wallet_avoidreuse.py | 63 +++++------ test/functional/wallet_basic.py | 84 +++++++------- test/functional/wallet_hd.py | 105 ++++++++++-------- test/functional/wallet_keypool.py | 78 +++++++++++-- test/functional/wallet_keypool_topup.py | 10 +- test/functional/wallet_labels.py | 19 ++-- 12 files changed, 347 insertions(+), 163 deletions(-) diff --git a/test/functional/rpc_createmultisig.py b/test/functional/rpc_createmultisig.py index a9837161777..56e9ecfcc21 100755 --- a/test/functional/rpc_createmultisig.py +++ b/test/functional/rpc_createmultisig.py @@ -4,13 +4,14 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test multisig RPCs""" +from test_framework.authproxy import JSONRPCException from test_framework.descriptors import descsum_create, drop_origins from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_raises_rpc_error, assert_equal, ) -from test_framework.key import ECPubKey +from test_framework.key import ECPubKey, ECKey, bytes_to_wif import binascii import decimal @@ -28,10 +29,14 @@ class RpcCreateMultiSigTest(BitcoinTestFramework): self.skip_if_no_wallet() def get_keys(self): + self.pub = [] + self.priv = [] node0, node1, node2 = self.nodes - add = [node1.getnewaddress() for _ in range(self.nkeys)] - self.pub = [node1.getaddressinfo(a)["pubkey"] for a in add] - self.priv = [node1.dumpprivkey(a) for a in add] + for _ in range(self.nkeys): + k = ECKey() + k.generate() + self.pub.append(k.get_pubkey().get_bytes().hex()) + self.priv.append(bytes_to_wif(k.get_bytes(), k.is_compressed)) self.final = node2.getnewaddress() def run_test(self): @@ -64,17 +69,20 @@ class RpcCreateMultiSigTest(BitcoinTestFramework): pk_obj.compressed = False pk2 = binascii.hexlify(pk_obj.get_bytes()).decode() + node0.createwallet(wallet_name='wmulti0', disable_private_keys=True) + wmulti0 = node0.get_wallet_rpc('wmulti0') + # Check all permutations of keys because order matters apparently for keys in itertools.permutations([pk0, pk1, pk2]): # Results should be the same as this legacy one legacy_addr = node0.createmultisig(2, keys, 'legacy')['address'] - assert_equal(legacy_addr, node0.addmultisigaddress(2, keys, '', 'legacy')['address']) + assert_equal(legacy_addr, wmulti0.addmultisigaddress(2, keys, '', 'legacy')['address']) # Generate addresses with the segwit types. These should all make legacy addresses - assert_equal(legacy_addr, node0.createmultisig(2, keys, 'bech32')['address']) - assert_equal(legacy_addr, node0.createmultisig(2, keys, 'p2sh-segwit')['address']) - assert_equal(legacy_addr, node0.addmultisigaddress(2, keys, '', 'bech32')['address']) - assert_equal(legacy_addr, node0.addmultisigaddress(2, keys, '', 'p2sh-segwit')['address']) + assert_equal(legacy_addr, wmulti0.createmultisig(2, keys, 'bech32')['address']) + assert_equal(legacy_addr, wmulti0.createmultisig(2, keys, 'p2sh-segwit')['address']) + assert_equal(legacy_addr, wmulti0.addmultisigaddress(2, keys, '', 'bech32')['address']) + assert_equal(legacy_addr, wmulti0.addmultisigaddress(2, keys, '', 'p2sh-segwit')['address']) self.log.info('Testing sortedmulti descriptors with BIP 67 test vectors') with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data/rpc_bip67.json'), encoding='utf-8') as f: @@ -89,6 +97,8 @@ class RpcCreateMultiSigTest(BitcoinTestFramework): assert_equal(self.nodes[0].deriveaddresses(sorted_key_desc)[0], t['address']) def check_addmultisigaddress_errors(self): + if self.options.descriptors: + return self.log.info('Check that addmultisigaddress fails when the private keys are missing') addresses = [self.nodes[1].getnewaddress(address_type='legacy') for _ in range(2)] assert_raises_rpc_error(-5, 'no full public key for address', lambda: self.nodes[0].addmultisigaddress(nrequired=1, keys=addresses)) @@ -115,6 +125,15 @@ class RpcCreateMultiSigTest(BitcoinTestFramework): def do_multisig(self): node0, node1, node2 = self.nodes + if 'wmulti' not in node1.listwallets(): + try: + node1.loadwallet('wmulti') + except JSONRPCException as e: + if e.error['code'] == -18 and 'Wallet wmulti not found' in e.error['message']: + node1.createwallet(wallet_name='wmulti', disable_private_keys=True) + else: + raise + wmulti = node1.get_wallet_rpc('wmulti') # Construct the expected descriptor desc = 'multi({},{})'.format(self.nsigs, ','.join(self.pub)) @@ -134,7 +153,7 @@ class RpcCreateMultiSigTest(BitcoinTestFramework): assert madd[0:4] == "bcrt" # actually a bech32 address # compare against addmultisigaddress - msigw = node1.addmultisigaddress(self.nsigs, self.pub, None, self.output_type) + msigw = wmulti.addmultisigaddress(self.nsigs, self.pub, None, self.output_type) maddw = msigw["address"] mredeemw = msigw["redeemScript"] assert_equal(desc, drop_origins(msigw['descriptor'])) @@ -194,6 +213,8 @@ class RpcCreateMultiSigTest(BitcoinTestFramework): txinfo = node0.getrawtransaction(tx, True, blk) self.log.info("n/m=%d/%d %s size=%d vsize=%d weight=%d" % (self.nsigs, self.nkeys, self.output_type, txinfo["size"], txinfo["vsize"], txinfo["weight"])) + wmulti.unloadwallet() + if __name__ == '__main__': RpcCreateMultiSigTest().main() diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py index ea8510f92b7..51d136d26af 100755 --- a/test/functional/rpc_psbt.py +++ b/test/functional/rpc_psbt.py @@ -48,18 +48,23 @@ class PSBTTest(BitcoinTestFramework): disconnect_nodes(offline_node, 2) disconnect_nodes(mining_node, 0) + # Create watchonly on online_node + online_node.createwallet(wallet_name='wonline', disable_private_keys=True) + wonline = online_node.get_wallet_rpc('wonline') + w2 = online_node.get_wallet_rpc('') + # Mine a transaction that credits the offline address offline_addr = offline_node.getnewaddress(address_type="p2sh-segwit") - online_addr = online_node.getnewaddress(address_type="p2sh-segwit") - online_node.importaddress(offline_addr, "", False) + online_addr = w2.getnewaddress(address_type="p2sh-segwit") + wonline.importaddress(offline_addr, "", False) mining_node.sendtoaddress(address=offline_addr, amount=1.0) mining_node.generate(nblocks=1) self.sync_blocks([mining_node, online_node]) # Construct an unsigned PSBT on the online node (who doesn't know the output is Segwit, so will include a non-witness UTXO) - utxos = online_node.listunspent(addresses=[offline_addr]) - raw = online_node.createrawtransaction([{"txid":utxos[0]["txid"], "vout":utxos[0]["vout"]}],[{online_addr:0.9999}]) - psbt = online_node.walletprocesspsbt(online_node.converttopsbt(raw))["psbt"] + utxos = wonline.listunspent(addresses=[offline_addr]) + raw = wonline.createrawtransaction([{"txid":utxos[0]["txid"], "vout":utxos[0]["vout"]}],[{online_addr:0.9999}]) + psbt = wonline.walletprocesspsbt(online_node.converttopsbt(raw))["psbt"] assert "non_witness_utxo" in mining_node.decodepsbt(psbt)["inputs"][0] # Have the offline node sign the PSBT (which will update the UTXO to segwit) @@ -72,6 +77,8 @@ class PSBTTest(BitcoinTestFramework): self.sync_blocks([mining_node, online_node]) assert_equal(online_node.gettxout(txid,0)["confirmations"], 1) + wonline.unloadwallet() + # Reconnect connect_nodes(self.nodes[0], 1) connect_nodes(self.nodes[0], 2) @@ -89,13 +96,23 @@ class PSBTTest(BitcoinTestFramework): final_tx = self.nodes[0].finalizepsbt(signed_tx)['hex'] self.nodes[0].sendrawtransaction(final_tx) - # Create p2sh, p2wpkh, and p2wsh addresses + # Get pubkeys pubkey0 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())['pubkey'] pubkey1 = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress())['pubkey'] pubkey2 = self.nodes[2].getaddressinfo(self.nodes[2].getnewaddress())['pubkey'] - p2sh = self.nodes[1].addmultisigaddress(2, [pubkey0, pubkey1, pubkey2], "", "legacy")['address'] - p2wsh = self.nodes[1].addmultisigaddress(2, [pubkey0, pubkey1, pubkey2], "", "bech32")['address'] - p2sh_p2wsh = self.nodes[1].addmultisigaddress(2, [pubkey0, pubkey1, pubkey2], "", "p2sh-segwit")['address'] + + # Setup watchonly wallets + self.nodes[2].createwallet(wallet_name='wmulti', disable_private_keys=True) + wmulti = self.nodes[2].get_wallet_rpc('wmulti') + + # Create all the addresses + p2sh = wmulti.addmultisigaddress(2, [pubkey0, pubkey1, pubkey2], "", "legacy")['address'] + p2wsh = wmulti.addmultisigaddress(2, [pubkey0, pubkey1, pubkey2], "", "bech32")['address'] + p2sh_p2wsh = wmulti.addmultisigaddress(2, [pubkey0, pubkey1, pubkey2], "", "p2sh-segwit")['address'] + if not self.options.descriptors: + wmulti.importaddress(p2sh) + wmulti.importaddress(p2wsh) + wmulti.importaddress(p2sh_p2wsh) p2wpkh = self.nodes[1].getnewaddress("", "bech32") p2pkh = self.nodes[1].getnewaddress("", "legacy") p2sh_p2wpkh = self.nodes[1].getnewaddress("", "p2sh-segwit") @@ -146,11 +163,14 @@ class PSBTTest(BitcoinTestFramework): assert_raises_rpc_error(-4, "Fee exceeds maximum configured by -maxtxfee", self.nodes[1].walletcreatefundedpsbt, [{"txid":txid,"vout":p2wpkh_pos},{"txid":txid,"vout":p2sh_p2wpkh_pos},{"txid":txid,"vout":p2pkh_pos}], {self.nodes[1].getnewaddress():29.99}, 0, {"feeRate": 10}) # partially sign multisig things with node 1 - psbtx = self.nodes[1].walletcreatefundedpsbt([{"txid":txid,"vout":p2wsh_pos},{"txid":txid,"vout":p2sh_pos},{"txid":txid,"vout":p2sh_p2wsh_pos}], {self.nodes[1].getnewaddress():29.99})['psbt'] + psbtx = wmulti.walletcreatefundedpsbt(inputs=[{"txid":txid,"vout":p2wsh_pos},{"txid":txid,"vout":p2sh_pos},{"txid":txid,"vout":p2sh_p2wsh_pos}], outputs={self.nodes[1].getnewaddress():29.99}, options={'changeAddress': self.nodes[1].getrawchangeaddress()})['psbt'] walletprocesspsbt_out = self.nodes[1].walletprocesspsbt(psbtx) psbtx = walletprocesspsbt_out['psbt'] assert_equal(walletprocesspsbt_out['complete'], False) + # Unload wmulti, we don't need it anymore + wmulti.unloadwallet() + # partially sign with node 2. This should be complete and sendable walletprocesspsbt_out = self.nodes[2].walletprocesspsbt(psbtx) assert_equal(walletprocesspsbt_out['complete'], True) @@ -297,7 +317,7 @@ class PSBTTest(BitcoinTestFramework): # Signer tests for i, signer in enumerate(signers): - self.nodes[2].createwallet("wallet{}".format(i)) + self.nodes[2].createwallet(wallet_name="wallet{}".format(i)) wrpc = self.nodes[2].get_wallet_rpc("wallet{}".format(i)) for key in signer['privkeys']: wrpc.importprivkey(key) diff --git a/test/functional/test_framework/key.py b/test/functional/test_framework/key.py index 912c0ca978e..f2d6fba4a6f 100644 --- a/test/functional/test_framework/key.py +++ b/test/functional/test_framework/key.py @@ -8,6 +8,8 @@ keys, and is trivially vulnerable to side channel attacks. Do not use for anything but tests.""" import random +from .address import byte_to_base58 + def modinv(a, n): """Compute the modular inverse of a modulo n @@ -384,3 +386,14 @@ class ECKey(): rb = r.to_bytes((r.bit_length() + 8) // 8, 'big') sb = s.to_bytes((s.bit_length() + 8) // 8, 'big') return b'\x30' + bytes([4 + len(rb) + len(sb), 2, len(rb)]) + rb + bytes([2, len(sb)]) + sb + +def bytes_to_wif(b, compressed=True): + if compressed: + b += b'\x01' + return byte_to_base58(b, 239) + +def generate_wif_key(): + # Makes a WIF privkey for imports + k = ECKey() + k.generate() + return bytes_to_wif(k.get_bytes(), k.is_compressed) diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index d4cf5f8896a..8719bd0d392 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -165,6 +165,8 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): help="run nodes under the valgrind memory error detector: expect at least a ~10x slowdown, valgrind 3.14 or later required") parser.add_argument("--randomseed", type=int, help="set a random seed for deterministically reproducing a previous test run") + parser.add_argument("--descriptors", default=False, action="store_true", + help="Run test using a descriptor wallet") self.add_options(parser) self.options = parser.parse_args() @@ -333,11 +335,23 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): def setup_nodes(self): """Override this method to customize test node setup""" - extra_args = None + extra_args = [[]] * self.num_nodes + wallets = [[]] * self.num_nodes if hasattr(self, "extra_args"): extra_args = self.extra_args + wallets = [[x for x in eargs if x.startswith('-wallet=')] for eargs in extra_args] + extra_args = [x + ['-nowallet'] for x in extra_args] self.add_nodes(self.num_nodes, extra_args) self.start_nodes() + for i, n in enumerate(self.nodes): + n.extra_args.pop() + if '-wallet=0' in n.extra_args or '-nowallet' in n.extra_args or '-disablewallet' in n.extra_args or not self.is_wallet_compiled(): + continue + if '-wallet=' not in wallets[i] and not any([x.startswith('-wallet=') for x in wallets[i]]): + wallets[i].append('-wallet=') + for w in wallets[i]: + wallet_name = w.split('=', 1)[1] + n.createwallet(wallet_name=wallet_name, descriptors=self.options.descriptors) self.import_deterministic_coinbase_privkeys() if not self.setup_clean_chain: for n in self.nodes: @@ -408,6 +422,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): use_cli=self.options.usecli, start_perf=self.options.perf, use_valgrind=self.options.valgrind, + descriptors=self.options.descriptors, )) def start_node(self, i, *args, **kwargs): @@ -547,6 +562,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): bitcoin_cli=self.options.bitcoincli, coverage_dir=None, cwd=self.options.tmpdir, + descriptors=self.options.descriptors, )) self.start_node(CACHE_NODE_ID) cache_node = self.nodes[CACHE_NODE_ID] diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index 61214e55536..de724407a02 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -62,7 +62,7 @@ class TestNode(): To make things easier for the test writer, any unrecognised messages will be dispatched to the RPC connection.""" - def __init__(self, i, datadir, *, chain, rpchost, timewait, bitcoind, bitcoin_cli, coverage_dir, cwd, extra_conf=None, extra_args=None, use_cli=False, start_perf=False, use_valgrind=False, version=None): + def __init__(self, i, datadir, *, chain, rpchost, timewait, bitcoind, bitcoin_cli, coverage_dir, cwd, extra_conf=None, extra_args=None, use_cli=False, start_perf=False, use_valgrind=False, version=None, descriptors=False): """ Kwargs: start_perf (bool): If True, begin profiling the node with `perf` as soon as @@ -80,6 +80,7 @@ class TestNode(): self.binary = bitcoind self.coverage_dir = coverage_dir self.cwd = cwd + self.descriptors = descriptors if extra_conf is not None: append_config(datadir, extra_conf) # Most callers will just need to add extra args to the standard list below. @@ -171,10 +172,10 @@ class TestNode(): def __getattr__(self, name): """Dispatches any unrecognised messages to the RPC connection or a CLI instance.""" if self.use_cli: - return getattr(RPCOverloadWrapper(self.cli, True), name) + return getattr(RPCOverloadWrapper(self.cli, True, self.descriptors), name) else: assert self.rpc_connected and self.rpc is not None, self._node_msg("Error: no RPC connection") - return getattr(RPCOverloadWrapper(self.rpc), name) + return getattr(RPCOverloadWrapper(self.rpc, descriptors=self.descriptors), name) def start(self, extra_args=None, *, cwd=None, stdout=None, stderr=None, **kwargs): """Start the node.""" @@ -266,11 +267,11 @@ class TestNode(): def get_wallet_rpc(self, wallet_name): if self.use_cli: - return RPCOverloadWrapper(self.cli("-rpcwallet={}".format(wallet_name)), True) + return RPCOverloadWrapper(self.cli("-rpcwallet={}".format(wallet_name)), True, self.descriptors) else: assert self.rpc_connected and self.rpc, self._node_msg("RPC not connected") wallet_path = "wallet/{}".format(urllib.parse.quote(wallet_name)) - return RPCOverloadWrapper(self.rpc / wallet_path) + return RPCOverloadWrapper(self.rpc / wallet_path, descriptors=self.descriptors) def stop_node(self, expected_stderr='', wait=0): """Stop the node.""" @@ -598,13 +599,28 @@ class TestNodeCLI(): return cli_stdout.rstrip("\n") class RPCOverloadWrapper(): - def __init__(self, rpc, cli=False): + def __init__(self, rpc, cli=False, descriptors=False): self.rpc = rpc self.is_cli = cli + self.descriptors = descriptors def __getattr__(self, name): return getattr(self.rpc, name) + def createwallet(self, wallet_name, disable_private_keys=None, blank=None, passphrase=None, avoid_reuse=None, descriptors=None): + if self.is_cli: + if disable_private_keys is None: + disable_private_keys = 'null' + if blank is None: + blank = 'null' + if passphrase is None: + passphrase = '' + if avoid_reuse is None: + avoid_reuse = 'null' + if descriptors is None: + descriptors = self.descriptors + return self.__getattr__('createwallet')(wallet_name, disable_private_keys, blank, passphrase, avoid_reuse, descriptors) + def importprivkey(self, privkey, label=None, rescan=None): wallet_info = self.getwalletinfo() if self.is_cli: diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 1c5afef3a8e..b8523e16b79 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -76,6 +76,7 @@ BASE_SCRIPTS = [ # Scripts that are run by default. # Longest test should go first, to favor running tests in parallel 'wallet_hd.py', + 'wallet_hd.py --descriptors', 'wallet_backup.py', # vv Tests less than 5m vv 'mining_getblocktemplate_longpoll.py', @@ -86,7 +87,9 @@ BASE_SCRIPTS = [ 'feature_segwit.py', # vv Tests less than 2m vv 'wallet_basic.py', + 'wallet_basic.py --descriptors', 'wallet_labels.py', + 'wallet_labels.py --descriptors', 'p2p_segwit.py', 'p2p_timeouts.py', 'p2p_tx_download.py', @@ -109,6 +112,7 @@ BASE_SCRIPTS = [ 'feature_abortnode.py', # vv Tests less than 30s vv 'wallet_keypool_topup.py', + 'wallet_keypool_topup.py --descriptors', 'feature_fee_estimation.py', 'interface_zmq.py', 'interface_bitcoin_cli.py', @@ -122,6 +126,7 @@ BASE_SCRIPTS = [ 'interface_rest.py', 'mempool_spend_coinbase.py', 'wallet_avoidreuse.py', + 'wallet_avoidreuse.py --descriptors', 'mempool_reorg.py', 'mempool_persist.py', 'wallet_multiwallet.py', @@ -134,6 +139,7 @@ BASE_SCRIPTS = [ 'interface_http.py', 'interface_rpc.py', 'rpc_psbt.py', + 'rpc_psbt.py --descriptors', 'rpc_users.py', 'rpc_whitelist.py', 'feature_proxy.py', @@ -147,6 +153,7 @@ BASE_SCRIPTS = [ 'p2p_addr_relay.py', 'rpc_net.py', 'wallet_keypool.py', + 'wallet_keypool.py --descriptors', 'wallet_descriptor.py', 'p2p_mempool.py', 'p2p_filter.py', @@ -169,6 +176,7 @@ BASE_SCRIPTS = [ 'mempool_packages.py', 'mempool_package_onemore.py', 'rpc_createmultisig.py', + 'rpc_createmultisig.py --descriptors', 'feature_versionbits_warning.py', 'rpc_preciousblock.py', 'wallet_importprunedfunds.py', @@ -192,6 +200,7 @@ BASE_SCRIPTS = [ 'wallet_listsinceblock.py', 'p2p_leak.py', 'wallet_encryption.py', + 'wallet_encryption.py --descriptors', 'feature_dersig.py', 'feature_cltv.py', 'rpc_uptime.py', diff --git a/test/functional/wallet_avoidreuse.py b/test/functional/wallet_avoidreuse.py index 78a51a1d5ff..780cce9d028 100755 --- a/test/functional/wallet_avoidreuse.py +++ b/test/functional/wallet_avoidreuse.py @@ -133,7 +133,7 @@ class AvoidReuseTest(BitcoinTestFramework): tempwallet = ".wallet_avoidreuse.py_test_immutable_wallet.dat" # Create a wallet with disable_private_keys set; this should work - self.nodes[1].createwallet(tempwallet, True) + self.nodes[1].createwallet(wallet_name=tempwallet, disable_private_keys=True) w = self.nodes[1].get_wallet_rpc(tempwallet) # Attempt to unset the disable_private_keys flag; this should not work @@ -249,43 +249,44 @@ class AvoidReuseTest(BitcoinTestFramework): # getbalances should show no used, 5 btc trusted assert_balances(self.nodes[1], mine={"used": 0, "trusted": 5}) - # For the second send, we transmute it to a related single-key address - # to make sure it's also detected as re-use - fund_spk = self.nodes[0].getaddressinfo(fundaddr)["scriptPubKey"] - fund_decoded = self.nodes[0].decodescript(fund_spk) - if second_addr_type == "p2sh-segwit": - new_fundaddr = fund_decoded["segwit"]["p2sh-segwit"] - elif second_addr_type == "bech32": - new_fundaddr = fund_decoded["segwit"]["addresses"][0] - else: - new_fundaddr = fundaddr - assert_equal(second_addr_type, "legacy") + if not self.options.descriptors: + # For the second send, we transmute it to a related single-key address + # to make sure it's also detected as re-use + fund_spk = self.nodes[0].getaddressinfo(fundaddr)["scriptPubKey"] + fund_decoded = self.nodes[0].decodescript(fund_spk) + if second_addr_type == "p2sh-segwit": + new_fundaddr = fund_decoded["segwit"]["p2sh-segwit"] + elif second_addr_type == "bech32": + new_fundaddr = fund_decoded["segwit"]["addresses"][0] + else: + new_fundaddr = fundaddr + assert_equal(second_addr_type, "legacy") - self.nodes[0].sendtoaddress(new_fundaddr, 10) - self.nodes[0].generate(1) - self.sync_all() + self.nodes[0].sendtoaddress(new_fundaddr, 10) + self.nodes[0].generate(1) + self.sync_all() - # listunspent should show 2 total outputs (5, 10 btc), one unused (5), one reused (10) - assert_unspent(self.nodes[1], total_count=2, total_sum=15, reused_count=1, reused_sum=10) - # getbalances should show 10 used, 5 btc trusted - assert_balances(self.nodes[1], mine={"used": 10, "trusted": 5}) + # listunspent should show 2 total outputs (5, 10 btc), one unused (5), one reused (10) + assert_unspent(self.nodes[1], total_count=2, total_sum=15, reused_count=1, reused_sum=10) + # getbalances should show 10 used, 5 btc trusted + assert_balances(self.nodes[1], mine={"used": 10, "trusted": 5}) - # node 1 should now have a balance of 5 (no dirty) or 15 (including dirty) - assert_approx(self.nodes[1].getbalance(), 5, 0.001) - assert_approx(self.nodes[1].getbalance(avoid_reuse=False), 15, 0.001) + # node 1 should now have a balance of 5 (no dirty) or 15 (including dirty) + assert_approx(self.nodes[1].getbalance(), 5, 0.001) + assert_approx(self.nodes[1].getbalance(avoid_reuse=False), 15, 0.001) - assert_raises_rpc_error(-6, "Insufficient funds", self.nodes[1].sendtoaddress, retaddr, 10) + assert_raises_rpc_error(-6, "Insufficient funds", self.nodes[1].sendtoaddress, retaddr, 10) - self.nodes[1].sendtoaddress(retaddr, 4) + self.nodes[1].sendtoaddress(retaddr, 4) - # listunspent should show 2 total outputs (1, 10 btc), one unused (1), one reused (10) - assert_unspent(self.nodes[1], total_count=2, total_sum=11, reused_count=1, reused_sum=10) - # getbalances should show 10 used, 1 btc trusted - assert_balances(self.nodes[1], mine={"used": 10, "trusted": 1}) + # listunspent should show 2 total outputs (1, 10 btc), one unused (1), one reused (10) + assert_unspent(self.nodes[1], total_count=2, total_sum=11, reused_count=1, reused_sum=10) + # getbalances should show 10 used, 1 btc trusted + assert_balances(self.nodes[1], mine={"used": 10, "trusted": 1}) - # node 1 should now have about 1 btc left (no dirty) and 11 (including dirty) - assert_approx(self.nodes[1].getbalance(), 1, 0.001) - assert_approx(self.nodes[1].getbalance(avoid_reuse=False), 11, 0.001) + # node 1 should now have about 1 btc left (no dirty) and 11 (including dirty) + assert_approx(self.nodes[1].getbalance(), 1, 0.001) + assert_approx(self.nodes[1].getbalance(avoid_reuse=False), 11, 0.001) def test_getbalances_used(self): ''' diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py index 15746d312c1..2dddbf2cf30 100755 --- a/test/functional/wallet_basic.py +++ b/test/functional/wallet_basic.py @@ -49,6 +49,7 @@ class WalletTest(BitcoinTestFramework): return self.nodes[0].decoderawtransaction(txn)['vsize'] def run_test(self): + # Check that there's no UTXO on none of the nodes assert_equal(len(self.nodes[0].listunspent()), 0) assert_equal(len(self.nodes[1].listunspent()), 0) @@ -219,7 +220,7 @@ class WalletTest(BitcoinTestFramework): assert_equal(self.nodes[2].getbalance(), node_2_bal) node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), node_0_bal + Decimal('10'), fee_per_byte, self.get_vsize(self.nodes[2].gettransaction(txid)['hex'])) - self.start_node(3) + self.start_node(3, self.nodes[3].extra_args) connect_nodes(self.nodes[0], 3) self.sync_all() @@ -315,57 +316,59 @@ class WalletTest(BitcoinTestFramework): # This will raise an exception since generate does not accept a string assert_raises_rpc_error(-1, "not an integer", self.nodes[0].generate, "2") - # This will raise an exception for the invalid private key format - assert_raises_rpc_error(-5, "Invalid private key encoding", self.nodes[0].importprivkey, "invalid") + if not self.options.descriptors: - # This will raise an exception for importing an address with the PS2H flag - temp_address = self.nodes[1].getnewaddress("", "p2sh-segwit") - assert_raises_rpc_error(-5, "Cannot use the p2sh flag with an address - use a script instead", self.nodes[0].importaddress, temp_address, "label", False, True) + # This will raise an exception for the invalid private key format + assert_raises_rpc_error(-5, "Invalid private key encoding", self.nodes[0].importprivkey, "invalid") - # This will raise an exception for attempting to dump the private key of an address you do not own - assert_raises_rpc_error(-3, "Address does not refer to a key", self.nodes[0].dumpprivkey, temp_address) + # This will raise an exception for importing an address with the PS2H flag + temp_address = self.nodes[1].getnewaddress("", "p2sh-segwit") + assert_raises_rpc_error(-5, "Cannot use the p2sh flag with an address - use a script instead", self.nodes[0].importaddress, temp_address, "label", False, True) - # This will raise an exception for attempting to get the private key of an invalid Bitcoin address - assert_raises_rpc_error(-5, "Invalid Bitcoin address", self.nodes[0].dumpprivkey, "invalid") + # This will raise an exception for attempting to dump the private key of an address you do not own + assert_raises_rpc_error(-3, "Address does not refer to a key", self.nodes[0].dumpprivkey, temp_address) - # This will raise an exception for attempting to set a label for an invalid Bitcoin address - assert_raises_rpc_error(-5, "Invalid Bitcoin address", self.nodes[0].setlabel, "invalid address", "label") + # This will raise an exception for attempting to get the private key of an invalid Bitcoin address + assert_raises_rpc_error(-5, "Invalid Bitcoin address", self.nodes[0].dumpprivkey, "invalid") - # This will raise an exception for importing an invalid address - assert_raises_rpc_error(-5, "Invalid Bitcoin address or script", self.nodes[0].importaddress, "invalid") + # This will raise an exception for attempting to set a label for an invalid Bitcoin address + assert_raises_rpc_error(-5, "Invalid Bitcoin address", self.nodes[0].setlabel, "invalid address", "label") - # This will raise an exception for attempting to import a pubkey that isn't in hex - assert_raises_rpc_error(-5, "Pubkey must be a hex string", self.nodes[0].importpubkey, "not hex") + # This will raise an exception for importing an invalid address + assert_raises_rpc_error(-5, "Invalid Bitcoin address or script", self.nodes[0].importaddress, "invalid") - # This will raise an exception for importing an invalid pubkey - assert_raises_rpc_error(-5, "Pubkey is not a valid public key", self.nodes[0].importpubkey, "5361746f736869204e616b616d6f746f") + # This will raise an exception for attempting to import a pubkey that isn't in hex + assert_raises_rpc_error(-5, "Pubkey must be a hex string", self.nodes[0].importpubkey, "not hex") - # Import address and private key to check correct behavior of spendable unspents - # 1. Send some coins to generate new UTXO - address_to_import = self.nodes[2].getnewaddress() - txid = self.nodes[0].sendtoaddress(address_to_import, 1) - self.nodes[0].generate(1) - self.sync_all(self.nodes[0:3]) + # This will raise an exception for importing an invalid pubkey + assert_raises_rpc_error(-5, "Pubkey is not a valid public key", self.nodes[0].importpubkey, "5361746f736869204e616b616d6f746f") - # 2. Import address from node2 to node1 - self.nodes[1].importaddress(address_to_import) + # Import address and private key to check correct behavior of spendable unspents + # 1. Send some coins to generate new UTXO + address_to_import = self.nodes[2].getnewaddress() + txid = self.nodes[0].sendtoaddress(address_to_import, 1) + self.nodes[0].generate(1) + self.sync_all(self.nodes[0:3]) - # 3. Validate that the imported address is watch-only on node1 - assert self.nodes[1].getaddressinfo(address_to_import)["iswatchonly"] + # 2. Import address from node2 to node1 + self.nodes[1].importaddress(address_to_import) - # 4. Check that the unspents after import are not spendable - assert_array_result(self.nodes[1].listunspent(), - {"address": address_to_import}, - {"spendable": False}) + # 3. Validate that the imported address is watch-only on node1 + assert self.nodes[1].getaddressinfo(address_to_import)["iswatchonly"] - # 5. Import private key of the previously imported address on node1 - priv_key = self.nodes[2].dumpprivkey(address_to_import) - self.nodes[1].importprivkey(priv_key) + # 4. Check that the unspents after import are not spendable + assert_array_result(self.nodes[1].listunspent(), + {"address": address_to_import}, + {"spendable": False}) - # 6. Check that the unspents are now spendable on node1 - assert_array_result(self.nodes[1].listunspent(), - {"address": address_to_import}, - {"spendable": True}) + # 5. Import private key of the previously imported address on node1 + priv_key = self.nodes[2].dumpprivkey(address_to_import) + self.nodes[1].importprivkey(priv_key) + + # 6. Check that the unspents are now spendable on node1 + assert_array_result(self.nodes[1].listunspent(), + {"address": address_to_import}, + {"spendable": True}) # Mine a block from node0 to an address from node1 coinbase_addr = self.nodes[1].getnewaddress() @@ -460,7 +463,8 @@ class WalletTest(BitcoinTestFramework): # Try with walletrejectlongchains # Double chain limit but require combining inputs, so we pass SelectCoinsMinConf self.stop_node(0) - self.start_node(0, extra_args=["-walletrejectlongchains", "-limitancestorcount=" + str(2 * chainlimit)]) + extra_args = ["-walletrejectlongchains", "-limitancestorcount=" + str(2 * chainlimit)] + self.start_node(0, extra_args=extra_args) # wait for loadmempool timeout = 10 diff --git a/test/functional/wallet_hd.py b/test/functional/wallet_hd.py index e4328f2b0e1..09f89eb59dc 100755 --- a/test/functional/wallet_hd.py +++ b/test/functional/wallet_hd.py @@ -27,17 +27,21 @@ class WalletHDTest(BitcoinTestFramework): def run_test(self): # Make sure we use hd, keep masterkeyid - masterkeyid = self.nodes[1].getwalletinfo()['hdseedid'] - assert_equal(len(masterkeyid), 40) + hd_fingerprint = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress())['hdmasterfingerprint'] + assert_equal(len(hd_fingerprint), 8) # create an internal key change_addr = self.nodes[1].getrawchangeaddress() change_addrV= self.nodes[1].getaddressinfo(change_addr) - assert_equal(change_addrV["hdkeypath"], "m/0'/1'/0'") #first internal child key + if self.options.descriptors: + assert_equal(change_addrV["hdkeypath"], "m/84'/1'/0'/1/0") + else: + assert_equal(change_addrV["hdkeypath"], "m/0'/1'/0'") #first internal child key # Import a non-HD private key in the HD wallet - non_hd_add = self.nodes[0].getnewaddress() - self.nodes[1].importprivkey(self.nodes[0].dumpprivkey(non_hd_add)) + non_hd_add = 'bcrt1qmevj8zfx0wdvp05cqwkmr6mxkfx60yezwjksmt' + non_hd_key = 'cS9umN9w6cDMuRVYdbkfE4c7YUFLJRoXMfhQ569uY4odiQbVN8Rt' + self.nodes[1].importprivkey(non_hd_key) # This should be enough to keep the master key and the non-HD key self.nodes[1].backupwallet(os.path.join(self.nodes[1].datadir, "hd.bak")) @@ -48,11 +52,14 @@ class WalletHDTest(BitcoinTestFramework): self.nodes[0].generate(101) hd_add = None NUM_HD_ADDS = 10 - for i in range(NUM_HD_ADDS): + for i in range(1, NUM_HD_ADDS + 1): hd_add = self.nodes[1].getnewaddress() hd_info = self.nodes[1].getaddressinfo(hd_add) - assert_equal(hd_info["hdkeypath"], "m/0'/0'/"+str(i)+"'") - assert_equal(hd_info["hdseedid"], masterkeyid) + if self.options.descriptors: + assert_equal(hd_info["hdkeypath"], "m/84'/1'/0'/0/" + str(i)) + else: + assert_equal(hd_info["hdkeypath"], "m/0'/0'/"+str(i)+"'") + assert_equal(hd_info["hdmasterfingerprint"], hd_fingerprint) self.nodes[0].sendtoaddress(hd_add, 1) self.nodes[0].generate(1) self.nodes[0].sendtoaddress(non_hd_add, 1) @@ -61,7 +68,10 @@ class WalletHDTest(BitcoinTestFramework): # create an internal key (again) change_addr = self.nodes[1].getrawchangeaddress() change_addrV= self.nodes[1].getaddressinfo(change_addr) - assert_equal(change_addrV["hdkeypath"], "m/0'/1'/1'") #second internal child key + if self.options.descriptors: + assert_equal(change_addrV["hdkeypath"], "m/84'/1'/0'/1/1") + else: + assert_equal(change_addrV["hdkeypath"], "m/0'/1'/1'") #second internal child key self.sync_all() assert_equal(self.nodes[1].getbalance(), NUM_HD_ADDS + 1) @@ -72,16 +82,19 @@ class WalletHDTest(BitcoinTestFramework): # otherwise node1 would auto-recover all funds in flag the keypool keys as used shutil.rmtree(os.path.join(self.nodes[1].datadir, self.chain, "blocks")) shutil.rmtree(os.path.join(self.nodes[1].datadir, self.chain, "chainstate")) - shutil.copyfile(os.path.join(self.nodes[1].datadir, "hd.bak"), os.path.join(self.nodes[1].datadir, self.chain, "wallets", "wallet.dat")) + shutil.copyfile(os.path.join(self.nodes[1].datadir, "hd.bak"), os.path.join(self.nodes[1].datadir, self.chain, 'wallets', "wallet.dat")) self.start_node(1) # Assert that derivation is deterministic hd_add_2 = None - for i in range(NUM_HD_ADDS): + for i in range(1, NUM_HD_ADDS + 1): hd_add_2 = self.nodes[1].getnewaddress() hd_info_2 = self.nodes[1].getaddressinfo(hd_add_2) - assert_equal(hd_info_2["hdkeypath"], "m/0'/0'/"+str(i)+"'") - assert_equal(hd_info_2["hdseedid"], masterkeyid) + if self.options.descriptors: + assert_equal(hd_info_2["hdkeypath"], "m/84'/1'/0'/0/" + str(i)) + else: + assert_equal(hd_info_2["hdkeypath"], "m/0'/0'/"+str(i)+"'") + assert_equal(hd_info_2["hdmasterfingerprint"], hd_fingerprint) assert_equal(hd_add, hd_add_2) connect_nodes(self.nodes[0], 1) self.sync_all() @@ -117,41 +130,45 @@ class WalletHDTest(BitcoinTestFramework): if out['value'] != 1: keypath = self.nodes[1].getaddressinfo(out['scriptPubKey']['addresses'][0])['hdkeypath'] - assert_equal(keypath[0:7], "m/0'/1'") + if self.options.descriptors: + assert_equal(keypath[0:14], "m/84'/1'/0'/1/") + else: + assert_equal(keypath[0:7], "m/0'/1'") - # Generate a new HD seed on node 1 and make sure it is set - orig_masterkeyid = self.nodes[1].getwalletinfo()['hdseedid'] - self.nodes[1].sethdseed() - new_masterkeyid = self.nodes[1].getwalletinfo()['hdseedid'] - assert orig_masterkeyid != new_masterkeyid - addr = self.nodes[1].getnewaddress() - assert_equal(self.nodes[1].getaddressinfo(addr)['hdkeypath'], 'm/0\'/0\'/0\'') # Make sure the new address is the first from the keypool - self.nodes[1].keypoolrefill(1) # Fill keypool with 1 key + if not self.options.descriptors: + # Generate a new HD seed on node 1 and make sure it is set + orig_masterkeyid = self.nodes[1].getwalletinfo()['hdseedid'] + self.nodes[1].sethdseed() + new_masterkeyid = self.nodes[1].getwalletinfo()['hdseedid'] + assert orig_masterkeyid != new_masterkeyid + addr = self.nodes[1].getnewaddress() + assert_equal(self.nodes[1].getaddressinfo(addr)['hdkeypath'], 'm/0\'/0\'/0\'') # Make sure the new address is the first from the keypool + self.nodes[1].keypoolrefill(1) # Fill keypool with 1 key - # Set a new HD seed on node 1 without flushing the keypool - new_seed = self.nodes[0].dumpprivkey(self.nodes[0].getnewaddress()) - orig_masterkeyid = new_masterkeyid - self.nodes[1].sethdseed(False, new_seed) - new_masterkeyid = self.nodes[1].getwalletinfo()['hdseedid'] - assert orig_masterkeyid != new_masterkeyid - addr = self.nodes[1].getnewaddress() - assert_equal(orig_masterkeyid, self.nodes[1].getaddressinfo(addr)['hdseedid']) - assert_equal(self.nodes[1].getaddressinfo(addr)['hdkeypath'], 'm/0\'/0\'/1\'') # Make sure the new address continues previous keypool + # Set a new HD seed on node 1 without flushing the keypool + new_seed = self.nodes[0].dumpprivkey(self.nodes[0].getnewaddress()) + orig_masterkeyid = new_masterkeyid + self.nodes[1].sethdseed(False, new_seed) + new_masterkeyid = self.nodes[1].getwalletinfo()['hdseedid'] + assert orig_masterkeyid != new_masterkeyid + addr = self.nodes[1].getnewaddress() + assert_equal(orig_masterkeyid, self.nodes[1].getaddressinfo(addr)['hdseedid']) + assert_equal(self.nodes[1].getaddressinfo(addr)['hdkeypath'], 'm/0\'/0\'/1\'') # Make sure the new address continues previous keypool - # Check that the next address is from the new seed - self.nodes[1].keypoolrefill(1) - next_addr = self.nodes[1].getnewaddress() - assert_equal(new_masterkeyid, self.nodes[1].getaddressinfo(next_addr)['hdseedid']) - assert_equal(self.nodes[1].getaddressinfo(next_addr)['hdkeypath'], 'm/0\'/0\'/0\'') # Make sure the new address is not from previous keypool - assert next_addr != addr + # Check that the next address is from the new seed + self.nodes[1].keypoolrefill(1) + next_addr = self.nodes[1].getnewaddress() + assert_equal(new_masterkeyid, self.nodes[1].getaddressinfo(next_addr)['hdseedid']) + assert_equal(self.nodes[1].getaddressinfo(next_addr)['hdkeypath'], 'm/0\'/0\'/0\'') # Make sure the new address is not from previous keypool + assert next_addr != addr - # Sethdseed parameter validity - assert_raises_rpc_error(-1, 'sethdseed', self.nodes[0].sethdseed, False, new_seed, 0) - assert_raises_rpc_error(-5, "Invalid private key", self.nodes[1].sethdseed, False, "not_wif") - assert_raises_rpc_error(-1, "JSON value is not a boolean as expected", self.nodes[1].sethdseed, "Not_bool") - assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[1].sethdseed, False, True) - assert_raises_rpc_error(-5, "Already have this key", self.nodes[1].sethdseed, False, new_seed) - assert_raises_rpc_error(-5, "Already have this key", self.nodes[1].sethdseed, False, self.nodes[1].dumpprivkey(self.nodes[1].getnewaddress())) + # Sethdseed parameter validity + assert_raises_rpc_error(-1, 'sethdseed', self.nodes[0].sethdseed, False, new_seed, 0) + assert_raises_rpc_error(-5, "Invalid private key", self.nodes[1].sethdseed, False, "not_wif") + assert_raises_rpc_error(-1, "JSON value is not a boolean as expected", self.nodes[1].sethdseed, "Not_bool") + assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[1].sethdseed, False, True) + assert_raises_rpc_error(-5, "Already have this key", self.nodes[1].sethdseed, False, new_seed) + assert_raises_rpc_error(-5, "Already have this key", self.nodes[1].sethdseed, False, self.nodes[1].dumpprivkey(self.nodes[1].getnewaddress())) if __name__ == '__main__': WalletHDTest().main () diff --git a/test/functional/wallet_keypool.py b/test/functional/wallet_keypool.py index 9e2f00e62fb..40a2b3ab6a1 100755 --- a/test/functional/wallet_keypool.py +++ b/test/functional/wallet_keypool.py @@ -22,16 +22,63 @@ class KeyPoolTest(BitcoinTestFramework): addr_before_encrypting = nodes[0].getnewaddress() addr_before_encrypting_data = nodes[0].getaddressinfo(addr_before_encrypting) wallet_info_old = nodes[0].getwalletinfo() - assert addr_before_encrypting_data['hdseedid'] == wallet_info_old['hdseedid'] + if not self.options.descriptors: + assert addr_before_encrypting_data['hdseedid'] == wallet_info_old['hdseedid'] # Encrypt wallet and wait to terminate nodes[0].encryptwallet('test') + if self.options.descriptors: + # Import hardened derivation only descriptors + nodes[0].walletpassphrase('test', 10) + nodes[0].importdescriptors([ + { + "desc": "wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0h/*h)#y4dfsj7n", + "timestamp": "now", + "range": [0,0], + "active": True + }, + { + "desc": "pkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1h/*h)#a0nyvl0k", + "timestamp": "now", + "range": [0,0], + "active": True + }, + { + "desc": "sh(wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/2h/*h))#lmeu2axg", + "timestamp": "now", + "range": [0,0], + "active": True + }, + { + "desc": "wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/3h/*h)#jkl636gm", + "timestamp": "now", + "range": [0,0], + "active": True, + "internal": True + }, + { + "desc": "pkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/4h/*h)#l3crwaus", + "timestamp": "now", + "range": [0,0], + "active": True, + "internal": True + }, + { + "desc": "sh(wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/5h/*h))#qg8wa75f", + "timestamp": "now", + "range": [0,0], + "active": True, + "internal": True + } + ]) + nodes[0].walletlock() # Keep creating keys addr = nodes[0].getnewaddress() addr_data = nodes[0].getaddressinfo(addr) wallet_info = nodes[0].getwalletinfo() - assert addr_before_encrypting_data['hdseedid'] != wallet_info['hdseedid'] - assert addr_data['hdseedid'] == wallet_info['hdseedid'] + assert addr_before_encrypting_data['hdmasterfingerprint'] != addr_data['hdmasterfingerprint'] + if not self.options.descriptors: + assert addr_data['hdseedid'] == wallet_info['hdseedid'] assert_raises_rpc_error(-12, "Error: Keypool ran out, please call keypoolrefill first", nodes[0].getnewaddress) # put six (plus 2) new keys in the keypool (100% external-, +100% internal-keys, 1 in min) @@ -39,8 +86,12 @@ class KeyPoolTest(BitcoinTestFramework): nodes[0].keypoolrefill(6) nodes[0].walletlock() wi = nodes[0].getwalletinfo() - assert_equal(wi['keypoolsize_hd_internal'], 6) - assert_equal(wi['keypoolsize'], 6) + if self.options.descriptors: + assert_equal(wi['keypoolsize_hd_internal'], 18) + assert_equal(wi['keypoolsize'], 18) + else: + assert_equal(wi['keypoolsize_hd_internal'], 6) + assert_equal(wi['keypoolsize'], 6) # drain the internal keys nodes[0].getrawchangeaddress() @@ -80,11 +131,15 @@ class KeyPoolTest(BitcoinTestFramework): nodes[0].walletpassphrase('test', 100) nodes[0].keypoolrefill(100) wi = nodes[0].getwalletinfo() - assert_equal(wi['keypoolsize_hd_internal'], 100) - assert_equal(wi['keypoolsize'], 100) + if self.options.descriptors: + assert_equal(wi['keypoolsize_hd_internal'], 300) + assert_equal(wi['keypoolsize'], 300) + else: + assert_equal(wi['keypoolsize_hd_internal'], 100) + assert_equal(wi['keypoolsize'], 100) # create a blank wallet - nodes[0].createwallet(wallet_name='w2', blank=True) + nodes[0].createwallet(wallet_name='w2', blank=True, disable_private_keys=True) w2 = nodes[0].get_wallet_rpc('w2') # refer to initial wallet as w1 @@ -92,8 +147,11 @@ class KeyPoolTest(BitcoinTestFramework): # import private key and fund it address = addr.pop() - privkey = w1.dumpprivkey(address) - res = w2.importmulti([{'scriptPubKey': {'address': address}, 'keys': [privkey], 'timestamp': 'now'}]) + desc = w1.getaddressinfo(address)['desc'] + if self.options.descriptors: + res = w2.importdescriptors([{'desc': desc, 'timestamp': 'now'}]) + else: + res = w2.importmulti([{'desc': desc, 'timestamp': 'now'}]) assert_equal(res[0]['success'], True) w1.walletpassphrase('test', 100) diff --git a/test/functional/wallet_keypool_topup.py b/test/functional/wallet_keypool_topup.py index 829633a0500..102ed23fba5 100755 --- a/test/functional/wallet_keypool_topup.py +++ b/test/functional/wallet_keypool_topup.py @@ -79,7 +79,15 @@ class KeypoolRestoreTest(BitcoinTestFramework): assert_equal(self.nodes[idx].getbalance(), 15) assert_equal(self.nodes[idx].listtransactions()[0]['category'], "receive") # Check that we have marked all keys up to the used keypool key as used - assert_equal(self.nodes[idx].getaddressinfo(self.nodes[idx].getnewaddress())['hdkeypath'], "m/0'/0'/110'") + if self.options.descriptors: + if output_type == 'legacy': + assert_equal(self.nodes[idx].getaddressinfo(self.nodes[idx].getnewaddress(address_type=output_type))['hdkeypath'], "m/44'/1'/0'/0/110") + elif output_type == 'p2sh-segwit': + assert_equal(self.nodes[idx].getaddressinfo(self.nodes[idx].getnewaddress(address_type=output_type))['hdkeypath'], "m/49'/1'/0'/0/110") + elif output_type == 'bech32': + assert_equal(self.nodes[idx].getaddressinfo(self.nodes[idx].getnewaddress(address_type=output_type))['hdkeypath'], "m/84'/1'/0'/0/110") + else: + assert_equal(self.nodes[idx].getaddressinfo(self.nodes[idx].getnewaddress(address_type=output_type))['hdkeypath'], "m/0'/0'/110'") if __name__ == '__main__': diff --git a/test/functional/wallet_labels.py b/test/functional/wallet_labels.py index 337d2e55d9f..f8d1720469e 100755 --- a/test/functional/wallet_labels.py +++ b/test/functional/wallet_labels.py @@ -115,15 +115,16 @@ class WalletLabelsTest(BitcoinTestFramework): assert_raises_rpc_error(-11, "No addresses with label", node.getaddressesbylabel, "") # Check that addmultisigaddress can assign labels. - for label in labels: - addresses = [] - for x in range(10): - addresses.append(node.getnewaddress()) - multisig_address = node.addmultisigaddress(5, addresses, label.name)['address'] - label.add_address(multisig_address) - label.purpose[multisig_address] = "send" - label.verify(node) - node.generate(101) + if not self.options.descriptors: + for label in labels: + addresses = [] + for x in range(10): + addresses.append(node.getnewaddress()) + multisig_address = node.addmultisigaddress(5, addresses, label.name)['address'] + label.add_address(multisig_address) + label.purpose[multisig_address] = "send" + label.verify(node) + node.generate(101) # Check that setlabel can change the label of an address from a # different label.