/ src / test / chainstate_write_tests.cpp
chainstate_write_tests.cpp
  1  // Copyright (c) The Bitcoin Core developers
  2  // Distributed under the MIT software license, see the accompanying
  3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  4  
  5  #include <test/util/setup_common.h>
  6  #include <test/util/time.h>
  7  #include <validation.h>
  8  #include <validationinterface.h>
  9  
 10  #include <boost/test/unit_test.hpp>
 11  
 12  using kernel::ChainstateRole;
 13  
 14  // Taken from validation.cpp
 15  static constexpr auto DATABASE_WRITE_INTERVAL_MIN{50min};
 16  static constexpr auto DATABASE_WRITE_INTERVAL_MAX{70min};
 17  
 18  BOOST_AUTO_TEST_SUITE(chainstate_write_tests)
 19  
 20  BOOST_FIXTURE_TEST_CASE(chainstate_write_interval, TestingSetup)
 21  {
 22      struct TestSubscriber final : CValidationInterface {
 23          bool m_did_flush{false};
 24          void ChainStateFlushed(const ChainstateRole&, const CBlockLocator&) override
 25          {
 26              m_did_flush = true;
 27          }
 28      };
 29  
 30      const auto sub{std::make_shared<TestSubscriber>()};
 31      m_node.validation_signals->RegisterSharedValidationInterface(sub);
 32      auto& chainstate{Assert(m_node.chainman)->ActiveChainstate()};
 33      BlockValidationState state_dummy{};
 34      NodeClockContext clock_ctx{};
 35  
 36      // The first periodic flush sets m_next_write and does not flush
 37      chainstate.FlushStateToDisk(state_dummy, FlushStateMode::PERIODIC);
 38      m_node.validation_signals->SyncWithValidationInterfaceQueue();
 39      BOOST_CHECK(!sub->m_did_flush);
 40  
 41      // The periodic flush interval is between 50 and 70 minutes (inclusive)
 42      clock_ctx += DATABASE_WRITE_INTERVAL_MIN - 1min;
 43      chainstate.FlushStateToDisk(state_dummy, FlushStateMode::PERIODIC);
 44      m_node.validation_signals->SyncWithValidationInterfaceQueue();
 45      BOOST_CHECK(!sub->m_did_flush);
 46  
 47      clock_ctx += DATABASE_WRITE_INTERVAL_MAX;
 48      chainstate.FlushStateToDisk(state_dummy, FlushStateMode::PERIODIC);
 49      m_node.validation_signals->SyncWithValidationInterfaceQueue();
 50      BOOST_CHECK(sub->m_did_flush);
 51  }
 52  
 53  // Test that we do PERIODIC flushes inside ActivateBestChain.
 54  // This is necessary for reindex-chainstate to be able to periodically flush
 55  // before reaching chain tip.
 56  BOOST_FIXTURE_TEST_CASE(write_during_multiblock_activation, TestChain100Setup)
 57  {
 58      struct TestSubscriber final : CValidationInterface
 59      {
 60          const CBlockIndex* m_tip{nullptr};
 61          const CBlockIndex* m_flushed_at_block{nullptr};
 62          void ChainStateFlushed(const ChainstateRole&, const CBlockLocator&) override
 63          {
 64              m_flushed_at_block = m_tip;
 65          }
 66          void UpdatedBlockTip(const CBlockIndex* block_index, const CBlockIndex*, bool) override {
 67              m_tip = block_index;
 68          }
 69      };
 70  
 71      auto& chainstate{Assert(m_node.chainman)->ActiveChainstate()};
 72      BlockValidationState state_dummy{};
 73  
 74      // Pop two blocks from the tip
 75      const CBlockIndex* tip{chainstate.m_chain.Tip()};
 76      CBlockIndex* second_from_tip{tip->pprev};
 77  
 78      {
 79          LOCK2(m_node.chainman->GetMutex(), chainstate.MempoolMutex());
 80          chainstate.DisconnectTip(state_dummy, nullptr);
 81          chainstate.DisconnectTip(state_dummy, nullptr);
 82      }
 83  
 84      BOOST_CHECK_EQUAL(second_from_tip->pprev, chainstate.m_chain.Tip());
 85  
 86      // Set m_next_write to current time
 87      chainstate.FlushStateToDisk(state_dummy, FlushStateMode::FORCE_FLUSH);
 88      m_node.validation_signals->SyncWithValidationInterfaceQueue();
 89      // The periodic flush interval is between 50 and 70 minutes (inclusive)
 90      // The next call to a PERIODIC write will flush
 91      SetMockTime(GetMockTime() + DATABASE_WRITE_INTERVAL_MAX);
 92  
 93      const auto sub{std::make_shared<TestSubscriber>()};
 94      m_node.validation_signals->RegisterSharedValidationInterface(sub);
 95  
 96      // ActivateBestChain back to tip
 97      chainstate.ActivateBestChain(state_dummy, nullptr);
 98      BOOST_CHECK_EQUAL(tip, chainstate.m_chain.Tip());
 99      // Check that we flushed inside ActivateBestChain while we were at the
100      // second block from tip, since FlushStateToDisk is called with PERIODIC
101      // inside the outer loop.
102      m_node.validation_signals->SyncWithValidationInterfaceQueue();
103      BOOST_CHECK_EQUAL(sub->m_flushed_at_block, second_from_tip);
104  }
105  
106  BOOST_AUTO_TEST_SUITE_END()