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