@@ -554,6 +554,19 @@ class MemPoolAccept
554554 */
555555 PackageMempoolAcceptResult AcceptMultipleTransactions (const std::vector<CTransactionRef>& txns, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
556556
557+ /* *
558+ * Submission of a subpackage.
559+ * If subpackage size == 1, calls AcceptSingleTransaction() with adjusted ATMPArgs to avoid
560+ * package policy restrictions like no CPFP carve out (PackageMempoolChecks) and disabled RBF
561+ * (m_allow_replacement), and creates a PackageMempoolAcceptResult wrapping the result.
562+ *
563+ * If subpackage size > 1, calls AcceptMultipleTransactions() with the provided ATMPArgs.
564+ *
565+ * Also cleans up all non-chainstate coins from m_view at the end.
566+ */
567+ PackageMempoolAcceptResult AcceptSubPackage (const std::vector<CTransactionRef>& subpackage, ATMPArgs& args)
568+ EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
569+
557570 /* *
558571 * Package (more specific than just multiple transactions) acceptance. Package must be a child
559572 * with all of its unconfirmed parents, and topologically sorted.
@@ -1326,6 +1339,54 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
13261339 return PackageMempoolAcceptResult (package_state, std::move (results));
13271340}
13281341
1342+ PackageMempoolAcceptResult MemPoolAccept::AcceptSubPackage (const std::vector<CTransactionRef>& subpackage, ATMPArgs& args)
1343+ {
1344+ AssertLockHeld (::cs_main);
1345+ AssertLockHeld (m_pool.cs );
1346+ auto result = [&]() EXCLUSIVE_LOCKS_REQUIRED (::cs_main, m_pool.cs ) {
1347+ if (subpackage.size () > 1 ) {
1348+ return AcceptMultipleTransactions (subpackage, args);
1349+ }
1350+ const auto & tx = subpackage.front ();
1351+ ATMPArgs single_args = ATMPArgs::SingleInPackageAccept (args);
1352+ const auto single_res = AcceptSingleTransaction (tx, single_args);
1353+ PackageValidationState package_state_wrapped;
1354+ if (single_res.m_result_type != MempoolAcceptResult::ResultType::VALID) {
1355+ package_state_wrapped.Invalid (PackageValidationResult::PCKG_TX, " transaction failed" );
1356+ }
1357+ return PackageMempoolAcceptResult (package_state_wrapped, {{tx->GetWitnessHash (), single_res}});
1358+ }();
1359+ // Clean up m_view and m_viewmempool so that other subpackage evaluations don't have access to
1360+ // coins they shouldn't. Keep some coins in order to minimize re-fetching coins from the UTXO set.
1361+ //
1362+ // There are 3 kinds of coins in m_view:
1363+ // (1) Temporary coins from the transactions in subpackage, constructed by m_viewmempool.
1364+ // (2) Mempool coins from transactions in the mempool, constructed by m_viewmempool.
1365+ // (3) Confirmed coins fetched from our current UTXO set.
1366+ //
1367+ // (1) Temporary coins need to be removed, regardless of whether the transaction was submitted.
1368+ // If the transaction was submitted to the mempool, m_viewmempool will be able to fetch them from
1369+ // there. If it wasn't submitted to mempool, it is incorrect to keep them - future calls may try
1370+ // to spend those coins that don't actually exist.
1371+ // (2) Mempool coins also need to be removed. If the mempool contents have changed as a result
1372+ // of submitting or replacing transactions, coins previously fetched from mempool may now be
1373+ // spent or nonexistent. Those coins need to be deleted from m_view.
1374+ // (3) Confirmed coins don't need to be removed. The chainstate has not changed (we are
1375+ // holding cs_main and no blocks have been processed) so the confirmed tx cannot disappear like
1376+ // a mempool tx can. The coin may now be spent after we submitted a tx to mempool, but
1377+ // we have already checked that the package does not have 2 transactions spending the same coin.
1378+ // Keeping them in m_view is an optimization to not re-fetch confirmed coins if we later look up
1379+ // inputs for this transaction again.
1380+ for (const auto & outpoint : m_viewmempool.GetNonBaseCoins ()) {
1381+ // In addition to resetting m_viewmempool, we also need to manually delete these coins from
1382+ // m_view because it caches copies of the coins it fetched from m_viewmempool previously.
1383+ m_view.Uncache (outpoint);
1384+ }
1385+ // This deletes the temporary and mempool coins.
1386+ m_viewmempool.Reset ();
1387+ return result;
1388+ }
1389+
13291390PackageMempoolAcceptResult MemPoolAccept::AcceptPackage (const Package& package, ATMPArgs& args)
13301391{
13311392 AssertLockHeld (cs_main);
@@ -1384,15 +1445,6 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
13841445 LOCK (m_pool.cs );
13851446 // Stores final results that won't change
13861447 std::map<const uint256, const MempoolAcceptResult> results_final;
1387- // Node operators are free to set their mempool policies however they please, nodes may receive
1388- // transactions in different orders, and malicious counterparties may try to take advantage of
1389- // policy differences to pin or delay propagation of transactions. As such, it's possible for
1390- // some package transaction(s) to already be in the mempool, and we don't want to reject the
1391- // entire package in that case (as that could be a censorship vector). De-duplicate the
1392- // transactions that are already in the mempool, and only call AcceptMultipleTransactions() with
1393- // the new transactions. This ensures we don't double-count transaction counts and sizes when
1394- // checking ancestor/descendant limits, or double-count transaction fees for fee-related policy.
1395- ATMPArgs single_args = ATMPArgs::SingleInPackageAccept (args);
13961448 // Results from individual validation. "Nonfinal" because if a transaction fails by itself but
13971449 // succeeds later (i.e. when evaluated with a fee-bumping child), the result changes (though not
13981450 // reflected in this map). If a transaction fails more than once, we want to return the first
@@ -1408,6 +1460,14 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
14081460 // we know is that the inputs aren't available.
14091461 if (m_pool.exists (GenTxid::Wtxid (wtxid))) {
14101462 // Exact transaction already exists in the mempool.
1463+ // Node operators are free to set their mempool policies however they please, nodes may receive
1464+ // transactions in different orders, and malicious counterparties may try to take advantage of
1465+ // policy differences to pin or delay propagation of transactions. As such, it's possible for
1466+ // some package transaction(s) to already be in the mempool, and we don't want to reject the
1467+ // entire package in that case (as that could be a censorship vector). De-duplicate the
1468+ // transactions that are already in the mempool, and only call AcceptMultipleTransactions() with
1469+ // the new transactions. This ensures we don't double-count transaction counts and sizes when
1470+ // checking ancestor/descendant limits, or double-count transaction fees for fee-related policy.
14111471 auto iter = m_pool.GetIter (txid);
14121472 assert (iter != std::nullopt );
14131473 results_final.emplace (wtxid, MempoolAcceptResult::MempoolTx (iter.value ()->GetTxSize (), iter.value ()->GetFee ()));
@@ -1426,7 +1486,8 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
14261486 } else {
14271487 // Transaction does not already exist in the mempool.
14281488 // Try submitting the transaction on its own.
1429- const auto single_res = AcceptSingleTransaction (tx, single_args);
1489+ const auto single_package_res = AcceptSubPackage ({tx}, args);
1490+ const auto & single_res = single_package_res.m_tx_results .at (wtxid);
14301491 if (single_res.m_result_type == MempoolAcceptResult::ResultType::VALID) {
14311492 // The transaction succeeded on its own and is now in the mempool. Don't include it
14321493 // in package validation, because its fees should only be "used" once.
@@ -1464,15 +1525,15 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
14641525 }
14651526 // Validate the (deduplicated) transactions as a package. Note that submission_result has its
14661527 // own PackageValidationState; package_state_quit_early is unused past this point.
1467- auto submission_result = AcceptMultipleTransactions (txns_package_eval, args);
1528+ auto submission_result = AcceptSubPackage (txns_package_eval, args);
14681529 // Include already-in-mempool transaction results in the final result.
14691530 for (const auto & [wtxid, mempoolaccept_res] : results_final) {
14701531 Assume (submission_result.m_tx_results .emplace (wtxid, mempoolaccept_res).second );
14711532 Assume (mempoolaccept_res.m_result_type != MempoolAcceptResult::ResultType::INVALID);
14721533 }
14731534 if (submission_result.m_state .GetResult () == PackageValidationResult::PCKG_TX) {
14741535 // Package validation failed because one or more transactions failed. Provide a result for
1475- // each transaction; if AcceptMultipleTransactions() didn 't return a result for a tx ,
1536+ // each transaction; if a transaction doesn 't have an entry in submission_result ,
14761537 // include the previous individual failure reason.
14771538 submission_result.m_tx_results .insert (individual_results_nonfinal.cbegin (),
14781539 individual_results_nonfinal.cend ());
0 commit comments