[validation] don't package validate if not policy or missing inputs

Package validation policy only differs from individual policy in its
evaluation of feerate. Minimize DoS surface; don't validate all over
again if we know the result will be the same.
This commit is contained in:
glozow 2022-02-23 11:42:33 +00:00
parent 51edcffa0e
commit 9bebf35e26

View file

@ -1342,6 +1342,7 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
// the new transactions. This ensures we don't double-count transaction counts and sizes when // the new transactions. This ensures we don't double-count transaction counts and sizes when
// checking ancestor/descendant limits, or double-count transaction fees for fee-related policy. // checking ancestor/descendant limits, or double-count transaction fees for fee-related policy.
ATMPArgs single_args = ATMPArgs::SingleInPackageAccept(args); ATMPArgs single_args = ATMPArgs::SingleInPackageAccept(args);
bool quit_early{false};
std::vector<CTransactionRef> txns_new; std::vector<CTransactionRef> txns_new;
for (const auto& tx : package) { for (const auto& tx : package) {
const auto& wtxid = tx->GetWitnessHash(); const auto& wtxid = tx->GetWitnessHash();
@ -1375,6 +1376,18 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
// in package validation, because its fees should only be "used" once. // in package validation, because its fees should only be "used" once.
assert(m_pool.exists(GenTxid::Wtxid(wtxid))); assert(m_pool.exists(GenTxid::Wtxid(wtxid)));
results.emplace(wtxid, single_res); results.emplace(wtxid, single_res);
} else if (single_res.m_state.GetResult() != TxValidationResult::TX_MEMPOOL_POLICY &&
single_res.m_state.GetResult() != TxValidationResult::TX_MISSING_INPUTS) {
// Package validation policy only differs from individual policy in its evaluation
// of feerate. For example, if a transaction fails here due to violation of a
// consensus rule, the result will not change when it is submitted as part of a
// package. To minimize the amount of repeated work, unless the transaction fails
// due to feerate or missing inputs (its parent is a previous transaction in the
// package that failed due to feerate), don't run package validation. Note that this
// decision might not make sense if different types of packages are allowed in the
// future. Continue individually validating the rest of the transactions, because
// some of them may still be valid.
quit_early = true;
} else { } else {
txns_new.push_back(tx); txns_new.push_back(tx);
} }
@ -1382,7 +1395,7 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
} }
// Nothing to do if the entire package has already been submitted. // Nothing to do if the entire package has already been submitted.
if (txns_new.empty()) { if (quit_early || txns_new.empty()) {
// No package feerate when no package validation was done. // No package feerate when no package validation was done.
return PackageMempoolAcceptResult(package_state, std::move(results)); return PackageMempoolAcceptResult(package_state, std::move(results));
} }