Coverage Report

Created: 2026-07-14 18:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/bitcoin/src/policy/rbf.cpp
Line
Count
Source
1
// Copyright (c) 2016-present 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 <policy/rbf.h>
6
7
#include <consensus/amount.h>
8
#include <kernel/mempool_entry.h>
9
#include <policy/feerate.h>
10
#include <primitives/transaction.h>
11
#include <sync.h>
12
#include <tinyformat.h>
13
#include <txmempool.h>
14
#include <uint256.h>
15
#include <util/check.h>
16
#include <util/moneystr.h>
17
#include <util/rbf.h>
18
19
#include <limits>
20
#include <vector>
21
22
#include <compare>
23
24
RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool)
25
0
{
26
0
    AssertLockHeld(pool.cs);
27
28
    // First check the transaction itself.
29
0
    if (SignalsOptInRBF(tx)) {
  Branch (29:9): [True: 0, False: 0]
30
0
        return RBFTransactionState::REPLACEABLE_BIP125;
31
0
    }
32
33
    // If this transaction is not in our mempool, then we can't be sure
34
    // we will know about all its inputs.
35
0
    if (!pool.exists(tx.GetHash())) {
  Branch (35:9): [True: 0, False: 0]
36
0
        return RBFTransactionState::UNKNOWN;
37
0
    }
38
39
    // If all the inputs have nSequence >= maxint-1, it still might be
40
    // signaled for RBF if any unconfirmed parents have signaled.
41
0
    const auto& entry{*Assert(pool.GetEntry(tx.GetHash()))};
42
0
    auto ancestors{pool.CalculateMemPoolAncestors(entry)};
43
44
0
    for (CTxMemPool::txiter it : ancestors) {
  Branch (44:32): [True: 0, False: 0]
45
0
        if (SignalsOptInRBF(it->GetTx())) {
  Branch (45:13): [True: 0, False: 0]
46
0
            return RBFTransactionState::REPLACEABLE_BIP125;
47
0
        }
48
0
    }
49
0
    return RBFTransactionState::FINAL;
50
0
}
51
52
RBFTransactionState IsRBFOptInEmptyMempool(const CTransaction& tx)
53
0
{
54
    // If we don't have a local mempool we can only check the transaction itself.
55
0
    return SignalsOptInRBF(tx) ? RBFTransactionState::REPLACEABLE_BIP125 : RBFTransactionState::UNKNOWN;
  Branch (55:12): [True: 0, False: 0]
56
0
}
57
58
std::optional<std::string> GetEntriesForConflicts(const CTransaction& tx,
59
                                                  CTxMemPool& pool,
60
                                                  const CTxMemPool::setEntries& iters_conflicting,
61
                                                  CTxMemPool::setEntries& all_conflicts)
62
34.1k
{
63
34.1k
    AssertLockHeld(pool.cs);
64
    // Rule #5: don't consider replacements that conflict directly with more
65
    // than MAX_REPLACEMENT_CANDIDATES distinct clusters. This implies a bound
66
    // on how many mempool clusters might need to be re-sorted in order to
67
    // process the replacement (though the actual number of clusters we
68
    // relinearize may be greater than this number, due to cluster splitting).
69
34.1k
    auto num_clusters = pool.GetUniqueClusterCount(iters_conflicting);
70
34.1k
    if (num_clusters > MAX_REPLACEMENT_CANDIDATES) {
  Branch (70:9): [True: 0, False: 34.1k]
71
0
        return strprintf("rejecting replacement %s; too many conflicting clusters (%u > %d)",
72
0
                tx.GetHash().ToString(),
73
0
                num_clusters,
74
0
                MAX_REPLACEMENT_CANDIDATES);
75
0
    }
76
    // Calculate the set of all transactions that would have to be evicted.
77
37.1k
    for (CTxMemPool::txiter it : iters_conflicting) {
  Branch (77:32): [True: 37.1k, False: 34.1k]
78
        // The cluster count limit ensures that we won't do too much work on a
79
        // single invocation of this function.
80
37.1k
        pool.CalculateDescendants(it, all_conflicts);
81
37.1k
    }
82
34.1k
    return std::nullopt;
83
34.1k
}
84
85
std::optional<std::string> EntriesAndTxidsDisjoint(const CTxMemPool::setEntries& ancestors,
86
                                                   const std::set<Txid>& direct_conflicts,
87
                                                   const Txid& txid)
