LCOV - code coverage report
Current view: top level - src/test/util - chainstate.h (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 0 51 0.0 %
Date: 2023-10-05 12:38:51 Functions: 0 23 0.0 %
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : // Copyright (c) 2021-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                 :            : #ifndef BITCOIN_TEST_UTIL_CHAINSTATE_H
       6                 :            : #define BITCOIN_TEST_UTIL_CHAINSTATE_H
       7                 :            : 
       8                 :            : #include <clientversion.h>
       9                 :            : #include <logging.h>
      10                 :            : #include <node/context.h>
      11                 :            : #include <node/utxo_snapshot.h>
      12                 :            : #include <rpc/blockchain.h>
      13                 :            : #include <test/util/setup_common.h>
      14                 :            : #include <util/fs.h>
      15                 :            : #include <validation.h>
      16                 :            : 
      17                 :            : #include <univalue.h>
      18                 :            : 
      19                 :          0 : const auto NoMalleation = [](AutoFile& file, node::SnapshotMetadata& meta){};
      20                 :            : 
      21                 :            : /**
      22                 :            :  * Create and activate a UTXO snapshot, optionally providing a function to
      23                 :            :  * malleate the snapshot.
      24                 :            :  *
      25                 :            :  * If `reset_chainstate` is true, reset the original chainstate back to the genesis
      26                 :            :  * block. This allows us to simulate more realistic conditions in which a snapshot is
      27                 :            :  * loaded into an otherwise mostly-uninitialized datadir. It also allows us to test
      28                 :            :  * conditions that would otherwise cause shutdowns based on the IBD chainstate going
      29                 :            :  * past the snapshot it generated.
      30                 :            :  */
      31                 :            : template<typename F = decltype(NoMalleation)>
      32                 :            : static bool
      33                 :          0 : CreateAndActivateUTXOSnapshot(
      34                 :            :     TestingSetup* fixture,
      35                 :            :     F malleation = NoMalleation,
      36                 :            :     bool reset_chainstate = false,
      37                 :            :     bool in_memory_chainstate = false)
      38                 :            : {
      39                 :          0 :     node::NodeContext& node = fixture->m_node;
      40                 :          0 :     fs::path root = fixture->m_path_root;
      41                 :            : 
      42                 :            :     // Write out a snapshot to the test's tempdir.
      43                 :            :     //
      44                 :            :     int height;
      45                 :          0 :     WITH_LOCK(::cs_main, height = node.chainman->ActiveHeight());
      46                 :          0 :     fs::path snapshot_path = root / fs::u8path(tfm::format("test_snapshot.%d.dat", height));
      47                 :          0 :     FILE* outfile{fsbridge::fopen(snapshot_path, "wb")};
      48                 :          0 :     AutoFile auto_outfile{outfile};
      49                 :            : 
      50                 :          0 :     UniValue result = CreateUTXOSnapshot(
      51                 :          0 :         node, node.chainman->ActiveChainstate(), auto_outfile, snapshot_path, snapshot_path);
      52                 :          0 :     LogPrintf(
      53                 :            :         "Wrote UTXO snapshot to %s: %s\n", fs::PathToString(snapshot_path.make_preferred()), result.write());
      54                 :            : 
      55                 :            :     // Read the written snapshot in and then activate it.
      56                 :            :     //
      57                 :          0 :     FILE* infile{fsbridge::fopen(snapshot_path, "rb")};
      58                 :          0 :     AutoFile auto_infile{infile};
      59                 :          0 :     node::SnapshotMetadata metadata;
      60                 :          0 :     auto_infile >> metadata;
      61                 :            : 
      62                 :          0 :     malleation(auto_infile, metadata);
      63                 :            : 
      64                 :          0 :     if (reset_chainstate) {
      65                 :            :         {
      66                 :            :             // What follows is code to selectively reset chainstate data without
      67                 :            :             // disturbing the existing BlockManager instance, which is needed to
      68                 :            :             // recognize the headers chain previously generated by the chainstate we're
      69                 :            :             // removing. Without those headers, we can't activate the snapshot below.
      70                 :            :             //
      71                 :            :             // This is a stripped-down version of node::LoadChainstate which
      72                 :            :             // preserves the block index.
      73                 :          0 :             LOCK(::cs_main);
      74                 :          0 :             CBlockIndex *orig_tip = node.chainman->ActiveChainstate().m_chain.Tip();
      75                 :          0 :             uint256 gen_hash = node.chainman->ActiveChainstate().m_chain[0]->GetBlockHash();
      76                 :          0 :             node.chainman->ResetChainstates();
      77                 :          0 :             node.chainman->InitializeChainstate(node.mempool.get());
      78                 :          0 :             Chainstate& chain = node.chainman->ActiveChainstate();
      79                 :          0 :             Assert(chain.LoadGenesisBlock());
      80                 :            :             // These cache values will be corrected shortly in `MaybeRebalanceCaches`.
      81                 :          0 :             chain.InitCoinsDB(1 << 20, true, false, "");
      82                 :          0 :             chain.InitCoinsCache(1 << 20);
      83                 :          0 :             chain.CoinsTip().SetBestBlock(gen_hash);
      84                 :          0 :             chain.setBlockIndexCandidates.insert(node.chainman->m_blockman.LookupBlockIndex(gen_hash));
      85                 :          0 :             chain.LoadChainTip();
      86                 :          0 :             node.chainman->MaybeRebalanceCaches();
      87                 :            : 
      88                 :            :             // Reset the HAVE_DATA flags below the snapshot height, simulating
      89                 :            :             // never-having-downloaded them in the first place.
      90                 :            :             // TODO: perhaps we could improve this by using pruning to delete
      91                 :            :             // these blocks instead
      92                 :          0 :             CBlockIndex *pindex = orig_tip;
      93                 :          0 :             while (pindex && pindex != chain.m_chain.Tip()) {
      94                 :          0 :                 pindex->nStatus &= ~BLOCK_HAVE_DATA;
      95                 :          0 :                 pindex->nStatus &= ~BLOCK_HAVE_UNDO;
      96                 :            :                 // We have to set the ASSUMED_VALID flag, because otherwise it
      97                 :            :                 // would not be possible to have a block index entry without HAVE_DATA
      98                 :            :                 // and with nTx > 0 (since we aren't setting the pruned flag);
      99                 :            :                 // see CheckBlockIndex().
     100                 :          0 :                 pindex->nStatus |= BLOCK_ASSUMED_VALID;
     101                 :          0 :                 pindex = pindex->pprev;
     102                 :            :             }
     103                 :          0 :         }
     104                 :          0 :         BlockValidationState state;
     105                 :          0 :         if (!node.chainman->ActiveChainstate().ActivateBestChain(state)) {
     106                 :          0 :             throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", state.ToString()));
     107                 :            :         }
     108                 :          0 :         Assert(
     109                 :            :             0 == WITH_LOCK(node.chainman->GetMutex(), return node.chainman->ActiveHeight()));
     110                 :          0 :     }
     111                 :            : 
     112                 :          0 :     auto& new_active = node.chainman->ActiveChainstate();
     113                 :          0 :     auto* tip = new_active.m_chain.Tip();
     114                 :            : 
     115                 :            :     // Disconnect a block so that the snapshot chainstate will be ahead, otherwise
     116                 :            :     // it will refuse to activate.
     117                 :            :     //
     118                 :            :     // TODO this is a unittest-specific hack, and we should probably rethink how to
     119                 :            :     // better generate/activate snapshots in unittests.
     120                 :          0 :     if (tip->pprev) {
     121                 :          0 :         new_active.m_chain.SetTip(*(tip->pprev));
     122                 :          0 :     }
     123                 :            : 
     124                 :          0 :     bool res = node.chainman->ActivateSnapshot(auto_infile, metadata, in_memory_chainstate);
     125                 :            : 
     126                 :            :     // Restore the old tip.
     127                 :          0 :     new_active.m_chain.SetTip(*tip);
     128                 :          0 :     return res;
     129                 :          0 : }
     130                 :            : 
     131                 :            : 
     132                 :            : #endif // BITCOIN_TEST_UTIL_CHAINSTATE_H

Generated by: LCOV version 1.14