@@ -633,8 +633,7 @@ class MemPoolAccept
633633 CTxMemPool::setEntries m_iters_conflicting;
634634 /* * All mempool ancestors of this transaction. */
635635 CTxMemPool::setEntries m_ancestors;
636- /* Changeset representing adding a transaction and removing its conflicts. */
637- std::unique_ptr<CTxMemPool::ChangeSet> m_changeset;
636+ /* Handle to the tx in the changeset */
638637 CTxMemPool::ChangeSet::TxHandle m_tx_handle;
639638 /* * Whether RBF-related data structures (m_conflicts, m_iters_conflicting, m_all_conflicting,
640639 * m_replaced_transactions) include a sibling in addition to txns with conflicting inputs. */
@@ -691,7 +690,7 @@ class MemPoolAccept
691690 // Try to add the transaction to the mempool, removing any conflicts first.
692691 // Returns true if the transaction is in the mempool after any size
693692 // limiting is performed, false otherwise.
694- bool Finalize (const ATMPArgs& args, Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
693+ bool FinalizeSubpackage (const ATMPArgs& args, std::vector< Workspace> & ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
695694
696695 // Submit all transactions to the mempool and call ConsensusScriptChecks to add to the script
697696 // cache - should only be called after successful validation of all transactions in the package.
@@ -746,6 +745,8 @@ class MemPoolAccept
746745 CTxMemPool::setEntries m_all_conflicts;
747746 /* * Mempool transactions that were replaced. */
748747 std::list<CTransactionRef> m_replaced_transactions;
748+ /* Changeset representing adding transactions and removing their conflicts. */
749+ std::unique_ptr<CTxMemPool::ChangeSet> m_changeset;
749750
750751 /* * Total modified fees of mempool transactions being replaced. */
751752 CAmount m_conflicting_fees{0 };
@@ -908,8 +909,10 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
908909 // Set entry_sequence to 0 when bypass_limits is used; this allows txs from a block
909910 // reorg to be marked earlier than any child txs that were already in the mempool.
910911 const uint64_t entry_sequence = bypass_limits ? 0 : m_pool.GetSequence ();
911- ws.m_changeset = m_pool.GetChangeSet ();
912- ws.m_tx_handle = ws.m_changeset ->StageAddition (ptx, ws.m_base_fees , nAcceptTime, m_active_chainstate.m_chain .Height (), entry_sequence, fSpendsCoinbase , nSigOpsCost, lock_points.value ());
912+ if (!m_subpackage.m_changeset ) {
913+ m_subpackage.m_changeset = m_pool.GetChangeSet ();
914+ }
915+ ws.m_tx_handle = m_subpackage.m_changeset ->StageAddition (ptx, ws.m_base_fees , nAcceptTime, m_active_chainstate.m_chain .Height (), entry_sequence, fSpendsCoinbase , nSigOpsCost, lock_points.value ());
913916
914917 ws.m_vsize = ws.m_tx_handle ->GetTxSize ();
915918
@@ -983,7 +986,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
983986 maybe_rbf_limits.descendant_size_vbytes += conflict->GetSizeWithDescendants ();
984987 }
985988
986- if (auto ancestors{ws .m_changeset ->CalculateMemPoolAncestors (ws.m_tx_handle , maybe_rbf_limits)}) {
989+ if (auto ancestors{m_subpackage .m_changeset ->CalculateMemPoolAncestors (ws.m_tx_handle , maybe_rbf_limits)}) {
987990 ws.m_ancestors = std::move (*ancestors);
988991 } else {
989992 // If CalculateMemPoolAncestors fails second time, we want the original error string.
@@ -1015,7 +1018,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
10151018 if (ws.m_vsize > EXTRA_DESCENDANT_TX_SIZE_LIMIT || ws.m_ptx ->version == TRUC_VERSION) {
10161019 return state.Invalid (TxValidationResult::TX_MEMPOOL_POLICY, " too-long-mempool-chain" , error_message);
10171020 }
1018- if (auto ancestors_retry{ws .m_changeset ->CalculateMemPoolAncestors (ws.m_tx_handle , cpfp_carve_out_limits)}) {
1021+ if (auto ancestors_retry{m_subpackage .m_changeset ->CalculateMemPoolAncestors (ws.m_tx_handle , cpfp_carve_out_limits)}) {
10191022 ws.m_ancestors = std::move (*ancestors_retry);
10201023 } else {
10211024 return state.Invalid (TxValidationResult::TX_MEMPOOL_POLICY, " too-long-mempool-chain" , error_message);
@@ -1117,7 +1120,7 @@ bool MemPoolAccept::ReplacementChecks(Workspace& ws)
11171120
11181121 // Add all the to-be-removed transactions to the changeset.
11191122 for (auto it : m_subpackage.m_all_conflicts ) {
1120- ws .m_changeset ->StageRemoval (it);
1123+ m_subpackage .m_changeset ->StageRemoval (it);
11211124 }
11221125 return true ;
11231126}
@@ -1180,7 +1183,7 @@ bool MemPoolAccept::PackageMempoolChecks(const std::vector<CTransactionRef>& txn
11801183
11811184
11821185 for (CTxMemPool::txiter it : m_subpackage.m_all_conflicts ) {
1183- parent_ws .m_changeset ->StageRemoval (it);
1186+ m_subpackage .m_changeset ->StageRemoval (it);
11841187 m_subpackage.m_conflicting_fees += it->GetModifiedFee ();
11851188 m_subpackage.m_conflicting_size += it->GetTxSize ();
11861189 }
@@ -1282,13 +1285,13 @@ bool MemPoolAccept::ConsensusScriptChecks(const ATMPArgs& args, Workspace& ws)
12821285 return true ;
12831286}
12841287
1285- bool MemPoolAccept::Finalize (const ATMPArgs& args, Workspace& ws )
1288+ bool MemPoolAccept::FinalizeSubpackage (const ATMPArgs& args, std::vector< Workspace>& workspaces )
12861289{
12871290 AssertLockHeld (cs_main);
12881291 AssertLockHeld (m_pool.cs );
1289- const CTransaction& tx = *ws .m_ptx ;
1290- const uint256& hash = ws .m_hash ;
1291- TxValidationState& state = ws .m_state ;
1292+ const CTransaction& tx = *workspaces. front () .m_ptx ;
1293+ const uint256& hash = workspaces. front () .m_hash ;
1294+ TxValidationState& state = workspaces. front () .m_state ;
12921295 const bool bypass_limits = args.m_bypass_limits ;
12931296
12941297 if (!m_subpackage.m_all_conflicts .empty ()) Assume (args.m_allow_replacement );
@@ -1302,20 +1305,21 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
13021305 it->GetTxSize (),
13031306 hash.ToString (),
13041307 tx.GetWitnessHash ().ToString (),
1305- ws .m_tx_handle ->GetFee (),
1306- ws .m_tx_handle ->GetTxSize ());
1308+ workspaces[ 0 ] .m_tx_handle ->GetFee (),
1309+ workspaces[ 0 ] .m_tx_handle ->GetTxSize ());
13071310 TRACEPOINT (mempool, replaced,
13081311 it->GetTx ().GetHash ().data (),
13091312 it->GetTxSize (),
13101313 it->GetFee (),
13111314 std::chrono::duration_cast<std::chrono::duration<std::uint64_t >>(it->GetTime ()).count (),
13121315 hash.data (),
1313- ws .m_tx_handle ->GetTxSize (),
1314- ws .m_tx_handle ->GetFee ()
1316+ workspaces[ 0 ] .m_tx_handle ->GetTxSize (),
1317+ workspaces[ 0 ] .m_tx_handle ->GetFee ()
13151318 );
13161319 m_subpackage.m_replaced_transactions .push_back (it->GetSharedTx ());
13171320 }
1318- ws.m_changeset ->Apply ();
1321+ m_subpackage.m_changeset ->Apply ();
1322+ m_subpackage.m_changeset .reset ();
13191323 // Don't attempt to process the same conflicts repeatedly during subpackage evaluation:
13201324 // they no longer exist on subsequent calls to Finalize() post-Apply()
13211325 m_subpackage.m_all_conflicts .clear ();
@@ -1345,6 +1349,7 @@ bool MemPoolAccept::SubmitPackage(const ATMPArgs& args, std::vector<Workspace>&
13451349 return !m_pool.exists (GenTxid::Txid (ws.m_ptx ->GetHash ())); }));
13461350
13471351 bool all_submitted = true ;
1352+ FinalizeSubpackage (args, workspaces);
13481353 // ConsensusScriptChecks adds to the script cache and is therefore consensus-critical;
13491354 // CheckInputsFromMempoolAndCache asserts that transactions only spend coins available from the
13501355 // mempool or UTXO set. Submit each transaction to the mempool immediately after calling
@@ -1358,36 +1363,19 @@ bool MemPoolAccept::SubmitPackage(const ATMPArgs& args, std::vector<Workspace>&
13581363 package_state.Invalid (PackageValidationResult::PCKG_MEMPOOL_ERROR,
13591364 strprintf (" BUG! PolicyScriptChecks succeeded but ConsensusScriptChecks failed: %s" ,
13601365 ws.m_ptx ->GetHash ().ToString ()));
1361- }
1362-
1363- // Re-calculate mempool ancestors to call addUnchecked(). They may have changed since the
1364- // last calculation done in PreChecks, since package ancestors have already been submitted.
1365- {
1366- auto ancestors{ws.m_changeset ->CalculateMemPoolAncestors (ws.m_tx_handle , m_pool.m_opts .limits )};
1367- if (!ancestors) {
1368- results.emplace (ws.m_ptx ->GetWitnessHash (), MempoolAcceptResult::Failure (ws.m_state ));
1369- // Since PreChecks() and PackageMempoolChecks() both enforce limits, this should never fail.
1370- Assume (false );
1371- all_submitted = false ;
1372- package_state.Invalid (PackageValidationResult::PCKG_MEMPOOL_ERROR,
1373- strprintf (" BUG! Mempool ancestors or descendants were underestimated: %s" ,
1374- ws.m_ptx ->GetHash ().ToString ()));
1375- }
1376- ws.m_ancestors = std::move (ancestors).value_or (ws.m_ancestors );
1377- }
1378- // If we call LimitMempoolSize() for each individual Finalize(), the mempool will not take
1379- // the transaction's descendant feerate into account because it hasn't seen them yet. Also,
1380- // we risk evicting a transaction that a subsequent package transaction depends on. Instead,
1381- // allow the mempool to temporarily bypass limits, the maximum package size) while
1382- // submitting transactions individually and then trim at the very end.
1383- if (!Finalize (args, ws)) {
1384- results.emplace (ws.m_ptx ->GetWitnessHash (), MempoolAcceptResult::Failure (ws.m_state ));
1385- // Since LimitMempoolSize() won't be called, this should never fail.
1386- Assume (false );
1387- all_submitted = false ;
1388- package_state.Invalid (PackageValidationResult::PCKG_MEMPOOL_ERROR,
1389- strprintf (" BUG! Adding to mempool failed: %s" , ws.m_ptx ->GetHash ().ToString ()));
1390- }
1366+ // Remove the transaction from the mempool.
1367+ if (!m_subpackage.m_changeset ) m_subpackage.m_changeset = m_pool.GetChangeSet ();
1368+ m_subpackage.m_changeset ->StageRemoval (m_pool.GetIter (ws.m_ptx ->GetHash ()).value ());
1369+ }
1370+ }
1371+ if (!all_submitted) {
1372+ Assume (m_subpackage.m_changeset );
1373+ // This code should be unreachable; it's here as belt-and-suspenders
1374+ // to try to ensure we have no consensus-invalid transactions in the
1375+ // mempool.
1376+ m_subpackage.m_changeset ->Apply ();
1377+ m_subpackage.m_changeset .reset ();
1378+ return false ;
13911379 }
13921380
13931381 std::vector<Wtxid> all_package_wtxids;
@@ -1430,7 +1418,8 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef
14301418 AssertLockHeld (cs_main);
14311419 LOCK (m_pool.cs ); // mempool "read lock" (held through m_pool.m_opts.signals->TransactionAddedToMempool())
14321420
1433- Workspace ws (ptx);
1421+ std::vector<Workspace> workspaces{Workspace (ptx)};
1422+ Workspace &ws = workspaces.front ();
14341423 const std::vector<Wtxid> single_wtxid{ws.m_ptx ->GetWitnessHash ()};
14351424
14361425 if (!PreChecks (args, ws)) {
@@ -1478,7 +1467,7 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef
14781467 ws.m_base_fees , effective_feerate, single_wtxid);
14791468 }
14801469
1481- if (!Finalize (args, ws )) {
1470+ if (!FinalizeSubpackage (args, workspaces )) {
14821471 // The only possible failure reason is fee-related (mempool full).
14831472 // Failed for fee reasons. Provide the effective feerate and which txns were included.
14841473 Assume (ws.m_state .GetResult () == TxValidationResult::TX_RECONSIDERABLE);
0 commit comments