Skip to content

Commit db88db4

Browse files
committed
Merge #19339: validation: re-delegate absurd fee checking from mempool to clients
b048b27 [validation] Remove absurdfee from accepttomempool (John Newbery) 932564b scripted-diff: update max-fee-exceeded error message to include RPC (gzhao408) 8f1290c [rpc/node] check for high fee before ATMP in clients (gzhao408) Pull request description: Picked up from #15810. Add separate fee-checking logic for clients that need to enforce max fee rates, then remove the `absurdFee` logic from ATMP. ATMP's `nAbsurdFee` argument is used to enforce user-specific behavior (it is not policy since it isn't applied consistently: it is only ever used in RPC and wallet, and set to 0 everywhere else internally). It should be removed from `AcceptToMemoryPool` because (1) validation results/mempool behavior should not be user-specific and (2) enforcing a max fee rate should be the responsibility of the client instead of the mempool. Note: this PR does not intend to _remove_ protection from high fees, just re-delegate the responsibility to clients. ACKs for top commit: jnewbery: utACK b048b27 LarryRuane: re-ACK b048b27 MarcoFalke: re-ACK b048b27 , only change is squashing one commit 🏦 instagibbs: utACK bitcoin/bitcoin@b048b27 Tree-SHA512: 57c17ba16d230a4cae2896dd6a64c924f307757824e35784bf96da7b10aff2d8ea910710edf35e981035623a155f8766209a92a0fdb856549fde78bc3eaae4d2
2 parents d8cd7b1 + b048b27 commit db88db4

15 files changed

+63
-47
lines changed

src/bench/block_assemble.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ static void AssembleBlock(benchmark::Bench& bench)
4949

5050
for (const auto& txr : txs) {
5151
TxValidationState state;
52-
bool ret{::AcceptToMemoryPool(*test_setup.m_node.mempool, state, txr, nullptr /* plTxnReplaced */, false /* bypass_limits */, /* nAbsurdFee */ 0)};
52+
bool ret{::AcceptToMemoryPool(*test_setup.m_node.mempool, state, txr, nullptr /* plTxnReplaced */, false /* bypass_limits */)};
5353
assert(ret);
5454
}
5555
}

src/net_processing.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2069,7 +2069,7 @@ void PeerManager::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
20692069
TxValidationState state;
20702070
std::list<CTransactionRef> removed_txn;
20712071

