chainstate_write_tests.cpp raw
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 FakeNodeClock clock{};
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 += 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 += 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 BOOST_CHECK_EQUAL(WITH_LOCK(::cs_main, return chainstate.GetLastFlushedBlock()), second_from_tip->pprev);
89 m_node.validation_signals->SyncWithValidationInterfaceQueue();
90 // The periodic flush interval is between 50 and 70 minutes (inclusive)
91 // The next call to a PERIODIC write will flush
92 m_clock += DATABASE_WRITE_INTERVAL_MAX;
93
94 const auto sub{std::make_shared<TestSubscriber>()};
95 m_node.validation_signals->RegisterSharedValidationInterface(sub);
96
97 // ActivateBestChain back to tip
98 chainstate.ActivateBestChain(state_dummy, nullptr);
99 BOOST_CHECK_EQUAL(tip, chainstate.m_chain.Tip());
100 // Check that we flushed inside ActivateBestChain while we were at the
101 // second block from tip, since FlushStateToDisk is called with PERIODIC
102 // inside the outer loop.
103 m_node.validation_signals->SyncWithValidationInterfaceQueue();
104 BOOST_CHECK_EQUAL(sub->m_flushed_at_block, second_from_tip);
105 BOOST_CHECK_EQUAL(WITH_LOCK(::cs_main, return chainstate.GetLastFlushedBlock()), second_from_tip);
106 }
107
108 BOOST_AUTO_TEST_SUITE_END()
109