Branch data Line data Source code
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 <node/blockstorage.h>
6 : : #include <test/util/logging.h>
7 : : #include <test/util/setup_common.h>
8 : : #include <test/util/time.h>
9 : : #include <txdb.h>
10 : : #include <util/fs.h>
11 : : #include <validation.h>
12 : : #include <validationinterface.h>
13 : :
14 : : #include <boost/test/unit_test.hpp>
15 : :
16 : : #include <cstdlib>
17 : :
18 : : using kernel::ChainstateRole;
19 : :
20 : : // Taken from validation.cpp
21 : : static constexpr auto DATABASE_WRITE_INTERVAL_MIN{50min};
22 : : static constexpr auto DATABASE_WRITE_INTERVAL_MAX{70min};
23 : :
24 : : BOOST_AUTO_TEST_SUITE(chainstate_write_tests)
25 : :
26 [ + - + - : 7 : BOOST_FIXTURE_TEST_CASE(chainstate_write_interval, TestingSetup)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
27 : : {
28 : 2 : struct TestSubscriber final : CValidationInterface {
29 : : bool m_did_flush{false};
30 : 1 : void ChainStateFlushed(const ChainstateRole&, const CBlockLocator&) override
31 : : {
32 : 1 : m_did_flush = true;
33 : 1 : }
34 : : };
35 : :
36 : 1 : const auto sub{std::make_shared<TestSubscriber>()};
37 [ + - + - ]: 2 : m_node.validation_signals->RegisterSharedValidationInterface(sub);
38 [ + - + - ]: 1 : auto& chainstate{Assert(m_node.chainman)->ActiveChainstate()};
39 [ + - ]: 1 : BlockValidationState state_dummy{};
40 [ + - ]: 1 : FakeNodeClock clock{};
41 : :
42 : : // The first periodic flush sets m_next_write and does not flush
43 [ + - ]: 1 : chainstate.FlushStateToDisk(state_dummy, FlushStateMode::PERIODIC);
44 [ + - ]: 1 : m_node.validation_signals->SyncWithValidationInterfaceQueue();
45 [ + - + - : 2 : BOOST_CHECK(!sub->m_did_flush);
+ - ]
46 : :
47 : : // The periodic flush interval is between 50 and 70 minutes (inclusive)
48 [ + - ]: 1 : clock += DATABASE_WRITE_INTERVAL_MIN - 1min;
49 [ + - ]: 1 : chainstate.FlushStateToDisk(state_dummy, FlushStateMode::PERIODIC);
50 [ + - ]: 1 : m_node.validation_signals->SyncWithValidationInterfaceQueue();
51 [ + - + - : 2 : BOOST_CHECK(!sub->m_did_flush);
+ - ]
52 : :
53 [ + - ]: 1 : clock += DATABASE_WRITE_INTERVAL_MAX;
54 [ + - ]: 1 : chainstate.FlushStateToDisk(state_dummy, FlushStateMode::PERIODIC);
55 [ + - ]: 1 : m_node.validation_signals->SyncWithValidationInterfaceQueue();
56 [ + - + - ]: 2 : BOOST_CHECK(sub->m_did_flush);
57 [ + - ]: 3 : }
58 : :
59 : : // Test that we do PERIODIC flushes inside ActivateBestChain.
60 : : // This is necessary for reindex-chainstate to be able to periodically flush
61 : : // before reaching chain tip.
62 [ + - + - : 7 : BOOST_FIXTURE_TEST_CASE(write_during_multiblock_activation, TestChain100Setup)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
63 : : {
64 : 2 : struct TestSubscriber final : CValidationInterface
65 : : {
66 : : const CBlockIndex* m_tip{nullptr};
67 : : const CBlockIndex* m_flushed_at_block{nullptr};
68 : 1 : void ChainStateFlushed(const ChainstateRole&, const CBlockLocator&) override
69 : : {
70 : 1 : m_flushed_at_block = m_tip;
71 : 1 : }
72 : 2 : void UpdatedBlockTip(const CBlockIndex* block_index, const CBlockIndex*, bool) override {
73 : 2 : m_tip = block_index;
74 : 2 : }
75 : : };
76 : :
77 : 1 : auto& chainstate{Assert(m_node.chainman)->ActiveChainstate()};
78 [ - + ]: 1 : BlockValidationState state_dummy{};
79 : :
80 : : // Pop two blocks from the tip
81 [ - + ]: 1 : const CBlockIndex* tip{chainstate.m_chain.Tip()};
82 : 1 : CBlockIndex* second_from_tip{tip->pprev};
83 : :
84 : 1 : {
85 [ + - + - : 2 : LOCK2(m_node.chainman->GetMutex(), chainstate.MempoolMutex());
+ - ]
86 [ + - ]: 1 : chainstate.DisconnectTip(state_dummy, nullptr);
87 [ + - ]: 1 : chainstate.DisconnectTip(state_dummy, nullptr);
88 [ + - ]: 1 : }
89 : :
90 [ + - - + : 2 : BOOST_CHECK_EQUAL(second_from_tip->pprev, chainstate.m_chain.Tip());
+ - ]
91 : :
92 : : // Set m_next_write to current time
93 [ + - ]: 1 : chainstate.FlushStateToDisk(state_dummy, FlushStateMode::FORCE_FLUSH);
94 [ + - + - : 2 : BOOST_CHECK_EQUAL(WITH_LOCK(::cs_main, return chainstate.GetLastFlushedBlock()), second_from_tip->pprev);
+ - ]
95 [ + - ]: 1 : m_node.validation_signals->SyncWithValidationInterfaceQueue();
96 : : // The periodic flush interval is between 50 and 70 minutes (inclusive)
97 : : // The next call to a PERIODIC write will flush
98 [ + - ]: 1 : m_clock += DATABASE_WRITE_INTERVAL_MAX;
99 : :
100 [ + - ]: 1 : const auto sub{std::make_shared<TestSubscriber>()};
101 [ + - + - ]: 2 : m_node.validation_signals->RegisterSharedValidationInterface(sub);
102 : :
103 : : // ActivateBestChain back to tip
104 [ + - ]: 1 : chainstate.ActivateBestChain(state_dummy, nullptr);
105 [ + - - + : 2 : BOOST_CHECK_EQUAL(tip, chainstate.m_chain.Tip());
+ - ]
106 : : // Check that we flushed inside ActivateBestChain while we were at the
107 : : // second block from tip, since FlushStateToDisk is called with PERIODIC
108 : : // inside the outer loop.
109 [ + - ]: 1 : m_node.validation_signals->SyncWithValidationInterfaceQueue();
110 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(sub->m_flushed_at_block, second_from_tip);
111 [ + - + - : 2 : BOOST_CHECK_EQUAL(WITH_LOCK(::cs_main, return chainstate.GetLastFlushedBlock()), second_from_tip);
+ - + - ]
112 : 2 : }
113 : :
114 [ + - + - : 7 : BOOST_FIXTURE_TEST_CASE(chainstate_flush_failure_boundary, TestChain100Setup)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
115 : : {
116 : 2 : auto inject_file_open_failure{[](const fs::path& file) { Assert(fs::remove(file)); Assert(fs::create_directory(file)); }};
117 : 1 : auto& chainstate{Assert(m_node.chainman)->ActiveChainstate()};
118 [ + - ]: 1 : BlockValidationState state{};
119 : :
120 [ + - + - : 2 : BOOST_REQUIRE(chainstate.FlushStateToDisk(state, FlushStateMode::FORCE_FLUSH) && state.IsValid());
+ - - + +
- + - ]
121 [ + - ]: 2 : auto* old_flushed{WITH_LOCK(::cs_main, return chainstate.GetLastFlushedBlock())};
122 : :
123 [ + - ]: 1 : mineBlocks(1);
124 [ + + + - ]: 3 : auto* new_tip{WITH_LOCK(::cs_main, return chainstate.m_chain.Tip())};
125 [ + - + - ]: 1 : BOOST_REQUIRE_NE(old_flushed, new_tip);
126 [ + - + - ]: 3 : inject_file_open_failure(WITH_LOCK(::cs_main, return chainstate.m_blockman.GetBlockPosFilename(new_tip->GetBlockPos())));
127 : :
128 [ + - + - ]: 2 : ASSERT_DEBUG_LOG("Flushing block file to disk failed");
129 [ + - ]: 1 : bool flushed{chainstate.FlushStateToDisk(state, FlushStateMode::FORCE_FLUSH)};
130 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(m_node.exit_status.load(), EXIT_FAILURE);
131 [ + - + - : 2 : BOOST_CHECK(!flushed && state.IsError());
- + + - +
- ]
132 [ + - + - : 2 : BOOST_CHECK_EQUAL(WITH_LOCK(::cs_main, return chainstate.GetLastFlushedBlock()), old_flushed);
+ - ]
133 [ + - + - : 5 : BOOST_CHECK(WITH_LOCK(::cs_main, return chainstate.CoinsDB().GetBestBlock()) == old_flushed->GetBlockHash());
+ - + - ]
134 : 1 : node::CBlockFileInfo file_info;
135 [ + - + - : 4 : BOOST_REQUIRE(WITH_LOCK(::cs_main, return chainstate.m_blockman.m_block_tree_db->ReadBlockFileInfo(new_tip->nFile, file_info)));
+ - + - ]
136 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(file_info.nHeightLast, old_flushed->nHeight);
137 : 2 : }
138 : :
139 : : BOOST_AUTO_TEST_SUITE_END()
|