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 % 99 97
Test Date: 2026-05-23 06:32:53 Functions: 100.0 % 10 10
Branches: 55.8 % 154 86

             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 <test/util/time.h>
      18                 :             : #include <util/chaintype.h>
      19                 :             : #include <util/time.h>
      20                 :             : #include <validation.h>
      21                 :             : 
      22                 :             : using node::BlockAssembler;
      23                 :             : 
      24         [ +  - ]:        1610 : FUZZ_TARGET(utxo_total_supply)
      25                 :             : {
      26                 :        1146 :     SeedRandomStateForTest(SeedRand::ZEROS);
      27                 :        1146 :     FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
      28                 :        1146 :     NodeClockContext clock_ctx{ConsumeTime(fuzzed_data_provider, /*min=*/1296688602)}; // regtest genesis block timestamp
      29                 :             :     /** The testing setup that creates a chainman only (no chainstate) */
      30                 :        1146 :     ChainTestingSetup test_setup{
      31                 :             :         ChainType::REGTEST,
      32                 :             :         {
      33                 :             :             .extra_args = {
      34                 :             :                 "-testactivationheight=bip34@2",
      35                 :             :             },
      36                 :             :         },
      37   [ +  -  +  - ]:        2292 :     };
      38                 :             :     // Create chainstate
      39         [ +  - ]:        1146 :     test_setup.LoadVerifyActivateChainstate();
      40                 :        1146 :     auto& node{test_setup.m_node};
      41   [ -  +  +  - ]:        1146 :     auto& chainman{*Assert(test_setup.m_node.chainman)};
      42                 :             : 
      43                 :      139794 :     const auto ActiveHeight = [&]() {
      44                 :      138648 :         LOCK(chainman.GetMutex());
      45   [ +  -  +  - ]:      138648 :         return chainman.ActiveHeight();
      46                 :      139794 :     };
      47         [ +  - ]:        1146 :     BlockAssembler::Options options;
      48         [ +  - ]:        1146 :     options.coinbase_output_script = CScript() << OP_FALSE;
      49                 :       95051 :     const auto PrepareNextBlock = [&]() {
      50                 :             :         // Use OP_FALSE to avoid BIP30 check from hitting early
      51                 :       93905 :         auto block = PrepareBlock(node, options);
      52                 :             :         // Replace OP_FALSE with OP_TRUE
      53                 :       93905 :         {
      54         [ +  - ]:       93905 :             CMutableTransaction tx{*block->vtx.back()};
      55                 :       93905 :             tx.nLockTime = 0; // Use the same nLockTime for all as we want to duplicate one of them.
      56   [ +  -  +  - ]:       93905 :             tx.vout.at(0).scriptPubKey = CScript{} << OP_TRUE;
      57   [ +  -  -  + ]:      187810 :             block->vtx.back() = MakeTransactionRef(tx);
      58                 :           0 :         }
      59                 :       93905 :         return block;
      60                 :        1146 :     };
      61                 :             : 
      62                 :             :     /** The block template this fuzzer is working on */
      63         [ +  - ]:        1146 :     auto current_block = PrepareNextBlock();
      64                 :             :     /** Append-only set of tx outpoints, entries are not removed when spent */
      65                 :        1146 :     std::vector<std::pair<COutPoint, CTxOut>> txos;
      66                 :             :     /** The utxo stats at the chain tip */
      67                 :        1146 :     kernel::CCoinsStats utxo_stats;
      68                 :             :     /** The total amount of coins in the utxo set */
      69                 :        1146 :     CAmount circulation{0};
      70                 :             : 
      71                 :             : 
      72                 :             :     // Store the tx out in the txo map
      73                 :      132522 :     const auto StoreLastTxo = [&]() {
      74                 :             :         // get last tx
      75         [ -  + ]:      131376 :         const CTransaction& tx = *current_block->vtx.back();
      76                 :             :         // get last out
      77         [ -  + ]:      131376 :         const uint32_t i = tx.vout.size() - 1;
      78                 :             :         // store it
      79                 :      131376 :         txos.emplace_back(COutPoint{tx.GetHash(), i}, tx.vout.at(i));
      80   [ -  +  +  +  :      237241 :         if (current_block->vtx.size() == 1 && tx.vout.at(i).scriptPubKey[0] == OP_RETURN) {
             +  +  +  + ]
      81                 :             :             // also store coinbase
      82         [ -  + ]:      100945 :             const uint32_t i = tx.vout.size() - 2;
      83                 :      100945 :             txos.emplace_back(COutPoint{tx.GetHash(), i}, tx.vout.at(i));
      84                 :             :         }
      85                 :      132522 :     };
      86                 :       38617 :     const auto AppendRandomTxo = [&](CMutableTransaction& tx) {
      87         [ -  + ]:       37471 :         const auto& txo = txos.at(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, txos.size() - 1));
      88                 :       37471 :         tx.vin.emplace_back(txo.first);
      89                 :       37471 :         tx.vout.emplace_back(txo.second.nValue, txo.second.scriptPubKey); // "Forward" coin with no fee
      90                 :       38617 :     };
      91                 :       93905 :     const auto UpdateUtxoStats = [&](bool wipe_cache) {
      92                 :       92759 :         LOCK(chainman.GetMutex());
      93   [ +  -  +  - ]:       92759 :         chainman.ActiveChainstate().ForceFlushStateToDisk(wipe_cache);
      94                 :       92759 :         utxo_stats = std::move(
      95   [ +  -  +  -  :      185518 :             *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   [ +  -  +  - ]:      185518 :         assert(circulation == utxo_stats.total_amount);
      98                 :       93905 :     };
      99                 :             : 
     100                 :             : 
     101                 :             :     // Update internal state to chain tip
     102         [ +  - ]:        1146 :     StoreLastTxo();
     103         [ +  - ]:        1146 :     UpdateUtxoStats(/*wipe_cache=*/fuzzed_data_provider.ConsumeBool());
     104   [ +  -  -  + ]:        1146 :     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                 :        1146 :     int64_t duplicate_coinbase_height = fuzzed_data_provider.ConsumeIntegralInRange(0, 300);
     109                 :             :     // Avoid bad-cb-length error at heights <= 16. Pad the BIP34-encoded height
     110                 :             :     // with OP_0 to satisfy the minimum 2-byte coinbase scriptSig length.
     111         [ +  - ]:        1146 :     CScript duplicate_coinbase_script = CScript() << duplicate_coinbase_height;
     112         [ +  + ]:        1146 :     if (duplicate_coinbase_height <= 16) {
     113         [ +  - ]:         116 :         duplicate_coinbase_script << OP_0;
     114                 :             :     }
     115                 :             :     // Mine the first block with this duplicate
     116   [ +  -  -  + ]:        2292 :     current_block = PrepareNextBlock();
     117         [ +  - ]:        1146 :     StoreLastTxo();
     118                 :             : 
     119                 :        1146 :     {
     120                 :             :         // Create duplicate (CScript should match exact format as in CreateNewBlock)
     121         [ +  - ]:        1146 :         CMutableTransaction tx{*current_block->vtx.front()};
     122         [ +  - ]:        1146 :         tx.vin.at(0).scriptSig = duplicate_coinbase_script;
     123                 :             : 
     124                 :             :         // Mine block and create next block template
     125   [ +  -  -  + ]:        2292 :         current_block->vtx.front() = MakeTransactionRef(tx);
     126                 :           0 :     }
     127         [ +  - ]:        1146 :     current_block->hashMerkleRoot = BlockMerkleRoot(*current_block);
     128   [ +  -  -  + ]:        1146 :     assert(!MineBlock(node, current_block).IsNull());
     129   [ +  -  +  -  :        1146 :     circulation += GetBlockSubsidy(ActiveHeight(), Params().GetConsensus());
                   +  - ]
     130                 :             : 
     131   [ +  -  -  + ]:        1146 :     assert(ActiveHeight() == 1);
     132         [ +  - ]:        1146 :     UpdateUtxoStats(/*wipe_cache=*/fuzzed_data_provider.ConsumeBool());
     133   [ +  -  -  + ]:        2292 :     current_block = PrepareNextBlock();
     134         [ +  - ]:        1146 :     StoreLastTxo();
     135                 :             : 
     136                 :             :     // Limit to avoid timeout, but enough to cover duplicate_coinbase_height
     137                 :             :     // and CVE-2018-17144.
     138   [ +  +  +  + ]:      129084 :     LIMITED_WHILE(fuzzed_data_provider.remaining_bytes(), 2'00)
     139                 :             :     {
     140         [ +  - ]:      127938 :         CallOneOf(
     141                 :             :             fuzzed_data_provider,
     142                 :       18086 :             [&] {
     143                 :             :                 // Append an input-output pair to the last tx in the current block
     144                 :       18086 :                 CMutableTransaction tx{*current_block->vtx.back()};
     145         [ +  - ]:       18086 :                 AppendRandomTxo(tx);
     146   [ +  -  -  + ]:       36172 :                 current_block->vtx.back() = MakeTransactionRef(tx);
     147         [ +  - ]:       18086 :                 StoreLastTxo();
     148                 :       18086 :             },
     149                 :       19385 :             [&] {
     150                 :             :                 // Append a tx to the list of txs in the current block
     151                 :       19385 :                 CMutableTransaction tx{};
     152         [ +  - ]:       19385 :                 AppendRandomTxo(tx);
     153   [ +  -  +  -  :       38770 :                 current_block->vtx.push_back(MakeTransactionRef(tx));
                   -  + ]
     154         [ +  - ]:       19385 :                 StoreLastTxo();
     155                 :       19385 :             },
     156                 :       90467 :             [&] {
     157                 :             :                 // Append the current block to the active chain
     158                 :       90467 :                 node::RegenerateCommitments(*current_block, chainman);
     159                 :       90467 :                 const bool was_valid = !MineBlock(node, current_block).IsNull();
     160                 :             : 
     161                 :       90467 :                 const uint256 prev_hash_serialized{utxo_stats.hashSerialized};
     162         [ +  + ]:       90467 :                 if (was_valid) {
     163         [ +  + ]:       67605 :                     if (duplicate_coinbase_height == ActiveHeight()) {
     164                 :             :                         // we mined the duplicate coinbase
     165         [ -  + ]:          22 :                         assert(current_block->vtx.at(0)->vin.at(0).scriptSig == duplicate_coinbase_script);
     166                 :             :                     }
     167                 :             : 
     168                 :       67605 :                     circulation += GetBlockSubsidy(ActiveHeight(), Params().GetConsensus());
     169                 :             :                 }
     170                 :             : 
     171                 :       90467 :                 UpdateUtxoStats(/*wipe_cache=*/fuzzed_data_provider.ConsumeBool());
     172                 :             : 
     173         [ +  + ]:       90467 :                 if (!was_valid) {
     174                 :             :                     // utxo stats must not change
     175         [ -  + ]:       22862 :                     assert(prev_hash_serialized == utxo_stats.hashSerialized);
     176                 :             :                 }
     177                 :             : 
     178         [ -  + ]:       90467 :                 current_block = PrepareNextBlock();
     179                 :       90467 :                 StoreLastTxo();
     180                 :       90467 :             });
     181                 :             :     }
     182   [ +  -  +  - ]:        3438 : }
        

Generated by: LCOV version 2.0-1