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