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 <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 : 1 : void initialize_miner()
32 : : {
33 [ + - + - : 1 : static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
+ - ]
34 [ + + ]: 101 : for (uint32_t i = 0; i < uint32_t{100}; ++i) {
35 : 100 : g_available_coins.emplace_back(Txid::FromUint256(uint256::ZERO), i);
36 : : }
37 : 1 : }
38 : :
39 : : // Test that the MiniMiner can run with various outpoints and feerates.
40 [ + - ]: 1196 : FUZZ_TARGET(mini_miner, .init = initialize_miner)
41 : : {
42 : 738 : SeedRandomStateForTest(SeedRand::ZEROS);
43 : 738 : FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
44 : 738 : NodeClockContext clock_ctx{ConsumeTime(fuzzed_data_provider)};
45 [ + - ]: 738 : bilingual_str error;
46 [ + - ]: 738 : CTxMemPool pool{CTxMemPool::Options{}, error};
47 [ - + ]: 738 : Assert(error.empty());
48 : 738 : std::vector<COutPoint> outpoints;
49 [ + - ]: 738 : std::deque<COutPoint> available_coins = g_available_coins;
50 [ + - + - ]: 738 : LOCK2(::cs_main, pool.cs);
51 : : // Cluster size cannot exceed 500
52 [ + + + + ]: 52670 : LIMITED_WHILE(!available_coins.empty(), 100)
53 : : {
54 [ + - ]: 51932 : CMutableTransaction mtx = CMutableTransaction();
55 [ - + ]: 51932 : const size_t num_inputs = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, available_coins.size());
56 : 51932 : const size_t num_outputs = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 50);
57 [ + + ]: 392275 : for (size_t n{0}; n < num_inputs; ++n) {
58 [ + - ]: 340343 : auto prevout = available_coins.front();
59 [ + - ]: 340343 : mtx.vin.emplace_back(prevout, CScript());
60 : 340343 : available_coins.pop_front();
61 : : }
62 [ + + ]: 481093 : for (uint32_t n{0}; n < num_outputs; ++n) {
63 [ + - ]: 429161 : mtx.vout.emplace_back(100, P2WSH_OP_TRUE);
64 : : }
65 [ + - ]: 51932 : CTransactionRef tx = MakeTransactionRef(mtx);
66 : 51932 : TestMemPoolEntryHelper entry;
67 : 51932 : const CAmount fee{ConsumeMoney(fuzzed_data_provider, /*max=*/MAX_MONEY/100000)};
68 [ - + ]: 51932 : assert(MoneyRange(fee));
69 [ + - + - ]: 51932 : TryAddToMempool(pool, entry.Fee(fee).FromTx(tx));
70 : :
71 : : // All outputs are available to spend
72 [ + + ]: 481093 : for (uint32_t n{0}; n < num_outputs; ++n) {
73 [ + + ]: 429161 : if (fuzzed_data_provider.ConsumeBool()) {
74 [ + - ]: 285135 : available_coins.emplace_back(tx->GetHash(), n);
75 : : }
76 : : }
77 : :
78 [ + + + - ]: 51932 : if (fuzzed_data_provider.ConsumeBool() && !tx->vout.empty()) {
79 : : // Add outpoint from this tx (may or not be spent by a later tx)
80 [ - + + - ]: 53340 : outpoints.emplace_back(tx->GetHash(),
81 [ - + + - ]: 26670 : (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 : 25262 : auto outpoint = ConsumeDeserializable<COutPoint>(fuzzed_data_provider);
86 [ + + + + ]: 25262 : if (outpoint.has_value() && std::find(outpoints.begin(), outpoints.end(), *outpoint) == outpoints.end()) {
87 [ + - ]: 2122 : outpoints.push_back(*outpoint);
88 : : }
89 : : }
90 : :
91 : 103864 : }
92 : :
93 [ + - ]: 738 : const CFeeRate target_feerate{CFeeRate{ConsumeMoney(fuzzed_data_provider, /*max=*/MAX_MONEY/1000)}};
94 : 738 : std::optional<CAmount> total_bumpfee;
95 : 738 : CAmount sum_fees = 0;
96 : 738 : {
97 [ + - ]: 738 : node::MiniMiner mini_miner{pool, outpoints};
98 [ - + ]: 738 : assert(mini_miner.IsReadyToCalculate());
99 [ + - ]: 738 : const auto bump_fees = mini_miner.CalculateBumpFees(target_feerate);
100 [ + + ]: 29530 : for (const auto& outpoint : outpoints) {
101 : 28792 : auto it = bump_fees.find(outpoint);
102 [ - + ]: 28792 : assert(it != bump_fees.end());
103 [ - + ]: 28792 : assert(it->second >= 0);
104 : 28792 : sum_fees += it->second;
105 : : }
106 [ - + ]: 738 : assert(!mini_miner.IsReadyToCalculate());
107 : 738 : }
108 : 738 : {
109 [ + - ]: 738 : node::MiniMiner mini_miner{pool, outpoints};
110 [ - + ]: 738 : assert(mini_miner.IsReadyToCalculate());
111 [ + - ]: 738 : total_bumpfee = mini_miner.CalculateTotalBumpFees(target_feerate);
112 [ - + ]: 738 : assert(total_bumpfee.has_value());
113 [ - + ]: 738 : assert(!mini_miner.IsReadyToCalculate());
114 : 738 : }
115 : : // Overlapping ancestry across multiple outpoints can only reduce the total bump fee.
116 [ - + ]: 738 : assert (sum_fees >= *total_bumpfee);
117 [ + - ]: 2214 : }
118 : : } // namespace
|