@@ -621,18 +621,11 @@ class MemPoolAccept
621621 /* * Iterators to mempool entries that this transaction directly conflicts with or may
622622 * replace via sibling eviction. */
623623 CTxMemPool::setEntries m_iters_conflicting;
624- /* * Iterators to all mempool entries that would be replaced by this transaction, including
625- * m_conflicts and their descendants. */
626- CTxMemPool::setEntries m_all_conflicting;
627624 /* * All mempool ancestors of this transaction. */
628625 CTxMemPool::setEntries m_ancestors;
629626 /* * Mempool entry constructed for this transaction. Constructed in PreChecks() but not
630627 * inserted into the mempool until Finalize(). */
631628 std::unique_ptr<CTxMemPoolEntry> m_entry;
632- /* * Pointers to the transactions that have been removed from the mempool and replaced by
633- * this transaction (everything in m_all_conflicting), used to return to the MemPoolAccept caller. Only populated if
634- * validation is successful and the original transactions are removed. */
635- std::list<CTransactionRef> m_replaced_transactions;
636629 /* * Whether RBF-related data structures (m_conflicts, m_iters_conflicting, m_all_conflicting,
637630 * m_replaced_transactions) include a sibling in addition to txns with conflicting inputs. */
638631 bool m_sibling_eviction{false };
@@ -644,10 +637,6 @@ class MemPoolAccept
644637 CAmount m_base_fees;
645638 /* * Base fees + any fee delta set by the user with prioritisetransaction. */
646639 CAmount m_modified_fees;
647- /* * Total modified fees of all transactions being replaced. */
648- CAmount m_conflicting_fees{0 };
649- /* * Total virtual size of all transactions being replaced. */
650- size_t m_conflicting_size{0 };
651640
652641 /* * If we're doing package validation (i.e. m_package_feerates=true), the "effective"
653642 * package feerate of this transaction is the total fees divided by the total size of
@@ -725,9 +714,39 @@ class MemPoolAccept
725714
726715 Chainstate& m_active_chainstate;
727716
728- /* * Whether the transaction(s) would replace any mempool transactions and/or evict any siblings.
729- * If so, RBF rules apply. */
730- bool m_rbf{false };
717+ // Fields below are per *sub*package state and must be reset prior to subsequent
718+ // AcceptSingleTransaction and AcceptMultipleTransactions invocations
719+ struct SubPackageState {
720+ /* * Aggregated modified fees of all transactions, used to calculate package feerate. */
721+ CAmount m_total_modified_fees{0 };
722+ /* * Aggregated virtual size of all transactions, used to calculate package feerate. */
723+ int64_t m_total_vsize{0 };
724+
725+ // RBF-related members
726+ /* * Whether the transaction(s) would replace any mempool transactions and/or evict any siblings.
727+ * If so, RBF rules apply. */
728+ bool m_rbf{false };
729+ /* * All directly conflicting mempool transactions and their descendants. */
730+ CTxMemPool::setEntries m_all_conflicts;
731+ /* * Mempool transactions that were replaced. */
732+ std::list<CTransactionRef> m_replaced_transactions;
733+
734+ /* * Total modified fees of mempool transactions being replaced. */
735+ CAmount m_conflicting_fees{0 };
736+ /* * Total size (in virtual bytes) of mempool transactions being replaced. */
737+ size_t m_conflicting_size{0 };
738+ };
739+
740+ struct SubPackageState m_subpackage;
741+
742+ /* * Re-set sub-package state to not leak between evaluations */
743+ void ClearSubPackageState () EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs)
744+ {
745+ m_subpackage = SubPackageState{};
746+
747+ // And clean coins while at it
748+ CleanupTemporaryCoins ();
749+ }
731750};
732751
733752bool MemPoolAccept::PreChecks (ATMPArgs& args, Workspace& ws)
@@ -1036,7 +1055,8 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
10361055 return state.Invalid (TxValidationResult::TX_CONSENSUS, " bad-txns-spends-conflicting-tx" , *err_string);
10371056 }
10381057
1039- m_rbf = !ws.m_conflicts .empty ();
1058+ // We want to detect conflicts in any tx in a package to trigger package RBF logic
1059+ m_subpackage.m_rbf |= !ws.m_conflicts .empty ();
10401060 return true ;
10411061}
10421062
@@ -1068,24 +1088,25 @@ bool MemPoolAccept::ReplacementChecks(Workspace& ws)
10681088 }
10691089
10701090 // Calculate all conflicting entries and enforce Rule #5.
1071- if (const auto err_string{GetEntriesForConflicts (tx, m_pool, ws.m_iters_conflicting , ws. m_all_conflicting )}) {
1091+ if (const auto err_string{GetEntriesForConflicts (tx, m_pool, ws.m_iters_conflicting , m_subpackage. m_all_conflicts )}) {
10721092 return state.Invalid (TxValidationResult::TX_MEMPOOL_POLICY,
10731093 strprintf (" too many potential replacements%s" , ws.m_sibling_eviction ? " (including sibling eviction)" : " " ), *err_string);
10741094 }
10751095 // Enforce Rule #2.
1076- if (const auto err_string{HasNoNewUnconfirmed (tx, m_pool, ws. m_iters_conflicting )}) {
1096+ if (const auto err_string{HasNoNewUnconfirmed (tx, m_pool, m_subpackage. m_all_conflicts )}) {
10771097 // Sibling eviction is only done for v3 transactions, which cannot have multiple ancestors.
10781098 Assume (!ws.m_sibling_eviction );
10791099 return state.Invalid (TxValidationResult::TX_MEMPOOL_POLICY,
10801100 strprintf (" replacement-adds-unconfirmed%s" , ws.m_sibling_eviction ? " (including sibling eviction)" : " " ), *err_string);
10811101 }
1102+
10821103 // Check if it's economically rational to mine this transaction rather than the ones it
10831104 // replaces and pays for its own relay fees. Enforce Rules #3 and #4.
1084- for (CTxMemPool::txiter it : ws. m_all_conflicting ) {
1085- ws .m_conflicting_fees += it->GetModifiedFee ();
1086- ws .m_conflicting_size += it->GetTxSize ();
1105+ for (CTxMemPool::txiter it : m_subpackage. m_all_conflicts ) {
1106+ m_subpackage .m_conflicting_fees += it->GetModifiedFee ();
1107+ m_subpackage .m_conflicting_size += it->GetTxSize ();
10871108 }
1088- if (const auto err_string{PaysForRBF (ws .m_conflicting_fees , ws.m_modified_fees , ws.m_vsize ,
1109+ if (const auto err_string{PaysForRBF (m_subpackage .m_conflicting_fees , ws.m_modified_fees , ws.m_vsize ,
10891110 m_pool.m_opts .incremental_relay_feerate , hash)}) {
10901111 // Even though this is a fee-related failure, this result is TX_MEMPOOL_POLICY, not
10911112 // TX_RECONSIDERABLE, because it cannot be bypassed using package validation.
@@ -1184,19 +1205,18 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
11841205 const uint256& hash = ws.m_hash ;
11851206 TxValidationState& state = ws.m_state ;
11861207 const bool bypass_limits = args.m_bypass_limits ;
1187-
11881208 std::unique_ptr<CTxMemPoolEntry>& entry = ws.m_entry ;
11891209
11901210 // Remove conflicting transactions from the mempool
1191- for (CTxMemPool::txiter it : ws. m_all_conflicting )
1211+ for (CTxMemPool::txiter it : m_subpackage. m_all_conflicts )
11921212 {
11931213 LogPrint (BCLog::MEMPOOL, " replacing tx %s (wtxid=%s) with %s (wtxid=%s) for %s additional fees, %d delta bytes\n " ,
11941214 it->GetTx ().GetHash ().ToString (),
11951215 it->GetTx ().GetWitnessHash ().ToString (),
11961216 hash.ToString (),
11971217 tx.GetWitnessHash ().ToString (),
1198- FormatMoney (ws.m_modified_fees - ws .m_conflicting_fees ),
1199- (int )entry->GetTxSize () - (int )ws .m_conflicting_size );
1218+ FormatMoney (ws.m_modified_fees - m_subpackage .m_conflicting_fees ),
1219+ (int )entry->GetTxSize () - (int )m_subpackage .m_conflicting_size );
12001220 TRACE7 (mempool, replaced,
12011221 it->GetTx ().GetHash ().data (),
12021222 it->GetTxSize (),
@@ -1206,9 +1226,12 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
12061226 entry->GetTxSize (),
12071227 entry->GetFee ()
12081228 );
1209- ws .m_replaced_transactions .push_back (it->GetSharedTx ());
1229+ m_subpackage .m_replaced_transactions .push_back (it->GetSharedTx ());
12101230 }
1211- m_pool.RemoveStaged (ws.m_all_conflicting , false , MemPoolRemovalReason::REPLACED);
1231+ m_pool.RemoveStaged (m_subpackage.m_all_conflicts , false , MemPoolRemovalReason::REPLACED);
1232+ // Don't attempt to process the same conflicts repeatedly during subpackage evaluation:
1233+ // they no longer exist on subsequent calls to Finalize() post-RemoveStaged
1234+ m_subpackage.m_all_conflicts .clear ();
12121235 // Store transaction in memory
12131236 m_pool.addUnchecked (*entry, ws.m_ancestors );
12141237
@@ -1294,7 +1317,7 @@ bool MemPoolAccept::SubmitPackage(const ATMPArgs& args, std::vector<Workspace>&
12941317 const auto effective_feerate_wtxids = args.m_package_feerates ? all_package_wtxids :
12951318 std::vector<Wtxid>{ws.m_ptx ->GetWitnessHash ()};
12961319 results.emplace (ws.m_ptx ->GetWitnessHash (),
1297- MempoolAcceptResult::Success (std::move (ws .m_replaced_transactions ), ws.m_vsize ,
1320+ MempoolAcceptResult::Success (std::move (m_subpackage .m_replaced_transactions ), ws.m_vsize ,
12981321 ws.m_base_fees , effective_feerate, effective_feerate_wtxids));
12991322 if (!m_pool.m_opts .signals ) continue ;
13001323 const CTransaction& tx = *ws.m_ptx ;
@@ -1330,7 +1353,7 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef
13301353 return MempoolAcceptResult::Failure (ws.m_state );
13311354 }
13321355
1333- if (m_rbf && !ReplacementChecks (ws)) return MempoolAcceptResult::Failure (ws.m_state );
1356+ if (m_subpackage. m_rbf && !ReplacementChecks (ws)) return MempoolAcceptResult::Failure (ws.m_state );
13341357
13351358 // Perform the inexpensive checks first and avoid hashing and signature verification unless
13361359 // those checks pass, to mitigate CPU exhaustion denial-of-service attacks.
@@ -1341,7 +1364,7 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef
13411364 const CFeeRate effective_feerate{ws.m_modified_fees , static_cast <uint32_t >(ws.m_vsize )};
13421365 // Tx was accepted, but not added
13431366 if (args.m_test_accept ) {
1344- return MempoolAcceptResult::Success (std::move (ws .m_replaced_transactions ), ws.m_vsize ,
1367+ return MempoolAcceptResult::Success (std::move (m_subpackage .m_replaced_transactions ), ws.m_vsize ,
13451368 ws.m_base_fees , effective_feerate, single_wtxid);
13461369 }
13471370
@@ -1362,7 +1385,7 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef
13621385 m_pool.m_opts .signals ->TransactionAddedToMempool (tx_info, m_pool.GetAndIncrementSequence ());
13631386 }
13641387
1365- return MempoolAcceptResult::Success (std::move (ws .m_replaced_transactions ), ws.m_vsize , ws.m_base_fees ,
1388+ return MempoolAcceptResult::Success (std::move (m_subpackage .m_replaced_transactions ), ws.m_vsize , ws.m_base_fees ,
13661389 effective_feerate, single_wtxid);
13671390}
13681391
@@ -1407,6 +1430,7 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
14071430 // replacements, we don't need to track the coins spent. Note that this logic will need to be
14081431 // updated if package replace-by-fee is allowed in the future.
14091432 assert (!args.m_allow_replacement );
1433+ assert (!m_subpackage.m_rbf );
14101434 m_viewmempool.PackageAddTransaction (ws.m_ptx );
14111435 }
14121436
@@ -1428,26 +1452,26 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
14281452 // a child that is below mempool minimum feerate. To avoid these behaviors, callers of
14291453 // AcceptMultipleTransactions need to restrict txns topology (e.g. to ancestor sets) and check
14301454 // the feerates of individuals and subsets.
1431- const auto m_total_vsize = std::accumulate (workspaces.cbegin (), workspaces.cend (), int64_t {0 },
1455+ m_subpackage. m_total_vsize = std::accumulate (workspaces.cbegin (), workspaces.cend (), int64_t {0 },
14321456 [](int64_t sum, auto & ws) { return sum + ws.m_vsize ; });
1433- const auto m_total_modified_fees = std::accumulate (workspaces.cbegin (), workspaces.cend (), CAmount{0 },
1457+ m_subpackage. m_total_modified_fees = std::accumulate (workspaces.cbegin (), workspaces.cend (), CAmount{0 },
14341458 [](CAmount sum, auto & ws) { return sum + ws.m_modified_fees ; });
1435- const CFeeRate package_feerate (m_total_modified_fees, m_total_vsize);
1459+ const CFeeRate package_feerate (m_subpackage. m_total_modified_fees , m_subpackage. m_total_vsize );
14361460 std::vector<Wtxid> all_package_wtxids;
14371461 all_package_wtxids.reserve (workspaces.size ());
14381462 std::transform (workspaces.cbegin (), workspaces.cend (), std::back_inserter (all_package_wtxids),
14391463 [](const auto & ws) { return ws.m_ptx ->GetWitnessHash (); });
14401464 TxValidationState placeholder_state;
14411465 if (args.m_package_feerates &&
1442- !CheckFeeRate (m_total_vsize, m_total_modified_fees, placeholder_state)) {
1466+ !CheckFeeRate (m_subpackage. m_total_vsize , m_subpackage. m_total_modified_fees , placeholder_state)) {
14431467 package_state.Invalid (PackageValidationResult::PCKG_TX, " transaction failed" );
14441468 return PackageMempoolAcceptResult (package_state, {{workspaces.back ().m_ptx ->GetWitnessHash (),
1445- MempoolAcceptResult::FeeFailure (placeholder_state, CFeeRate (m_total_modified_fees, m_total_vsize), all_package_wtxids)}});
1469+ MempoolAcceptResult::FeeFailure (placeholder_state, CFeeRate (m_subpackage. m_total_modified_fees , m_subpackage. m_total_vsize ), all_package_wtxids)}});
14461470 }
14471471
14481472 // Apply package mempool ancestor/descendant limits. Skip if there is only one transaction,
14491473 // because it's unnecessary.
1450- if (txns.size () > 1 && !PackageMempoolChecks (txns, m_total_vsize, package_state)) {
1474+ if (txns.size () > 1 && !PackageMempoolChecks (txns, m_subpackage. m_total_vsize , package_state)) {
14511475 return PackageMempoolAcceptResult (package_state, std::move (results));
14521476 }
14531477
@@ -1465,7 +1489,7 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
14651489 const auto effective_feerate_wtxids = args.m_package_feerates ? all_package_wtxids :
14661490 std::vector<Wtxid>{ws.m_ptx ->GetWitnessHash ()};
14671491 results.emplace (ws.m_ptx ->GetWitnessHash (),
1468- MempoolAcceptResult::Success (std::move (ws .m_replaced_transactions ),
1492+ MempoolAcceptResult::Success (std::move (m_subpackage .m_replaced_transactions ),
14691493 ws.m_vsize , ws.m_base_fees , effective_feerate,
14701494 effective_feerate_wtxids));
14711495 }
@@ -1530,7 +1554,8 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptSubPackage(const std::vector<CTr
15301554
15311555 // Clean up m_view and m_viewmempool so that other subpackage evaluations don't have access to
15321556 // coins they shouldn't. Keep some coins in order to minimize re-fetching coins from the UTXO set.
1533- CleanupTemporaryCoins ();
1557+ // Clean up package feerate and rbf calculations
1558+ ClearSubPackageState ();
15341559
15351560 return result;
15361561}
0 commit comments