Skip to content

Commit 3ea71fe

Browse files
committed
[validation] don't LimitMempoolSize in any subpackage submissions
Don't do any mempool evictions until package validation is done, preventing the mempool minimum feerate from changing. Whether we submit transactions separately or as a package depends on whether they meet the mempool minimum feerate threshold, so it's best that the value not change while we are evaluating a package. This avoids a situation where we have a CPFP package in which the parents meet the mempool minimum feerate and are submitted by themselves, but they are evicted before we have submitted the child.
1 parent d227b72 commit 3ea71fe

File tree

1 file changed

+24
-22
lines changed

1 file changed

+24
-22
lines changed

src/validation.cpp

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,7 @@ class MemPoolAccept
455455
* any transaction spending the same inputs as a transaction in the mempool is considered
456456
* a conflict. */
457457
const bool m_allow_replacement;
458-
/** When true, the mempool will not be trimmed when individual transactions are submitted in
458+
/** When true, the mempool will not be trimmed when any transactions are submitted in
459459
* Finalize(). Instead, limits should be enforced at the end to ensure the package is not
460460
* partially submitted.
461461
*/
@@ -516,7 +516,7 @@ class MemPoolAccept
516516
/* m_coins_to_uncache */ package_args.m_coins_to_uncache,
517517
/* m_test_accept */ package_args.m_test_accept,
518518
/* m_allow_replacement */ true,
519-
/* m_package_submission */ false,
519+
/* m_package_submission */ true, // do not LimitMempoolSize in Finalize()
520520
/* m_package_feerates */ false, // only 1 transaction
521521
};
522522
}
@@ -652,8 +652,7 @@ class MemPoolAccept
652652

653653
// Submit all transactions to the mempool and call ConsensusScriptChecks to add to the script
654654
// cache - should only be called after successful validation of all transactions in the package.
655-
// The package may end up partially-submitted after size limiting; returns true if all
656-
// transactions are successfully added to the mempool, false otherwise.
655+
// Does not call LimitMempoolSize(), so mempool max_size_bytes may be temporarily exceeded.
657656
bool SubmitPackage(const ATMPArgs& args, std::vector<Workspace>& workspaces, PackageValidationState& package_state,
658657
std::map<uint256, MempoolAcceptResult>& results)
659658
EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
@@ -1187,32 +1186,21 @@ bool MemPoolAccept::SubmitPackage(const ATMPArgs& args, std::vector<Workspace>&
11871186
}
11881187
}
11891188

1190-
// It may or may not be the case that all the transactions made it into the mempool. Regardless,
1191-
// make sure we haven't exceeded max mempool size.
1192-
LimitMempoolSize(m_pool, m_active_chainstate.CoinsTip());
1193-
11941189
std::vector<uint256> all_package_wtxids;
11951190
all_package_wtxids.reserve(workspaces.size());
11961191
std::transform(workspaces.cbegin(), workspaces.cend(), std::back_inserter(all_package_wtxids),
11971192
[](const auto& ws) { return ws.m_ptx->GetWitnessHash(); });
1198-
// Find the wtxids of the transactions that made it into the mempool. Allow partial submission,
1199-
// but don't report success unless they all made it into the mempool.
1193+
1194+
// Add successful results. The returned results may change later if LimitMempoolSize() evicts them.
12001195
for (Workspace& ws : workspaces) {
12011196
const auto effective_feerate = args.m_package_feerates ? ws.m_package_feerate :
12021197
CFeeRate{ws.m_modified_fees, static_cast<uint32_t>(ws.m_vsize)};
12031198
const auto effective_feerate_wtxids = args.m_package_feerates ? all_package_wtxids :
12041199
std::vector<uint256>({ws.m_ptx->GetWitnessHash()});
1205-
if (m_pool.exists(GenTxid::Wtxid(ws.m_ptx->GetWitnessHash()))) {
1206-
results.emplace(ws.m_ptx->GetWitnessHash(),
1207-
MempoolAcceptResult::Success(std::move(ws.m_replaced_transactions), ws.m_vsize,
1208-
ws.m_base_fees, effective_feerate, effective_feerate_wtxids));
1209-
GetMainSignals().TransactionAddedToMempool(ws.m_ptx, m_pool.GetAndIncrementSequence());
1210-
} else {
1211-
all_submitted = false;
1212-
ws.m_state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "mempool full");
1213-
package_state.Invalid(PackageValidationResult::PCKG_TX, "transaction failed");
1214-
results.emplace(ws.m_ptx->GetWitnessHash(), MempoolAcceptResult::Failure(ws.m_state));
1215-
}
1200+
results.emplace(ws.m_ptx->GetWitnessHash(),
1201+
MempoolAcceptResult::Success(std::move(ws.m_replaced_transactions), ws.m_vsize,
1202+
ws.m_base_fees, effective_feerate, effective_feerate_wtxids));
1203+
GetMainSignals().TransactionAddedToMempool(ws.m_ptx, m_pool.GetAndIncrementSequence());
12161204
}
12171205
return all_submitted;
12181206
}
@@ -1518,12 +1506,26 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
15181506
AcceptSubPackage(txns_package_eval, args);
15191507
PackageValidationState& package_state_final = multi_submission_result.m_state;
15201508

1509+
// Make sure we haven't exceeded max mempool size.
1510+
// Package transactions that were submitted to mempool or already in mempool may be evicted.
1511+
LimitMempoolSize(m_pool, m_active_chainstate.CoinsTip());
1512+
15211513
for (const auto& tx : package) {
15221514
const auto& wtxid = tx->GetWitnessHash();
15231515
if (multi_submission_result.m_tx_results.count(wtxid) > 0) {
15241516
// We shouldn't have re-submitted if the tx result was already in results_final.
15251517
Assume(results_final.count(wtxid) == 0);
1526-
results_final.emplace(wtxid, multi_submission_result.m_tx_results.at(wtxid));
1518+
// If it was submitted, check to see if the tx is still in the mempool. It could have
1519+
// been evicted due to LimitMempoolSize() above.
1520+
const auto& txresult = multi_submission_result.m_tx_results.at(wtxid);
1521+
if (txresult.m_result_type == MempoolAcceptResult::ResultType::VALID && !m_pool.exists(GenTxid::Wtxid(wtxid))) {
1522+
package_state_final.Invalid(PackageValidationResult::PCKG_TX, "transaction failed");
1523+
TxValidationState mempool_full_state;
1524+
mempool_full_state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "mempool full");
1525+
results_final.emplace(wtxid, MempoolAcceptResult::Failure(mempool_full_state));
1526+
} else {
1527+
results_final.emplace(wtxid, txresult);
1528+
}
15271529
} else if (const auto it{results_final.find(wtxid)}; it != results_final.end()) {
15281530
// Already-in-mempool transaction. Check to see if it's still there, as it could have
15291531
// been evicted when LimitMempoolSize() was called.

0 commit comments

Comments
 (0)