// Copyright (c) The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include using kernel::ChainstateRole; // Taken from validation.cpp static constexpr auto DATABASE_WRITE_INTERVAL_MIN{50min}; static constexpr auto DATABASE_WRITE_INTERVAL_MAX{70min}; BOOST_AUTO_TEST_SUITE(chainstate_write_tests) BOOST_FIXTURE_TEST_CASE(chainstate_write_interval, TestingSetup) { struct TestSubscriber final : CValidationInterface { bool m_did_flush{false}; void ChainStateFlushed(const ChainstateRole&, const CBlockLocator&) override { m_did_flush = true; } }; const auto sub{std::make_shared()}; m_node.validation_signals->RegisterSharedValidationInterface(sub); auto& chainstate{Assert(m_node.chainman)->ActiveChainstate()}; BlockValidationState state_dummy{}; // The first periodic flush sets m_next_write and does not flush chainstate.FlushStateToDisk(state_dummy, FlushStateMode::PERIODIC); m_node.validation_signals->SyncWithValidationInterfaceQueue(); BOOST_CHECK(!sub->m_did_flush); // The periodic flush interval is between 50 and 70 minutes (inclusive) SetMockTime(GetTime() + DATABASE_WRITE_INTERVAL_MIN - 1min); chainstate.FlushStateToDisk(state_dummy, FlushStateMode::PERIODIC); m_node.validation_signals->SyncWithValidationInterfaceQueue(); BOOST_CHECK(!sub->m_did_flush); SetMockTime(GetTime() + DATABASE_WRITE_INTERVAL_MAX); chainstate.FlushStateToDisk(state_dummy, FlushStateMode::PERIODIC); m_node.validation_signals->SyncWithValidationInterfaceQueue(); BOOST_CHECK(sub->m_did_flush); } // Test that we do PERIODIC flushes inside ActivateBestChain. // This is necessary for reindex-chainstate to be able to periodically flush // before reaching chain tip. BOOST_FIXTURE_TEST_CASE(write_during_multiblock_activation, TestChain100Setup) { struct TestSubscriber final : CValidationInterface { const CBlockIndex* m_tip{nullptr}; const CBlockIndex* m_flushed_at_block{nullptr}; void ChainStateFlushed(const ChainstateRole&, const CBlockLocator&) override { m_flushed_at_block = m_tip; } void UpdatedBlockTip(const CBlockIndex* block_index, const CBlockIndex*, bool) override { m_tip = block_index; } }; auto& chainstate{Assert(m_node.chainman)->ActiveChainstate()}; BlockValidationState state_dummy{}; // Pop two blocks from the tip const CBlockIndex* tip{chainstate.m_chain.Tip()}; CBlockIndex* second_from_tip{tip->pprev}; { LOCK2(m_node.chainman->GetMutex(), chainstate.MempoolMutex()); chainstate.DisconnectTip(state_dummy, nullptr); chainstate.DisconnectTip(state_dummy, nullptr); } BOOST_CHECK_EQUAL(second_from_tip->pprev, chainstate.m_chain.Tip()); // Set m_next_write to current time chainstate.FlushStateToDisk(state_dummy, FlushStateMode::ALWAYS); m_node.validation_signals->SyncWithValidationInterfaceQueue(); // The periodic flush interval is between 50 and 70 minutes (inclusive) // The next call to a PERIODIC write will flush SetMockTime(GetMockTime() + DATABASE_WRITE_INTERVAL_MAX); const auto sub{std::make_shared()}; m_node.validation_signals->RegisterSharedValidationInterface(sub); // ActivateBestChain back to tip chainstate.ActivateBestChain(state_dummy, nullptr); BOOST_CHECK_EQUAL(tip, chainstate.m_chain.Tip()); // Check that we flushed inside ActivateBestChain while we were at the // second block from tip, since FlushStateToDisk is called with PERIODIC // inside the outer loop. m_node.validation_signals->SyncWithValidationInterfaceQueue(); BOOST_CHECK_EQUAL(sub->m_flushed_at_block, second_from_tip); } BOOST_AUTO_TEST_SUITE_END()