Skip to content

Commit

Permalink
[validation] don't LimitMempoolSize in any subpackage submissions
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
glozow committed Sep 13, 2023
1 parent d227b72 commit 3ea71fe
Showing 1 changed file with 24 additions and 22 deletions.
46 changes: 24 additions & 22 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ class MemPoolAccept
* any transaction spending the same inputs as a transaction in the mempool is considered
* a conflict. */
const bool m_allow_replacement;
/** When true, the mempool will not be trimmed when individual transactions are submitted in
/** When true, the mempool will not be trimmed when any transactions are submitted in
* Finalize(). Instead, limits should be enforced at the end to ensure the package is not
* partially submitted.
*/
Expand Down Expand Up @@ -516,7 +516,7 @@ class MemPoolAccept
/* m_coins_to_uncache */ package_args.m_coins_to_uncache,
/* m_test_accept */ package_args.m_test_accept,
/* m_allow_replacement */ true,
/* m_package_submission */ false,
/* m_package_submission */ true, // do not LimitMempoolSize in Finalize()
/* m_package_feerates */ false, // only 1 transaction
};
}
Expand Down Expand Up @@ -652,8 +652,7 @@ class MemPoolAccept

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

// It may or may not be the case that all the transactions made it into the mempool. Regardless,
// make sure we haven't exceeded max mempool size.
LimitMempoolSize(m_pool, m_active_chainstate.CoinsTip());

std::vector<uint256> all_package_wtxids;
all_package_wtxids.reserve(workspaces.size());
std::transform(workspaces.cbegin(), workspaces.cend(), std::back_inserter(all_package_wtxids),
[](const auto& ws) { return ws.m_ptx->GetWitnessHash(); });
// Find the wtxids of the transactions that made it into the mempool. Allow partial submission,
// but don't report success unless they all made it into the mempool.

// Add successful results. The returned results may change later if LimitMempoolSize() evicts them.
for (Workspace& ws : workspaces) {
const auto effective_feerate = args.m_package_feerates ? ws.m_package_feerate :
CFeeRate{ws.m_modified_fees, static_cast<uint32_t>(ws.m_vsize)};
const auto effective_feerate_wtxids = args.m_package_feerates ? all_package_wtxids :
std::vector<uint256>({ws.m_ptx->GetWitnessHash()});
if (m_pool.exists(GenTxid::Wtxid(ws.m_ptx->GetWitnessHash()))) {
results.emplace(ws.m_ptx->GetWitnessHash(),
MempoolAcceptResult::Success(std::move(ws.m_replaced_transactions), ws.m_vsize,
ws.m_base_fees, effective_feerate, effective_feerate_wtxids));
GetMainSignals().TransactionAddedToMempool(ws.m_ptx, m_pool.GetAndIncrementSequence());
} else {
all_submitted = false;
ws.m_state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "mempool full");
package_state.Invalid(PackageValidationResult::PCKG_TX, "transaction failed");
results.emplace(ws.m_ptx->GetWitnessHash(), MempoolAcceptResult::Failure(ws.m_state));
}
results.emplace(ws.m_ptx->GetWitnessHash(),
MempoolAcceptResult::Success(std::move(ws.m_replaced_transactions), ws.m_vsize,
ws.m_base_fees, effective_feerate, effective_feerate_wtxids));
GetMainSignals().TransactionAddedToMempool(ws.m_ptx, m_pool.GetAndIncrementSequence());
}
return all_submitted;
}
Expand Down Expand Up @@ -1518,12 +1506,26 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
AcceptSubPackage(txns_package_eval, args);
PackageValidationState& package_state_final = multi_submission_result.m_state;

// Make sure we haven't exceeded max mempool size.
// Package transactions that were submitted to mempool or already in mempool may be evicted.
LimitMempoolSize(m_pool, m_active_chainstate.CoinsTip());

for (const auto& tx : package) {
const auto& wtxid = tx->GetWitnessHash();
if (multi_submission_result.m_tx_results.count(wtxid) > 0) {
// We shouldn't have re-submitted if the tx result was already in results_final.
Assume(results_final.count(wtxid) == 0);
results_final.emplace(wtxid, multi_submission_result.m_tx_results.at(wtxid));
// If it was submitted, check to see if the tx is still in the mempool. It could have
// been evicted due to LimitMempoolSize() above.
const auto& txresult = multi_submission_result.m_tx_results.at(wtxid);
if (txresult.m_result_type == MempoolAcceptResult::ResultType::VALID && !m_pool.exists(GenTxid::Wtxid(wtxid))) {
package_state_final.Invalid(PackageValidationResult::PCKG_TX, "transaction failed");
TxValidationState mempool_full_state;
mempool_full_state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "mempool full");
results_final.emplace(wtxid, MempoolAcceptResult::Failure(mempool_full_state));
} else {
results_final.emplace(wtxid, txresult);
}
} else if (const auto it{results_final.find(wtxid)}; it != results_final.end()) {
// Already-in-mempool transaction. Check to see if it's still there, as it could have
// been evicted when LimitMempoolSize() was called.
Expand Down

0 comments on commit 3ea71fe

Please sign in to comment.