Skip to content

Commit 9ebc373

Browse files
committed
AcceptToMemoryPool: Support overriding many top-level rejections
1 parent 8a18e65 commit 9ebc373

File tree

3 files changed

+59
-18
lines changed

3 files changed

+59
-18
lines changed

src/policy/rbf.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <consensus/amount.h>
88
#include <kernel/mempool_entry.h>
99
#include <policy/feerate.h>
10+
#include <policy/policy.h>
1011
#include <primitives/transaction.h>
1112
#include <sync.h>
1213
#include <tinyformat.h>
@@ -59,7 +60,8 @@ RBFTransactionState IsRBFOptInEmptyMempool(const CTransaction& tx)
5960
std::optional<std::string> GetEntriesForConflicts(const CTransaction& tx,
6061
CTxMemPool& pool,
6162
const CTxMemPool::setEntries& iters_conflicting,
62-
CTxMemPool::setEntries& all_conflicts)
63+
CTxMemPool::setEntries& all_conflicts,
64+
const ignore_rejects_type& ignore_rejects)
6365
{
6466
AssertLockHeld(pool.cs);
6567
const uint256 txid = tx.GetHash();
@@ -70,7 +72,7 @@ std::optional<std::string> GetEntriesForConflicts(const CTransaction& tx,
7072
// entries from the mempool. This potentially overestimates the number of actual
7173
// descendants (i.e. if multiple conflicts share a descendant, it will be counted multiple
7274
// times), but we just want to be conservative to avoid doing too much work.
73-
if (nConflictingCount > MAX_REPLACEMENT_CANDIDATES) {
75+
if (nConflictingCount > MAX_REPLACEMENT_CANDIDATES && !ignore_rejects.count("too-many-replacements")) {
7476
return strprintf("rejecting replacement %s; too many potential replacements (%d > %d)\n",
7577
txid.ToString(),
7678
nConflictingCount,

src/policy/rbf.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#define BITCOIN_POLICY_RBF_H
77

88
#include <consensus/amount.h>
9+
#include <policy/policy.h>
910
#include <primitives/transaction.h>
1011
#include <threadsafety.h>
1112
#include <txmempool.h>
@@ -68,7 +69,8 @@ RBFTransactionState IsRBFOptInEmptyMempool(const CTransaction& tx);
6869
*/
6970
std::optional<std::string> GetEntriesForConflicts(const CTransaction& tx, CTxMemPool& pool,
7071
const CTxMemPool::setEntries& iters_conflicting,
71-
CTxMemPool::setEntries& all_conflicts)
72+
CTxMemPool::setEntries& all_conflicts,
73+
const ignore_rejects_type& ignore_rejects=empty_ignore_rejects)
7274
EXCLUSIVE_LOCKS_REQUIRED(pool.cs);
7375

7476
/** The replacement transaction may only include an unconfirmed input if that input was included in

src/validation.cpp

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -663,18 +663,35 @@ class MemPoolAccept
663663
PrecomputedTransactionData m_precomputed_txdata;
664664
};
665665

666+
static inline bool MaybeReject_(TxValidationResult reason, const std::string& reason_str, const std::string& debug_msg, const ignore_rejects_type& ignore_rejects, TxValidationState& state) {
667+
if (ignore_rejects.count(reason_str)) {
668+
return false;
669+
}
670+
671+
state.Invalid(reason, reason_str, debug_msg);
672+
return true;
673+
}
674+
675+
#define MaybeRejectDbg(reason, reason_str, debug_msg) do { \
676+
if (MaybeReject_(reason, reason_str, debug_msg, ignore_rejects, state)) { \
677+
return false; \
678+
} \
679+
} while(0)
680+
681+
#define MaybeReject(reason, reason_str) MaybeRejectDbg(reason, reason_str, "")
682+
666683
// Run the policy checks on a given transaction, excluding any script checks.
667684
// Looks up inputs, calculates feerate, considers replacement, evaluates
668685
// package limits, etc. As this function can be invoked for "free" by a peer,
669686
// only tests that are fast should be done here (to avoid CPU DoS).
670687
bool PreChecks(ATMPArgs& args, Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
671688

672689
// Run checks for mempool replace-by-fee, only used in AcceptSingleTransaction.
673-
bool ReplacementChecks(Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
690+
bool ReplacementChecks(ATMPArgs& args, Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
674691

675692
// Enforce package mempool ancestor/descendant limits (distinct from individual
676693
// ancestor/descendant limits done in PreChecks) and run Package RBF checks.
677-
bool PackageMempoolChecks(const std::vector<CTransactionRef>& txns,
694+
bool PackageMempoolChecks(const ATMPArgs& args, const std::vector<CTransactionRef>& txns,
678695
std::vector<Workspace>& workspaces,
679696
int64_t total_vsize,
680697
PackageValidationState& package_state) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
@@ -776,6 +793,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
776793

777794
// Copy/alias what we need out of args
778795
const int64_t nAcceptTime = args.m_accept_time;
796+
const ignore_rejects_type& ignore_rejects = args.m_ignore_rejects;
779797
std::vector<COutPoint>& coins_to_uncache = args.m_coins_to_uncache;
780798

781799
// Alias what we need out of ws
@@ -798,13 +816,13 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
798816

799817
// Transactions smaller than 65 non-witness bytes are not relayed to mitigate CVE-2017-12842.
800818
if (::GetSerializeSize(TX_NO_WITNESS(tx)) < MIN_STANDARD_TX_NONWITNESS_SIZE)
801-
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "tx-size-small");
819+
MaybeReject(TxValidationResult::TX_NOT_STANDARD, "tx-size-small");
802820

803821
// Only accept nLockTime-using transactions that can be mined in the next
804822
// block; we don't want our mempool filled up with transactions that can't
805823
// be mined yet.
806824
if (!CheckFinalTxAtTip(*Assert(m_active_chainstate.m_chain.Tip()), tx)) {
807-
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-final");
825+
MaybeReject(TxValidationResult::TX_PREMATURE_SPEND, "non-final");
808826
}
809827

810828
if (m_pool.exists(GenTxid::Wtxid(tx.GetWitnessHash()))) {
@@ -839,7 +857,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
839857
//
840858
// Replaceability signaling of the original transactions may be
841859
// ignored due to node setting.
842-
const bool allow_rbf{m_pool.m_opts.full_rbf || SignalsOptInRBF(*ptxConflicting) || ptxConflicting->version == TRUC_VERSION};
860+
const bool allow_rbf{(m_pool.m_opts.full_rbf || ignore_rejects.count("txn-mempool-conflict")) || SignalsOptInRBF(*ptxConflicting) || ptxConflicting->version == TRUC_VERSION};
843861
if (!allow_rbf) {
844862
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "txn-mempool-conflict");
845863
}
@@ -891,6 +909,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
891909
// Pass in m_view which has all of the relevant inputs cached. Note that, since m_view's
892910
// backend was removed, it no longer pulls coins from the mempool.
893911
const std::optional<LockPoints> lock_points{CalculateLockPointsAtTip(m_active_chainstate.m_chain.Tip(), m_view, tx)};
912+
// NOTE: The miner doesn't check this again, so for now it may not be overridden.
894913
if (!lock_points.has_value() || !CheckSequenceLocksAtTip(m_active_chainstate.m_chain.Tip(), *lock_points)) {
895914
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-BIP68-final");
896915
}
@@ -906,7 +925,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
906925

907926
// Check for non-standard witnesses.
908927
if (tx.HasWitness() && m_pool.m_opts.require_standard && !IsWitnessStandard(tx, m_view)) {
909-
return state.Invalid(TxValidationResult::TX_WITNESS_MUTATED, "bad-witness-nonstandard");
928+
MaybeReject(TxValidationResult::TX_WITNESS_MUTATED, "bad-witness-nonstandard");
910929
}
911930

912931
int64_t nSigOpsCost = GetTransactionSigOpCost(tx, m_view, STANDARD_SCRIPT_VERIFY_FLAGS);
@@ -934,7 +953,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
934953
ws.m_vsize = entry->GetTxSize();
935954

936955
if (nSigOpsCost > MAX_STANDARD_TX_SIGOPS_COST)
937-
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "bad-txns-too-many-sigops",
956+
MaybeRejectDbg(TxValidationResult::TX_NOT_STANDARD, "bad-txns-too-many-sigops",
938957
strprintf("%d", nSigOpsCost));
939958

940959
// No individual transactions are allowed below the min relay feerate except from disconnected blocks.
@@ -996,7 +1015,13 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
9961015
maybe_rbf_limits.descendant_size_vbytes += conflict->GetSizeWithDescendants();
9971016
}
9981017

999-
if (auto ancestors{m_pool.CalculateMemPoolAncestors(*entry, maybe_rbf_limits)}) {
1018+
CTxMemPool::Limits limits;
1019+
if (ignore_rejects.count("too-long-mempool-chain")) {
1020+
limits = CTxMemPool::Limits::NoLimits();
1021+
} else {
1022+
limits = maybe_rbf_limits;
1023+
}
1024+
if (auto ancestors{m_pool.CalculateMemPoolAncestors(*entry, limits)}) {
10001025
ws.m_ancestors = std::move(*ancestors);
10011026
} else {
10021027
// If CalculateMemPoolAncestors fails second time, we want the original error string.
@@ -1076,7 +1101,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
10761101
return true;
10771102
}
10781103

1079-
bool MemPoolAccept::ReplacementChecks(Workspace& ws)
1104+
bool MemPoolAccept::ReplacementChecks(ATMPArgs& args, Workspace& ws)
10801105
{
10811106
AssertLockHeld(cs_main);
10821107
AssertLockHeld(m_pool.cs);
@@ -1095,42 +1120,48 @@ bool MemPoolAccept::ReplacementChecks(Workspace& ws)
10951120
// guarantee that this is incentive-compatible for miners, because it is possible for a
10961121
// descendant transaction of a direct conflict to pay a higher feerate than the transaction that
10971122
// might replace them, under these rules.
1123+
if (!args.m_ignore_rejects.count("insufficient fee")) {
10981124
if (const auto err_string{PaysMoreThanConflicts(ws.m_iters_conflicting, newFeeRate, hash)}) {
10991125
// This fee-related failure is TX_RECONSIDERABLE because validating in a package may change
11001126
// the result.
11011127
return state.Invalid(TxValidationResult::TX_RECONSIDERABLE,
11021128
strprintf("insufficient fee%s", ws.m_sibling_eviction ? " (including sibling eviction)" : ""), *err_string);
11031129
}
1130+
} // ignore_rejects
11041131

11051132
// Calculate all conflicting entries and enforce Rule #5.
1106-
if (const auto err_string{GetEntriesForConflicts(tx, m_pool, ws.m_iters_conflicting, m_subpackage.m_all_conflicts)}) {
1133+
if (const auto err_string{GetEntriesForConflicts(tx, m_pool, ws.m_iters_conflicting, m_subpackage.m_all_conflicts, args.m_ignore_rejects)}) {
11071134
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY,
11081135
strprintf("too many potential replacements%s", ws.m_sibling_eviction ? " (including sibling eviction)" : ""), *err_string);
11091136
}
11101137
// Enforce Rule #2.
1138+
if (!args.m_ignore_rejects.count("replacement-adds-unconfirmed")) {
11111139
if (const auto err_string{HasNoNewUnconfirmed(tx, m_pool, m_subpackage.m_all_conflicts)}) {
11121140
// Sibling eviction is only done for TRUC transactions, which cannot have multiple ancestors.
11131141
Assume(!ws.m_sibling_eviction);
11141142
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY,
11151143
strprintf("replacement-adds-unconfirmed%s", ws.m_sibling_eviction ? " (including sibling eviction)" : ""), *err_string);
11161144
}
1145+
} // ignore_rejects
11171146

11181147
// Check if it's economically rational to mine this transaction rather than the ones it
11191148
// replaces and pays for its own relay fees. Enforce Rules #3 and #4.
11201149
for (CTxMemPool::txiter it : m_subpackage.m_all_conflicts) {
11211150
m_subpackage.m_conflicting_fees += it->GetModifiedFee();
11221151
m_subpackage.m_conflicting_size += it->GetTxSize();
11231152
}
1153+
if (!args.m_ignore_rejects.count("insufficient fee")) {
11241154
if (const auto err_string{PaysForRBF(m_subpackage.m_conflicting_fees, ws.m_modified_fees, ws.m_vsize,
11251155
m_pool.m_opts.incremental_relay_feerate, hash)}) {
11261156
// Result may change in a package context
11271157
return state.Invalid(TxValidationResult::TX_RECONSIDERABLE,
11281158
strprintf("insufficient fee%s", ws.m_sibling_eviction ? " (including sibling eviction)" : ""), *err_string);
11291159
}
1160+
} // ignore_rejects
11301161
return true;
11311162
}
11321163

1133-
bool MemPoolAccept::PackageMempoolChecks(const std::vector<CTransactionRef>& txns,
1164+
bool MemPoolAccept::PackageMempoolChecks(const ATMPArgs& args, const std::vector<CTransactionRef>& txns,
11341165
std::vector<Workspace>& workspaces,
11351166
const int64_t total_vsize,
11361167
PackageValidationState& package_state)
@@ -1144,7 +1175,13 @@ bool MemPoolAccept::PackageMempoolChecks(const std::vector<CTransactionRef>& txn
11441175

11451176
assert(txns.size() == workspaces.size());
11461177

1147-
auto result = m_pool.CheckPackageLimits(txns, total_vsize);
1178+
util::Result<void> result = [&]() EXCLUSIVE_LOCKS_REQUIRED(m_pool.cs) {
1179+
if (args.m_ignore_rejects.count("package-mempool-limits")) {
1180+
return util::Result<void>();
1181+
} else {
1182+
return m_pool.CheckPackageLimits(txns, total_vsize);
1183+
}
1184+
}();
11481185
if (!result) {
11491186
// This is a package-wide error, separate from an individual transaction error.
11501187
return package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-mempool-limits", util::ErrorString(result).original);
@@ -1370,7 +1407,7 @@ bool MemPoolAccept::SubmitPackage(const ATMPArgs& args, std::vector<Workspace>&
13701407
// Re-calculate mempool ancestors to call addUnchecked(). They may have changed since the
13711408
// last calculation done in PreChecks, since package ancestors have already been submitted.
13721409
{
1373-
auto ancestors{m_pool.CalculateMemPoolAncestors(*ws.m_entry, m_pool.m_opts.limits)};
1410+
auto ancestors{m_pool.CalculateMemPoolAncestors(*ws.m_entry, CTxMemPool::Limits::NoLimits())};
13741411
if(!ancestors) {
13751412
results.emplace(ws.m_ptx->GetWitnessHash(), MempoolAcceptResult::Failure(ws.m_state));
13761413
// Since PreChecks() and PackageMempoolChecks() both enforce limits, this should never fail.
@@ -1452,7 +1489,7 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef
14521489
return MempoolAcceptResult::Failure(ws.m_state);
14531490
}
14541491

1455-
if (m_subpackage.m_rbf && !ReplacementChecks(ws)) {
1492+
if (m_subpackage.m_rbf && !ReplacementChecks(args, ws)) {
14561493
if (ws.m_state.GetResult() == TxValidationResult::TX_RECONSIDERABLE) {
14571494
// Failed for incentives-based fee reasons. Provide the effective feerate and which tx was included.
14581495
return MempoolAcceptResult::FeeFailure(ws.m_state, CFeeRate(ws.m_modified_fees, ws.m_vsize), single_wtxid);
@@ -1586,7 +1623,7 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
15861623

15871624
// Apply package mempool ancestor/descendant limits. Skip if there is only one transaction,
15881625
// because it's unnecessary.
1589-
if (txns.size() > 1 && !PackageMempoolChecks(txns, workspaces, m_subpackage.m_total_vsize, package_state)) {
1626+
if (txns.size() > 1 && !PackageMempoolChecks(args, txns, workspaces, m_subpackage.m_total_vsize, package_state)) {
15901627
return PackageMempoolAcceptResult(package_state, std::move(results));
15911628
}
15921629

0 commit comments

Comments
 (0)