Coverage Report

Created: 2025-09-19 18:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/root/bitcoin/src/policy/rbf.cpp
Line
Count
Source
1
// Copyright (c) 2016-2022 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)) {
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())) {
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.AssumeCalculateMemPoolAncestors(__func__, entry, CTxMemPool::Limits::NoLimits(),
43
0
                                                        /*fSearchForParents=*/false)};
44
45
0
    for (CTxMemPool::txiter it : ancestors) {
46
0
        if (SignalsOptInRBF(it->GetTx())) {
47
0
            return RBFTransactionState::REPLACEABLE_BIP125;
48
0
        }
49
0
    }
50
0
    return RBFTransactionState::FINAL;
51
0
}
52
53
RBFTransactionState IsRBFOptInEmptyMempool(const CTransaction& tx)
54
0
{
55
    // If we don't have a local mempool we can only check the transaction itself.
56
0
    return SignalsOptInRBF(tx) ? RBFTransactionState::REPLACEABLE_BIP125 : RBFTransactionState::UNKNOWN;
57
0
}
58
59
std::optional<std::string> GetEntriesForConflicts(const CTransaction& tx,
60
                                                  CTxMemPool& pool,
61
                                                  const CTxMemPool::setEntries& iters_conflicting,
62
                                                  CTxMemPool::setEntries& all_conflicts)
63
5.72k
{
64
5.72k
    AssertLockHeld(pool.cs);
65
5.72k
    uint64_t nConflictingCount = 0;
66
6.91k
    for (const auto& mi : iters_conflicting) {
67
6.91k
        nConflictingCount += mi->GetCountWithDescendants();
68
        // Rule #5: don't consider replacing more than MAX_REPLACEMENT_CANDIDATES
69
        // entries from the mempool. This potentially overestimates the number of actual
70
        // descendants (i.e. if multiple conflicts share a descendant, it will be counted multiple
71
        // times), but we just want to be conservative to avoid doing too much work.
72
6.91k
        if (nConflictingCount > MAX_REPLACEMENT_CANDIDATES) {
73
0
            return strprintf("rejecting replacement %s; too many potential replacements (%d > %d)",
74
0
                             tx.GetHash().ToString(),
75
0
                             nConflictingCount,
76
0
                             MAX_REPLACEMENT_CANDIDATES);
77
0
        }
78
6.91k
    }
79
    // Calculate the set of all transactions that would have to be evicted.
80
6.91k
    for (CTxMemPool::txiter it : iters_conflicting) {
81
6.91k
        pool.CalculateDescendants(it, all_conflicts);
82
6.91k
    }
83
5.72k
    return std::nullopt;
84
5.72k
}
85
86
std::optional<std::string> HasNoNewUnconfirmed(const CTransaction& tx,
87
                                               const CTxMemPool& pool,
88
                                               const CTxMemPool::setEntries& iters_conflicting)
89
5.72k
{
90
5.72k
    AssertLockHeld(pool.cs);
91
5.72k
    std::set<Txid> parents_of_conflicts;
92
7.28k
    for (const auto& mi : iters_conflicting) {
93
51.7k
        for (const CTxIn& txin : mi->GetTx().vin) {
94
51.7k
            parents_of_conflicts.insert(txin.prevout.hash);
95
51.7k
        }
96
7.28k
    }
97
98
24.6k
    for (unsigned int j = 0; j < tx.vin.size(); j++) {
99
        // Rule #2: We don't want to accept replacements that require low feerate junk to be
100
        // mined first.  Ideally we'd keep track of the ancestor feerates and make the decision
101
        // based on that, but for now requiring all new inputs to be confirmed works.
102
        //
103
        // Note that if you relax this to make RBF a little more useful, this may break the
104
        // CalculateMempoolAncestors RBF relaxation which subtracts the conflict count/size from the
105
        // descendant limit.
106
19.3k
        if (!parents_of_conflicts.count(tx.vin[j].prevout.hash)) {
107
            // Rather than check the UTXO set - potentially expensive - it's cheaper to just check
108
            // if the new input refers to a tx that's in the mempool.
109
5.03k
            if (pool.exists(tx.vin[j].prevout.hash)) {
110
512
                return strprintf("replacement %s adds unconfirmed input, idx %d",
111
512
                                 tx.GetHash().ToString(), j);
112
512
            }
113
5.03k
        }
114
19.3k
    }
115
5.20k
    return std::nullopt;
116
5.72k
}
117
118
std::optional<std::string> EntriesAndTxidsDisjoint(const CTxMemPool::setEntries& ancestors,
119
                                                   const std::set<Txid>& direct_conflicts,
120
                                                   const Txid& txid)
