diff --git a/src/validation.cpp b/src/validation.cpp index c416582df23dce..6f8487d01d0531 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -640,6 +640,12 @@ class MemPoolAccept // Run checks for mempool replace-by-fee, only used in AcceptSingleTransaction. bool ReplacementChecks(Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs); + // Run checks for mempool replace-by-fee, only used in AcceptMultipleTransactions. + bool PackageReplacementChecks(std::vector& workspaces, + PackageValidationState& package_state, + CTxMemPool::setEntries& collective_ancestors, + std::string& err_string) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs); + // Enforce package mempool ancestor/descendant limits (distinct from individual // ancestor/descendant limits done in PreChecks) and run Package RBF checks. bool PackageMempoolChecks(const ATMPArgs& args, @@ -1061,31 +1067,12 @@ bool MemPoolAccept::ReplacementChecks(Workspace& ws) return true; } -bool MemPoolAccept::PackageMempoolChecks(const ATMPArgs& args, - const std::vector& txns, - std::vector& workspaces, - const int64_t total_vsize, - PackageValidationState& package_state) -{ - AssertLockHeld(cs_main); - AssertLockHeld(m_pool.cs); - - // CheckPackageLimits expects the package transactions to not already be in the mempool. - assert(std::all_of(txns.cbegin(), txns.cend(), [this](const auto& tx) - { return !m_pool.exists(GenTxid::Txid(tx->GetHash()));})); - - // Populate with the union of all transactions' ancestors. - CTxMemPool::setEntries m_collective_ancestors; - for (const auto& ws : workspaces) { - for (const auto& it : ws.m_ancestors) m_collective_ancestors.insert(it); - } - - std::string err_string; - if (!m_pool.CheckPackageLimits(txns, total_vsize, err_string)) { - // This is a package-wide error, separate from an individual transaction error. - return package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-mempool-limits", err_string); - } +bool MemPoolAccept::PackageReplacementChecks(std::vector& workspaces, + PackageValidationState& package_state, + CTxMemPool::setEntries& collective_ancestors, + std::string &err_string) +{ // Further checks are all RBF-only. m_rbf = std::any_of(workspaces.cbegin(), workspaces.cend(), [](const auto& ws){return !ws.m_conflicts.empty();}); if (!m_rbf) return true; @@ -1123,7 +1110,7 @@ bool MemPoolAccept::PackageMempoolChecks(const ATMPArgs& args, std::transform(m_all_conflicts.cbegin(), m_all_conflicts.cend(), std::inserter(all_conflicting_txids, all_conflicting_txids.end()), [](const auto& entry) { return entry->GetTx().GetHash(); }); - if (const auto err_string{EntriesAndTxidsDisjoint(m_collective_ancestors, all_conflicting_txids, hash)}) { + if (const auto err_string{EntriesAndTxidsDisjoint(collective_ancestors, all_conflicting_txids, hash)}) { // Note that we handle this differently in individual transaction validation (a transaction // that conflicts with its own dependency is inconsistent, but this could just be // conflicting transactions in a package). @@ -1132,7 +1119,7 @@ bool MemPoolAccept::PackageMempoolChecks(const ATMPArgs& args, } // Check if it's economically rational to mine this package rather than the ones it replaces. - if (const auto err_string{CheckMinerScores(m_total_modified_fees, m_total_vsize, m_collective_ancestors, + if (const auto err_string{CheckMinerScores(m_total_modified_fees, m_total_vsize, collective_ancestors, direct_conflict_iters, m_all_conflicts)}) { return package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package RBF failed: insufficient fees", *err_string); @@ -1152,6 +1139,40 @@ bool MemPoolAccept::PackageMempoolChecks(const ATMPArgs& args, return true; } + +bool MemPoolAccept::PackageMempoolChecks(const ATMPArgs& args, + const std::vector& txns, + std::vector& workspaces, + const int64_t total_vsize, + PackageValidationState& package_state) +{ + AssertLockHeld(cs_main); + AssertLockHeld(m_pool.cs); + + // CheckPackageLimits expects the package transactions to not already be in the mempool. + assert(std::all_of(txns.cbegin(), txns.cend(), [this](const auto& tx) + { return !m_pool.exists(GenTxid::Txid(tx->GetHash()));})); + + // Populate with the union of all transactions' ancestors. + CTxMemPool::setEntries collective_ancestors; + for (const auto& ws : workspaces) { + for (const auto& it : ws.m_ancestors) collective_ancestors.insert(it); + } + + std::string err_string; + if (!m_pool.CheckPackageLimits(txns, total_vsize, err_string)) { + // This is a package-wide error, separate from an individual transaction error. + return package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-mempool-limits", err_string); + } + + if (!PackageReplacementChecks(workspaces, package_state, collective_ancestors, err_string)) { + // package_state set by PackageReplacementChecks() + return false; + } + + return true; +} + bool MemPoolAccept::PolicyScriptChecks(const ATMPArgs& args, Workspace& ws) { AssertLockHeld(cs_main);