feature_reindex.py raw

   1  #!/usr/bin/env python3
   2  # Copyright (c) 2014-present The Bitcoin Core developers
   3  # Distributed under the MIT software license, see the accompanying
   4  # file COPYING or http://www.opensource.org/licenses/mit-license.php.
   5  """Test running bitcoind with -reindex and -reindex-chainstate options.
   6  
   7  - Start a single node and generate 3 blocks.
   8  - Stop the node and restart it with -reindex. Verify that the node has reindexed up to block 3.
   9  - Stop the node and restart it with -reindex-chainstate. Verify that the node has reindexed up to block 3.
  10  - Verify that out-of-order blocks are correctly processed, see LoadExternalBlockFile()
  11  """
  12  
  13  from test_framework.test_framework import BitcoinTestFramework
  14  from test_framework.messages import MAGIC_BYTES
  15  from test_framework.util import (
  16      assert_equal,
  17      util_xor,
  18  )
  19  
  20  
  21  class ReindexTest(BitcoinTestFramework):
  22      def set_test_params(self):
  23          self.rpc_timeout *= 2  # To avoid timeout when generating the reindex chain
  24          self.setup_clean_chain = True
  25          self.num_nodes = 1
  26  
  27      def reindex(self, justchainstate=False):
  28          self.generatetoaddress(self.nodes[0], 3, self.nodes[0].get_deterministic_priv_key().address)
  29          blockcount = self.nodes[0].getblockcount()
  30          self.stop_nodes()
  31          extra_args = [["-reindex-chainstate" if justchainstate else "-reindex"]]
  32          self.start_nodes(extra_args)
  33          assert_equal(self.nodes[0].getblockcount(), blockcount)  # start_node is blocking on reindex
  34          self.log.info("Success")
  35  
  36      # Check that blocks can be processed out of order
  37      def out_of_order(self):
  38          # The previous test created 12 blocks
  39          assert_equal(self.nodes[0].getblockcount(), 12)
  40          self.stop_nodes()
  41  
  42          # In this test environment, blocks will always be in order (since
  43          # we're generating them rather than getting them from peers), so to
  44          # test out-of-order handling, swap blocks 1 and 2 on disk.
  45          blk0 = self.nodes[0].blocks_path / "blk00000.dat"
  46          xor_dat = self.nodes[0].read_xor_key()
  47  
  48          with open(blk0, 'r+b') as bf:
  49              # Read at least the first few blocks (including genesis)
  50              b = util_xor(bf.read(2000), xor_dat, offset=0)
  51  
  52              # Find the offsets of blocks 2, 3, and 4 (the first 3 blocks beyond genesis)
  53              # by searching for the regtest marker bytes (see pchMessageStart).
  54              def find_block(b, start):
  55                  return b.find(MAGIC_BYTES["regtest"], start)+4
  56  
  57              genesis_start = find_block(b, 0)
  58              assert_equal(genesis_start, 4)
  59              b2_start = find_block(b, genesis_start)
  60              b3_start = find_block(b, b2_start)
  61              b4_start = find_block(b, b3_start)
  62  
  63              # Blocks 2 and 3 should be the same size.
  64              assert_equal(b3_start - b2_start, b4_start - b3_start)
  65  
  66              # Swap the second and third blocks (don't disturb the genesis block).
  67              bf.seek(b2_start)
  68              bf.write(util_xor(b[b3_start:b4_start], xor_dat, offset=b2_start))
  69              bf.write(util_xor(b[b2_start:b3_start], xor_dat, offset=b3_start))
  70  
  71          # The reindexing code should detect and accommodate out of order blocks.
  72          with self.nodes[0].assert_debug_log([
  73              'LoadExternalBlockFile: Out of order block',
  74              'LoadExternalBlockFile: Processing out of order child',
  75          ]):
  76              extra_args = [["-reindex"]]
  77              self.start_nodes(extra_args)
  78  
  79          # All blocks should be accepted and processed.
  80          assert_equal(self.nodes[0].getblockcount(), 12)
  81  
  82      def continue_reindex_after_shutdown(self):
  83          node = self.nodes[0]
  84          self.generate(node, 1500)
  85  
  86          # Restart node with reindex and stop reindex as soon as it starts reindexing
  87          self.log.info("Restarting node while reindexing..")
  88          node.stop_node()
  89          with node.busy_wait_for_debug_log([b'initload thread start']):
  90              node.start(['-blockfilterindex', '-reindex'])
  91              node.wait_for_rpc_connection(wait_for_import=False)
  92          node.stop_node()
  93  
  94          # Start node without the reindex flag and verify it does not wipe the indexes data again
  95          db_path = node.chain_path / 'indexes' / 'blockfilter' / 'basic' / 'db'
  96          with node.assert_debug_log(expected_msgs=[f'Opening LevelDB in {db_path}'], unexpected_msgs=[f'Wiping LevelDB in {db_path}']):
  97              node.start(['-blockfilterindex'])
  98              node.wait_for_rpc_connection(wait_for_import=False)
  99          node.stop_node()
 100  
 101      def run_test(self):
 102          self.reindex(False)
 103          self.reindex(True)
 104          self.reindex(False)
 105          self.reindex(True)
 106  
 107          self.out_of_order()
 108          self.continue_reindex_after_shutdown()
 109  
 110  
 111  if __name__ == '__main__':
 112      ReindexTest(__file__).main()
 113