121
49.8k
{
122
49.8k
    for (CTxMemPool::txiter ancestorIt : ancestors) {
123
18.1k
        const Txid& hashAncestor = ancestorIt->GetTx().GetHash();
124
18.1k
        if (direct_conflicts.count(hashAncestor)) {
125
1.41k
            return strprintf("%s spends conflicting transaction %s",
126
1.41k
                             txid.ToString(),
127
1.41k
                             hashAncestor.ToString());
128
1.41k
        }
129
18.1k
    }
130
48.4k
    return std::nullopt;
131
49.8k
}
132
133
std::optional<std::string> PaysMoreThanConflicts(const CTxMemPool::setEntries& iters_conflicting,
134
                                                 CFeeRate replacement_feerate,
135
                                                 const Txid& txid)
136
17.5k
{
137
19.0k
    for (const auto& mi : iters_conflicting) {
138
        // Don't allow the replacement to reduce the feerate of the mempool.
139
        //
140
        // We usually don't want to accept replacements with lower feerates than what they replaced
141
        // as that would lower the feerate of the next block. Requiring that the feerate always be
142
        // increased is also an easy-to-reason about way to prevent DoS attacks via replacements.
143
        //
144
        // We only consider the feerates of transactions being directly replaced, not their indirect
145
        // descendants. While that does mean high feerate children are ignored when deciding whether
146
        // or not to replace, we do require the replacement to pay more overall fees too, mitigating
147
        // most cases.
148
19.0k
        CFeeRate original_feerate(mi->GetModifiedFee(), mi->GetTxSize());
149
19.0k
        if (replacement_feerate <= original_feerate) {
150
11.8k
            return strprintf("rejecting replacement %s; new feerate %s <= old feerate %s",
151
11.8k
                             txid.ToString(),
152
11.8k
                             replacement_feerate.ToString(),
153
11.8k
                             original_feerate.ToString());
154
11.8k
        }
155
19.0k
    }
156
5.72k
    return std::nullopt;
157
17.5k
}
158
159
std::optional<std::string> PaysForRBF(CAmount original_fees,
160
                                      CAmount replacement_fees,
161
                                      size_t replacement_vsize,
162
                                      CFeeRate relay_fee,
163
                                      const Txid& txid)
164
5.20k
{
165
    // Rule #3: The replacement fees must be greater than or equal to fees of the
166
    // transactions it replaces, otherwise the bandwidth used by those conflicting transactions
167
    // would not be paid for.
168
5.20k
    if (replacement_fees < original_fees) {
169
794
        return strprintf("rejecting replacement %s, less fees than conflicting txs; %s < %s",
170
794
                         txid.ToString(), FormatMoney(replacement_fees), FormatMoney(original_fees));
171
794
    }
172
173
    // Rule #4: The new transaction must pay for its own bandwidth. Otherwise, we have a DoS
174
    // vector where attackers can cause a transaction to be replaced (and relayed) repeatedly by
175
    // increasing the fee by tiny amounts.
176
4.41k
    CAmount additional_fees = replacement_fees - original_fees;
177
4.41k
    if (additional_fees < relay_fee.GetFee(replacement_vsize)) {
178
545
        return strprintf("rejecting replacement %s, not enough additional fees to relay; %s < %s",
179
545
                         txid.ToString(),
180
545
                         FormatMoney(additional_fees),
181
545
                         FormatMoney(relay_fee.GetFee(replacement_vsize)));
182
545
    }
183
3.87k
    return std::nullopt;
184
4.41k
}
185
186
std::optional<std::pair<DiagramCheckError, std::string>> ImprovesFeerateDiagram(CTxMemPool::ChangeSet& changeset)
187
0
{
188
    // Require that the replacement strictly improves the mempool's feerate diagram.
189
0
    const auto chunk_results{changeset.CalculateChunksForRBF()};
190
191
0
    if (!chunk_results.has_value()) {
192
0
        return std::make_pair(DiagramCheckError::UNCALCULABLE, util::ErrorString(chunk_results).original);
193
0
    }
194
195
0
    if (!std::is_gt(CompareChunks(chunk_results.value().second, chunk_results.value().first))) {
196
0
        return std::make_pair(DiagramCheckError::FAILURE, "insufficient feerate: does not improve feerate diagram");
197
0
    }
198
0
    return std::nullopt;
199
0
}