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