LCOV - code coverage report
Current view: top level - src/test/fuzz - utxo_total_supply.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 5 98 5.1 %
Date: 2023-09-26 12:08:55 Functions: 5 14 35.7 %

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

Generated by: LCOV version 1.14