Branch data Line data Source code
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 : 1 : void initialize_miner()
40 : : {
41 [ + - + - : 1 : static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
+ - ]
42 [ + + ]: 101 : for (uint32_t i = 0; i < uint32_t{100}; ++i) {
43 : 100 : g_available_coins.emplace_back(Txid::FromUint256(uint256::ZERO), i);
44 : : }
45 : 1 : }
46 : :
47 : : // Test that the MiniMiner can run with various outpoints and feerates.
48 [ + - ]: 1680 : FUZZ_TARGET(mini_miner, .init = initialize_miner)
49 : : {
50 : 1214 : SeedRandomStateForTest(SeedRand::ZEROS);
51 : 1214 : FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
52 : 1214 : FakeNodeClock clock{ConsumeTime(fuzzed_data_provider)};
53 [ + - ]: 1214 : bilingual_str error;
54 [ + - ]: 1214 : CTxMemPool pool{CTxMemPool::Options{}, error};
55 [ - + ]: 1214 : Assert(error.empty());
56 : 1214 : std::vector<COutPoint> outpoints;
57 [ + - ]: 1214 : std::deque<COutPoint> available_coins = g_available_coins;
58 [ + - + - ]: 1214 : LOCK2(::cs_main, pool.cs);
59 : : // Cluster size cannot exceed 500
60 [ + + + + ]: 84516 : LIMITED_WHILE(!available_coins.empty(), 100)
61 : : {
62 [ + - ]: 83302 : CMutableTransaction mtx = CMutableTransaction();
63 [ - + ]: 83302 : const size_t num_inputs = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, available_coins.size());
64 : 83302 : const size_t num_outputs = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 50);
65 [ + + ]: 629365 : for (size_t n{0}; n < num_inputs; ++n) {
66 [ + - ]: 546063 : auto prevout = available_coins.front();
67 [ + - ]: 546063 : mtx.vin.emplace_back(prevout, CScript());
68 : 546063 : available_coins.pop_front();
69 : : }
70 [ + + ]: 763265 : for (uint32_t n{0}; n < num_outputs; ++n) {
71 [ + - ]: 679963 : mtx.vout.emplace_back(100, P2WSH_OP_TRUE);
72 : : }
73 [ + - ]: 83302 : CTransactionRef tx = MakeTransactionRef(mtx);
74 : 83302 : TestMemPoolEntryHelper entry;
75 : 83302 : const CAmount fee{ConsumeMoney(fuzzed_data_provider, /*max=*/MAX_MONEY/100000)};
76 [ - + ]: 83302 : assert(MoneyRange(fee));
77 [ + - + - ]: 83302 : TryAddToMempool(pool, entry.Fee(fee).FromTx(tx));
78 : :
79 : : // All outputs are available to spend
80 [ + + ]: 763265 : for (uint32_t n{0}; n < num_outputs; ++n) {
81 [ + + ]: 679963 : if (fuzzed_data_provider.ConsumeBool()) {
82 [ + - ]: 450448 : available_coins.emplace_back(tx->GetHash(), n);
83 : : }
84 : : }
85 : :
86 [ + + + - ]: 83302 : if (fuzzed_data_provider.ConsumeBool() && !tx->vout.empty()) {
87 : : // Add outpoint from this tx (may or not be spent by a later tx)
88 [ - + + - ]: 86628 : outpoints.emplace_back(tx->GetHash(),
89 [ - + + - ]: 43314 : (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 : 39988 : auto outpoint = ConsumeDeserializable<COutPoint>(fuzzed_data_provider);
94 [ + + + + ]: 39988 : if (outpoint.has_value() && std::find(outpoints.begin(), outpoints.end(), *outpoint) == outpoints.end()) {
95 [ + - ]: 4033 : outpoints.push_back(*outpoint);
96 : : }
97 : : }
98 : :
99 : 166604 : }
100 : :
101 [ + - ]: 1214 : const CFeeRate target_feerate{CFeeRate{ConsumeMoney(fuzzed_data_provider, /*max=*/MAX_MONEY/1000)}};
102 : 1214 : std::optional<CAmount> total_bumpfee;
103 : 1214 : CAmount sum_fees = 0;
104 : 1214 : {
105 [ + - ]: 1214 : node::MiniMiner mini_miner{pool, outpoints};
106 [ - + ]: 1214 : assert(mini_miner.IsReadyToCalculate());
107 [ + - ]: 1214 : const auto bump_fees = mini_miner.CalculateBumpFees(target_feerate);
108 [ + + ]: 48561 : for (const auto& outpoint : outpoints) {
109 : 47347 : auto it = bump_fees.find(outpoint);
110 [ - + ]: 47347 : assert(it != bump_fees.end());
111 [ - + ]: 47347 : assert(it->second >= 0);
112 : 47347 : sum_fees += it->second;
113 : : }
114 [ - + ]: 1214 : assert(!mini_miner.IsReadyToCalculate());
115 : 1214 : }
116 : 1214 : {
117 [ + - ]: 1214 : node::MiniMiner mini_miner{pool, outpoints};
118 [ - + ]: 1214 : assert(mini_miner.IsReadyToCalculate());
119 [ + - ]: 1214 : total_bumpfee = mini_miner.CalculateTotalBumpFees(target_feerate);
120 [ - + ]: 1214 : assert(total_bumpfee.has_value());
121 [ - + ]: 1214 : assert(!mini_miner.IsReadyToCalculate());
122 : 1214 : }
123 : : // Overlapping ancestry across multiple outpoints can only reduce the total bump fee.
124 [ - + ]: 1214 : assert (sum_fees >= *total_bumpfee);
125 [ + - ]: 3642 : }
126 : : } // namespace
|