mini_miner.cpp raw

   1  // Copyright (c) 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 <node/mini_miner.h>
   6  
   7  #include <consensus/amount.h>
   8  #include <kernel/cs_main.h>
   9  #include <policy/feerate.h>
  10  #include <primitives/transaction.h>
  11  #include <script/script.h>
  12  #include <sync.h>
  13  #include <test/fuzz/FuzzedDataProvider.h>
  14  #include <test/fuzz/fuzz.h>
  15  #include <test/fuzz/util.h>
  16  #include <test/util/script.h>
  17  #include <test/util/random.h>
  18  #include <test/util/setup_common.h>
  19  #include <test/util/time.h>
  20  #include <test/util/txmempool.h>
  21  #include <txmempool.h>
  22  #include <uint256.h>
  23  #include <util/check.h>
  24  #include <util/translation.h>
  25  
  26  #include <algorithm>
  27  #include <cstddef>
  28  #include <cstdint>
  29  #include <deque>
  30  #include <functional>
  31  #include <map>
  32  #include <optional>
  33  #include <utility>
  34  #include <vector>
  35  
  36  namespace {
  37  
  38  std::deque<COutPoint> g_available_coins;
  39  void initialize_miner()
  40  {
  41      static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
  42      for (uint32_t i = 0; i < uint32_t{100}; ++i) {
  43          g_available_coins.emplace_back(Txid::FromUint256(uint256::ZERO), i);
  44      }
  45  }
  46  
  47  // Test that the MiniMiner can run with various outpoints and feerates.
  48  FUZZ_TARGET(mini_miner, .init = initialize_miner)
  49  {
  50      SeedRandomStateForTest(SeedRand::ZEROS);
  51      FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
  52      FakeNodeClock clock{ConsumeTime(fuzzed_data_provider)};
  53      bilingual_str error;
  54      CTxMemPool pool{CTxMemPool::Options{}, error};
  55      Assert(error.empty());
  56      std::vector<COutPoint> outpoints;
  57      std::deque<COutPoint> available_coins = g_available_coins;
  58      LOCK2(::cs_main, pool.cs);
  59      // Cluster size cannot exceed 500
  60      LIMITED_WHILE(!available_coins.empty(), 100)
  61      {
  62          CMutableTransaction mtx = CMutableTransaction();
  63          const size_t num_inputs = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, available_coins.size());
  64          const size_t num_outputs = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 50);
  65          for (size_t n{0}; n < num_inputs; ++n) {
  66              auto prevout = available_coins.front();
  67              mtx.vin.emplace_back(prevout, CScript());
  68              available_coins.pop_front();
  69          }
  70          for (uint32_t n{0}; n < num_outputs; ++n) {
  71              mtx.vout.emplace_back(100, P2WSH_OP_TRUE);
  72          }
  73          CTransactionRef tx = MakeTransactionRef(mtx);
  74          TestMemPoolEntryHelper entry;
  75          const CAmount fee{ConsumeMoney(fuzzed_data_provider, /*max=*/MAX_MONEY/100000)};
  76          assert(MoneyRange(fee));
  77          TryAddToMempool(pool, entry.Fee(fee).FromTx(tx));
  78  
  79          // All outputs are available to spend
  80          for (uint32_t n{0}; n < num_outputs; ++n) {
  81              if (fuzzed_data_provider.ConsumeBool()) {
  82                  available_coins.emplace_back(tx->GetHash(), n);
  83              }
  84          }
  85  
  86          if (fuzzed_data_provider.ConsumeBool() && !tx->vout.empty()) {
  87              // Add outpoint from this tx (may or not be spent by a later tx)
  88              outpoints.emplace_back(tx->GetHash(),
  89                                            (uint32_t)fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, tx->vout.size()));
  90          } else {
  91              // Add some random outpoint (will be interpreted as confirmed or not yet submitted
  92              // to mempool).
  93              auto outpoint = ConsumeDeserializable<COutPoint>(fuzzed_data_provider);
  94              if (outpoint.has_value() && std::find(outpoints.begin(), outpoints.end(), *outpoint) == outpoints.end()) {
  95                  outpoints.push_back(*outpoint);
  96              }
  97          }
  98  
  99      }
 100  
 101      const CFeeRate target_feerate{CFeeRate{ConsumeMoney(fuzzed_data_provider, /*max=*/MAX_MONEY/1000)}};
 102      std::optional<CAmount> total_bumpfee;
 103      CAmount sum_fees = 0;
 104      {
 105          node::MiniMiner mini_miner{pool, outpoints};
 106          assert(mini_miner.IsReadyToCalculate());
 107          const auto bump_fees = mini_miner.CalculateBumpFees(target_feerate);
 108          for (const auto& outpoint : outpoints) {
 109              auto it = bump_fees.find(outpoint);
 110              assert(it != bump_fees.end());
 111              assert(it->second >= 0);
 112              sum_fees += it->second;
 113          }
 114          assert(!mini_miner.IsReadyToCalculate());
 115      }
 116      {
 117          node::MiniMiner mini_miner{pool, outpoints};
 118          assert(mini_miner.IsReadyToCalculate());
 119          total_bumpfee = mini_miner.CalculateTotalBumpFees(target_feerate);
 120          assert(total_bumpfee.has_value());
 121          assert(!mini_miner.IsReadyToCalculate());
 122      }
 123      // Overlapping ancestry across multiple outpoints can only reduce the total bump fee.
 124      assert (sum_fees >= *total_bumpfee);
 125  }
 126  } // namespace
 127