coinstatsindex_tests.cpp raw
1 // Copyright (c) 2020-2022 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 <chainparams.h>
6 #include <index/coinstatsindex.h>
7 #include <interfaces/chain.h>
8 #include <kernel/coinstats.h>
9 #include <test/util/index.h>
10 #include <test/util/setup_common.h>
11 #include <test/util/validation.h>
12 #include <validation.h>
13
14 #include <boost/test/unit_test.hpp>
15
16 BOOST_AUTO_TEST_SUITE(coinstatsindex_tests)
17
18 BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup)
19 {
20 CoinStatsIndex coin_stats_index{interfaces::MakeChain(m_node), 1 << 20, true};
21 BOOST_REQUIRE(coin_stats_index.Init());
22
23 const CBlockIndex* block_index;
24 {
25 LOCK(cs_main);
26 block_index = m_node.chainman->ActiveChain().Tip();
27 }
28
29 // CoinStatsIndex should not be found before it is started.
30 BOOST_CHECK(!coin_stats_index.LookUpStats(*block_index));
31
32 // BlockUntilSyncedToCurrentChain should return false before CoinStatsIndex
33 // is started.
34 BOOST_CHECK(!coin_stats_index.BlockUntilSyncedToCurrentChain());
35
36 BOOST_REQUIRE(coin_stats_index.StartBackgroundSync());
37
38 IndexWaitSynced(coin_stats_index, *Assert(m_node.shutdown_signal));
39
40 // Check that CoinStatsIndex works for genesis block.
41 const CBlockIndex* genesis_block_index;
42 {
43 LOCK(cs_main);
44 genesis_block_index = m_node.chainman->ActiveChain().Genesis();
45 }
46 BOOST_CHECK(coin_stats_index.LookUpStats(*genesis_block_index));
47
48 // Check that CoinStatsIndex updates with new blocks.
49 BOOST_CHECK(coin_stats_index.LookUpStats(*block_index));
50
51 const CScript script_pub_key{CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG};
52 std::vector<CMutableTransaction> noTxns;
53 CreateAndProcessBlock(noTxns, script_pub_key);
54
55 // Let the CoinStatsIndex to catch up again.
56 BOOST_CHECK(coin_stats_index.BlockUntilSyncedToCurrentChain());
57
58 const CBlockIndex* new_block_index;
59 {
60 LOCK(cs_main);
61 new_block_index = m_node.chainman->ActiveChain().Tip();
62 }
63 BOOST_CHECK(coin_stats_index.LookUpStats(*new_block_index));
64
65 BOOST_CHECK(block_index != new_block_index);
66
67 // It is not safe to stop and destroy the index until it finishes handling
68 // the last BlockConnected notification. The BlockUntilSyncedToCurrentChain()
69 // call above is sufficient to ensure this, but the
70 // SyncWithValidationInterfaceQueue() call below is also needed to ensure
71 // TSAN always sees the test thread waiting for the notification thread, and
72 // avoid potential false positive reports.
73 m_node.validation_signals->SyncWithValidationInterfaceQueue();
74
75 // Shutdown sequence (c.f. Shutdown() in init.cpp)
76 coin_stats_index.Stop();
77 }
78
79 // Test shutdown between BlockConnected and ChainStateFlushed notifications,
80 // make sure index is not corrupted and is able to reload.
81 BOOST_FIXTURE_TEST_CASE(coinstatsindex_unclean_shutdown, TestChain100Setup)
82 {
83 Chainstate& chainstate = Assert(m_node.chainman)->ActiveChainstate();
84 const CChainParams& params = Params();
85 {
86 CoinStatsIndex index{interfaces::MakeChain(m_node), 1 << 20};
87 BOOST_REQUIRE(index.Init());
88 BOOST_REQUIRE(index.StartBackgroundSync());
89 IndexWaitSynced(index, *Assert(m_node.shutdown_signal));
90 std::shared_ptr<const CBlock> new_block;
91 CBlockIndex* new_block_index = nullptr;
92 {
93 const CScript script_pub_key{CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG};
94 const CBlock block = this->CreateBlock({}, script_pub_key, chainstate);
95
96 new_block = std::make_shared<CBlock>(block);
97
98 LOCK(cs_main);
99 BlockValidationState state;
100 BOOST_CHECK(CheckBlock(block, state, params.GetConsensus()));
101 BOOST_CHECK(m_node.chainman->AcceptBlock(new_block, state, &new_block_index, true, nullptr, nullptr, true));
102 CCoinsViewCache view(&chainstate.CoinsTip());
103 BOOST_CHECK(chainstate.ConnectBlock(block, state, new_block_index, view));
104 }
105 // Send block connected notification, then stop the index without
106 // sending a chainstate flushed notification. Prior to #24138, this
107 // would cause the index to be corrupted and fail to reload.
108 ValidationInterfaceTest::BlockConnected(ChainstateRole::NORMAL, index, new_block, new_block_index);
109 index.Stop();
110 }
111
112 {
113 CoinStatsIndex index{interfaces::MakeChain(m_node), 1 << 20};
114 BOOST_REQUIRE(index.Init());
115 // Make sure the index can be loaded.
116 BOOST_REQUIRE(index.StartBackgroundSync());
117 index.Stop();
118 }
119 }
120
121 BOOST_AUTO_TEST_SUITE_END()
122