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