// Copyright (c) 2020-2022 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include #include BOOST_AUTO_TEST_SUITE(coinstatsindex_tests) BOOST_FIXTURE_TEST_CASE(coinstatsindex_initial_sync, TestChain100Setup) { CoinStatsIndex coin_stats_index{interfaces::MakeChain(m_node), 1 << 20, true}; BOOST_REQUIRE(coin_stats_index.Init()); const CBlockIndex* block_index; { LOCK(cs_main); block_index = m_node.chainman->ActiveChain().Tip(); } // CoinStatsIndex should not be found before it is started. BOOST_CHECK(!coin_stats_index.LookUpStats(*block_index)); // BlockUntilSyncedToCurrentChain should return false before CoinStatsIndex // is started. BOOST_CHECK(!coin_stats_index.BlockUntilSyncedToCurrentChain()); BOOST_REQUIRE(coin_stats_index.StartBackgroundSync()); IndexWaitSynced(coin_stats_index, *Assert(m_node.shutdown_signal)); // Check that CoinStatsIndex works for genesis block. const CBlockIndex* genesis_block_index; { LOCK(cs_main); genesis_block_index = m_node.chainman->ActiveChain().Genesis(); } BOOST_CHECK(coin_stats_index.LookUpStats(*genesis_block_index)); // Check that CoinStatsIndex updates with new blocks. BOOST_CHECK(coin_stats_index.LookUpStats(*block_index)); const CScript script_pub_key{CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG}; std::vector noTxns; CreateAndProcessBlock(noTxns, script_pub_key); // Let the CoinStatsIndex to catch up again. BOOST_CHECK(coin_stats_index.BlockUntilSyncedToCurrentChain()); const CBlockIndex* new_block_index; { LOCK(cs_main); new_block_index = m_node.chainman->ActiveChain().Tip(); } BOOST_CHECK(coin_stats_index.LookUpStats(*new_block_index)); BOOST_CHECK(block_index != new_block_index); // It is not safe to stop and destroy the index until it finishes handling // the last BlockConnected notification. The BlockUntilSyncedToCurrentChain() // call above is sufficient to ensure this, but the // SyncWithValidationInterfaceQueue() call below is also needed to ensure // TSAN always sees the test thread waiting for the notification thread, and // avoid potential false positive reports. m_node.validation_signals->SyncWithValidationInterfaceQueue(); // Shutdown sequence (c.f. Shutdown() in init.cpp) coin_stats_index.Stop(); } // Test shutdown between BlockConnected and ChainStateFlushed notifications, // make sure index is not corrupted and is able to reload. BOOST_FIXTURE_TEST_CASE(coinstatsindex_unclean_shutdown, TestChain100Setup) { Chainstate& chainstate = Assert(m_node.chainman)->ActiveChainstate(); const CChainParams& params = Params(); { CoinStatsIndex index{interfaces::MakeChain(m_node), 1 << 20}; BOOST_REQUIRE(index.Init()); BOOST_REQUIRE(index.StartBackgroundSync()); IndexWaitSynced(index, *Assert(m_node.shutdown_signal)); std::shared_ptr new_block; CBlockIndex* new_block_index = nullptr; { const CScript script_pub_key{CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG}; const CBlock block = this->CreateBlock({}, script_pub_key, chainstate); new_block = std::make_shared(block); LOCK(cs_main); BlockValidationState state; BOOST_CHECK(CheckBlock(block, state, params.GetConsensus())); BOOST_CHECK(m_node.chainman->AcceptBlock(new_block, state, &new_block_index, true, nullptr, nullptr, true)); CCoinsViewCache view(&chainstate.CoinsTip()); BOOST_CHECK(chainstate.ConnectBlock(block, state, new_block_index, view)); } // Send block connected notification, then stop the index without // sending a chainstate flushed notification. Prior to #24138, this // would cause the index to be corrupted and fail to reload. ValidationInterfaceTest::BlockConnected(ChainstateRole::NORMAL, index, new_block, new_block_index); index.Stop(); } { CoinStatsIndex index{interfaces::MakeChain(m_node), 1 << 20}; BOOST_REQUIRE(index.Init()); // Make sure the index can be loaded. BOOST_REQUIRE(index.StartBackgroundSync()); index.Stop(); } } BOOST_AUTO_TEST_SUITE_END()