Skip to content

Commit

Permalink
merge bitcoin#23866: use MiniWallet for rpc_scantxoutset.py
Browse files Browse the repository at this point in the history
  • Loading branch information
kwvg committed Jan 4, 2025
1 parent d908de9 commit 75e0be5
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 52 deletions.
10 changes: 5 additions & 5 deletions test/functional/p2p_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.wallet import (
MiniWallet,
random_p2pkh,
getnewdestination,
)


Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
81 changes: 39 additions & 42 deletions test/functional/rpc_scantxoutset.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,76 +3,73 @@
# 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'])

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)
assert_equal(scan['height'], info['height'])
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}])
Expand Down
37 changes: 32 additions & 5 deletions test/functional/test_framework/wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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):
Expand Down

0 comments on commit 75e0be5

Please sign in to comment.