LCOV - code coverage report
Current view: top level - src/test - validation_chainstatemanager_tests.cpp (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 217 217 100.0 %
Date: 2021-06-29 14:35:33 Functions: 10 10 100.0 %
Legend: Modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed

Not modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed
Branches: 15 16 93.8 %

           Branch data     Line data    Source code
#       1                 :            : // Copyright (c) 2019-2020 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 <consensus/validation.h>
#       7                 :            : #include <node/utxo_snapshot.h>
#       8                 :            : #include <random.h>
#       9                 :            : #include <rpc/blockchain.h>
#      10                 :            : #include <sync.h>
#      11                 :            : #include <test/util/setup_common.h>
#      12                 :            : #include <uint256.h>
#      13                 :            : #include <validation.h>
#      14                 :            : #include <validationinterface.h>
#      15                 :            : 
#      16                 :            : #include <tinyformat.h>
#      17                 :            : #include <univalue.h>
#      18                 :            : 
#      19                 :            : #include <vector>
#      20                 :            : 
#      21                 :            : #include <boost/test/unit_test.hpp>
#      22                 :            : 
#      23                 :            : BOOST_FIXTURE_TEST_SUITE(validation_chainstatemanager_tests, ChainTestingSetup)
#      24                 :            : 
#      25                 :            : //! Basic tests for ChainstateManager.
#      26                 :            : //!
#      27                 :            : //! First create a legacy (IBD) chainstate, then create a snapshot chainstate.
#      28                 :            : BOOST_AUTO_TEST_CASE(chainstatemanager)
#      29                 :          1 : {
#      30                 :          1 :     ChainstateManager& manager = *m_node.chainman;
#      31                 :          1 :     CTxMemPool& mempool = *m_node.mempool;
#      32                 :            : 
#      33                 :          1 :     std::vector<CChainState*> chainstates;
#      34                 :          1 :     const CChainParams& chainparams = Params();
#      35                 :            : 
#      36                 :          1 :     BOOST_CHECK(!manager.SnapshotBlockhash().has_value());
#      37                 :            : 
#      38                 :            :     // Create a legacy (IBD) chainstate.
#      39                 :            :     //
#      40                 :          1 :     CChainState& c1 = WITH_LOCK(::cs_main, return manager.InitializeChainstate(mempool));
#      41                 :          1 :     chainstates.push_back(&c1);
#      42                 :          1 :     c1.InitCoinsDB(
#      43                 :          1 :         /* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
#      44                 :          1 :     WITH_LOCK(::cs_main, c1.InitCoinsCache(1 << 23));
#      45                 :            : 
#      46                 :          1 :     BOOST_CHECK(!manager.IsSnapshotActive());
#      47                 :          1 :     BOOST_CHECK(!manager.IsSnapshotValidated());
#      48                 :          1 :     BOOST_CHECK(!manager.IsBackgroundIBD(&c1));
#      49                 :          1 :     auto all = manager.GetAll();
#      50                 :          1 :     BOOST_CHECK_EQUAL_COLLECTIONS(all.begin(), all.end(), chainstates.begin(), chainstates.end());
#      51                 :            : 
#      52                 :          1 :     auto& active_chain = manager.ActiveChain();
#      53                 :          1 :     BOOST_CHECK_EQUAL(&active_chain, &c1.m_chain);
#      54                 :            : 
#      55                 :          1 :     BOOST_CHECK_EQUAL(manager.ActiveHeight(), -1);
#      56                 :            : 
#      57                 :          1 :     auto active_tip = manager.ActiveTip();
#      58                 :          1 :     auto exp_tip = c1.m_chain.Tip();
#      59                 :          1 :     BOOST_CHECK_EQUAL(active_tip, exp_tip);
#      60                 :            : 
#      61                 :          1 :     auto& validated_cs = manager.ValidatedChainstate();
#      62                 :          1 :     BOOST_CHECK_EQUAL(&validated_cs, &c1);
#      63                 :            : 
#      64                 :          1 :     BOOST_CHECK(!manager.SnapshotBlockhash().has_value());
#      65                 :            : 
#      66                 :            :     // Create a snapshot-based chainstate.
#      67                 :            :     //
#      68                 :          1 :     const uint256 snapshot_blockhash = GetRandHash();
#      69                 :          1 :     CChainState& c2 = WITH_LOCK(::cs_main, return manager.InitializeChainstate(
#      70                 :          1 :         mempool, snapshot_blockhash));
#      71                 :          1 :     chainstates.push_back(&c2);
#      72                 :            : 
#      73                 :          1 :     BOOST_CHECK_EQUAL(manager.SnapshotBlockhash().value(), snapshot_blockhash);
#      74                 :            : 
#      75                 :          1 :     c2.InitCoinsDB(
#      76                 :          1 :         /* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
#      77                 :          1 :     WITH_LOCK(::cs_main, c2.InitCoinsCache(1 << 23));
#      78                 :            :     // Unlike c1, which doesn't have any blocks. Gets us different tip, height.
#      79                 :          1 :     c2.LoadGenesisBlock(chainparams);
#      80                 :          1 :     BlockValidationState _;
#      81                 :          1 :     BOOST_CHECK(c2.ActivateBestChain(_, chainparams, nullptr));
#      82                 :            : 
#      83                 :          1 :     BOOST_CHECK(manager.IsSnapshotActive());
#      84                 :          1 :     BOOST_CHECK(!manager.IsSnapshotValidated());
#      85                 :          1 :     BOOST_CHECK(manager.IsBackgroundIBD(&c1));
#      86                 :          1 :     BOOST_CHECK(!manager.IsBackgroundIBD(&c2));
#      87                 :          1 :     auto all2 = manager.GetAll();
#      88                 :          1 :     BOOST_CHECK_EQUAL_COLLECTIONS(all2.begin(), all2.end(), chainstates.begin(), chainstates.end());
#      89                 :            : 
#      90                 :          1 :     auto& active_chain2 = manager.ActiveChain();
#      91                 :          1 :     BOOST_CHECK_EQUAL(&active_chain2, &c2.m_chain);
#      92                 :            : 
#      93                 :          1 :     BOOST_CHECK_EQUAL(manager.ActiveHeight(), 0);
#      94                 :            : 
#      95                 :          1 :     auto active_tip2 = manager.ActiveTip();
#      96                 :          1 :     auto exp_tip2 = c2.m_chain.Tip();
#      97                 :          1 :     BOOST_CHECK_EQUAL(active_tip2, exp_tip2);
#      98                 :            : 
#      99                 :            :     // Ensure that these pointers actually correspond to different
#     100                 :            :     // CCoinsViewCache instances.
#     101                 :          1 :     BOOST_CHECK(exp_tip != exp_tip2);
#     102                 :            : 
#     103                 :          1 :     auto& validated_cs2 = manager.ValidatedChainstate();
#     104                 :          1 :     BOOST_CHECK_EQUAL(&validated_cs2, &c1);
#     105                 :            : 
#     106                 :          1 :     auto& validated_chain = manager.ValidatedChain();
#     107                 :          1 :     BOOST_CHECK_EQUAL(&validated_chain, &c1.m_chain);
#     108                 :            : 
#     109                 :          1 :     auto validated_tip = manager.ValidatedTip();
#     110                 :          1 :     exp_tip = c1.m_chain.Tip();
#     111                 :          1 :     BOOST_CHECK_EQUAL(validated_tip, exp_tip);
#     112                 :            : 
#     113                 :            :     // Let scheduler events finish running to avoid accessing memory that is going to be unloaded
#     114                 :          1 :     SyncWithValidationInterfaceQueue();
#     115                 :            : 
#     116                 :          1 :     WITH_LOCK(::cs_main, manager.Unload());
#     117                 :          1 : }
#     118                 :            : 
#     119                 :            : //! Test rebalancing the caches associated with each chainstate.
#     120                 :            : BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
#     121                 :          1 : {
#     122                 :          1 :     ChainstateManager& manager = *m_node.chainman;
#     123                 :          1 :     CTxMemPool& mempool = *m_node.mempool;
#     124                 :            : 
#     125                 :          1 :     size_t max_cache = 10000;
#     126                 :          1 :     manager.m_total_coinsdb_cache = max_cache;
#     127                 :          1 :     manager.m_total_coinstip_cache = max_cache;
#     128                 :            : 
#     129                 :          1 :     std::vector<CChainState*> chainstates;
#     130                 :            : 
#     131                 :            :     // Create a legacy (IBD) chainstate.
#     132                 :            :     //
#     133                 :          1 :     CChainState& c1 = WITH_LOCK(cs_main, return manager.InitializeChainstate(mempool));
#     134                 :          1 :     chainstates.push_back(&c1);
#     135                 :          1 :     c1.InitCoinsDB(
#     136                 :          1 :         /* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
#     137                 :            : 
#     138                 :          1 :     {
#     139                 :          1 :         LOCK(::cs_main);
#     140                 :          1 :         c1.InitCoinsCache(1 << 23);
#     141                 :          1 :         BOOST_REQUIRE(c1.LoadGenesisBlock(Params()));
#     142                 :          1 :         c1.CoinsTip().SetBestBlock(InsecureRand256());
#     143                 :          1 :         manager.MaybeRebalanceCaches();
#     144                 :          1 :     }
#     145                 :            : 
#     146                 :          1 :     BOOST_CHECK_EQUAL(c1.m_coinstip_cache_size_bytes, max_cache);
#     147                 :          1 :     BOOST_CHECK_EQUAL(c1.m_coinsdb_cache_size_bytes, max_cache);
#     148                 :            : 
#     149                 :            :     // Create a snapshot-based chainstate.
#     150                 :            :     //
#     151                 :          1 :     CChainState& c2 = WITH_LOCK(cs_main, return manager.InitializeChainstate(mempool, GetRandHash()));
#     152                 :          1 :     chainstates.push_back(&c2);
#     153                 :          1 :     c2.InitCoinsDB(
#     154                 :          1 :         /* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
#     155                 :            : 
#     156                 :          1 :     {
#     157                 :          1 :         LOCK(::cs_main);
#     158                 :          1 :         c2.InitCoinsCache(1 << 23);
#     159                 :          1 :         BOOST_REQUIRE(c2.LoadGenesisBlock(Params()));
#     160                 :          1 :         c2.CoinsTip().SetBestBlock(InsecureRand256());
#     161                 :          1 :         manager.MaybeRebalanceCaches();
#     162                 :          1 :     }
#     163                 :            : 
#     164                 :            :     // Since both chainstates are considered to be in initial block download,
#     165                 :            :     // the snapshot chainstate should take priority.
#     166                 :          1 :     BOOST_CHECK_CLOSE(c1.m_coinstip_cache_size_bytes, max_cache * 0.05, 1);
#     167                 :          1 :     BOOST_CHECK_CLOSE(c1.m_coinsdb_cache_size_bytes, max_cache * 0.05, 1);
#     168                 :          1 :     BOOST_CHECK_CLOSE(c2.m_coinstip_cache_size_bytes, max_cache * 0.95, 1);
#     169                 :          1 :     BOOST_CHECK_CLOSE(c2.m_coinsdb_cache_size_bytes, max_cache * 0.95, 1);
#     170                 :          1 : }
#     171                 :            : 
#     172                 :          3 : auto NoMalleation = [](CAutoFile& file, SnapshotMetadata& meta){};
#     173                 :            : 
#     174                 :            : template<typename F = decltype(NoMalleation)>
#     175                 :            : static bool
#     176                 :            : CreateAndActivateUTXOSnapshot(NodeContext& node, const fs::path root, F malleation = NoMalleation)
#     177                 :          8 : {
#     178                 :            :     // Write out a snapshot to the test's tempdir.
#     179                 :            :     //
#     180                 :          8 :     int height;
#     181                 :          8 :     WITH_LOCK(::cs_main, height = node.chainman->ActiveHeight());
#     182                 :          8 :     fs::path snapshot_path = root / tfm::format("test_snapshot.%d.dat", height);
#     183                 :          8 :     FILE* outfile{fsbridge::fopen(snapshot_path, "wb")};
#     184                 :          8 :     CAutoFile auto_outfile{outfile, SER_DISK, CLIENT_VERSION};
#     185                 :            : 
#     186                 :          8 :     UniValue result = CreateUTXOSnapshot(node, node.chainman->ActiveChainstate(), auto_outfile);
#     187                 :          8 :     BOOST_TEST_MESSAGE(
#     188                 :          8 :         "Wrote UTXO snapshot to " << snapshot_path.make_preferred().string() << ": " << result.write());
#     189                 :            : 
#     190                 :            :     // Read the written snapshot in and then activate it.
#     191                 :            :     //
#     192                 :          8 :     FILE* infile{fsbridge::fopen(snapshot_path, "rb")};
#     193                 :          8 :     CAutoFile auto_infile{infile, SER_DISK, CLIENT_VERSION};
#     194                 :          8 :     SnapshotMetadata metadata;
#     195                 :          8 :     auto_infile >> metadata;
#     196                 :            : 
#     197                 :          8 :     malleation(auto_infile, metadata);
#     198                 :            : 
#     199                 :          8 :     return node.chainman->ActivateSnapshot(auto_infile, metadata, /*in_memory*/ true);
#     200                 :          8 : }
#     201                 :            : 
#     202                 :            : //! Test basic snapshot activation.
#     203                 :            : BOOST_FIXTURE_TEST_CASE(chainstatemanager_activate_snapshot, TestChain100Setup)
#     204                 :          1 : {
#     205                 :          1 :     ChainstateManager& chainman = *Assert(m_node.chainman);
#     206                 :            : 
#     207                 :          1 :     size_t initial_size;
#     208                 :          1 :     size_t initial_total_coins{100};
#     209                 :            : 
#     210                 :            :     // Make some initial assertions about the contents of the chainstate.
#     211                 :          1 :     {
#     212                 :          1 :         LOCK(::cs_main);
#     213                 :          1 :         CCoinsViewCache& ibd_coinscache = chainman.ActiveChainstate().CoinsTip();
#     214                 :          1 :         initial_size = ibd_coinscache.GetCacheSize();
#     215                 :          1 :         size_t total_coins{0};
#     216                 :            : 
#     217         [ +  + ]:        100 :         for (CTransactionRef& txn : m_coinbase_txns) {
#     218                 :        100 :             COutPoint op{txn->GetHash(), 0};
#     219                 :        100 :             BOOST_CHECK(ibd_coinscache.HaveCoin(op));
#     220                 :        100 :             total_coins++;
#     221                 :        100 :         }
#     222                 :            : 
#     223                 :          1 :         BOOST_CHECK_EQUAL(total_coins, initial_total_coins);
#     224                 :          1 :         BOOST_CHECK_EQUAL(initial_size, initial_total_coins);
#     225                 :          1 :     }
#     226                 :            : 
#     227                 :            :     // Snapshot should refuse to load at this height.
#     228                 :          1 :     BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(m_node, m_path_root));
#     229                 :          1 :     BOOST_CHECK(!chainman.ActiveChainstate().m_from_snapshot_blockhash);
#     230                 :          1 :     BOOST_CHECK(!chainman.SnapshotBlockhash());
#     231                 :            : 
#     232                 :            :     // Mine 10 more blocks, putting at us height 110 where a valid assumeutxo value can
#     233                 :            :     // be found.
#     234                 :          1 :     constexpr int snapshot_height = 110;
#     235                 :          1 :     mineBlocks(10);
#     236                 :          1 :     initial_size += 10;
#     237                 :          1 :     initial_total_coins += 10;
#     238                 :            : 
#     239                 :            :     // Should not load malleated snapshots
#     240                 :          1 :     BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(
#     241                 :          1 :         m_node, m_path_root, [](CAutoFile& auto_infile, SnapshotMetadata& metadata) {
#     242                 :            :             // A UTXO is missing but count is correct
#     243                 :          1 :             metadata.m_coins_count -= 1;
#     244                 :            : 
#     245                 :          1 :             COutPoint outpoint;
#     246                 :          1 :             Coin coin;
#     247                 :            : 
#     248                 :          1 :             auto_infile >> outpoint;
#     249                 :          1 :             auto_infile >> coin;
#     250                 :          1 :     }));
#     251                 :          1 :     BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(
#     252                 :          1 :         m_node, m_path_root, [](CAutoFile& auto_infile, SnapshotMetadata& metadata) {
#     253                 :            :             // Coins count is larger than coins in file
#     254                 :          1 :             metadata.m_coins_count += 1;
#     255                 :          1 :     }));
#     256                 :          1 :     BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(
#     257                 :          1 :         m_node, m_path_root, [](CAutoFile& auto_infile, SnapshotMetadata& metadata) {
#     258                 :            :             // Coins count is smaller than coins in file
#     259                 :          1 :             metadata.m_coins_count -= 1;
#     260                 :          1 :     }));
#     261                 :          1 :     BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(
#     262                 :          1 :         m_node, m_path_root, [](CAutoFile& auto_infile, SnapshotMetadata& metadata) {
#     263                 :            :             // Wrong hash
#     264                 :          1 :             metadata.m_base_blockhash = uint256::ZERO;
#     265                 :          1 :     }));
#     266                 :          1 :     BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(
#     267                 :          1 :         m_node, m_path_root, [](CAutoFile& auto_infile, SnapshotMetadata& metadata) {
#     268                 :            :             // Wrong hash
#     269                 :          1 :             metadata.m_base_blockhash = uint256::ONE;
#     270                 :          1 :     }));
#     271                 :            : 
#     272                 :          1 :     BOOST_REQUIRE(CreateAndActivateUTXOSnapshot(m_node, m_path_root));
#     273                 :            : 
#     274                 :            :     // Ensure our active chain is the snapshot chainstate.
#     275                 :          1 :     BOOST_CHECK(!chainman.ActiveChainstate().m_from_snapshot_blockhash->IsNull());
#     276                 :          1 :     BOOST_CHECK_EQUAL(
#     277                 :          1 :         *chainman.ActiveChainstate().m_from_snapshot_blockhash,
#     278                 :          1 :         *chainman.SnapshotBlockhash());
#     279                 :            : 
#     280                 :          1 :     const AssumeutxoData& au_data = *ExpectedAssumeutxo(snapshot_height, ::Params());
#     281                 :          1 :     const CBlockIndex* tip = chainman.ActiveTip();
#     282                 :            : 
#     283                 :          1 :     BOOST_CHECK_EQUAL(tip->nChainTx, au_data.nChainTx);
#     284                 :            : 
#     285                 :            :     // To be checked against later when we try loading a subsequent snapshot.
#     286                 :          1 :     uint256 loaded_snapshot_blockhash{*chainman.SnapshotBlockhash()};
#     287                 :            : 
#     288                 :            :     // Make some assertions about the both chainstates. These checks ensure the
#     289                 :            :     // legacy chainstate hasn't changed and that the newly created chainstate
#     290                 :            :     // reflects the expected content.
#     291                 :          1 :     {
#     292                 :          1 :         LOCK(::cs_main);
#     293                 :          1 :         int chains_tested{0};
#     294                 :            : 
#     295         [ +  + ]:          2 :         for (CChainState* chainstate : chainman.GetAll()) {
#     296                 :          2 :             BOOST_TEST_MESSAGE("Checking coins in " << chainstate->ToString());
#     297                 :          2 :             CCoinsViewCache& coinscache = chainstate->CoinsTip();
#     298                 :            : 
#     299                 :            :             // Both caches will be empty initially.
#     300                 :          2 :             BOOST_CHECK_EQUAL((unsigned int)0, coinscache.GetCacheSize());
#     301                 :            : 
#     302                 :          2 :             size_t total_coins{0};
#     303                 :            : 
#     304         [ +  + ]:        220 :             for (CTransactionRef& txn : m_coinbase_txns) {
#     305                 :        220 :                 COutPoint op{txn->GetHash(), 0};
#     306                 :        220 :                 BOOST_CHECK(coinscache.HaveCoin(op));
#     307                 :        220 :                 total_coins++;
#     308                 :        220 :             }
#     309                 :            : 
#     310                 :          2 :             BOOST_CHECK_EQUAL(initial_size , coinscache.GetCacheSize());
#     311                 :          2 :             BOOST_CHECK_EQUAL(total_coins, initial_total_coins);
#     312                 :          2 :             chains_tested++;
#     313                 :          2 :         }
#     314                 :            : 
#     315                 :          1 :         BOOST_CHECK_EQUAL(chains_tested, 2);
#     316                 :          1 :     }
#     317                 :            : 
#     318                 :            :     // Mine some new blocks on top of the activated snapshot chainstate.
#     319                 :          1 :     constexpr size_t new_coins{100};
#     320                 :          1 :     mineBlocks(new_coins);  // Defined in TestChain100Setup.
#     321                 :            : 
#     322                 :          1 :     {
#     323                 :          1 :         LOCK(::cs_main);
#     324                 :          1 :         size_t coins_in_active{0};
#     325                 :          1 :         size_t coins_in_ibd{0};
#     326                 :          1 :         size_t coins_missing_ibd{0};
#     327                 :            : 
#     328         [ +  + ]:          2 :         for (CChainState* chainstate : chainman.GetAll()) {
#     329                 :          2 :             BOOST_TEST_MESSAGE("Checking coins in " << chainstate->ToString());
#     330                 :          2 :             CCoinsViewCache& coinscache = chainstate->CoinsTip();
#     331                 :          2 :             bool is_ibd = chainman.IsBackgroundIBD(chainstate);
#     332                 :            : 
#     333         [ +  + ]:        420 :             for (CTransactionRef& txn : m_coinbase_txns) {
#     334                 :        420 :                 COutPoint op{txn->GetHash(), 0};
#     335         [ +  + ]:        420 :                 if (coinscache.HaveCoin(op)) {
#     336         [ +  + ]:        320 :                     (is_ibd ? coins_in_ibd : coins_in_active)++;
#     337         [ +  - ]:        320 :                 } else if (is_ibd) {
#     338                 :        100 :                     coins_missing_ibd++;
#     339                 :        100 :                 }
#     340                 :        420 :             }
#     341                 :          2 :         }
#     342                 :            : 
#     343                 :          1 :         BOOST_CHECK_EQUAL(coins_in_active, initial_total_coins + new_coins);
#     344                 :          1 :         BOOST_CHECK_EQUAL(coins_in_ibd, initial_total_coins);
#     345                 :          1 :         BOOST_CHECK_EQUAL(coins_missing_ibd, new_coins);
#     346                 :          1 :     }
#     347                 :            : 
#     348                 :            :     // Snapshot should refuse to load after one has already loaded.
#     349                 :          1 :     BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(m_node, m_path_root));
#     350                 :            : 
#     351                 :            :     // Snapshot blockhash should be unchanged.
#     352                 :          1 :     BOOST_CHECK_EQUAL(
#     353                 :          1 :         *chainman.ActiveChainstate().m_from_snapshot_blockhash,
#     354                 :          1 :         loaded_snapshot_blockhash);
#     355                 :          1 : }
#     356                 :            : 
#     357                 :            : BOOST_AUTO_TEST_SUITE_END()

Generated by: LCOV version 1.14