@@ -477,6 +477,17 @@ class MemPoolAccept
477
477
};
478
478
}
479
479
480
+ /* * Parameters for child-with-unconfirmed-parents package validation. */
481
+ static ATMPArgs PackageChildWithParents (const CChainParams& chainparams, int64_t accept_time,
482
+ std::vector<COutPoint>& coins_to_uncache) {
483
+ return ATMPArgs{/* m_chainparams */ chainparams,
484
+ /* m_accept_time */ accept_time,
485
+ /* m_bypass_limits */ false ,
486
+ /* m_coins_to_uncache */ coins_to_uncache,
487
+ /* m_test_accept */ false ,
488
+ /* m_allow_bip125_replacement */ false ,
489
+ };
490
+ }
480
491
// No default ctor to avoid exposing details to clients and allowing the possibility of
481
492
// mixing up the order of the arguments. Use static functions above instead.
482
493
ATMPArgs () = delete ;
@@ -492,6 +503,12 @@ class MemPoolAccept
492
503
*/
493
504
PackageMempoolAcceptResult AcceptMultipleTransactions (const std::vector<CTransactionRef>& txns, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
494
505
506
+ /* *
507
+ * Package (more specific than just multiple transactions) acceptance. Package must be a child
508
+ * with all of its unconfirmed parents, and topologically sorted.
509
+ */
510
+ PackageMempoolAcceptResult AcceptPackage (const Package& package, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
511
+
495
512
private:
496
513
// All the intermediate state that gets passed between the various levels
497
514
// of checking a given transaction.
@@ -1077,6 +1094,62 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
1077
1094
return PackageMempoolAcceptResult (package_state, std::move (results));
1078
1095
}
1079
1096
1097
+ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage (const Package& package, ATMPArgs& args)
1098
+ {
1099
+ AssertLockHeld (cs_main);
1100
+ PackageValidationState package_state;
1101
+
1102
+ // Check that the package is well-formed. If it isn't, we won't try to validate any of the
1103
+ // transactions and thus won't return any MempoolAcceptResults, just a package-wide error.
1104
+
1105
+ // Context-free package checks.
1106
+ if (!CheckPackage (package, package_state)) return PackageMempoolAcceptResult (package_state, {});
1107
+
1108
+ // All transactions in the package must be a parent of the last transaction. This is just an
1109
+ // opportunity for us to fail fast on a context-free check without taking the mempool lock.
1110
+ if (!IsChildWithParents (package)) {
1111
+ package_state.Invalid (PackageValidationResult::PCKG_POLICY, " package-not-child-with-parents" );
1112
+ return PackageMempoolAcceptResult (package_state, {});
1113
+ }
1114
+
1115
+ const auto & child = package[package.size () - 1 ];
1116
+ // The package must be 1 child with all of its unconfirmed parents. The package is expected to
1117
+ // be sorted, so the last transaction is the child.
1118
+ std::unordered_set<uint256, SaltedTxidHasher> unconfirmed_parent_txids;
1119
+ std::transform (package.cbegin (), package.end () - 1 ,
1120
+ std::inserter (unconfirmed_parent_txids, unconfirmed_parent_txids.end ()),
1121
+ [](const auto & tx) { return tx->GetHash (); });
1122
+
1123
+ // All child inputs must refer to a preceding package transaction or a confirmed UTXO. The only
1124
+ // way to verify this is to look up the child's inputs in our current coins view (not including
1125
+ // mempool), and enforce that all parents not present in the package be available at chain tip.
1126
+ // Since this check can bring new coins into the coins cache, keep track of these coins and
1127
+ // uncache them if we don't end up submitting this package to the mempool.
1128
+ const CCoinsViewCache& coins_tip_cache = m_active_chainstate.CoinsTip ();
1129
+ for (const auto & input : child->vin ) {
1130
+ if (!coins_tip_cache.HaveCoinInCache (input.prevout )) {
1131
+ args.m_coins_to_uncache .push_back (input.prevout );
1132
+ }
1133
+ }
1134
+ // Using the MemPoolAccept m_view cache allows us to look up these same coins faster later.
1135
+ // This should be connecting directly to CoinsTip, not to m_viewmempool, because we specifically
1136
+ // require inputs to be confirmed if they aren't in the package.
1137
+ m_view.SetBackend (m_active_chainstate.CoinsTip ());
1138
+ const auto package_or_confirmed = [this , &unconfirmed_parent_txids](const auto & input) {
1139
+ return unconfirmed_parent_txids.count (input.prevout .hash ) > 0 || m_view.HaveCoin (input.prevout );
1140
+ };
1141
+ if (!std::all_of (child->vin .cbegin (), child->vin .cend (), package_or_confirmed)) {
1142
+ package_state.Invalid (PackageValidationResult::PCKG_POLICY, " package-not-child-with-unconfirmed-parents" );
1143
+ return PackageMempoolAcceptResult (package_state, {});
1144
+ }
1145
+ // Protect against bugs where we pull more inputs from disk that miss being added to
1146
+ // coins_to_uncache. The backend will be connected again when needed in PreChecks.
1147
+ m_view.SetBackend (m_dummy);
1148
+
1149
+ LOCK (m_pool.cs );
1150
+ return AcceptMultipleTransactions (package, args);
1151
+ }
1152
+
1080
1153
} // anon namespace
1081
1154
1082
1155
/* * (try to) add transaction to memory pool with a specified acceptance time **/
@@ -1120,8 +1193,16 @@ PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTx
1120
1193
1121
1194
std::vector<COutPoint> coins_to_uncache;
1122
1195
const CChainParams& chainparams = Params ();
1123
- auto args = MemPoolAccept::ATMPArgs::PackageTestAccept (chainparams, GetTime (), coins_to_uncache);
1124
- const PackageMempoolAcceptResult result = MemPoolAccept (pool, active_chainstate).AcceptMultipleTransactions (package, args);
1196
+ const auto result = [&]() EXCLUSIVE_LOCKS_REQUIRED (cs_main) {
1197
+ AssertLockHeld (cs_main);
1198
+ if (test_accept) {
1199
+ auto args = MemPoolAccept::ATMPArgs::PackageTestAccept (chainparams, GetTime (), coins_to_uncache);
1200
+ return MemPoolAccept (pool, active_chainstate).AcceptMultipleTransactions (package, args);
1201
+ } else {
1202
+ auto args = MemPoolAccept::ATMPArgs::PackageChildWithParents (chainparams, GetTime (), coins_to_uncache);
1203
+ return MemPoolAccept (pool, active_chainstate).AcceptPackage (package, args);
1204
+ }
1205
+ }();
1125
1206
1126
1207
// Uncache coins pertaining to transactions that were not submitted to the mempool.
1127
1208
for (const COutPoint& hashTx : coins_to_uncache) {
0 commit comments