mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-04-07 14:18:18 +02:00
The fixed non-range descriptor address ensured that the FastWalletRescanFilter would match all Blocks even if the filter wasn't properly updated. This commit moves the non-range descriptor tx to a different block, so that the filters must be updated after each TopUp for the test to pass. Co-authored-by: rkrux <rkrux.connect@gmail.com>
113 lines
5.6 KiB
Python
Executable File
113 lines
5.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# Copyright (c) 2022-present The Bitcoin Core developers
|
|
# Distributed under the MIT software license, see the accompanying
|
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
"""Test that fast rescan using block filters for descriptor wallets detects
|
|
top-ups correctly and finds the same transactions than the slow variant."""
|
|
from test_framework.address import address_to_scriptpubkey
|
|
from test_framework.descriptors import descsum_create
|
|
from test_framework.test_framework import BitcoinTestFramework
|
|
from test_framework.test_node import TestNode
|
|
from test_framework.util import assert_equal
|
|
from test_framework.wallet import MiniWallet
|
|
from test_framework.wallet_util import get_generate_key
|
|
|
|
|
|
KEYPOOL_SIZE = 100 # smaller than default size to speed-up test
|
|
NUM_BLOCKS = 6 # number of blocks to mine
|
|
|
|
|
|
class WalletFastRescanTest(BitcoinTestFramework):
|
|
def set_test_params(self):
|
|
self.num_nodes = 1
|
|
self.extra_args = [[f'-keypool={KEYPOOL_SIZE}', '-blockfilterindex=1']]
|
|
|
|
def skip_test_if_missing_module(self):
|
|
self.skip_if_no_wallet()
|
|
|
|
def get_wallet_txids(self, node: TestNode, wallet_name: str) -> list[str]:
|
|
w = node.get_wallet_rpc(wallet_name)
|
|
txs = w.listtransactions('*', 1000000)
|
|
return [tx['txid'] for tx in txs]
|
|
|
|
def run_test(self):
|
|
node = self.nodes[0]
|
|
funding_wallet = MiniWallet(node)
|
|
|
|
self.log.info("Create descriptor wallet with backup")
|
|
WALLET_BACKUP_FILENAME = node.datadir_path / 'wallet.bak'
|
|
node.createwallet(wallet_name='topup_test')
|
|
w = node.get_wallet_rpc('topup_test')
|
|
fixed_key = get_generate_key()
|
|
w.importdescriptors([{"desc": descsum_create(f"wpkh({fixed_key.privkey})"), "timestamp": "now"}])
|
|
descriptors = w.listdescriptors()['descriptors']
|
|
w.backupwallet(WALLET_BACKUP_FILENAME)
|
|
|
|
num_txs = 0
|
|
|
|
fast_rescan_messages = []
|
|
def append_fast_rescan_message():
|
|
chain_info = self.nodes[0].getblockchaininfo()
|
|
fast_rescan_messages.append(f"Fast rescan: inspect block {chain_info['blocks']} [{chain_info['bestblockhash']}] (filter matched)")
|
|
|
|
self.log.info("Create tx sending to non-ranged descriptors")
|
|
self.log.debug(f"Block 1/{NUM_BLOCKS}")
|
|
spk = bytes.fromhex(fixed_key.p2wpkh_script)
|
|
self.log.debug(f"-> fixed non-range descriptor address {fixed_key.p2wpkh_addr}")
|
|
funding_wallet.send_to(from_node=node, scriptPubKey=spk, amount=10000)
|
|
num_txs += 1
|
|
self.generate(node, 1)
|
|
append_fast_rescan_message()
|
|
|
|
self.log.info("Create txs sending to end range address of each descriptor, triggering top-ups")
|
|
for i in range(1, NUM_BLOCKS):
|
|
self.log.debug(f"Block {i+1}/{NUM_BLOCKS}")
|
|
# Get descriptors with updated ranges
|
|
ranged_descs = [desc for desc in w.listdescriptors()['descriptors'] if 'range' in desc]
|
|
for desc_info in ranged_descs:
|
|
start_range, end_range = desc_info['range']
|
|
addr = w.deriveaddresses(desc_info['desc'], [end_range, end_range])[0]
|
|
spk = address_to_scriptpubkey(addr)
|
|
self.log.debug(f"-> range [{start_range},{end_range}], last address {addr}")
|
|
funding_wallet.send_to(from_node=node, scriptPubKey=spk, amount=10000)
|
|
num_txs += 1
|
|
self.generate(node, 1)
|
|
append_fast_rescan_message()
|
|
|
|
self.log.info("Import wallet backup with block filter index")
|
|
with node.assert_debug_log(['fast variant using block filters', *fast_rescan_messages]):
|
|
node.restorewallet('rescan_fast', WALLET_BACKUP_FILENAME)
|
|
txids_fast = self.get_wallet_txids(node, 'rescan_fast')
|
|
|
|
self.log.info("Import non-active descriptors with block filter index")
|
|
node.createwallet(wallet_name='rescan_fast_nonactive', disable_private_keys=True, blank=True)
|
|
with node.assert_debug_log(['fast variant using block filters', *fast_rescan_messages]):
|
|
w = node.get_wallet_rpc('rescan_fast_nonactive')
|
|
w.importdescriptors([{"desc": descriptor['desc'], "timestamp": 0} for descriptor in descriptors])
|
|
txids_fast_nonactive = self.get_wallet_txids(node, 'rescan_fast_nonactive')
|
|
|
|
self.restart_node(0, [f'-keypool={KEYPOOL_SIZE}', '-blockfilterindex=0'])
|
|
self.log.info("Import wallet backup w/o block filter index")
|
|
with node.assert_debug_log(['slow variant inspecting all blocks']):
|
|
node.restorewallet("rescan_slow", WALLET_BACKUP_FILENAME)
|
|
txids_slow = self.get_wallet_txids(node, 'rescan_slow')
|
|
|
|
self.log.info("Import non-active descriptors w/o block filter index")
|
|
node.createwallet(wallet_name='rescan_slow_nonactive', disable_private_keys=True, blank=True)
|
|
with node.assert_debug_log(['slow variant inspecting all blocks']):
|
|
w = node.get_wallet_rpc('rescan_slow_nonactive')
|
|
w.importdescriptors([{"desc": descriptor['desc'], "timestamp": 0} for descriptor in descriptors])
|
|
txids_slow_nonactive = self.get_wallet_txids(node, 'rescan_slow_nonactive')
|
|
|
|
self.log.info("Verify that all rescans found the same txs in slow and fast variants")
|
|
assert_equal(len(txids_slow), num_txs)
|
|
assert_equal(len(txids_fast), num_txs)
|
|
assert_equal(len(txids_slow_nonactive), num_txs)
|
|
assert_equal(len(txids_fast_nonactive), num_txs)
|
|
assert_equal(sorted(txids_slow), sorted(txids_fast))
|
|
assert_equal(sorted(txids_slow_nonactive), sorted(txids_fast_nonactive))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
WalletFastRescanTest(__file__).main()
|