From 75e0be5263c35b82a601a930e7530cddfec85624 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sat, 4 Jan 2025 08:31:50 +0000 Subject: [PATCH] merge bitcoin#23866: use MiniWallet for rpc_scantxoutset.py --- test/functional/p2p_filter.py | 10 +-- test/functional/rpc_scantxoutset.py | 81 ++++++++++++------------ test/functional/test_framework/wallet.py | 37 +++++++++-- 3 files changed, 76 insertions(+), 52 deletions(-) diff --git a/test/functional/p2p_filter.py b/test/functional/p2p_filter.py index ab9f58727dc0b..ae6d419f5ae58 100755 --- a/test/functional/p2p_filter.py +++ b/test/functional/p2p_filter.py @@ -31,7 +31,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.wallet import ( MiniWallet, - random_p2pkh, + getnewdestination, ) @@ -169,14 +169,14 @@ def test_filter(self, filter_peer): self.log.info('Check that we only receive a merkleblock if the filter does not match a tx in a block') filter_peer.tx_received = False - block_hash = self.generatetoscriptpubkey(random_p2pkh()) + block_hash = self.generatetoscriptpubkey(getnewdestination()[1]) filter_peer.wait_for_merkleblock(block_hash) assert not filter_peer.tx_received self.log.info('Check that we not receive a tx if the filter does not match a mempool tx') filter_peer.merkleblock_received = False filter_peer.tx_received = False - self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=random_p2pkh(), amount=7 * COIN) + self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=getnewdestination()[1], amount=7 * COIN) filter_peer.sync_send_with_ping() assert not filter_peer.merkleblock_received assert not filter_peer.tx_received @@ -190,14 +190,14 @@ def test_filter(self, filter_peer): self.log.info('Check that after deleting filter all txs get relayed again') filter_peer.send_and_ping(msg_filterclear()) for _ in range(5): - txid, _ = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=random_p2pkh(), amount=7 * COIN) + txid, _ = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=getnewdestination()[1], amount=7 * COIN) filter_peer.wait_for_tx(txid) self.log.info('Check that request for filtered blocks is ignored if no filter is set') filter_peer.merkleblock_received = False filter_peer.tx_received = False with self.nodes[0].assert_debug_log(expected_msgs=['received getdata']): - block_hash = self.generatetoscriptpubkey(random_p2pkh()) + block_hash = self.generatetoscriptpubkey(getnewdestination()[1]) filter_peer.wait_for_inv([CInv(MSG_BLOCK, int(block_hash, 16))]) filter_peer.sync_with_ping() assert not filter_peer.merkleblock_received diff --git a/test/functional/rpc_scantxoutset.py b/test/functional/rpc_scantxoutset.py index 43ec5e683284c..14c0999045e50 100755 --- a/test/functional/rpc_scantxoutset.py +++ b/test/functional/rpc_scantxoutset.py @@ -3,11 +3,16 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test the scantxoutset rpc call.""" +from test_framework.messages import COIN from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, assert_raises_rpc_error, Decimal +from test_framework.util import assert_equal, assert_raises_rpc_error +from test_framework.wallet import ( + MiniWallet, + address_to_scriptpubkey, + getnewdestination, +) -import shutil -import os +from decimal import Decimal def descriptors(out): return sorted(u['desc'] for u in out['unspents']) @@ -15,50 +20,43 @@ def descriptors(out): class ScantxoutsetTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 - self.setup_clean_chain = True self.supports_cli = False - def skip_test_if_missing_module(self): - self.skip_if_no_wallet() + def sendtodestination(self, destination, amount): + # interpret strings as addresses, assume scriptPubKey otherwise + if isinstance(destination, str): + destination = address_to_scriptpubkey(destination) + self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=destination, amount=int(COIN * amount)) def run_test(self): - self.log.info("Mining blocks...") - self.generate(self.nodes[0], 110) - - addr1 = self.nodes[0].getnewaddress("") - pubk1 = self.nodes[0].getaddressinfo(addr1)['pubkey'] - addr2 = self.nodes[0].getnewaddress("") - pubk2 = self.nodes[0].getaddressinfo(addr2)['pubkey'] - addr3 = self.nodes[0].getnewaddress("") - pubk3 = self.nodes[0].getaddressinfo(addr3)['pubkey'] - self.nodes[0].sendtoaddress(addr1, 0.001) - self.nodes[0].sendtoaddress(addr2, 0.002) - self.nodes[0].sendtoaddress(addr3, 0.004) + self.wallet = MiniWallet(self.nodes[0]) + self.wallet.rescan_utxos() + + self.log.info("Create UTXOs...") + pubk1, spk1, addr1 = getnewdestination("legacy") + pubk2, spk2, addr2 = getnewdestination("legacy") + pubk3, spk3, addr3 = getnewdestination("legacy") + self.sendtodestination(spk1, 0.001) + self.sendtodestination(spk2, 0.002) + self.sendtodestination(spk3, 0.004) #send to child keys of tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK - self.nodes[0].sendtoaddress("yR5yZLjevw5kX3UxGiQN1g96LXGJni2wSS", 0.008) # (m/0'/0'/0') - self.nodes[0].sendtoaddress("yPcxzaQekxTjaJVSaZ58r22o37H8moWPK2", 0.016) # (m/0'/0'/1') - self.nodes[0].sendtoaddress("yhv7iRHSx4SgyvCPmkm6Js8gTuJTtJH9ec", 0.032) # (m/0'/0'/1500') - self.nodes[0].sendtoaddress("yWEdyyKVNbmaiXHkg3LVPqgoXpMA3S6Xt7", 0.064) # (m/0'/0'/0) - self.nodes[0].sendtoaddress("yTGAdq8sSHJ1QrcqSUaMHs8RMj3Bqz3bkb", 0.128) # (m/0'/0'/1) - self.nodes[0].sendtoaddress("yRTNkmjXjhatND4Dv3V4GwBzYJQ4o9ukQr", 0.256) # (m/0'/0'/1500) - self.nodes[0].sendtoaddress("yPwUp9Vwmr4zE6rSuZg3TBxeyRerdRAbNd", 0.512) # (m/1/1/0') - self.nodes[0].sendtoaddress("yLapNU3bG8E8JNGXRhZbRHHDifrqTucGcg", 1.024) # (m/1/1/1') - self.nodes[0].sendtoaddress("yUhbAKf7AcTC5sPXb2dkABKm3FYENqdzv2", 2.048) # (m/1/1/1500') - self.nodes[0].sendtoaddress("yZTyMdEJjZWJi6CwY6g3WurLESH3UsWrrM", 4.096) # (m/1/1/0) - self.nodes[0].sendtoaddress("ydccVGNV2EcEouAxbbgdu8pi8gkdaqkiav", 8.192) # (m/1/1/1) - self.nodes[0].sendtoaddress("yVCdQxPXJ3SrtTLv8FuLXDNaynz6kmjPNq", 16.384) # (m/1/1/1500) + self.sendtodestination("yR5yZLjevw5kX3UxGiQN1g96LXGJni2wSS", 0.008) # (m/0'/0'/0') + self.sendtodestination("yPcxzaQekxTjaJVSaZ58r22o37H8moWPK2", 0.016) # (m/0'/0'/1') + self.sendtodestination("yhv7iRHSx4SgyvCPmkm6Js8gTuJTtJH9ec", 0.032) # (m/0'/0'/1500') + self.sendtodestination("yWEdyyKVNbmaiXHkg3LVPqgoXpMA3S6Xt7", 0.064) # (m/0'/0'/0) + self.sendtodestination("yTGAdq8sSHJ1QrcqSUaMHs8RMj3Bqz3bkb", 0.128) # (m/0'/0'/1) + self.sendtodestination("yRTNkmjXjhatND4Dv3V4GwBzYJQ4o9ukQr", 0.256) # (m/0'/0'/1500) + self.sendtodestination("yPwUp9Vwmr4zE6rSuZg3TBxeyRerdRAbNd", 0.512) # (m/1/1/0') + self.sendtodestination("yLapNU3bG8E8JNGXRhZbRHHDifrqTucGcg", 1.024) # (m/1/1/1') + self.sendtodestination("yUhbAKf7AcTC5sPXb2dkABKm3FYENqdzv2", 2.048) # (m/1/1/1500') + self.sendtodestination("yZTyMdEJjZWJi6CwY6g3WurLESH3UsWrrM", 4.096) # (m/1/1/0) + self.sendtodestination("ydccVGNV2EcEouAxbbgdu8pi8gkdaqkiav", 8.192) # (m/1/1/1) + self.sendtodestination("yVCdQxPXJ3SrtTLv8FuLXDNaynz6kmjPNq", 16.384) # (m/1/1/1500) self.generate(self.nodes[0], 1) - self.log.info("Stop node, remove wallet, mine again some blocks...") - self.stop_node(0) - shutil.rmtree(os.path.join(self.nodes[0].datadir, self.chain, 'wallets')) - self.start_node(0, ['-nowallet']) - self.import_deterministic_coinbase_privkeys() - self.generate(self.nodes[0], 110) - scan = self.nodes[0].scantxoutset("start", []) info = self.nodes[0].gettxoutsetinfo() assert_equal(scan['success'], True) @@ -66,13 +64,12 @@ def run_test(self): assert_equal(scan['txouts'], info['txouts']) assert_equal(scan['bestblock'], info['bestblock']) - self.restart_node(0, ['-nowallet']) self.log.info("Test if we have found the non HD unspent outputs.") - assert_equal(self.nodes[0].scantxoutset("start", [ "pkh(" + pubk1 + ")", "pkh(" + pubk2 + ")", "pkh(" + pubk3 + ")"])['total_amount'], Decimal("0.007")) - assert_equal(self.nodes[0].scantxoutset("start", [ "combo(" + pubk1 + ")", "combo(" + pubk2 + ")", "combo(" + pubk3 + ")"])['total_amount'], Decimal("0.007")) - assert_equal(self.nodes[0].scantxoutset("start", [ "addr(" + addr1 + ")", "addr(" + addr2 + ")", "combo(" + pubk3 + ")"])['total_amount'], Decimal("0.007")) + assert_equal(self.nodes[0].scantxoutset("start", [ "pkh(" + pubk1.hex() + ")", "pkh(" + pubk2.hex() + ")", "pkh(" + pubk3.hex() + ")"])['total_amount'], Decimal("0.007")) + assert_equal(self.nodes[0].scantxoutset("start", [ "combo(" + pubk1.hex() + ")", "combo(" + pubk2.hex() + ")", "combo(" + pubk3.hex() + ")"])['total_amount'], Decimal("0.007")) + assert_equal(self.nodes[0].scantxoutset("start", [ "addr(" + addr1 + ")", "addr(" + addr2 + ")", "combo(" + pubk3.hex() + ")"])['total_amount'], Decimal("0.007")) assert_equal(self.nodes[0].scantxoutset("start", [ "addr(" + addr1 + ")", "addr(" + addr2 + ")", "addr(" + addr3 + ")"])['total_amount'], Decimal("0.007")) - assert_equal(self.nodes[0].scantxoutset("start", [ "addr(" + addr1 + ")", "addr(" + addr2 + ")", "pkh(" + pubk3 + ")"])['total_amount'], Decimal("0.007")) + assert_equal(self.nodes[0].scantxoutset("start", [ "addr(" + addr1 + ")", "addr(" + addr2 + ")", "pkh(" + pubk3.hex() + ")"])['total_amount'], Decimal("0.007")) self.log.info("Test range validation.") assert_raises_rpc_error(-8, "End of range is too high", self.nodes[0].scantxoutset, "start", [ {"desc": "desc", "range": -1}]) diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py index c13e911d9ee59..6b5d2dde28d9b 100644 --- a/test/functional/test_framework/wallet.py +++ b/test/functional/test_framework/wallet.py @@ -7,7 +7,11 @@ from copy import deepcopy from decimal import Decimal from enum import Enum -from test_framework.address import ADDRESS_BCRT1_P2SH_OP_TRUE +from test_framework.address import ( + base58_to_byte, + key_to_p2pkh, + ADDRESS_BCRT1_P2SH_OP_TRUE, +) from test_framework.descriptors import descsum_create from test_framework.key import ECKey from random import choice @@ -30,6 +34,8 @@ from test_framework.script_util import ( key_to_p2pk_script, key_to_p2pkh_script, + keyhash_to_p2pkh_script, + scripthash_to_p2sh_script, ) from test_framework.util import ( assert_equal, @@ -213,12 +219,33 @@ def sendrawtransaction(self, *, from_node, tx_hex): return txid -def random_p2pkh(): - """Generate a random P2PKH scriptPubKey. Can be used when a random destination is needed, - but no compiled wallet is available (e.g. as replacement to the getnewaddress RPC).""" +def getnewdestination(address_type='legacy'): + """Generate a random destination of the specified type and return the + corresponding public key, scriptPubKey and address. Supported types are + 'legacy'. Can be used when a random destination is needed, but no + compiled wallet is available (e.g. as replacement to the + getnewaddress/getaddressinfo RPCs).""" key = ECKey() key.generate() - return key_to_p2pkh_script(key.get_pubkey().get_bytes()) + pubkey = key.get_pubkey().get_bytes() + if address_type == 'legacy': + scriptpubkey = key_to_p2pkh_script(pubkey) + address = key_to_p2pkh(pubkey) + else: + assert False + return pubkey, scriptpubkey, address + + +def address_to_scriptpubkey(address): + """Converts a given address to the corresponding output script (scriptPubKey).""" + payload, version = base58_to_byte(address) + if version == 140: # testnet pubkey hash + return keyhash_to_p2pkh_script(payload) + elif version == 19: # testnet script hash + return scripthash_to_p2sh_script(payload) + # TODO: also support other address formats + else: + assert False def make_chain(node, address, privkeys, parent_txid, parent_value, n=0, parent_locking_script=None, fee=DEFAULT_FEE):