feature_reindex.py raw

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