@@ -1672,10 +1672,14 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptSubPackage(const std::vector<CTr
16721672
16731673PackageMempoolAcceptResult MemPoolAccept::AcceptPackage (const Package& package, ATMPArgs& args)
16741674{
1675+ Assert (!package.empty ());
16751676 AssertLockHeld (cs_main);
16761677 // Used if returning a PackageMempoolAcceptResult directly from this function.
16771678 PackageValidationState package_state_quit_early;
16781679
1680+ // There are two topologies we are able to handle through this function:
1681+ // (1) A single transaction
1682+ // (2) A child-with-unconfirmed-parents package.
16791683 // Check that the package is well-formed. If it isn't, we won't try to validate any of the
16801684 // transactions and thus won't return any MempoolAcceptResults, just a package-wide error.
16811685
@@ -1684,48 +1688,50 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
16841688 return PackageMempoolAcceptResult (package_state_quit_early, {});
16851689 }
16861690
1687- // All transactions in the package must be a parent of the last transaction. This is just an
1688- // opportunity for us to fail fast on a context-free check without taking the mempool lock.
1689- if (!IsChildWithParents (package)) {
1690- package_state_quit_early.Invalid (PackageValidationResult::PCKG_POLICY, " package-not-child-with-parents" );
1691- return PackageMempoolAcceptResult (package_state_quit_early, {});
1692- }
1693-
1694- // IsChildWithParents() guarantees the package is > 1 transactions.
1695- assert (package.size () > 1 );
1696- // The package must be 1 child with all of its unconfirmed parents. The package is expected to
1697- // be sorted, so the last transaction is the child.
1698- const auto & child = package.back ();
1699- std::unordered_set<uint256, SaltedTxidHasher> unconfirmed_parent_txids;
1700- std::transform (package.cbegin (), package.cend () - 1 ,
1701- std::inserter (unconfirmed_parent_txids, unconfirmed_parent_txids.end ()),
1702- [](const auto & tx) { return tx->GetHash (); });
1703-
1704- // All child inputs must refer to a preceding package transaction or a confirmed UTXO. The only
1705- // way to verify this is to look up the child's inputs in our current coins view (not including
1706- // mempool), and enforce that all parents not present in the package be available at chain tip.
1707- // Since this check can bring new coins into the coins cache, keep track of these coins and
1708- // uncache them if we don't end up submitting this package to the mempool.
1709- const CCoinsViewCache& coins_tip_cache = m_active_chainstate.CoinsTip ();
1710- for (const auto & input : child->vin ) {
1711- if (!coins_tip_cache.HaveCoinInCache (input.prevout )) {
1712- args.m_coins_to_uncache .push_back (input.prevout );
1713- }
1714- }
1715- // Using the MemPoolAccept m_view cache allows us to look up these same coins faster later.
1716- // This should be connecting directly to CoinsTip, not to m_viewmempool, because we specifically
1717- // require inputs to be confirmed if they aren't in the package.
1718- m_view.SetBackend (m_active_chainstate.CoinsTip ());
1719- const auto package_or_confirmed = [this , &unconfirmed_parent_txids](const auto & input) {
1720- return unconfirmed_parent_txids.count (input.prevout .hash ) > 0 || m_view.HaveCoin (input.prevout );
1721- };
1722- if (!std::all_of (child->vin .cbegin (), child->vin .cend (), package_or_confirmed)) {
1723- package_state_quit_early.Invalid (PackageValidationResult::PCKG_POLICY, " package-not-child-with-unconfirmed-parents" );
1724- return PackageMempoolAcceptResult (package_state_quit_early, {});
1691+ if (package.size () > 1 ) {
1692+ // All transactions in the package must be a parent of the last transaction. This is just an
1693+ // opportunity for us to fail fast on a context-free check without taking the mempool lock.
1694+ if (!IsChildWithParents (package)) {
1695+ package_state_quit_early.Invalid (PackageValidationResult::PCKG_POLICY, " package-not-child-with-parents" );
1696+ return PackageMempoolAcceptResult (package_state_quit_early, {});
1697+ }
1698+
1699+ // IsChildWithParents() guarantees the package is > 1 transactions.
1700+ assert (package.size () > 1 );
1701+ // The package must be 1 child with all of its unconfirmed parents. The package is expected to
1702+ // be sorted, so the last transaction is the child.
1703+ const auto & child = package.back ();
1704+ std::unordered_set<uint256, SaltedTxidHasher> unconfirmed_parent_txids;
1705+ std::transform (package.cbegin (), package.cend () - 1 ,
1706+ std::inserter (unconfirmed_parent_txids, unconfirmed_parent_txids.end ()),
1707+ [](const auto & tx) { return tx->GetHash (); });
1708+
1709+ // All child inputs must refer to a preceding package transaction or a confirmed UTXO. The only
1710+ // way to verify this is to look up the child's inputs in our current coins view (not including
1711+ // mempool), and enforce that all parents not present in the package be available at chain tip.
1712+ // Since this check can bring new coins into the coins cache, keep track of these coins and
1713+ // uncache them if we don't end up submitting this package to the mempool.
1714+ const CCoinsViewCache& coins_tip_cache = m_active_chainstate.CoinsTip ();
1715+ for (const auto & input : child->vin ) {
1716+ if (!coins_tip_cache.HaveCoinInCache (input.prevout )) {
1717+ args.m_coins_to_uncache .push_back (input.prevout );
1718+ }
1719+ }
1720+ // Using the MemPoolAccept m_view cache allows us to look up these same coins faster later.
1721+ // This should be connecting directly to CoinsTip, not to m_viewmempool, because we specifically
1722+ // require inputs to be confirmed if they aren't in the package.
1723+ m_view.SetBackend (m_active_chainstate.CoinsTip ());
1724+ const auto package_or_confirmed = [this , &unconfirmed_parent_txids](const auto & input) {
1725+ return unconfirmed_parent_txids.count (input.prevout .hash ) > 0 || m_view.HaveCoin (input.prevout );
1726+ };
1727+ if (!std::all_of (child->vin .cbegin (), child->vin .cend (), package_or_confirmed)) {
1728+ package_state_quit_early.Invalid (PackageValidationResult::PCKG_POLICY, " package-not-child-with-unconfirmed-parents" );
1729+ return PackageMempoolAcceptResult (package_state_quit_early, {});
1730+ }
1731+ // Protect against bugs where we pull more inputs from disk that miss being added to
1732+ // coins_to_uncache. The backend will be connected again when needed in PreChecks.
1733+ m_view.SetBackend (m_dummy);
17251734 }
1726- // Protect against bugs where we pull more inputs from disk that miss being added to
1727- // coins_to_uncache. The backend will be connected again when needed in PreChecks.
1728- m_view.SetBackend (m_dummy);
17291735
17301736 LOCK (m_pool.cs );
17311737 // Stores results from which we will create the returned PackageMempoolAcceptResult.
@@ -1735,6 +1741,7 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
17351741 // this transaction. "Nonfinal" because if a transaction fails by itself but succeeds later
17361742 // (i.e. when evaluated with a fee-bumping child), the result in this map may be discarded.
17371743 std::map<uint256, MempoolAcceptResult> individual_results_nonfinal;
1744+ // Tracks whether we think package submission could result in successful entry to the mempool
17381745 bool quit_early{false };
17391746 std::vector<CTransactionRef> txns_package_eval;
17401747 for (const auto & tx : package) {
@@ -1776,8 +1783,9 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
17761783 // in package validation, because its fees should only be "used" once.
17771784 assert (m_pool.exists (GenTxid::Wtxid (wtxid)));
17781785 results_final.emplace (wtxid, single_res);
1779- } else if (single_res.m_state .GetResult () != TxValidationResult::TX_RECONSIDERABLE &&
1780- single_res.m_state .GetResult () != TxValidationResult::TX_MISSING_INPUTS) {
1786+ } else if (package.size () == 1 || // If there is only one transaction, no need to retry it "as a package"
1787+ (single_res.m_state .GetResult () != TxValidationResult::TX_RECONSIDERABLE &&
1788+ single_res.m_state .GetResult () != TxValidationResult::TX_MISSING_INPUTS)) {
17811789 // Package validation policy only differs from individual policy in its evaluation
17821790 // of feerate. For example, if a transaction fails here due to violation of a
17831791 // consensus rule, the result will not change when it is submitted as part of a
0 commit comments