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