LCOV - code coverage report
Current view: top level - src/test/fuzz - utxo_total_supply.cpp (source / functions) Coverage Total Hit
Test: fuzz_coverage.info Lines: 98.0 % 98 96
Test Date: 2026-02-19 05:12:30 Functions: 100.0 % 10 10
Branches: 55.3 % 150 83

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) 2020-present 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/consensus.h>
       7                 :             : #include <consensus/merkle.h>
       8                 :             : #include <kernel/coinstats.h>
       9                 :             : #include <node/miner.h>
      10                 :             : #include <script/interpreter.h>
      11                 :             : #include <streams.h>
      12                 :             : #include <test/fuzz/FuzzedDataProvider.h>
      13                 :             : #include <test/fuzz/fuzz.h>
      14                 :             : #include <test/fuzz/util.h>
      15                 :             : #include <test/util/mining.h>
      16                 :             : #include <test/util/setup_common.h>
      17                 :             : #include <util/chaintype.h>
      18                 :             : #include <util/time.h>
      19                 :             : #include <validation.h>
      20                 :             : 
      21                 :             : using node::BlockAssembler;
      22                 :             : 
      23         [ +  - ]:        1933 : FUZZ_TARGET(utxo_total_supply)
      24                 :             : {
      25                 :        1479 :     SeedRandomStateForTest(SeedRand::ZEROS);
      26                 :        1479 :     FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
      27                 :        1479 :     SetMockTime(ConsumeTime(fuzzed_data_provider, /*min=*/1296688602)); // regtest genesis block timestamp
      28                 :             :     /** The testing setup that creates a chainman only (no chainstate) */
      29                 :        1479 :     ChainTestingSetup test_setup{
      30                 :             :         ChainType::REGTEST,
      31                 :             :         {
      32                 :             :             .extra_args = {
      33                 :             :                 "-testactivationheight=bip34@2",
      34                 :             :             },
      35                 :             :         },
      36   [ +  -  +  - ]:        2958 :     };
      37                 :             :     // Create chainstate
      38         [ +  - ]:        1479 :     test_setup.LoadVerifyActivateChainstate();
      39                 :        1479 :     auto& node{test_setup.m_node};
      40   [ -  +  +  - ]:        1479 :     auto& chainman{*Assert(test_setup.m_node.chainman)};
      41                 :             : 
      42                 :      110670 :     const auto ActiveHeight = [&]() {
      43                 :      109191 :         LOCK(chainman.GetMutex());
      44   [ +  -  +  - ]:      109191 :         return chainman.ActiveHeight();
      45                 :      110670 :     };
      46         [ +  - ]:        1479 :     BlockAssembler::Options options;
      47         [ +  - ]:        1479 :     options.coinbase_output_script = CScript() << OP_FALSE;
      48                 :        1479 :     options.include_dummy_extranonce = true;
      49                 :       80007 :     const auto PrepareNextBlock = [&]() {
      50                 :             :         // Use OP_FALSE to avoid BIP30 check from hitting early
      51                 :       78528 :         auto block = PrepareBlock(node, options);
      52                 :             :         // Replace OP_FALSE with OP_TRUE
      53                 :       78528 :         {
      54         [ +  - ]:       78528 :             CMutableTransaction tx{*block->vtx.back()};
      55                 :       78528 :             tx.nLockTime = 0; // Use the same nLockTime for all as we want to duplicate one of them.
      56   [ +  -  +  - ]:       78528 :             tx.vout.at(0).scriptPubKey = CScript{} << OP_TRUE;
      57   [ +  -  -  + ]:      157056 :             block->vtx.back() = MakeTransactionRef(tx);
      58                 :           0 :         }
      59                 :       78528 :         return block;
      60                 :        1479 :     };
      61                 :             : 
      62                 :             :     /** The block template this fuzzer is working on */
      63         [ +  - ]:        1479 :     auto current_block = PrepareNextBlock();
      64                 :             :     /** Append-only set of tx outpoints, entries are not removed when spent */
      65                 :        1479 :     std::vector<std::pair<COutPoint, CTxOut>> txos;
      66                 :             :     /** The utxo stats at the chain tip */
      67                 :        1479 :     kernel::CCoinsStats utxo_stats;
      68                 :             :     /** The total amount of coins in the utxo set */
      69                 :        1479 :     CAmount circulation{0};
      70                 :             : 
      71                 :             : 
      72                 :             :     // Store the tx out in the txo map
      73                 :      125324 :     const auto StoreLastTxo = [&]() {
      74                 :             :         // get last tx
      75         [ -  + ]:      123845 :         const CTransaction& tx = *current_block->vtx.back();
      76                 :             :         // get last out
      77         [ -  + ]:      123845 :         const uint32_t i = tx.vout.size() - 1;
      78                 :             :         // store it
      79                 :      123845 :         txos.emplace_back(COutPoint{tx.GetHash(), i}, tx.vout.at(i));
      80   [ -  +  +  +  :      216861 :         if (current_block->vtx.size() == 1 && tx.vout.at(i).scriptPubKey[0] == OP_RETURN) {
             +  +  +  + ]
      81                 :             :             // also store coinbase
      82         [ -  + ]:       87203 :             const uint32_t i = tx.vout.size() - 2;
      83                 :       87203 :             txos.emplace_back(COutPoint{tx.GetHash(), i}, tx.vout.at(i));
      84                 :             :         }
      85                 :      125324 :     };
      86                 :       46796 :     const auto AppendRandomTxo = [&](CMutableTransaction& tx) {
      87         [ -  + ]:       45317 :         const auto& txo = txos.at(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, txos.size() - 1));
      88                 :       45317 :         tx.vin.emplace_back(txo.first);
      89                 :       45317 :         tx.vout.emplace_back(txo.second.nValue, txo.second.scriptPubKey); // "Forward" coin with no fee
      90                 :       46796 :     };
      91                 :       78528 :     const auto UpdateUtxoStats = [&](bool wipe_cache) {
      92                 :       77049 :         LOCK(chainman.GetMutex());
      93   [ +  -  +  - ]:       77049 :         chainman.ActiveChainstate().ForceFlushStateToDisk(wipe_cache);
      94                 :       77049 :         utxo_stats = std::move(
      95   [ +  -  +  -  :      154098 :             *Assert(kernel::ComputeUTXOStats(kernel::CoinStatsHashType::NONE, &chainman.ActiveChainstate().CoinsDB(), chainman.m_blockman, {})));
                   +  - ]
      96                 :             :         // Check that miner can't print more money than they are allowed to
      97   [ +  -  +  - ]:      154098 :         assert(circulation == utxo_stats.total_amount);
      98                 :       78528 :     };
      99                 :             : 
     100                 :             : 
     101                 :             :     // Update internal state to chain tip
     102         [ +  - ]:        1479 :     StoreLastTxo();
     103         [ +  - ]:        1479 :     UpdateUtxoStats(/*wipe_cache=*/fuzzed_data_provider.ConsumeBool());
     104   [ +  -  -  + ]:        1479 :     assert(ActiveHeight() == 0);
     105                 :             :     // Get at which height we duplicate the coinbase
     106                 :             :     // Assuming that the fuzzer will mine relatively short chains (less than 200 blocks), we want the duplicate coinbase to be not too high.
     107                 :             :     // Up to 300 seems reasonable.
     108                 :        1479 :     int64_t duplicate_coinbase_height = fuzzed_data_provider.ConsumeIntegralInRange(0, 300);
     109                 :             :     // Always pad with OP_0 at the end to avoid bad-cb-length error
     110   [ +  -  +  - ]:        1479 :     const CScript duplicate_coinbase_script = CScript() << duplicate_coinbase_height << OP_0;
     111                 :             :     // Mine the first block with this duplicate
     112   [ +  -  -  + ]:        2958 :     current_block = PrepareNextBlock();
     113         [ +  - ]:        1479 :     StoreLastTxo();
     114                 :             : 
     115                 :        1479 :     {
     116                 :             :         // Create duplicate (CScript should match exact format as in CreateNewBlock)
     117         [ +  - ]:        1479 :         CMutableTransaction tx{*current_block->vtx.front()};
     118         [ +  - ]:        1479 :         tx.vin.at(0).scriptSig = duplicate_coinbase_script;
     119                 :             : 
     120                 :             :         // Mine block and create next block template
     121   [ +  -  -  + ]:        2958 :         current_block->vtx.front() = MakeTransactionRef(tx);
     122                 :           0 :     }
     123         [ +  - ]:        1479 :     current_block->hashMerkleRoot = BlockMerkleRoot(*current_block);
     124   [ +  -  -  + ]:        1479 :     assert(!MineBlock(node, current_block).IsNull());
     125   [ +  -  +  -  :        1479 :     circulation += GetBlockSubsidy(ActiveHeight(), Params().GetConsensus());
                   +  - ]
     126                 :             : 
     127   [ +  -  -  + ]:        1479 :     assert(ActiveHeight() == 1);
     128         [ +  - ]:        1479 :     UpdateUtxoStats(/*wipe_cache=*/fuzzed_data_provider.ConsumeBool());
     129   [ +  -  -  + ]:        2958 :     current_block = PrepareNextBlock();
     130         [ +  - ]:        1479 :     StoreLastTxo();
     131                 :             : 
     132                 :             :     // Limit to avoid timeout, but enough to cover duplicate_coinbase_height
     133                 :             :     // and CVE-2018-17144.
     134   [ +  +  +  + ]:      120887 :     LIMITED_WHILE(fuzzed_data_provider.remaining_bytes(), 2'00)
     135                 :             :     {
     136         [ +  - ]:      119408 :         CallOneOf(
     137                 :             :             fuzzed_data_provider,
     138                 :       22606 :             [&] {
     139                 :             :                 // Append an input-output pair to the last tx in the current block
     140                 :       22606 :                 CMutableTransaction tx{*current_block->vtx.back()};
     141         [ +  - ]:       22606 :                 AppendRandomTxo(tx);
     142   [ +  -  -  + ]:       45212 :                 current_block->vtx.back() = MakeTransactionRef(tx);
     143         [ +  - ]:       22606 :                 StoreLastTxo();
     144                 :       22606 :             },
     145                 :       22711 :             [&] {
     146                 :             :                 // Append a tx to the list of txs in the current block
     147                 :       22711 :                 CMutableTransaction tx{};
     148         [ +  - ]:       22711 :                 AppendRandomTxo(tx);
     149   [ +  -  +  -  :       45422 :                 current_block->vtx.push_back(MakeTransactionRef(tx));
                   -  + ]
     150         [ +  - ]:       22711 :                 StoreLastTxo();
     151                 :       22711 :             },
     152                 :       74091 :             [&] {
     153                 :             :                 // Append the current block to the active chain
     154                 :       74091 :                 node::RegenerateCommitments(*current_block, chainman);
     155                 :       74091 :                 const bool was_valid = !MineBlock(node, current_block).IsNull();
     156                 :             : 
     157                 :       74091 :                 const uint256 prev_hash_serialized{utxo_stats.hashSerialized};
     158         [ +  + ]:       74091 :                 if (was_valid) {
     159         [ +  + ]:       52377 :                     if (duplicate_coinbase_height == ActiveHeight()) {
     160                 :             :                         // we mined the duplicate coinbase
     161         [ -  + ]:           1 :                         assert(current_block->vtx.at(0)->vin.at(0).scriptSig == duplicate_coinbase_script);
     162                 :             :                     }
     163                 :             : 
     164                 :       52377 :                     circulation += GetBlockSubsidy(ActiveHeight(), Params().GetConsensus());
     165                 :             :                 }
     166                 :             : 
     167                 :       74091 :                 UpdateUtxoStats(/*wipe_cache=*/fuzzed_data_provider.ConsumeBool());
     168                 :             : 
     169         [ +  + ]:       74091 :                 if (!was_valid) {
     170                 :             :                     // utxo stats must not change
     171         [ -  + ]:       21714 :                     assert(prev_hash_serialized == utxo_stats.hashSerialized);
     172                 :             :                 }
     173                 :             : 
     174         [ -  + ]:       74091 :                 current_block = PrepareNextBlock();
     175                 :       74091 :                 StoreLastTxo();
     176                 :       74091 :             });
     177                 :             :     }
     178         [ +  - ]:        4437 : }
        

Generated by: LCOV version 2.0-1