2072-
if (AcceptToMemoryPool(m_mempool, state, porphanTx, &removed_txn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
2072+
if (AcceptToMemoryPool(m_mempool, state, porphanTx, &removed_txn, false /* bypass_limits */)) {
20732073
LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString());
20742074
RelayTransaction(orphanHash, porphanTx->GetWitnessHash(), m_connman);
20752075
for (unsigned int i = 0; i < porphanTx->vout.size(); i++) {
@@ -3024,7 +3024,7 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
30243024
// (older than our recency filter) if trying to DoS us, without any need
30253025
// for witness malleation.
30263026
if (!AlreadyHaveTx(GenTxid(/* is_wtxid=*/true, wtxid), m_mempool) &&
3027-
AcceptToMemoryPool(m_mempool, state, ptx, &lRemovedTxn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
3027+
AcceptToMemoryPool(m_mempool, state, ptx, &lRemovedTxn, false /* bypass_limits */)) {
30283028
m_mempool.check(&::ChainstateActive().CoinsTip());
30293029
RelayTransaction(tx.GetHash(), tx.GetWitnessHash(), m_connman);
30303030
for (unsigned int i = 0; i < tx.vout.size(); i++) {

src/node/transaction.cpp

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,18 @@
1313

1414
#include <future>
1515

16+
static TransactionError HandleATMPError(const TxValidationState& state, std::string& err_string_out) {
17+
err_string_out = state.ToString();
18+
if (state.IsInvalid()) {
19+
if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
20+
return TransactionError::MISSING_INPUTS;
21+
}
22+
return TransactionError::MEMPOOL_REJECTED;
23+
} else {
24+
return TransactionError::MEMPOOL_ERROR;
25+
}
26+
}
27+
1628
TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef tx, std::string& err_string, const CAmount& max_tx_fee, bool relay, bool wait_callback)
1729
{
1830
// BroadcastTransaction can be called by either sendrawtransaction RPC or wallet RPCs.
@@ -36,20 +48,24 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
3648
if (!existingCoin.IsSpent()) return TransactionError::ALREADY_IN_CHAIN;
3749
}
3850
if (!node.mempool->exists(hashTx)) {
39-
// Transaction is not already in the mempool. Submit it.
51+
// Transaction is not already in the mempool.
4052
TxValidationState state;
41-
if (!AcceptToMemoryPool(*node.mempool, state, tx,
42-
nullptr /* plTxnReplaced */, false /* bypass_limits */, max_tx_fee)) {
43-
err_string = state.ToString();
44-
if (state.IsInvalid()) {
45-
if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
46-
return TransactionError::MISSING_INPUTS;
47-
}
48-
return TransactionError::MEMPOOL_REJECTED;
49-
} else {
50-
return TransactionError::MEMPOOL_ERROR;
53+
CAmount fee{0};
54+
if (max_tx_fee) {
55+
// First, call ATMP with test_accept and check the fee. If ATMP
56+
// fails here, return error immediately.
57+
if (!AcceptToMemoryPool(*node.mempool, state, tx,
58+
nullptr /* plTxnReplaced */, false /* bypass_limits */, /* test_accept */ true, &fee)) {
59+
return HandleATMPError(state, err_string);
60+
} else if (fee > max_tx_fee) {
61+
return TransactionError::MAX_FEE_EXCEEDED;
5162
}
5263
}
64+
// Try to submit the transaction to the mempool.
65+
if (!AcceptToMemoryPool(*node.mempool, state, tx,
66+
nullptr /* plTxnReplaced */, false /* bypass_limits */)) {
67+
return HandleATMPError(state, err_string);
68+
}
5369

5470
// Transaction was accepted to the mempool.
5571

src/rpc/rawtransaction.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -947,11 +947,19 @@ static RPCHelpMan testmempoolaccept()
947947

948948
TxValidationState state;
949949
bool test_accept_res;
950-
CAmount fee;
950+
CAmount fee{0};
951951
{
952952
LOCK(cs_main);
953953
test_accept_res = AcceptToMemoryPool(mempool, state, std::move(tx),
954-
nullptr /* plTxnReplaced */, false /* bypass_limits */, max_raw_tx_fee, /* test_accept */ true, &fee);
954+
nullptr /* plTxnReplaced */, false /* bypass_limits */, /* test_accept */ true, &fee);
955+
}
956+
957+
// Check that fee does not exceed maximum fee
958+
if (test_accept_res && max_raw_tx_fee && fee > max_raw_tx_fee) {
959+
result_0.pushKV("allowed", false);
960+
result_0.pushKV("reject-reason", "max-fee-exceeded");
961+
result.push_back(std::move(result_0));
962+
return result;
955963
}
956964
result_0.pushKV("allowed", test_accept_res);
957965

src/test/txvalidation_tests.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
4040
false,
4141
AcceptToMemoryPool(*m_node.mempool, state, MakeTransactionRef(coinbaseTx),
4242
nullptr /* plTxnReplaced */,
43-
true /* bypass_limits */,
44-
0 /* nAbsurdFee */));
43+
true /* bypass_limits */));
4544

4645
// Check that the transaction hasn't been added to mempool.
4746
BOOST_CHECK_EQUAL(m_node.mempool->size(), initialPoolSize);

src/test/txvalidationcache_tests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
3030

3131
TxValidationState state;
3232
return AcceptToMemoryPool(*m_node.mempool, state, MakeTransactionRef(tx),
33-
nullptr /* plTxnReplaced */, true /* bypass_limits */, 0 /* nAbsurdFee */);
33+
nullptr /* plTxnReplaced */, true /* bypass_limits */);
3434
};
3535

3636
// Create a double-spend of mature coinbase txn:

src/test/validation_block_tests.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -291,8 +291,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
291291
state,
292292
tx,
293293
&plTxnReplaced,
294-
/* bypass_limits */ false,
295-
/* nAbsurdFee */ 0));
294+
/* bypass_limits */ false));
296295
}
297296
}
298297

src/util/error.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ bilingual_str TransactionErrorString(const TransactionError err)
3030
case TransactionError::SIGHASH_MISMATCH:
3131
return Untranslated("Specified sighash value does not match value stored in PSBT");
3232
case TransactionError::MAX_FEE_EXCEEDED:
33-
return Untranslated("Fee exceeds maximum configured by -maxtxfee");
33+
return Untranslated("Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)");
3434
// no default case, so the compiler can warn about missing cases
3535
}
3636
assert(false);

