/ src / test / coinstatsindex_tests.cpp
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()