From 94c87bbbd06eb9a57930b9f59315533cfbe8f460 Mon Sep 17 00:00:00 2001 From: Ava Chow Date: Thu, 8 May 2025 11:32:15 -0700 Subject: [PATCH 1/6] test: Remove unnecessary importprivkey from wallet_createwallet This test was testing importprivkey behavior in a legacy wallet without private keys. As legacy wallets no longer exist, this test case is no longer relevant. --- test/functional/wallet_createwallet.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/functional/wallet_createwallet.py b/test/functional/wallet_createwallet.py index 85970d049c9..f6da0605b8a 100755 --- a/test/functional/wallet_createwallet.py +++ b/test/functional/wallet_createwallet.py @@ -45,7 +45,6 @@ class CreateWalletTest(BitcoinTestFramework): self.log.info('Test that private keys cannot be imported') privkey, pubkey = generate_keypair(wif=True) - assert_raises_rpc_error(-4, 'Cannot import private keys to a wallet with private keys disabled', w1.importprivkey, privkey) result = w1.importdescriptors([{'desc': descsum_create('wpkh(' + privkey + ')'), 'timestamp': 'now'}]) assert not result[0]['success'] assert 'warnings' not in result[0] From fcc457573f9b39e6f173a4f51c45d7dbb47e7ab0 Mon Sep 17 00:00:00 2001 From: Ava Chow Date: Thu, 8 May 2025 11:26:34 -0700 Subject: [PATCH 2/6] test: Replace importprivkey with wallet_importprivkey importprivkey was a legacy wallet only RPC which had a helper for descriptor wallets in tests. Add wallet_importprivkey helper and use it wherever importprivkey is used (other than backward compatibility tests) --- test/functional/rpc_getblockstats.py | 3 ++- test/functional/rpc_psbt.py | 5 +++-- test/functional/test_framework/test_framework.py | 3 ++- test/functional/test_framework/test_node.py | 14 -------------- test/functional/test_framework/util.py | 11 +++++++++++ test/functional/tool_signet_miner.py | 7 +++++-- test/functional/wallet_createwallet.py | 3 ++- test/functional/wallet_hd.py | 3 ++- test/functional/wallet_importprunedfunds.py | 5 +++-- test/functional/wallet_listsinceblock.py | 5 +++-- 10 files changed, 33 insertions(+), 26 deletions(-) diff --git a/test/functional/rpc_getblockstats.py b/test/functional/rpc_getblockstats.py index 002763201a5..8a88aaa5787 100755 --- a/test/functional/rpc_getblockstats.py +++ b/test/functional/rpc_getblockstats.py @@ -12,6 +12,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, assert_raises_rpc_error, + wallet_importprivkey, ) import json import os @@ -45,7 +46,7 @@ class GetblockstatsTest(BitcoinTestFramework): self.nodes[0].setmocktime(mocktime) self.nodes[0].createwallet(wallet_name='test') privkey = self.nodes[0].get_deterministic_priv_key().key - self.nodes[0].importprivkey(privkey) + wallet_importprivkey(self.nodes[0], privkey, 0) self.generate(self.nodes[0], COINBASE_MATURITY + 1) diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py index deb519803cf..9d4750a7322 100755 --- a/test/functional/rpc_psbt.py +++ b/test/functional/rpc_psbt.py @@ -44,6 +44,7 @@ from test_framework.util import ( assert_greater_than_or_equal, assert_raises_rpc_error, find_vout_for_address, + wallet_importprivkey, ) from test_framework.wallet_util import ( calculate_input_weight, @@ -622,7 +623,7 @@ class PSBTTest(BitcoinTestFramework): 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) + wallet_importprivkey(wrpc, key, "now") signed_tx = wrpc.walletprocesspsbt(signer['psbt'], True, "ALL")['psbt'] assert_equal(signed_tx, signer['result']) @@ -878,7 +879,7 @@ class PSBTTest(BitcoinTestFramework): addr = self.nodes[0].deriveaddresses(desc)[0] self.nodes[0].sendtoaddress(addr, 10) self.generate(self.nodes[0], 1) - self.nodes[0].importprivkey(privkey) + wallet_importprivkey(self.nodes[0], privkey, "now") psbt = watchonly.sendall([wallet.getnewaddress()])["psbt"] signed_tx = self.nodes[0].walletprocesspsbt(psbt) diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 75a0cb6f112..062ea5f5291 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -35,6 +35,7 @@ from .util import ( initialize_datadir, p2p_port, wait_until_helper_internal, + wallet_importprivkey, ) @@ -485,7 +486,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): n = self.nodes[node] if wallet_name is not None: n.createwallet(wallet_name=wallet_name, load_on_startup=True) - n.importprivkey(privkey=n.get_deterministic_priv_key().key, label='coinbase', rescan=True) + wallet_importprivkey(n.get_wallet_rpc(wallet_name), n.get_deterministic_priv_key().key, 0, label="coinbase") def run_test(self): """Tests must override this method to define test logic""" diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index 9f0baaccca2..02e3b0c4e30 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -931,20 +931,6 @@ class RPCOverloadWrapper(): def __getattr__(self, name): return getattr(self.rpc, name) - def importprivkey(self, privkey, *, label=None, rescan=None): - wallet_info = self.getwalletinfo() - if 'descriptors' not in wallet_info or ('descriptors' in wallet_info and not wallet_info['descriptors']): - return self.__getattr__('importprivkey')(privkey, label, rescan) - desc = descsum_create('combo(' + privkey + ')') - req = [{ - 'desc': desc, - 'timestamp': 0 if rescan else 'now', - 'label': label if label else '', - }] - import_res = self.importdescriptors(req) - if not import_res[0]['success']: - raise JSONRPCException(import_res[0]['error']) - def addmultisigaddress(self, nrequired, keys, *, label=None, address_type=None): wallet_info = self.getwalletinfo() if 'descriptors' not in wallet_info or ('descriptors' in wallet_info and not wallet_info['descriptors']): diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index c7b5cc5d592..172f26de73b 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -20,6 +20,7 @@ import time from . import coverage from .authproxy import AuthServiceProxy, JSONRPCException +from .descriptors import descsum_create from collections.abc import Callable from typing import Optional, Union @@ -609,3 +610,13 @@ def sync_txindex(test_framework, node): sync_start = int(time.time()) test_framework.wait_until(lambda: node.getindexinfo("txindex")["txindex"]["synced"]) test_framework.log.debug(f"Synced in {time.time() - sync_start} seconds") + +def wallet_importprivkey(wallet_rpc, privkey, timestamp, *, label=""): + desc = descsum_create("combo(" + privkey + ")") + req = [{ + "desc": desc, + "timestamp": timestamp, + "label": label, + }] + import_res = wallet_rpc.importdescriptors(req) + assert_equal(import_res[0]["success"], True) diff --git a/test/functional/tool_signet_miner.py b/test/functional/tool_signet_miner.py index c7d5e90fce1..d2c0f6e3abc 100755 --- a/test/functional/tool_signet_miner.py +++ b/test/functional/tool_signet_miner.py @@ -14,7 +14,10 @@ from test_framework.blocktools import DIFF_1_N_BITS from test_framework.key import ECKey from test_framework.script_util import key_to_p2wpkh_script from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal +from test_framework.util import ( + assert_equal, + wallet_importprivkey, +) from test_framework.wallet_util import bytes_to_wif @@ -42,7 +45,7 @@ class SignetMinerTest(BitcoinTestFramework): def run_test(self): node = self.nodes[0] # import private key needed for signing block - node.importprivkey(bytes_to_wif(CHALLENGE_PRIVATE_KEY)) + wallet_importprivkey(node, bytes_to_wif(CHALLENGE_PRIVATE_KEY), "now") # generate block with signet miner tool base_dir = self.config["environment"]["SRCDIR"] diff --git a/test/functional/wallet_createwallet.py b/test/functional/wallet_createwallet.py index f6da0605b8a..edd54fe4387 100755 --- a/test/functional/wallet_createwallet.py +++ b/test/functional/wallet_createwallet.py @@ -10,6 +10,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, assert_raises_rpc_error, + wallet_importprivkey, ) from test_framework.wallet_util import generate_keypair, WalletUnlock @@ -65,7 +66,7 @@ class CreateWalletTest(BitcoinTestFramework): assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w3.getnewaddress) assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w3.getrawchangeaddress) # Import private key - w3.importprivkey(generate_keypair(wif=True)[0]) + wallet_importprivkey(w3, generate_keypair(wif=True)[0], "now") # Imported private keys are currently ignored by the keypool assert_equal(w3.getwalletinfo()['keypoolsize'], 0) assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w3.getnewaddress) diff --git a/test/functional/wallet_hd.py b/test/functional/wallet_hd.py index 23b14f00c0c..4eb080329cd 100755 --- a/test/functional/wallet_hd.py +++ b/test/functional/wallet_hd.py @@ -10,6 +10,7 @@ from test_framework.blocktools import COINBASE_MATURITY from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, + wallet_importprivkey, ) @@ -39,7 +40,7 @@ class WalletHDTest(BitcoinTestFramework): # Import a non-HD private key in the HD wallet non_hd_add = 'bcrt1qmevj8zfx0wdvp05cqwkmr6mxkfx60yezwjksmt' non_hd_key = 'cS9umN9w6cDMuRVYdbkfE4c7YUFLJRoXMfhQ569uY4odiQbVN8Rt' - self.nodes[1].importprivkey(non_hd_key) + wallet_importprivkey(self.nodes[1], non_hd_key, "now") # This should be enough to keep the master key and the non-HD key self.nodes[1].backupwallet(self.nodes[1].datadir_path / "hd.bak") diff --git a/test/functional/wallet_importprunedfunds.py b/test/functional/wallet_importprunedfunds.py index 6c822d1cd9f..b97735389c7 100755 --- a/test/functional/wallet_importprunedfunds.py +++ b/test/functional/wallet_importprunedfunds.py @@ -15,6 +15,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, assert_raises_rpc_error, + wallet_importprivkey, ) from test_framework.wallet_util import generate_keypair @@ -38,7 +39,7 @@ class ImportPrunedFundsTest(BitcoinTestFramework): # privkey address3_privkey, address3_pubkey = generate_keypair(wif=True) address3 = key_to_p2wpkh(address3_pubkey) - self.nodes[0].importprivkey(address3_privkey) + wallet_importprivkey(self.nodes[0], address3_privkey, "now") # Check only one address address_info = self.nodes[0].getaddressinfo(address1) @@ -95,7 +96,7 @@ class ImportPrunedFundsTest(BitcoinTestFramework): # Import with private key with no rescan w1 = self.nodes[1].get_wallet_rpc(self.default_wallet_name) - w1.importprivkey(privkey=address3_privkey, rescan=False) + wallet_importprivkey(w1, address3_privkey, "now") w1.importprunedfunds(rawtxn3, proof3) assert [tx for tx in w1.listtransactions() if tx['txid'] == txnid3] balance3 = w1.getbalance() diff --git a/test/functional/wallet_listsinceblock.py b/test/functional/wallet_listsinceblock.py index 3328ae98e62..d1f5d3f2c35 100755 --- a/test/functional/wallet_listsinceblock.py +++ b/test/functional/wallet_listsinceblock.py @@ -13,6 +13,7 @@ from test_framework.util import ( assert_array_result, assert_equal, assert_raises_rpc_error, + wallet_importprivkey, ) from test_framework.wallet_util import generate_keypair @@ -227,10 +228,10 @@ class ListSinceBlockTest(BitcoinTestFramework): address = key_to_p2wpkh(pubkey) self.nodes[2].sendtoaddress(address, 10) self.generate(self.nodes[2], 6) - self.nodes[2].importprivkey(privkey) + wallet_importprivkey(self.nodes[2], privkey, "now") utxos = self.nodes[2].listunspent() utxo = [u for u in utxos if u["address"] == address][0] - self.nodes[1].importprivkey(privkey) + wallet_importprivkey(self.nodes[1], privkey, "now") # Split network into two self.split_network() From d3142077794f4e910d3cdc749020d725e30feb24 Mon Sep 17 00:00:00 2001 From: Ava Chow Date: Thu, 8 May 2025 11:48:38 -0700 Subject: [PATCH 3/6] test: Replace usage of importaddress --- test/functional/rpc_psbt.py | 3 +- test/functional/test_framework/test_node.py | 27 ------------------ test/functional/wallet_basic.py | 3 +- test/functional/wallet_importprunedfunds.py | 2 +- test/functional/wallet_labels.py | 13 +++++---- test/functional/wallet_reindex.py | 6 ++-- .../wallet_transactiontime_rescan.py | 28 +++++++++++++------ 7 files changed, 34 insertions(+), 48 deletions(-) diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py index 9d4750a7322..fbbbae91a7c 100755 --- a/test/functional/rpc_psbt.py +++ b/test/functional/rpc_psbt.py @@ -112,7 +112,8 @@ class PSBTTest(BitcoinTestFramework): # Mine a transaction that credits the offline address offline_addr = offline_node.getnewaddress(address_type="bech32m") online_addr = w2.getnewaddress(address_type="bech32m") - wonline.importaddress(offline_addr, label="", rescan=False) + import_res = wonline.importdescriptors([{"desc": offline_node.getaddressinfo(offline_addr)["desc"], "timestamp": "now"}]) + assert_equal(import_res[0]["success"], True) mining_wallet = mining_node.get_wallet_rpc(self.default_wallet_name) mining_wallet.sendtoaddress(address=offline_addr, amount=1.0) self.generate(mining_node, nblocks=1, sync_fun=lambda: self.sync_all([online_node, mining_node])) diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index 02e3b0c4e30..14d1e5a09ed 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -959,30 +959,3 @@ class RPCOverloadWrapper(): import_res = self.importdescriptors(req) if not import_res[0]['success']: raise JSONRPCException(import_res[0]['error']) - - def importaddress(self, address, *, label=None, rescan=None, p2sh=None): - wallet_info = self.getwalletinfo() - if 'descriptors' not in wallet_info or ('descriptors' in wallet_info and not wallet_info['descriptors']): - return self.__getattr__('importaddress')(address, label, rescan, p2sh) - is_hex = False - try: - int(address ,16) - is_hex = True - desc = descsum_create('raw(' + address + ')') - except Exception: - desc = descsum_create('addr(' + address + ')') - reqs = [{ - 'desc': desc, - 'timestamp': 0 if rescan else 'now', - 'label': label if label else '', - }] - if is_hex and p2sh: - reqs.append({ - 'desc': descsum_create('p2sh(raw(' + address + '))'), - 'timestamp': 0 if rescan else 'now', - 'label': label if label else '', - }) - import_res = self.importdescriptors(reqs) - for res in import_res: - if not res['success']: - raise JSONRPCException(res['error']) diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py index ce5790100b8..fe8b53bcaa7 100755 --- a/test/functional/wallet_basic.py +++ b/test/functional/wallet_basic.py @@ -673,7 +673,8 @@ class WalletTest(BitcoinTestFramework): self.generate(self.wallet, 1, sync_fun=self.no_op) self.nodes[0].createwallet("watch_wallet", disable_private_keys=True) watch_wallet = self.nodes[0].get_wallet_rpc("watch_wallet") - watch_wallet.importaddress(self.wallet.get_address()) + import_res = watch_wallet.importdescriptors([{"desc": self.wallet.get_descriptor(), "timestamp": "now"}]) + assert_equal(import_res[0]["success"], True) # DEFAULT_ANCESTOR_LIMIT transactions off a confirmed tx should be fine chain = self.wallet.create_self_transfer_chain(chain_length=DEFAULT_ANCESTOR_LIMIT) diff --git a/test/functional/wallet_importprunedfunds.py b/test/functional/wallet_importprunedfunds.py index b97735389c7..b7b2589f554 100755 --- a/test/functional/wallet_importprunedfunds.py +++ b/test/functional/wallet_importprunedfunds.py @@ -90,7 +90,7 @@ class ImportPrunedFundsTest(BitcoinTestFramework): # Import with affiliated address with no rescan self.nodes[1].createwallet('wwatch', disable_private_keys=True) wwatch = self.nodes[1].get_wallet_rpc('wwatch') - wwatch.importaddress(address=address2, rescan=False) + wwatch.importdescriptors([{"desc": self.nodes[0].getaddressinfo(address2)["desc"], "timestamp": "now"}]) wwatch.importprunedfunds(rawtransaction=rawtxn2, txoutproof=proof2) assert [tx for tx in wwatch.listtransactions(include_watchonly=True) if tx['txid'] == txnid2] diff --git a/test/functional/wallet_labels.py b/test/functional/wallet_labels.py index 800f3ad7c40..2f580e18d92 100755 --- a/test/functional/wallet_labels.py +++ b/test/functional/wallet_labels.py @@ -12,6 +12,7 @@ RPCs tested are: from collections import defaultdict from test_framework.blocktools import COINBASE_MATURITY +from test_framework.descriptors import descsum_create from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_raises_rpc_error from test_framework.wallet_util import test_address @@ -176,17 +177,17 @@ class WalletLabelsTest(BitcoinTestFramework): } for l in BECH32_VALID: ad = BECH32_VALID[l] - wallet_watch_only.importaddress(label=l, rescan=False, address=ad) + import_res = wallet_watch_only.importdescriptors([{"desc": descsum_create(f"addr({ad})"), "timestamp": "now", "label": l}]) + assert_equal(import_res[0]["success"], True) self.generatetoaddress(node, 1, ad) assert_equal(wallet_watch_only.getaddressesbylabel(label=l), {ad: {'purpose': 'receive'}}) assert_equal(wallet_watch_only.getreceivedbylabel(label=l), 0) for l in BECH32_INVALID: ad = BECH32_INVALID[l] - assert_raises_rpc_error( - -5, - "Address is not valid", - lambda: wallet_watch_only.importaddress(label=l, rescan=False, address=ad), - ) + import_res = wallet_watch_only.importdescriptors([{"desc": descsum_create(f"addr({ad})"), "timestamp": "now", "label": l}]) + assert_equal(import_res[0]["success"], False) + assert_equal(import_res[0]["error"]["code"], -5) + assert_equal(import_res[0]["error"]["message"], "Address is not valid") class Label: diff --git a/test/functional/wallet_reindex.py b/test/functional/wallet_reindex.py index 32e717d1a9e..71ab69e01bf 100755 --- a/test/functional/wallet_reindex.py +++ b/test/functional/wallet_reindex.py @@ -46,10 +46,8 @@ class WalletReindexTest(BitcoinTestFramework): # Blank wallets don't have a birth time assert 'birthtime' not in wallet_watch_only.getwalletinfo() - # For a descriptors wallet: Import address with timestamp=now. - # For legacy wallet: There is no way of importing a script/address with a custom time. The wallet always imports it with birthtime=1. - # In both cases, disable rescan to not detect the transaction. - wallet_watch_only.importaddress(wallet_addr, rescan=False) + # Import address with timestamp=now. + wallet_watch_only.importdescriptors([{"desc": miner_wallet.getaddressinfo(wallet_addr)["desc"], "timestamp": "now"}]) assert_equal(len(wallet_watch_only.listtransactions()), 0) # Depending on the wallet type, the birth time changes. diff --git a/test/functional/wallet_transactiontime_rescan.py b/test/functional/wallet_transactiontime_rescan.py index a40048fa81b..834e6d07686 100755 --- a/test/functional/wallet_transactiontime_rescan.py +++ b/test/functional/wallet_transactiontime_rescan.py @@ -50,15 +50,23 @@ class TransactionTimeRescanTest(BitcoinTestFramework): # prepare the user wallet with 3 watch only addresses wo1 = usernode.getnewaddress() + wo1_desc = usernode.getaddressinfo(wo1)["desc"] wo2 = usernode.getnewaddress() + wo2_desc = usernode.getaddressinfo(wo2)["desc"] wo3 = usernode.getnewaddress() + wo3_desc = usernode.getaddressinfo(wo3)["desc"] usernode.createwallet(wallet_name='wo', disable_private_keys=True) wo_wallet = usernode.get_wallet_rpc('wo') - wo_wallet.importaddress(wo1) - wo_wallet.importaddress(wo2) - wo_wallet.importaddress(wo3) + import_res = wo_wallet.importdescriptors( + [ + {"desc": wo1_desc, "timestamp": "now"}, + {"desc": wo2_desc, "timestamp": "now"}, + {"desc": wo3_desc, "timestamp": "now"}, + ] + ) + assert_equal(all([r["success"] for r in import_res]), True) self.log.info('Start transactions') @@ -124,16 +132,20 @@ class TransactionTimeRescanTest(BitcoinTestFramework): restorenode.createwallet(wallet_name='wo', disable_private_keys=True) restorewo_wallet = restorenode.get_wallet_rpc('wo') - # for descriptor wallets, the test framework maps the importaddress RPC to the - # importdescriptors RPC (with argument 'timestamp'='now'), which always rescans + # importdescriptors with "timestamp": "now" always rescans # blocks of the past 2 hours, based on the current MTP timestamp; in order to avoid # importing the last address (wo3), we advance the time further and generate 10 blocks set_node_times(self.nodes, cur_time + ten_days + ten_days + ten_days + ten_days) self.generatetoaddress(minernode, 10, m1) - restorewo_wallet.importaddress(wo1, rescan=False) - restorewo_wallet.importaddress(wo2, rescan=False) - restorewo_wallet.importaddress(wo3, rescan=False) + import_res = restorewo_wallet.importdescriptors( + [ + {"desc": wo1_desc, "timestamp": "now"}, + {"desc": wo2_desc, "timestamp": "now"}, + {"desc": wo3_desc, "timestamp": "now"}, + ] + ) + assert_equal(all([r["success"] for r in import_res]), True) # check user has 0 balance and no transactions assert_equal(restorewo_wallet.getbalance(), 0) From fe838dd391be669ccd0765b95f81c25fecfd3636 Mon Sep 17 00:00:00 2001 From: Ava Chow Date: Thu, 8 May 2025 11:48:47 -0700 Subject: [PATCH 4/6] test: Replace usage of addmultisigaddress --- test/functional/rpc_psbt.py | 16 +++++++++++++--- test/functional/test_framework/test_node.py | 15 --------------- test/functional/wallet_address_types.py | 10 ++++++---- test/functional/wallet_fundrawtransaction.py | 8 +++++--- test/functional/wallet_labels.py | 1 - 5 files changed, 24 insertions(+), 26 deletions(-) diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py index fbbbae91a7c..e1122e5f475 100755 --- a/test/functional/rpc_psbt.py +++ b/test/functional/rpc_psbt.py @@ -314,9 +314,19 @@ class PSBTTest(BitcoinTestFramework): wmulti = self.nodes[2].get_wallet_rpc('wmulti') # Create all the addresses - p2sh = wmulti.addmultisigaddress(2, [pubkey0, pubkey1, pubkey2], label="", address_type="legacy")["address"] - p2wsh = wmulti.addmultisigaddress(2, [pubkey0, pubkey1, pubkey2], label="", address_type="bech32")["address"] - p2sh_p2wsh = wmulti.addmultisigaddress(2, [pubkey0, pubkey1, pubkey2], label="", address_type="p2sh-segwit")["address"] + p2sh_ms = wmulti.createmultisig(2, [pubkey0, pubkey1, pubkey2], address_type="legacy") + p2sh = p2sh_ms["address"] + p2wsh_ms = wmulti.createmultisig(2, [pubkey0, pubkey1, pubkey2], address_type="bech32") + p2wsh = p2wsh_ms["address"] + p2sh_p2wsh_ms = wmulti.createmultisig(2, [pubkey0, pubkey1, pubkey2], address_type="p2sh-segwit") + p2sh_p2wsh = p2sh_p2wsh_ms["address"] + import_res = wmulti.importdescriptors( + [ + {"desc": p2sh_ms["descriptor"], "timestamp": "now"}, + {"desc": p2wsh_ms["descriptor"], "timestamp": "now"}, + {"desc": p2sh_p2wsh_ms["descriptor"], "timestamp": "now"}, + ]) + assert_equal(all([r["success"] for r in import_res]), True) p2wpkh = self.nodes[1].getnewaddress("", "bech32") p2pkh = self.nodes[1].getnewaddress("", "legacy") p2sh_p2wpkh = self.nodes[1].getnewaddress("", "p2sh-segwit") diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index 14d1e5a09ed..ffd9252edab 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -931,21 +931,6 @@ class RPCOverloadWrapper(): def __getattr__(self, name): return getattr(self.rpc, name) - def addmultisigaddress(self, nrequired, keys, *, label=None, address_type=None): - wallet_info = self.getwalletinfo() - if 'descriptors' not in wallet_info or ('descriptors' in wallet_info and not wallet_info['descriptors']): - return self.__getattr__('addmultisigaddress')(nrequired, keys, label, address_type) - cms = self.createmultisig(nrequired, keys, address_type) - req = [{ - 'desc': cms['descriptor'], - 'timestamp': 0, - 'label': label if label else '', - }] - import_res = self.importdescriptors(req) - if not import_res[0]['success']: - raise JSONRPCException(import_res[0]['error']) - return cms - def importpubkey(self, pubkey, *, label=None, rescan=None): wallet_info = self.getwalletinfo() if 'descriptors' not in wallet_info or ('descriptors' in wallet_info and not wallet_info['descriptors']): diff --git a/test/functional/wallet_address_types.py b/test/functional/wallet_address_types.py index d8c2aab36de..cfb8f926f00 100755 --- a/test/functional/wallet_address_types.py +++ b/test/functional/wallet_address_types.py @@ -260,9 +260,11 @@ class AddressTypeTest(BitcoinTestFramework): else: address = self.nodes[to_node].getnewaddress(address_type=address_type) else: - addr1 = self.nodes[to_node].getnewaddress() - addr2 = self.nodes[to_node].getnewaddress() - address = self.nodes[to_node].addmultisigaddress(2, [addr1, addr2])['address'] + pubkey1 = self.nodes[to_node].getaddressinfo(self.nodes[to_node].getnewaddress())["pubkey"] + pubkey2 = self.nodes[to_node].getaddressinfo(self.nodes[to_node].getnewaddress())["pubkey"] + ms = self.nodes[to_node].createmultisig(2, [pubkey1, pubkey2]) + import_res = self.nodes[to_node].importdescriptors([{"desc": ms["descriptor"], "timestamp": 0}]) + assert_equal(import_res[0]["success"], True) # Do some sanity checking on the created address if address_type is not None: @@ -344,7 +346,7 @@ class AddressTypeTest(BitcoinTestFramework): self.test_address(3, self.nodes[3].getrawchangeaddress(), multisig=False, typ='bech32') self.log.info('test invalid address type arguments') - assert_raises_rpc_error(-5, "Unknown address type ''", self.nodes[3].addmultisigaddress, 2, [compressed_1, compressed_2], address_type="") + assert_raises_rpc_error(-5, "Unknown address type ''", self.nodes[3].createmultisig, 2, [compressed_1, compressed_2], address_type="") assert_raises_rpc_error(-5, "Unknown address type ''", self.nodes[3].getnewaddress, None, '') assert_raises_rpc_error(-5, "Unknown address type ''", self.nodes[3].getrawchangeaddress, '') assert_raises_rpc_error(-5, "Unknown address type 'bech23'", self.nodes[3].getrawchangeaddress, 'bech23') diff --git a/test/functional/wallet_fundrawtransaction.py b/test/functional/wallet_fundrawtransaction.py index aeaa8e54622..08d9e9226cb 100755 --- a/test/functional/wallet_fundrawtransaction.py +++ b/test/functional/wallet_fundrawtransaction.py @@ -565,16 +565,18 @@ class RawTransactionsTest(BitcoinTestFramework): self.nodes[2].createwallet(wallet_name='wmulti', disable_private_keys=True) wmulti = self.nodes[2].get_wallet_rpc('wmulti') w2 = self.nodes[2].get_wallet_rpc(self.default_wallet_name) - mSigObj = wmulti.addmultisigaddress( + mSigObj = self.nodes[2].createmultisig( 2, [ addr1Obj['pubkey'], addr2Obj['pubkey'], ] - )['address'] + ) + import_res = wmulti.importdescriptors([{"desc": mSigObj["descriptor"], "timestamp": "now"}]) + assert_equal(import_res[0]["success"], True) # Send 1.2 BTC to msig addr. - self.nodes[0].sendtoaddress(mSigObj, 1.2) + self.nodes[0].sendtoaddress(mSigObj["address"], 1.2) self.generate(self.nodes[0], 1) oldBalance = self.nodes[1].getbalance() diff --git a/test/functional/wallet_labels.py b/test/functional/wallet_labels.py index 2f580e18d92..56b721551b3 100755 --- a/test/functional/wallet_labels.py +++ b/test/functional/wallet_labels.py @@ -35,7 +35,6 @@ class WalletLabelsTest(BitcoinTestFramework): [node.setlabel, address], [node.getaddressesbylabel], [node.importpubkey, pubkey], - [node.addmultisigaddress, 1, [pubkey]], [node.getreceivedbylabel], [node.listsinceblock, node.getblockhash(0), 1, False, True, False], ] From 4d32c19516fdb65b364638fa088b8d7167b438b6 Mon Sep 17 00:00:00 2001 From: Ava Chow Date: Thu, 8 May 2025 11:54:19 -0700 Subject: [PATCH 5/6] test: Replace importpubkey --- test/functional/test_framework/test_node.py | 15 --------------- test/functional/wallet_createwallet.py | 6 ++++-- test/functional/wallet_fundrawtransaction.py | 4 ++-- test/functional/wallet_labels.py | 1 - test/functional/wallet_simulaterawtx.py | 3 ++- 5 files changed, 8 insertions(+), 21 deletions(-) diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index ffd9252edab..e978c1e3790 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -27,7 +27,6 @@ from .authproxy import ( JSONRPCException, serialization_fallback, ) -from .descriptors import descsum_create from .messages import NODE_P2P_V2 from .p2p import P2P_SERVICES, P2P_SUBVERSION from .util import ( @@ -930,17 +929,3 @@ class RPCOverloadWrapper(): def __getattr__(self, name): return getattr(self.rpc, name) - - def importpubkey(self, pubkey, *, label=None, rescan=None): - wallet_info = self.getwalletinfo() - if 'descriptors' not in wallet_info or ('descriptors' in wallet_info and not wallet_info['descriptors']): - return self.__getattr__('importpubkey')(pubkey, label, rescan) - desc = descsum_create('combo(' + pubkey + ')') - req = [{ - 'desc': desc, - 'timestamp': 0 if rescan else 'now', - 'label': label if label else '', - }] - import_res = self.importdescriptors(req) - if not import_res[0]['success']: - raise JSONRPCException(import_res[0]['error']) diff --git a/test/functional/wallet_createwallet.py b/test/functional/wallet_createwallet.py index edd54fe4387..1f86b265e8d 100755 --- a/test/functional/wallet_createwallet.py +++ b/test/functional/wallet_createwallet.py @@ -42,7 +42,8 @@ class CreateWalletTest(BitcoinTestFramework): w1 = node.get_wallet_rpc('w1') assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w1.getnewaddress) assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w1.getrawchangeaddress) - w1.importpubkey(w0.getaddressinfo(address1)['pubkey']) + import_res = w1.importdescriptors([{"desc": w0.getaddressinfo(address1)['desc'], "timestamp": "now"}]) + assert_equal(import_res[0]["success"], True) self.log.info('Test that private keys cannot be imported') privkey, pubkey = generate_keypair(wif=True) @@ -57,7 +58,8 @@ class CreateWalletTest(BitcoinTestFramework): w2 = node.get_wallet_rpc('w2') assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w2.getnewaddress) assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w2.getrawchangeaddress) - w2.importpubkey(w0.getaddressinfo(address1)['pubkey']) + import_res = w2.importdescriptors([{"desc": w0.getaddressinfo(address1)['desc'], "timestamp": "now"}]) + assert_equal(import_res[0]["success"], True) self.log.info("Test blank creation with private keys enabled.") self.nodes[0].createwallet(wallet_name='w3', disable_private_keys=False, blank=True) diff --git a/test/functional/wallet_fundrawtransaction.py b/test/functional/wallet_fundrawtransaction.py index 08d9e9226cb..a3b19847335 100755 --- a/test/functional/wallet_fundrawtransaction.py +++ b/test/functional/wallet_fundrawtransaction.py @@ -190,9 +190,9 @@ class RawTransactionsTest(BitcoinTestFramework): self.nodes[3].createwallet(wallet_name="wwatch", disable_private_keys=True) wwatch = self.nodes[3].get_wallet_rpc('wwatch') watchonly_address = self.nodes[0].getnewaddress() - watchonly_pubkey = self.nodes[0].getaddressinfo(watchonly_address)["pubkey"] self.watchonly_amount = Decimal(200) - wwatch.importpubkey(watchonly_pubkey, label="", rescan=True) + import_res = wwatch.importdescriptors([{"desc": self.nodes[0].getaddressinfo(watchonly_address)["desc"], "timestamp": "now"}]) + assert_equal(import_res[0]["success"], True) self.watchonly_utxo = self.create_outpoints(self.nodes[0], outputs=[{watchonly_address: self.watchonly_amount}])[0] # Lock UTXO so nodes[0] doesn't accidentally spend it diff --git a/test/functional/wallet_labels.py b/test/functional/wallet_labels.py index 56b721551b3..068a4980db7 100755 --- a/test/functional/wallet_labels.py +++ b/test/functional/wallet_labels.py @@ -34,7 +34,6 @@ class WalletLabelsTest(BitcoinTestFramework): [node.getnewaddress], [node.setlabel, address], [node.getaddressesbylabel], - [node.importpubkey, pubkey], [node.getreceivedbylabel], [node.listsinceblock, node.getblockhash(0), 1, False, True, False], ] diff --git a/test/functional/wallet_simulaterawtx.py b/test/functional/wallet_simulaterawtx.py index 91955d751e3..4ee663067ee 100755 --- a/test/functional/wallet_simulaterawtx.py +++ b/test/functional/wallet_simulaterawtx.py @@ -45,7 +45,8 @@ class SimulateTxTest(BitcoinTestFramework): address2 = w1.getnewaddress() # Add address1 as watch-only to w2 - w2.importpubkey(pubkey=w1.getaddressinfo(address1)["pubkey"]) + import_res = w2.importdescriptors([{"desc": w1.getaddressinfo(address1)["desc"], "timestamp": "now"}]) + assert_equal(import_res[0]["success"], True) tx1 = node.createrawtransaction([], [{address1: 5.0}]) tx2 = node.createrawtransaction([], [{address2: 10.0}]) From b104d442277090337ce405d92f1398b7cc9bcdb7 Mon Sep 17 00:00:00 2001 From: Ava Chow Date: Thu, 8 May 2025 11:38:17 -0700 Subject: [PATCH 6/6] test: Remove RPCOverloadWrapper With the legacy wallet now removed, these overloads are no longer needed. --- test/functional/test_framework/test_node.py | 15 ++++----------- test/functional/wallet_migration.py | 6 +++--- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index e978c1e3790..a7d29e8df00 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -208,10 +208,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), name) + return getattr(self.cli, 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(self.rpc, name) def start(self, extra_args=None, *, cwd=None, stdout=None, stderr=None, env=None, **kwargs): """Start the node.""" @@ -373,11 +373,11 @@ class TestNode(): def get_wallet_rpc(self, wallet_name): if self.use_cli: - return RPCOverloadWrapper(self.cli("-rpcwallet={}".format(wallet_name))) + return self.cli("-rpcwallet={}".format(wallet_name)) 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 self.rpc / wallet_path def version_is_at_least(self, ver): return self.version is None or self.version >= ver @@ -922,10 +922,3 @@ class TestNodeCLI(): return json.loads(cli_stdout, parse_float=decimal.Decimal) except (json.JSONDecodeError, decimal.InvalidOperation): return cli_stdout.rstrip("\n") - -class RPCOverloadWrapper(): - def __init__(self, rpc): - self.rpc = rpc - - def __getattr__(self, name): - return getattr(self.rpc, name) diff --git a/test/functional/wallet_migration.py b/test/functional/wallet_migration.py index fe348d4d73f..700d801891c 100755 --- a/test/functional/wallet_migration.py +++ b/test/functional/wallet_migration.py @@ -726,7 +726,7 @@ class WalletMigrationTest(BitcoinTestFramework): send_to_script(script=script_sh_pkh, amount=2) # Import script and check balance - wallet.rpc.importaddress(address=script_pkh.hex(), label="raw_spk", rescan=True, p2sh=True) + wallet.importaddress(address=script_pkh.hex(), label="raw_spk", rescan=True, p2sh=True) assert_equal(wallet.getbalances()['watchonly']['trusted'], 2) # Craft wsh(pkh(key)) and send coins to it @@ -735,7 +735,7 @@ class WalletMigrationTest(BitcoinTestFramework): send_to_script(script=script_wsh_pkh, amount=3) # Import script and check balance - wallet.rpc.importaddress(address=script_wsh_pkh.hex(), label="raw_spk2", rescan=True, p2sh=False) + wallet.importaddress(address=script_wsh_pkh.hex(), label="raw_spk2", rescan=True, p2sh=False) assert_equal(wallet.getbalances()['watchonly']['trusted'], 5) # Import sh(pkh()) script, by using importaddress(), with the p2sh flag enabled. @@ -751,7 +751,7 @@ class WalletMigrationTest(BitcoinTestFramework): # Note: 'importaddress()' will add two scripts, a valid one sh(pkh()) and an invalid one 'sh(sh(pkh()))'. # Both of them will be stored with the same addressbook label. And only the latter one should # be discarded during migration. The first one must be migrated. - wallet.rpc.importaddress(address=script_sh_pkh.hex(), label=label_sh_pkh, rescan=False, p2sh=True) + wallet.importaddress(address=script_sh_pkh.hex(), label=label_sh_pkh, rescan=False, p2sh=True) # Migrate wallet and re-check balance info_migration, wallet = self.migrate_and_get_rpc("raw_p2sh")