@@ -554,6 +554,19 @@ class MemPoolAccept
554
554
*/
555
555
PackageMempoolAcceptResult AcceptMultipleTransactions (const std::vector<CTransactionRef>& txns, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
556
556
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
+
557
570
/* *
558
571
* Package (more specific than just multiple transactions) acceptance. Package must be a child
559
572
* with all of its unconfirmed parents, and topologically sorted.
@@ -1326,6 +1339,54 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
1326
1339
return PackageMempoolAcceptResult (package_state, std::move (results));
1327
1340
}
1328
1341
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
+
1329
1390
PackageMempoolAcceptResult MemPoolAccept::AcceptPackage (const Package& package, ATMPArgs& args)
1330
1391
{
1331
1392
AssertLockHeld (cs_main);
@@ -1384,15 +1445,6 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
1384
1445
LOCK (m_pool.cs );
1385
1446
// Stores final results that won't change
1386
1447
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);
1396
1448
// Results from individual validation. "Nonfinal" because if a transaction fails by itself but
1397
1449
// succeeds later (i.e. when evaluated with a fee-bumping child), the result changes (though not
1398
1450
// 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,
1408
1460
// we know is that the inputs aren't available.
1409
1461
if (m_pool.exists (GenTxid::Wtxid (wtxid))) {
1410
1462
// 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.
1411
1471
auto iter = m_pool.GetIter (txid);
1412
1472
assert (iter != std::nullopt);
1413
1473
results_final.emplace (wtxid, MempoolAcceptResult::MempoolTx (iter.value ()->GetTxSize (), iter.value ()->GetFee ()));
@@ -1426,7 +1486,8 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
1426
1486
} else {
1427
1487
// Transaction does not already exist in the mempool.
1428
1488
// 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);
1430
1491
if (single_res.m_result_type == MempoolAcceptResult::ResultType::VALID) {
1431
1492
// The transaction succeeded on its own and is now in the mempool. Don't include it
1432
1493
// in package validation, because its fees should only be "used" once.
@@ -1464,15 +1525,15 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
1464
1525
}
1465
1526
// Validate the (deduplicated) transactions as a package. Note that submission_result has its
1466
1527
// 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);
1468
1529
// Include already-in-mempool transaction results in the final result.
1469
1530
for (const auto & [wtxid, mempoolaccept_res] : results_final) {
1470
1531
Assume (submission_result.m_tx_results .emplace (wtxid, mempoolaccept_res).second );
1471
1532
Assume (mempoolaccept_res.m_result_type != MempoolAcceptResult::ResultType::INVALID);
1472
1533
}
1473
1534
if (submission_result.m_state .GetResult () == PackageValidationResult::PCKG_TX) {
1474
1535
// 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 ,
1476
1537
// include the previous individual failure reason.
1477
1538
submission_result.m_tx_results .insert (individual_results_nonfinal.cbegin (),
1478
1539
individual_results_nonfinal.cend ());
0 commit comments