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