coinstatsindex_tests.cpp
1 // Copyright (c) 2020-2022 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 <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)); 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)); 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()