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()