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