88
11.2k
{
89
60.6k
    for (CTxMemPool::txiter ancestorIt : ancestors) {
  Branch (89:40): [True: 60.6k, False: 10.9k]
90
60.6k
        const Txid& hashAncestor = ancestorIt->GetTx().GetHash();
91
60.6k
        if (direct_conflicts.contains(hashAncestor)) {
  Branch (91:13): [True: 283, False: 60.3k]
92
283
            return strprintf("%s spends conflicting transaction %s",
93
283
                             txid.ToString(),
94
283
                             hashAncestor.ToString());
95
283
        }
96
60.6k
    }
97
10.9k
    return std::nullopt;
98
11.2k
}
99
100
std::optional<std::string> PaysForRBF(CAmount original_fees,
101
                                      CAmount replacement_fees,
102
                                      size_t replacement_vsize,
103
                                      CFeeRate relay_fee,
104
                                      const Txid& txid)
105
34.1k
{
106
    // Rule #3: The replacement fees must be greater than or equal to fees of the
107
    // transactions it replaces, otherwise the bandwidth used by those conflicting transactions
108
    // would not be paid for.
109
34.1k
    if (replacement_fees < original_fees) {
  Branch (109:9): [True: 14.1k, False: 19.9k]
110
14.1k
        return strprintf("rejecting replacement %s, less fees than conflicting txs; %s < %s",
111
14.1k
                         txid.ToString(), FormatMoney(replacement_fees), FormatMoney(original_fees));
112
14.1k
    }
113
114
    // Rule #4: The new transaction must pay for its own bandwidth. Otherwise, we have a DoS
115
    // vector where attackers can cause a transaction to be replaced (and relayed) repeatedly by
116
    // increasing the fee by tiny amounts.
117
19.9k
    CAmount additional_fees = replacement_fees - original_fees;
118
19.9k
    if (additional_fees < relay_fee.GetFee(replacement_vsize)) {
  Branch (118:9): [True: 1.85k, False: 18.1k]
119
1.85k
        return strprintf("rejecting replacement %s, not enough additional fees to relay; %s < %s",
120
1.85k
                         txid.ToString(),
121
1.85k
                         FormatMoney(additional_fees),
122
1.85k
                         FormatMoney(relay_fee.GetFee(replacement_vsize)));
123
1.85k
    }
124
18.1k
    return std::nullopt;
125
19.9k
}
126
127
std::optional<std::pair<DiagramCheckError, std::string>> ImprovesFeerateDiagram(CTxMemPool::ChangeSet& changeset)
128
17.7k
{
129
    // Require that the replacement strictly improves the mempool's feerate diagram.
130
17.7k
    const auto chunk_results{changeset.CalculateChunksForRBF()};
131
132
17.7k
    if (!chunk_results.has_value()) {
  Branch (132:9): [True: 0, False: 17.7k]
133
0
        return std::make_pair(DiagramCheckError::UNCALCULABLE, util::ErrorString(chunk_results).original);
134
0
    }
135
136
17.7k
    if (!std::is_gt(CompareChunks(chunk_results.value().second, chunk_results.value().first))) {
  Branch (136:9): [True: 6.28k, False: 11.4k]
137
6.28k
        return std::make_pair(DiagramCheckError::FAILURE, "insufficient feerate: does not improve feerate diagram");
138
6.28k
    }
139
11.4k
    return std::nullopt;
140
17.7k
}