wallet_reindex.py raw

   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 set_test_params(self):
  19          self.num_nodes = 1
  20          self.setup_clean_chain = True
  21  
  22      def skip_test_if_missing_module(self):
  23          self.skip_if_no_wallet()
  24  
  25      def advance_time(self, node, secs):
  26          self.node_time += secs
  27          node.setmocktime(self.node_time)
  28  
  29      # Verify the wallet updates the birth time accordingly when it detects a transaction
  30      # with a time older than the oldest descriptor timestamp.
  31      # This could happen when the user blindly imports a descriptor with 'timestamp=now'.
  32      def birthtime_test(self, node, miner_wallet):
  33          self.log.info("Test birth time update during tx scanning")
  34          # Fund address to test
  35          wallet_addr = miner_wallet.getnewaddress()
  36          tx_id = miner_wallet.sendtoaddress(wallet_addr, 2)
  37  
  38          # Generate 50 blocks, one every 10 min to surpass the 2 hours rescan window the wallet has
  39          for _ in range(50):
  40              self.generate(node, 1)
  41              self.advance_time(node, BLOCK_TIME)
  42  
  43          # Now create a new wallet, and import the descriptor
  44          node.createwallet(wallet_name='watch_only', disable_private_keys=True, load_on_startup=True)
  45          wallet_watch_only = node.get_wallet_rpc('watch_only')
  46          # Blank wallets don't have a birth time
  47          assert 'birthtime' not in wallet_watch_only.getwalletinfo()
  48  
  49          # Import address with timestamp=now.
  50          wallet_watch_only.importdescriptors([{"desc": miner_wallet.getaddressinfo(wallet_addr)["desc"], "timestamp": "now"}])
  51          assert_equal(len(wallet_watch_only.listtransactions()), 0)
  52  
  53          # Depending on the wallet type, the birth time changes.
  54          wallet_birthtime = wallet_watch_only.getwalletinfo()['birthtime']
  55          # As blocks were generated every 10 min, the chain MTP timestamp is node_time - 60 min.
  56          assert_equal(self.node_time - BLOCK_TIME * 6, wallet_birthtime)
  57  
  58          # Rescan the wallet to detect the missing transaction
  59          wallet_watch_only.rescanblockchain()
  60          assert_equal(wallet_watch_only.gettransaction(tx_id)['confirmations'], 50)
  61          assert_equal(wallet_watch_only.getbalances()['mine']['trusted'], 2)
  62  
  63          self.log.info("Reindex ...")  # restart_node waits for it to finish
  64          self.restart_node(0, extra_args=[ f'-mocktime={self.node_time}'])
  65  
  66          # Verify the transaction is still 'confirmed' after reindex
  67          wallet_watch_only = node.get_wallet_rpc('watch_only')
  68          tx_info = wallet_watch_only.gettransaction(tx_id)
  69          assert_equal(tx_info['confirmations'], 50)
  70  
  71          # Depending on the wallet type, the birth time changes.
  72          # For descriptors, verify the wallet updated the birth time to the transaction time
  73          assert_equal(tx_info['time'], wallet_watch_only.getwalletinfo()['birthtime'])
  74  
  75          wallet_watch_only.unloadwallet()
  76  
  77      def run_test(self):
  78          node = self.nodes[0]
  79          self.node_time = int(time.time())
  80          node.setmocktime(self.node_time)
  81  
  82          # Fund miner
  83          node.createwallet(wallet_name='miner', load_on_startup=True)
  84          miner_wallet = node.get_wallet_rpc('miner')
  85          self.generatetoaddress(node, COINBASE_MATURITY + 10, miner_wallet.getnewaddress())
  86  
  87          # Tests
  88          self.birthtime_test(node, miner_wallet)
  89  
  90  
  91  if __name__ == '__main__':
  92      WalletReindexTest(__file__).main()
  93