@@ -1685,10 +1685,14 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptSubPackage(const std::vector<CTr
16851685
16861686PackageMempoolAcceptResult MemPoolAccept::AcceptPackage (const Package& package, ATMPArgs& args)
16871687{
1688+ Assert (!package.empty ());
16881689 AssertLockHeld (cs_main);
16891690 // Used if returning a PackageMempoolAcceptResult directly from this function.
16901691 PackageValidationState package_state_quit_early;
16911692
1693+ // There are two topologies we are able to handle through this function:
1694+ // (1) A single transaction
1695+ // (2) A child-with-unconfirmed-parents package.
16921696 // Check that the package is well-formed. If it isn't, we won't try to validate any of the
16931697 // transactions and thus won't return any MempoolAcceptResults, just a package-wide error.
16941698
@@ -1697,48 +1701,50 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
16971701 return PackageMempoolAcceptResult (package_state_quit_early, {});
16981702 }
16991703
1700- // All transactions in the package must be a parent of the last transaction. This is just an
1701- // opportunity for us to fail fast on a context-free check without taking the mempool lock.
1702- if (!IsChildWithParents (package)) {
1703- package_state_quit_early.Invalid (PackageValidationResult::PCKG_POLICY, " package-not-child-with-parents" );
1704- return PackageMempoolAcceptResult (package_state_quit_early, {});
1705- }
1706-
1707- // IsChildWithParents() guarantees the package is > 1 transactions.
1708- assert (package.size () > 1 );
1709- // The package must be 1 child with all of its unconfirmed parents. The package is expected to
1710- // be sorted, so the last transaction is the child.
1711- const auto & child = package.back ();
1712- std::unordered_set<uint256, SaltedTxidHasher> unconfirmed_parent_txids;
1713- std::transform (package.cbegin (), package.cend () - 1 ,
1714- std::inserter (unconfirmed_parent_txids, unconfirmed_parent_txids.end ()),
1715- [](const auto & tx) { return tx->GetHash (); });
1716-
1717- // All child inputs must refer to a preceding package transaction or a confirmed UTXO. The only
1718- // way to verify this is to look up the child's inputs in our current coins view (not including
1719- // mempool), and enforce that all parents not present in the package be available at chain tip.
1720- // Since this check can bring new coins into the coins cache, keep track of these coins and
1721- // uncache them if we don't end up submitting this package to the mempool.
1722- const CCoinsViewCache& coins_tip_cache = m_active_chainstate.CoinsTip ();
1723- for (const auto & input : child->vin ) {
1724- if (!coins_tip_cache.HaveCoinInCache (input.prevout )) {
1725- args.m_coins_to_uncache .push_back (input.prevout );
1726- }
1727- }
1728- // Using the MemPoolAccept m_view cache allows us to look up these same coins faster later.
1729- // This should be connecting directly to CoinsTip, not to m_viewmempool, because we specifically
1730- // require inputs to be confirmed if they aren't in the package.
1731- m_view.SetBackend (m_active_chainstate.CoinsTip ());
1732- const auto package_or_confirmed = [this , &unconfirmed_parent_txids](const auto & input) {
1733- return unconfirmed_parent_txids.count (input.prevout .hash ) > 0 || m_view.HaveCoin (input.prevout );
1734- };
1735- if (!std::all_of (child->vin .cbegin (), child->vin .cend (), package_or_confirmed)) {
1736- package_state_quit_early.Invalid (PackageValidationResult::PCKG_POLICY, " package-not-child-with-unconfirmed-parents" );
1737- return PackageMempoolAcceptResult (package_state_quit_early, {});
1704+ if (package.size () > 1 ) {
1705+ // All transactions in the package must be a parent of the last transaction. This is just an
1706+ // opportunity for us to fail fast on a context-free check without taking the mempool lock.
1707+ if (!IsChildWithParents (package)) {
1708+ package_state_quit_early.Invalid (PackageValidationResult::PCKG_POLICY, " package-not-child-with-parents" );
1709+ return PackageMempoolAcceptResult (package_state_quit_early, {});
1710+ }
1711+
1712+ // IsChildWithParents() guarantees the package is > 1 transactions.
1713+ assert (package.size () > 1 );
1714+ // The package must be 1 child with all of its unconfirmed parents. The package is expected to
1715+ // be sorted, so the last transaction is the child.
1716+ const auto & child = package.back ();
1717+ std::unordered_set<uint256, SaltedTxidHasher> unconfirmed_parent_txids;
1718+ std::transform (package.cbegin (), package.cend () - 1 ,
1719+ std::inserter (unconfirmed_parent_txids, unconfirmed_parent_txids.end ()),
1720+ [](const auto & tx) { return tx->GetHash (); });
1721+
1722+ // All child inputs must refer to a preceding package transaction or a confirmed UTXO. The only
1723+ // way to verify this is to look up the child's inputs in our current coins view (not including
1724+ // mempool), and enforce that all parents not present in the package be available at chain tip.
1725+ // Since this check can bring new coins into the coins cache, keep track of these coins and
1726+ // uncache them if we don't end up submitting this package to the mempool.
1727+ const CCoinsViewCache& coins_tip_cache = m_active_chainstate.CoinsTip ();
1728+ for (const auto & input : child->vin ) {
1729+ if (!coins_tip_cache.HaveCoinInCache (input.prevout )) {
1730+ args.m_coins_to_uncache .push_back (input.prevout );
1731+ }
1732+ }
1733+ // Using the MemPoolAccept m_view cache allows us to look up these same coins faster later.
1734+ // This should be connecting directly to CoinsTip, not to m_viewmempool, because we specifically
1735+ // require inputs to be confirmed if they aren't in the package.
1736+ m_view.SetBackend (m_active_chainstate.CoinsTip ());
1737+ const auto package_or_confirmed = [this , &unconfirmed_parent_txids](const auto & input) {
1738+ return unconfirmed_parent_txids.count (input.prevout .hash ) > 0 || m_view.HaveCoin (input.prevout );
1739+ };
1740+ if (!std::all_of (child->vin .cbegin (), child->vin .cend (), package_or_confirmed)) {
1741+ package_state_quit_early.Invalid (PackageValidationResult::PCKG_POLICY, " package-not-child-with-unconfirmed-parents" );
1742+ return PackageMempoolAcceptResult (package_state_quit_early, {});
1743+ }
1744+ // Protect against bugs where we pull more inputs from disk that miss being added to
1745+ // coins_to_uncache. The backend will be connected again when needed in PreChecks.
1746+ m_view.SetBackend (m_dummy);
17381747 }
1739- // Protect against bugs where we pull more inputs from disk that miss being added to
1740- // coins_to_uncache. The backend will be connected again when needed in PreChecks.
1741- m_view.SetBackend (m_dummy);
17421748
17431749 LOCK (m_pool.cs );
17441750 // Stores results from which we will create the returned PackageMempoolAcceptResult.
@@ -1748,6 +1754,7 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
17481754 // this transaction. "Nonfinal" because if a transaction fails by itself but succeeds later
17491755 // (i.e. when evaluated with a fee-bumping child), the result in this map may be discarded.
17501756 std::map<uint256, MempoolAcceptResult> individual_results_nonfinal;
1757+ // Tracks whether we think package submission could result in successful entry to the mempool
17511758 bool quit_early{false };
17521759 std::vector<CTransactionRef> txns_package_eval;
17531760 for (const auto & tx : package) {
@@ -1789,8 +1796,9 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
17891796 // in package validation, because its fees should only be "used" once.
17901797 assert (m_pool.exists (GenTxid::Wtxid (wtxid)));
17911798 results_final.emplace (wtxid, single_res);
1792- } else if (single_res.m_state .GetResult () != TxValidationResult::TX_RECONSIDERABLE &&
1793- single_res.m_state .GetResult () != TxValidationResult::TX_MISSING_INPUTS) {
1799+ } else if (package.size () == 1 || // If there is only one transaction, no need to retry it "as a package"
1800+ (single_res.m_state .GetResult () != TxValidationResult::TX_RECONSIDERABLE &&
1801+ single_res.m_state .GetResult () != TxValidationResult::TX_MISSING_INPUTS)) {
17941802 // Package validation policy only differs from individual policy in its evaluation
17951803 // of feerate. For example, if a transaction fails here due to violation of a
17961804 // consensus rule, the result will not change when it is submitted as part of a
0 commit comments