Skip to content

Commit 83c6644

Browse files
committed
test: coverage for wallet birth time interaction with -reindex
Verifying the wallet updates the birth time accordingly when it detects a transaction with a time older than the oldest descriptor timestamp. This could happen when the user blindly imports a descriptor with 'timestamp=now'.
1 parent 6f49737 commit 83c6644

File tree

2 files changed

+89
-0
lines changed

2 files changed

+89
-0
lines changed

test/functional/test_runner.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,8 @@
206206
'wallet_createwallet.py --descriptors',
207207
'wallet_watchonly.py --legacy-wallet',
208208
'wallet_watchonly.py --usecli --legacy-wallet',
209+
'wallet_reindex.py --legacy-wallet',
210+
'wallet_reindex.py --descriptors',
209211
'wallet_reorgsrestore.py',
210212
'wallet_conflicts.py --legacy-wallet',
211213
'wallet_conflicts.py --descriptors',

test/functional/wallet_reindex.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) 2023-present The Bitcoin Core developers
3+
# Distributed under the MIT software license, see the accompanying
4+
# file COPYING or https://www.opensource.org/licenses/mit-license.php.
5+
6+
"""Test wallet-reindex interaction"""
7+
8+
import time
9+
10+
from test_framework.blocktools import COINBASE_MATURITY
11+
from test_framework.test_framework import BitcoinTestFramework
12+
from test_framework.util import (
13+
assert_equal,
14+
)
15+
BLOCK_TIME = 60 * 10
16+
17+
class WalletReindexTest(BitcoinTestFramework):
18+
def add_options(self, parser):
19+
self.add_wallet_options(parser)
20+
21+
def set_test_params(self):
22+
self.num_nodes = 1
23+
self.setup_clean_chain = True
24+
25+
def skip_test_if_missing_module(self):
26+
self.skip_if_no_wallet()
27+
28+
def advance_time(self, node, secs):
29+
self.node_time += secs
30+
node.setmocktime(self.node_time)
31+
32+
# Verify the wallet updates the birth time accordingly when it detects a transaction
33+
# with a time older than the oldest descriptor timestamp.
34+
# This could happen when the user blindly imports a descriptor with 'timestamp=now'.
35+
def birthtime_test(self, node, miner_wallet):
36+
self.log.info("Test birth time update during tx scanning")
37+
# Fund address to test
38+
wallet_addr = miner_wallet.getnewaddress()
39+
tx_id = miner_wallet.sendtoaddress(wallet_addr, 2)
40+
41+
# Generate 50 blocks, one every 10 min to surpass the 2 hours rescan window the wallet has
42+
for _ in range(50):
43+
self.generate(node, 1)
44+
self.advance_time(node, BLOCK_TIME)
45+
46+
# Now create a new wallet, and import the descriptor
47+
node.createwallet(wallet_name='watch_only', disable_private_keys=True, blank=True, load_on_startup=True)
48+
wallet_watch_only = node.get_wallet_rpc('watch_only')
49+
50+
# For a descriptors wallet: Import address with timestamp=now.
51+
# For legacy wallet: There is no way of importing a script/address with a custom time. The wallet always imports it with birthtime=1.
52+
# In both cases, disable rescan to not detect the transaction.
53+
wallet_watch_only.importaddress(wallet_addr, rescan=False)
54+
assert_equal(len(wallet_watch_only.listtransactions()), 0)
55+
56+
# Rescan the wallet to detect the missing transaction
57+
wallet_watch_only.rescanblockchain()
58+
assert_equal(wallet_watch_only.gettransaction(tx_id)['confirmations'], 50)
59+
assert_equal(wallet_watch_only.getbalances()['mine' if self.options.descriptors else 'watchonly']['trusted'], 2)
60+
61+
# Reindex and wait for it to finish
62+
with node.assert_debug_log(expected_msgs=["initload thread exit"]):
63+
self.restart_node(0, extra_args=['-reindex=1', f'-mocktime={self.node_time}'])
64+
node.syncwithvalidationinterfacequeue()
65+
66+
# Verify the transaction is still 'confirmed' after reindex
67+
wallet_watch_only = node.get_wallet_rpc('watch_only')
68+
assert_equal(wallet_watch_only.gettransaction(tx_id)['confirmations'], 50)
69+
70+
wallet_watch_only.unloadwallet()
71+
72+
def run_test(self):
73+
node = self.nodes[0]
74+
self.node_time = int(time.time())
75+
node.setmocktime(self.node_time)
76+
77+
# Fund miner
78+
node.createwallet(wallet_name='miner', load_on_startup=True)
79+
miner_wallet = node.get_wallet_rpc('miner')
80+
self.generatetoaddress(node, COINBASE_MATURITY + 10, miner_wallet.getnewaddress())
81+
82+
# Tests
83+
self.birthtime_test(node, miner_wallet)
84+
85+
86+
if __name__ == '__main__':
87+
WalletReindexTest().main()

0 commit comments

Comments
 (0)