Coverage Report

Created: 2026-08-25 19:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/root/bitcoin/src/test/fuzz/mini_miner.cpp
Line
Count
Source
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
0
{
41
0
    static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
42
0
    for (uint32_t i = 0; i < uint32_t{100}; ++i) {
  Branch (42:26): [True: 0, False: 0]
43
0
        g_available_coins.emplace_back(Txid::FromUint256(uint256::ZERO), i);
44
0
    }
45
0
}
46
47
// Test that the MiniMiner can run with various outpoints and feerates.
48
FUZZ_TARGET(mini_miner, .init = initialize_miner)
49
3.23k
{
50
3.23k
    SeedRandomStateForTest(SeedRand::ZEROS);
51
3.23k
    FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
52
3.23k
    FakeNodeClock clock{ConsumeTime(fuzzed_data_provider)};
53
3.23k
    bilingual_str error;
54
3.23k
    CTxMemPool pool{CTxMemPool::Options{}, error};
55
3.23k
    Assert(error.empty());
56
3.23k
    std::vector<COutPoint> outpoints;
57
3.23k
    std::deque<COutPoint> available_coins = g_available_coins;
58
3.23k
    LOCK2(::cs_main, pool.cs);
59
    // Cluster size cannot exceed 500
60
110k
    LIMITED_WHILE (!available_coins.empty(), 100) {
61
110k
        CMutableTransaction mtx = CMutableTransaction();
62
110k
        const size_t num_inputs = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, available_coins.size());
63
110k
        const size_t num_outputs = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 50);
64
840k
        for (size_t n{0}; n < num_inputs; ++n) {
  Branch (64:27): [True: 730k, False: 110k]
65
730k
            auto prevout = available_coins.front();
66
730k
            mtx.vin.emplace_back(prevout, CScript());
67
730k
            available_coins.pop_front();
68
730k
        }
69
1.01M
        for (uint32_t n{0}; n < num_outputs; ++n) {
  Branch (69:29): [True: 900k, False: 110k]
70
900k
            mtx.vout.emplace_back(100, P2WSH_OP_TRUE);
71
900k
        }
72
110k
        CTransactionRef tx = MakeTransactionRef(mtx);
73
110k
        TestMemPoolEntryHelper entry;
74
110k
        const CAmount fee{ConsumeMoney(fuzzed_data_provider, /*max=*/MAX_MONEY/100000)};
75
110k
        assert(MoneyRange(fee));
  Branch (75:9): [True: 110k, False: 0]
76
110k
        TryAddToMempool(pool, entry.Fee(fee).FromTx(tx));
77
78
        // All outputs are available to spend
79
1.01M
        for (uint32_t n{0}; n < num_outputs; ++n) {
  Branch (79:29): [True: 900k, False: 110k]
80
900k
            if (fuzzed_data_provider.ConsumeBool()) {
  Branch (80:17): [True: 602k, False: 298k]
81
602k
                available_coins.emplace_back(tx->GetHash(), n);
82
602k
            }
83
900k
        }
84
85
110k
        if (fuzzed_data_provider.ConsumeBool() && !tx->vout.empty()) {
  Branch (85:13): [True: 59.0k, False: 51.1k]
  Branch (85:51): [True: 59.0k, False: 0]
86
            // Add outpoint from this tx (may or not be spent by a later tx)
87
59.0k
            outpoints.emplace_back(tx->GetHash(),
88
59.0k
                                          (uint32_t)fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, tx->vout.size()));
89
59.0k
        } else {
90
            // Add some random outpoint (will be interpreted as confirmed or not yet submitted
91
            // to mempool).
92
51.1k
            auto outpoint = ConsumeDeserializable<COutPoint>(fuzzed_data_provider);
93
51.1k
            if (outpoint.has_value() && std::find(outpoints.begin(), outpoints.end(), *outpoint) == outpoints.end()) {
  Branch (93:17): [True: 8.19k, False: 42.9k]
  Branch (93:17): [True: 5.27k, False: 45.8k]
  Branch (93:41): [True: 5.27k, False: 2.91k]
94
5.27k
                outpoints.push_back(*outpoint);
95
5.27k
            }
96
51.1k
        }
97
110k
    }
98
99
3.23k
    const CFeeRate target_feerate{CFeeRate{ConsumeMoney(fuzzed_data_provider, /*max=*/MAX_MONEY/1000)}};
100
3.23k
    std::optional<CAmount> total_bumpfee;
101
3.23k
    CAmount sum_fees = 0;
102
3.23k
    {
103
3.23k
        node::MiniMiner mini_miner{pool, outpoints};
104
3.23k
        assert(mini_miner.IsReadyToCalculate());
  Branch (104:9): [True: 1.61k, False: 1.61k]
105
1.61k
        const auto bump_fees = mini_miner.CalculateBumpFees(target_feerate);
106
64.3k
        for (const auto& outpoint : outpoints) {
  Branch (106:35): [True: 64.3k, False: 1.61k]
107
64.3k
            auto it = bump_fees.find(outpoint);
108
64.3k
            assert(it != bump_fees.end());
  Branch (108:13): [True: 64.3k, False: 0]
109
64.3k
            assert(it->second >= 0);
  Branch (109:13): [True: 64.3k, False: 0]
110
64.3k
            sum_fees += it->second;
111
64.3k
        }
112
1.61k
        assert(!mini_miner.IsReadyToCalculate());
  Branch (112:9): [True: 1.61k, False: 0]
113
1.61k
    }
114
1.61k
    {
115
1.61k
        node::MiniMiner mini_miner{pool, outpoints};
116
1.61k
        assert(mini_miner.IsReadyToCalculate());
  Branch (116:9): [True: 1.61k, False: 0]
117
1.61k
        total_bumpfee = mini_miner.CalculateTotalBumpFees(target_feerate);
118
1.61k
        assert(total_bumpfee.has_value());
  Branch (118:9): [True: 1.61k, False: 0]
119
1.61k
        assert(!mini_miner.IsReadyToCalculate());
  Branch (119:9): [True: 1.61k, False: 0]
120
1.61k
    }
121
    // Overlapping ancestry across multiple outpoints can only reduce the total bump fee.
122
1.61k
    assert (sum_fees >= *total_bumpfee);
  Branch (122:5): [True: 1.61k, False: 0]
123
1.61k
}
124
} // namespace