src/validation.cpp

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ static void UpdateMempoolForReorg(CTxMemPool& mempool, DisconnectedBlockTransact
384384
TxValidationState stateDummy;
385385
if (!fAddToMempool || (*it)->IsCoinBase() ||
386386
!AcceptToMemoryPool(mempool, stateDummy, *it,
387-
nullptr /* plTxnReplaced */, true /* bypass_limits */, 0 /* nAbsurdFee */)) {
387+
nullptr /* plTxnReplaced */, true /* bypass_limits */)) {
388388
// If the transaction doesn't make it in to the mempool, remove any
389389
// transactions that depend on it (which would now be orphans).
390390
mempool.removeRecursive(**it, MemPoolRemovalReason::REORG);
@@ -463,7 +463,6 @@ class MemPoolAccept
463463
const int64_t m_accept_time;
464464
std::list<CTransactionRef>* m_replaced_transactions;
465465
const bool m_bypass_limits;
466-
const CAmount& m_absurd_fee;
467466
/*
468467
* Return any outpoints which were not previously present in the coins
469468
* cache, but were added as a result of validating the tx for mempool
@@ -558,7 +557,6 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
558557
TxValidationState &state = args.m_state;
559558
const int64_t nAcceptTime = args.m_accept_time;
560559
const bool bypass_limits = args.m_bypass_limits;
561-
const CAmount& nAbsurdFee = args.m_absurd_fee;
562560
std::vector<COutPoint>& coins_to_uncache = args.m_coins_to_uncache;
563561

564562
// Alias what we need out of ws
@@ -729,10 +727,6 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
729727
// blocks
730728
if (!bypass_limits && !CheckFeeRate(nSize, nModifiedFees, state)) return false;
731729

732-
if (nAbsurdFee && nFees > nAbsurdFee)
733-
return state.Invalid(TxValidationResult::TX_NOT_STANDARD,
734-
"absurdly-high-fee", strprintf("%d > %d", nFees, nAbsurdFee));
735-
736730
const CTxMemPool::setEntries setIterConflicting = m_pool.GetIterSet(setConflicts);
737731
// Calculate in-mempool ancestors, up to a limit.
738732
if (setConflicts.size() == 1) {
@@ -1065,10 +1059,10 @@ bool MemPoolAccept::AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs
10651059
/** (try to) add transaction to memory pool with a specified acceptance time **/
10661060
static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, TxValidationState &state, const CTransactionRef &tx,
10671061
int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
1068-
bool bypass_limits, const CAmount nAbsurdFee, bool test_accept, CAmount* fee_out=nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
1062+
bool bypass_limits, bool test_accept, CAmount* fee_out=nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
10691063
{
10701064
std::vector<COutPoint> coins_to_uncache;
1071-
MemPoolAccept::ATMPArgs args { chainparams, state, nAcceptTime, plTxnReplaced, bypass_limits, nAbsurdFee, coins_to_uncache, test_accept, fee_out };
1065+
MemPoolAccept::ATMPArgs args { chainparams, state, nAcceptTime, plTxnReplaced, bypass_limits, coins_to_uncache, test_accept, fee_out };
10721066
bool res = MemPoolAccept(pool).AcceptSingleTransaction(tx, args);
10731067
if (!res) {
10741068
// Remove coins that were not present in the coins cache before calling ATMPW;
@@ -1087,10 +1081,10 @@ static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPo
10871081

10881082
bool AcceptToMemoryPool(CTxMemPool& pool, TxValidationState &state, const CTransactionRef &tx,
10891083
std::list<CTransactionRef>* plTxnReplaced,
1090-
bool bypass_limits, const CAmount nAbsurdFee, bool test_accept, CAmount* fee_out)
1084+
bool bypass_limits, bool test_accept, CAmount* fee_out)
10911085
{
10921086
const CChainParams& chainparams = Params();
1093-
return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, GetTime(), plTxnReplaced, bypass_limits, nAbsurdFee, test_accept, fee_out);
1087+
return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, GetTime(), plTxnReplaced, bypass_limits, test_accept, fee_out);
10941088
}
10951089

10961090
CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock)
@@ -5079,7 +5073,7 @@ bool LoadMempool(CTxMemPool& pool)
50795073
if (nTime + nExpiryTimeout > nNow) {
50805074
LOCK(cs_main);
50815075
AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, nTime,
5082-
nullptr /* plTxnReplaced */, false /* bypass_limits */, 0 /* nAbsurdFee */,
5076+
nullptr /* plTxnReplaced */, false /* bypass_limits */,
50835077
false /* test_accept */);
50845078
if (state.IsValid()) {
50855079
++count;

src/validation.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ void PruneBlockFilesManual(int nManualPruneHeight);
201201
* @param[out] fee_out optional argument to return tx fee to the caller **/
202202
bool AcceptToMemoryPool(CTxMemPool& pool, TxValidationState &state, const CTransactionRef &tx,
203203
std::list<CTransactionRef>* plTxnReplaced,
204-
bool bypass_limits, const CAmount nAbsurdFee, bool test_accept=false, CAmount* fee_out=nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
204+
bool bypass_limits, bool test_accept=false, CAmount* fee_out=nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
205205

206206
/** Get the BIP9 state for a given deployment at the current tip. */
207207
ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos);

0 commit comments

Comments
 (0)