@@ -621,18 +621,11 @@ class MemPoolAccept
621
621
/* * Iterators to mempool entries that this transaction directly conflicts with or may
622
622
* replace via sibling eviction. */
623
623
CTxMemPool::setEntries m_iters_conflicting;
624
- /* * Iterators to all mempool entries that would be replaced by this transaction, including
625
- * m_conflicts and their descendants. */
626
- CTxMemPool::setEntries m_all_conflicting;
627
624
/* * All mempool ancestors of this transaction. */
628
625
CTxMemPool::setEntries m_ancestors;
629
626
/* * Mempool entry constructed for this transaction. Constructed in PreChecks() but not
630
627
* inserted into the mempool until Finalize(). */
631
628
std::unique_ptr<CTxMemPoolEntry> m_entry;
632
- /* * Pointers to the transactions that have been removed from the mempool and replaced by
633
- * this transaction (everything in m_all_conflicting), used to return to the MemPoolAccept caller. Only populated if
634
- * validation is successful and the original transactions are removed. */
635
- std::list<CTransactionRef> m_replaced_transactions;
636
629
/* * Whether RBF-related data structures (m_conflicts, m_iters_conflicting, m_all_conflicting,
637
630
* m_replaced_transactions) include a sibling in addition to txns with conflicting inputs. */
638
631
bool m_sibling_eviction{false };
@@ -644,10 +637,6 @@ class MemPoolAccept
644
637
CAmount m_base_fees;
645
638
/* * Base fees + any fee delta set by the user with prioritisetransaction. */
646
639
CAmount m_modified_fees;
647
- /* * Total modified fees of all transactions being replaced. */
648
- CAmount m_conflicting_fees{0 };
649
- /* * Total virtual size of all transactions being replaced. */
650
- size_t m_conflicting_size{0 };
651
640
652
641
/* * If we're doing package validation (i.e. m_package_feerates=true), the "effective"
653
642
* package feerate of this transaction is the total fees divided by the total size of
@@ -725,9 +714,39 @@ class MemPoolAccept
725
714
726
715
Chainstate& m_active_chainstate;
727
716
728
- /* * Whether the transaction(s) would replace any mempool transactions and/or evict any siblings.
729
- * If so, RBF rules apply. */
730
- bool m_rbf{false };
717
+ // Fields below are per *sub*package state and must be reset prior to subsequent
718
+ // AcceptSingleTransaction and AcceptMultipleTransactions invocations
719
+ struct SubPackageState {
720
+ /* * Aggregated modified fees of all transactions, used to calculate package feerate. */
721
+ CAmount m_total_modified_fees{0 };
722
+ /* * Aggregated virtual size of all transactions, used to calculate package feerate. */
723
+ int64_t m_total_vsize{0 };
724
+
725
+ // RBF-related members
726
+ /* * Whether the transaction(s) would replace any mempool transactions and/or evict any siblings.
727
+ * If so, RBF rules apply. */
728
+ bool m_rbf{false };
729
+ /* * All directly conflicting mempool transactions and their descendants. */
730
+ CTxMemPool::setEntries m_all_conflicts;
731
+ /* * Mempool transactions that were replaced. */
732
+ std::list<CTransactionRef> m_replaced_transactions;
733
+
734
+ /* * Total modified fees of mempool transactions being replaced. */
735
+ CAmount m_conflicting_fees{0 };
736
+ /* * Total size (in virtual bytes) of mempool transactions being replaced. */
737
+ size_t m_conflicting_size{0 };
738
+ };
739
+
740
+ struct SubPackageState m_subpackage;
741
+
742
+ /* * Re-set sub-package state to not leak between evaluations */
743
+ void ClearSubPackageState () EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs)
744
+ {
745
+ m_subpackage = SubPackageState{};
746
+
747
+ // And clean coins while at it
748
+ CleanupTemporaryCoins ();
749
+ }
731
750
};
732
751
733
752
bool MemPoolAccept::PreChecks (ATMPArgs& args, Workspace& ws)
@@ -1036,7 +1055,8 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
1036
1055
return state.Invalid (TxValidationResult::TX_CONSENSUS, " bad-txns-spends-conflicting-tx" , *err_string);
1037
1056
}
1038
1057
1039
- m_rbf = !ws.m_conflicts .empty ();
1058
+ // We want to detect conflicts in any tx in a package to trigger package RBF logic
1059
+ m_subpackage.m_rbf |= !ws.m_conflicts .empty ();
1040
1060
return true ;
1041
1061
}
1042
1062
@@ -1068,24 +1088,25 @@ bool MemPoolAccept::ReplacementChecks(Workspace& ws)
1068
1088
}
1069
1089
1070
1090
// Calculate all conflicting entries and enforce Rule #5.
1071
- if (const auto err_string{GetEntriesForConflicts (tx, m_pool, ws.m_iters_conflicting , ws. m_all_conflicting )}) {
1091
+ if (const auto err_string{GetEntriesForConflicts (tx, m_pool, ws.m_iters_conflicting , m_subpackage. m_all_conflicts )}) {
1072
1092
return state.Invalid (TxValidationResult::TX_MEMPOOL_POLICY,
1073
1093
strprintf (" too many potential replacements%s" , ws.m_sibling_eviction ? " (including sibling eviction)" : " " ), *err_string);
1074
1094
}
1075
1095
// Enforce Rule #2.
1076
- if (const auto err_string{HasNoNewUnconfirmed (tx, m_pool, ws. m_iters_conflicting )}) {
1096
+ if (const auto err_string{HasNoNewUnconfirmed (tx, m_pool, m_subpackage. m_all_conflicts )}) {
1077
1097
// Sibling eviction is only done for v3 transactions, which cannot have multiple ancestors.
1078
1098
Assume (!ws.m_sibling_eviction );
1079
1099
return state.Invalid (TxValidationResult::TX_MEMPOOL_POLICY,
1080
1100
strprintf (" replacement-adds-unconfirmed%s" , ws.m_sibling_eviction ? " (including sibling eviction)" : " " ), *err_string);
1081
1101
}
1102
+
1082
1103
// Check if it's economically rational to mine this transaction rather than the ones it
1083
1104
// replaces and pays for its own relay fees. Enforce Rules #3 and #4.
1084
- for (CTxMemPool::txiter it : ws. m_all_conflicting ) {
1085
- ws .m_conflicting_fees += it->GetModifiedFee ();
1086
- ws .m_conflicting_size += it->GetTxSize ();
1105
+ for (CTxMemPool::txiter it : m_subpackage. m_all_conflicts ) {
1106
+ m_subpackage .m_conflicting_fees += it->GetModifiedFee ();
1107
+ m_subpackage .m_conflicting_size += it->GetTxSize ();
1087
1108
}
1088
- if (const auto err_string{PaysForRBF (ws .m_conflicting_fees , ws.m_modified_fees , ws.m_vsize ,
1109
+ if (const auto err_string{PaysForRBF (m_subpackage .m_conflicting_fees , ws.m_modified_fees , ws.m_vsize ,
1089
1110
m_pool.m_opts .incremental_relay_feerate , hash)}) {
1090
1111
// Even though this is a fee-related failure, this result is TX_MEMPOOL_POLICY, not
1091
1112
// TX_RECONSIDERABLE, because it cannot be bypassed using package validation.
@@ -1184,19 +1205,18 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
1184
1205
const uint256& hash = ws.m_hash ;
1185
1206
TxValidationState& state = ws.m_state ;
1186
1207
const bool bypass_limits = args.m_bypass_limits ;
1187
-
1188
1208
std::unique_ptr<CTxMemPoolEntry>& entry = ws.m_entry ;
1189
1209
1190
1210
// Remove conflicting transactions from the mempool
1191
- for (CTxMemPool::txiter it : ws. m_all_conflicting )
1211
+ for (CTxMemPool::txiter it : m_subpackage. m_all_conflicts )
1192
1212
{
1193
1213
LogPrint (BCLog::MEMPOOL, " replacing tx %s (wtxid=%s) with %s (wtxid=%s) for %s additional fees, %d delta bytes\n " ,
1194
1214
it->GetTx ().GetHash ().ToString (),
1195
1215
it->GetTx ().GetWitnessHash ().ToString (),
1196
1216
hash.ToString (),
1197
1217
tx.GetWitnessHash ().ToString (),
1198
- FormatMoney (ws.m_modified_fees - ws .m_conflicting_fees ),
1199
- (int )entry->GetTxSize () - (int )ws .m_conflicting_size );
1218
+ FormatMoney (ws.m_modified_fees - m_subpackage .m_conflicting_fees ),
1219
+ (int )entry->GetTxSize () - (int )m_subpackage .m_conflicting_size );
1200
1220
TRACE7 (mempool, replaced,
1201
1221
it->GetTx ().GetHash ().data (),
1202
1222
it->GetTxSize (),
@@ -1206,9 +1226,12 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
1206
1226
entry->GetTxSize (),
1207
1227
entry->GetFee ()
1208
1228
);
1209
- ws .m_replaced_transactions .push_back (it->GetSharedTx ());
1229
+ m_subpackage .m_replaced_transactions .push_back (it->GetSharedTx ());
1210
1230
}
1211
- m_pool.RemoveStaged (ws.m_all_conflicting , false , MemPoolRemovalReason::REPLACED);
1231
+ m_pool.RemoveStaged (m_subpackage.m_all_conflicts , false , MemPoolRemovalReason::REPLACED);
1232
+ // Don't attempt to process the same conflicts repeatedly during subpackage evaluation:
1233
+ // they no longer exist on subsequent calls to Finalize() post-RemoveStaged
1234
+ m_subpackage.m_all_conflicts .clear ();
1212
1235
// Store transaction in memory
1213
1236
m_pool.addUnchecked (*entry, ws.m_ancestors );
1214
1237
@@ -1294,7 +1317,7 @@ bool MemPoolAccept::SubmitPackage(const ATMPArgs& args, std::vector<Workspace>&
1294
1317
const auto effective_feerate_wtxids = args.m_package_feerates ? all_package_wtxids :
1295
1318
std::vector<Wtxid>{ws.m_ptx ->GetWitnessHash ()};
1296
1319
results.emplace (ws.m_ptx ->GetWitnessHash (),
1297
- MempoolAcceptResult::Success (std::move (ws .m_replaced_transactions ), ws.m_vsize ,
1320
+ MempoolAcceptResult::Success (std::move (m_subpackage .m_replaced_transactions ), ws.m_vsize ,
1298
1321
ws.m_base_fees , effective_feerate, effective_feerate_wtxids));
1299
1322
if (!m_pool.m_opts .signals ) continue ;
1300
1323
const CTransaction& tx = *ws.m_ptx ;
@@ -1330,7 +1353,7 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef
1330
1353
return MempoolAcceptResult::Failure (ws.m_state );
1331
1354
}
1332
1355
1333
- if (m_rbf && !ReplacementChecks (ws)) return MempoolAcceptResult::Failure (ws.m_state );
1356
+ if (m_subpackage. m_rbf && !ReplacementChecks (ws)) return MempoolAcceptResult::Failure (ws.m_state );
1334
1357
1335
1358
// Perform the inexpensive checks first and avoid hashing and signature verification unless
1336
1359
// those checks pass, to mitigate CPU exhaustion denial-of-service attacks.
@@ -1341,7 +1364,7 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef
1341
1364
const CFeeRate effective_feerate{ws.m_modified_fees , static_cast <uint32_t >(ws.m_vsize )};
1342
1365
// Tx was accepted, but not added
1343
1366
if (args.m_test_accept ) {
1344
- return MempoolAcceptResult::Success (std::move (ws .m_replaced_transactions ), ws.m_vsize ,
1367
+ return MempoolAcceptResult::Success (std::move (m_subpackage .m_replaced_transactions ), ws.m_vsize ,
1345
1368
ws.m_base_fees , effective_feerate, single_wtxid);
1346
1369
}
1347
1370
@@ -1362,7 +1385,7 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef
1362
1385
m_pool.m_opts .signals ->TransactionAddedToMempool (tx_info, m_pool.GetAndIncrementSequence ());
1363
1386
}
1364
1387
1365
- return MempoolAcceptResult::Success (std::move (ws .m_replaced_transactions ), ws.m_vsize , ws.m_base_fees ,
1388
+ return MempoolAcceptResult::Success (std::move (m_subpackage .m_replaced_transactions ), ws.m_vsize , ws.m_base_fees ,
1366
1389
effective_feerate, single_wtxid);
1367
1390
}
1368
1391
@@ -1407,6 +1430,7 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
1407
1430
// replacements, we don't need to track the coins spent. Note that this logic will need to be
1408
1431
// updated if package replace-by-fee is allowed in the future.
1409
1432
assert (!args.m_allow_replacement );
1433
+ assert (!m_subpackage.m_rbf );
1410
1434
m_viewmempool.PackageAddTransaction (ws.m_ptx );
1411
1435
}
1412
1436
@@ -1428,26 +1452,26 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
1428
1452
// a child that is below mempool minimum feerate. To avoid these behaviors, callers of
1429
1453
// AcceptMultipleTransactions need to restrict txns topology (e.g. to ancestor sets) and check
1430
1454
// the feerates of individuals and subsets.
1431
- const auto m_total_vsize = std::accumulate (workspaces.cbegin (), workspaces.cend (), int64_t {0 },
1455
+ m_subpackage. m_total_vsize = std::accumulate (workspaces.cbegin (), workspaces.cend (), int64_t {0 },
1432
1456
[](int64_t sum, auto & ws) { return sum + ws.m_vsize ; });
1433
- const auto m_total_modified_fees = std::accumulate (workspaces.cbegin (), workspaces.cend (), CAmount{0 },
1457
+ m_subpackage. m_total_modified_fees = std::accumulate (workspaces.cbegin (), workspaces.cend (), CAmount{0 },
1434
1458
[](CAmount sum, auto & ws) { return sum + ws.m_modified_fees ; });
1435
- const CFeeRate package_feerate (m_total_modified_fees, m_total_vsize);
1459
+ const CFeeRate package_feerate (m_subpackage. m_total_modified_fees , m_subpackage. m_total_vsize );
1436
1460
std::vector<Wtxid> all_package_wtxids;
1437
1461
all_package_wtxids.reserve (workspaces.size ());
1438
1462
std::transform (workspaces.cbegin (), workspaces.cend (), std::back_inserter (all_package_wtxids),
1439
1463
[](const auto & ws) { return ws.m_ptx ->GetWitnessHash (); });
1440
1464
TxValidationState placeholder_state;
1441
1465
if (args.m_package_feerates &&
1442
- !CheckFeeRate (m_total_vsize, m_total_modified_fees, placeholder_state)) {
1466
+ !CheckFeeRate (m_subpackage. m_total_vsize , m_subpackage. m_total_modified_fees , placeholder_state)) {
1443
1467
package_state.Invalid (PackageValidationResult::PCKG_TX, " transaction failed" );
1444
1468
return PackageMempoolAcceptResult (package_state, {{workspaces.back ().m_ptx ->GetWitnessHash (),
1445
- MempoolAcceptResult::FeeFailure (placeholder_state, CFeeRate (m_total_modified_fees, m_total_vsize), all_package_wtxids)}});
1469
+ MempoolAcceptResult::FeeFailure (placeholder_state, CFeeRate (m_subpackage. m_total_modified_fees , m_subpackage. m_total_vsize ), all_package_wtxids)}});
1446
1470
}
1447
1471
1448
1472
// Apply package mempool ancestor/descendant limits. Skip if there is only one transaction,
1449
1473
// because it's unnecessary.
1450
- if (txns.size () > 1 && !PackageMempoolChecks (txns, m_total_vsize, package_state)) {
1474
+ if (txns.size () > 1 && !PackageMempoolChecks (txns, m_subpackage. m_total_vsize , package_state)) {
1451
1475
return PackageMempoolAcceptResult (package_state, std::move (results));
1452
1476
}
1453
1477
@@ -1465,7 +1489,7 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
1465
1489
const auto effective_feerate_wtxids = args.m_package_feerates ? all_package_wtxids :
1466
1490
std::vector<Wtxid>{ws.m_ptx ->GetWitnessHash ()};
1467
1491
results.emplace (ws.m_ptx ->GetWitnessHash (),
1468
- MempoolAcceptResult::Success (std::move (ws .m_replaced_transactions ),
1492
+ MempoolAcceptResult::Success (std::move (m_subpackage .m_replaced_transactions ),
1469
1493
ws.m_vsize , ws.m_base_fees , effective_feerate,
1470
1494
effective_feerate_wtxids));
1471
1495
}
@@ -1530,7 +1554,8 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptSubPackage(const std::vector<CTr
1530
1554
1531
1555
// Clean up m_view and m_viewmempool so that other subpackage evaluations don't have access to
1532
1556
// coins they shouldn't. Keep some coins in order to minimize re-fetching coins from the UTXO set.
1533
- CleanupTemporaryCoins ();
1557
+ // Clean up package feerate and rbf calculations
1558
+ ClearSubPackageState ();
1534
1559
1535
1560
return result;
1536
1561
}
0 commit comments