@@ -633,8 +633,7 @@ class MemPoolAccept
633
633
CTxMemPool::setEntries m_iters_conflicting;
634
634
/* * All mempool ancestors of this transaction. */
635
635
CTxMemPool::setEntries m_ancestors;
636
- /* Changeset representing adding a transaction and removing its conflicts. */
637
- std::unique_ptr<CTxMemPool::ChangeSet> m_changeset;
636
+ /* Handle to the tx in the changeset */
638
637
CTxMemPool::ChangeSet::TxHandle m_tx_handle;
639
638
/* * Whether RBF-related data structures (m_conflicts, m_iters_conflicting, m_all_conflicting,
640
639
* m_replaced_transactions) include a sibling in addition to txns with conflicting inputs. */
@@ -691,7 +690,7 @@ class MemPoolAccept
691
690
// Try to add the transaction to the mempool, removing any conflicts first.
692
691
// Returns true if the transaction is in the mempool after any size
693
692
// limiting is performed, false otherwise.
694
- bool Finalize (const ATMPArgs& args, Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
693
+ bool FinalizeSubpackage (const ATMPArgs& args, std::vector< Workspace> & ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
695
694
696
695
// Submit all transactions to the mempool and call ConsensusScriptChecks to add to the script
697
696
// cache - should only be called after successful validation of all transactions in the package.
@@ -746,6 +745,8 @@ class MemPoolAccept
746
745
CTxMemPool::setEntries m_all_conflicts;
747
746
/* * Mempool transactions that were replaced. */
748
747
std::list<CTransactionRef> m_replaced_transactions;
748
+ /* Changeset representing adding transactions and removing their conflicts. */
749
+ std::unique_ptr<CTxMemPool::ChangeSet> m_changeset;
749
750
750
751
/* * Total modified fees of mempool transactions being replaced. */
751
752
CAmount m_conflicting_fees{0 };
@@ -908,8 +909,10 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
908
909
// Set entry_sequence to 0 when bypass_limits is used; this allows txs from a block
909
910
// reorg to be marked earlier than any child txs that were already in the mempool.
910
911
const uint64_t entry_sequence = bypass_limits ? 0 : m_pool.GetSequence ();
911
- ws.m_changeset = m_pool.GetChangeSet ();
912
- ws.m_tx_handle = ws.m_changeset ->StageAddition (ptx, ws.m_base_fees , nAcceptTime, m_active_chainstate.m_chain .Height (), entry_sequence, fSpendsCoinbase , nSigOpsCost, lock_points.value ());
912
+ if (!m_subpackage.m_changeset ) {
913
+ m_subpackage.m_changeset = m_pool.GetChangeSet ();
914
+ }
915
+ ws.m_tx_handle = m_subpackage.m_changeset ->StageAddition (ptx, ws.m_base_fees , nAcceptTime, m_active_chainstate.m_chain .Height (), entry_sequence, fSpendsCoinbase , nSigOpsCost, lock_points.value ());
913
916
914
917
ws.m_vsize = ws.m_tx_handle ->GetTxSize ();
915
918
@@ -983,7 +986,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
983
986
maybe_rbf_limits.descendant_size_vbytes += conflict->GetSizeWithDescendants ();
984
987
}
985
988
986
- if (auto ancestors{ws .m_changeset ->CalculateMemPoolAncestors (ws.m_tx_handle , maybe_rbf_limits)}) {
989
+ if (auto ancestors{m_subpackage .m_changeset ->CalculateMemPoolAncestors (ws.m_tx_handle , maybe_rbf_limits)}) {
987
990
ws.m_ancestors = std::move (*ancestors);
988
991
} else {
989
992
// If CalculateMemPoolAncestors fails second time, we want the original error string.
@@ -1015,7 +1018,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
1015
1018
if (ws.m_vsize > EXTRA_DESCENDANT_TX_SIZE_LIMIT || ws.m_ptx ->version == TRUC_VERSION) {
1016
1019
return state.Invalid (TxValidationResult::TX_MEMPOOL_POLICY, " too-long-mempool-chain" , error_message);
1017
1020
}
1018
- if (auto ancestors_retry{ws .m_changeset ->CalculateMemPoolAncestors (ws.m_tx_handle , cpfp_carve_out_limits)}) {
1021
+ if (auto ancestors_retry{m_subpackage .m_changeset ->CalculateMemPoolAncestors (ws.m_tx_handle , cpfp_carve_out_limits)}) {
1019
1022
ws.m_ancestors = std::move (*ancestors_retry);
1020
1023
} else {
1021
1024
return state.Invalid (TxValidationResult::TX_MEMPOOL_POLICY, " too-long-mempool-chain" , error_message);
@@ -1117,7 +1120,7 @@ bool MemPoolAccept::ReplacementChecks(Workspace& ws)
1117
1120
1118
1121
// Add all the to-be-removed transactions to the changeset.
1119
1122
for (auto it : m_subpackage.m_all_conflicts ) {
1120
- ws .m_changeset ->StageRemoval (it);
1123
+ m_subpackage .m_changeset ->StageRemoval (it);
1121
1124
}
1122
1125
return true ;
1123
1126
}
@@ -1180,7 +1183,7 @@ bool MemPoolAccept::PackageMempoolChecks(const std::vector<CTransactionRef>& txn
1180
1183
1181
1184
1182
1185
for (CTxMemPool::txiter it : m_subpackage.m_all_conflicts ) {
1183
- parent_ws .m_changeset ->StageRemoval (it);
1186
+ m_subpackage .m_changeset ->StageRemoval (it);
1184
1187
m_subpackage.m_conflicting_fees += it->GetModifiedFee ();
1185
1188
m_subpackage.m_conflicting_size += it->GetTxSize ();
1186
1189
}
@@ -1282,13 +1285,13 @@ bool MemPoolAccept::ConsensusScriptChecks(const ATMPArgs& args, Workspace& ws)
1282
1285
return true ;
1283
1286
}
1284
1287
1285
- bool MemPoolAccept::Finalize (const ATMPArgs& args, Workspace& ws )
1288
+ bool MemPoolAccept::FinalizeSubpackage (const ATMPArgs& args, std::vector< Workspace>& workspaces )
1286
1289
{
1287
1290
AssertLockHeld (cs_main);
1288
1291
AssertLockHeld (m_pool.cs );
1289
- const CTransaction& tx = *ws .m_ptx ;
1290
- const uint256& hash = ws .m_hash ;
1291
- TxValidationState& state = ws .m_state ;
1292
+ const CTransaction& tx = *workspaces. front () .m_ptx ;
1293
+ const uint256& hash = workspaces. front () .m_hash ;
1294
+ TxValidationState& state = workspaces. front () .m_state ;
1292
1295
const bool bypass_limits = args.m_bypass_limits ;
1293
1296
1294
1297
if (!m_subpackage.m_all_conflicts .empty ()) Assume (args.m_allow_replacement );
@@ -1302,20 +1305,21 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
1302
1305
it->GetTxSize (),
1303
1306
hash.ToString (),
1304
1307
tx.GetWitnessHash ().ToString (),
1305
- ws .m_tx_handle ->GetFee (),
1306
- ws .m_tx_handle ->GetTxSize ());
1308
+ workspaces[ 0 ] .m_tx_handle ->GetFee (),
1309
+ workspaces[ 0 ] .m_tx_handle ->GetTxSize ());
1307
1310
TRACEPOINT (mempool, replaced,
1308
1311
it->GetTx ().GetHash ().data (),
1309
1312
it->GetTxSize (),
1310
1313
it->GetFee (),
1311
1314
std::chrono::duration_cast<std::chrono::duration<std::uint64_t >>(it->GetTime ()).count (),
1312
1315
hash.data (),
1313
- ws .m_tx_handle ->GetTxSize (),
1314
- ws .m_tx_handle ->GetFee ()
1316
+ workspaces[ 0 ] .m_tx_handle ->GetTxSize (),
1317
+ workspaces[ 0 ] .m_tx_handle ->GetFee ()
1315
1318
);
1316
1319
m_subpackage.m_replaced_transactions .push_back (it->GetSharedTx ());
1317
1320
}
1318
- ws.m_changeset ->Apply ();
1321
+ m_subpackage.m_changeset ->Apply ();
1322
+ m_subpackage.m_changeset .reset ();
1319
1323
// Don't attempt to process the same conflicts repeatedly during subpackage evaluation:
1320
1324
// they no longer exist on subsequent calls to Finalize() post-Apply()
1321
1325
m_subpackage.m_all_conflicts .clear ();
@@ -1345,6 +1349,7 @@ bool MemPoolAccept::SubmitPackage(const ATMPArgs& args, std::vector<Workspace>&
1345
1349
return !m_pool.exists (GenTxid::Txid (ws.m_ptx ->GetHash ())); }));
1346
1350
1347
1351
bool all_submitted = true ;
1352
+ FinalizeSubpackage (args, workspaces);
1348
1353
// ConsensusScriptChecks adds to the script cache and is therefore consensus-critical;
1349
1354
// CheckInputsFromMempoolAndCache asserts that transactions only spend coins available from the
1350
1355
// mempool or UTXO set. Submit each transaction to the mempool immediately after calling
@@ -1358,36 +1363,19 @@ bool MemPoolAccept::SubmitPackage(const ATMPArgs& args, std::vector<Workspace>&
1358
1363
package_state.Invalid (PackageValidationResult::PCKG_MEMPOOL_ERROR,
1359
1364
strprintf (" BUG! PolicyScriptChecks succeeded but ConsensusScriptChecks failed: %s" ,
1360
1365
ws.m_ptx ->GetHash ().ToString ()));
1361
- }
1362
-
1363
- // Re-calculate mempool ancestors to call addUnchecked(). They may have changed since the
1364
- // last calculation done in PreChecks, since package ancestors have already been submitted.
1365
- {
1366
- auto ancestors{ws.m_changeset ->CalculateMemPoolAncestors (ws.m_tx_handle , m_pool.m_opts .limits )};
1367
- if (!ancestors) {
1368
- results.emplace (ws.m_ptx ->GetWitnessHash (), MempoolAcceptResult::Failure (ws.m_state ));
1369
- // Since PreChecks() and PackageMempoolChecks() both enforce limits, this should never fail.
1370
- Assume (false );
1371
- all_submitted = false ;
1372
- package_state.Invalid (PackageValidationResult::PCKG_MEMPOOL_ERROR,
1373
- strprintf (" BUG! Mempool ancestors or descendants were underestimated: %s" ,
1374
- ws.m_ptx ->GetHash ().ToString ()));
1375
- }
1376
- ws.m_ancestors = std::move (ancestors).value_or (ws.m_ancestors );
1377
- }
1378
- // If we call LimitMempoolSize() for each individual Finalize(), the mempool will not take
1379
- // the transaction's descendant feerate into account because it hasn't seen them yet. Also,
1380
- // we risk evicting a transaction that a subsequent package transaction depends on. Instead,
1381
- // allow the mempool to temporarily bypass limits, the maximum package size) while
1382
- // submitting transactions individually and then trim at the very end.
1383
- if (!Finalize (args, ws)) {
1384
- results.emplace (ws.m_ptx ->GetWitnessHash (), MempoolAcceptResult::Failure (ws.m_state ));
1385
- // Since LimitMempoolSize() won't be called, this should never fail.
1386
- Assume (false );
1387
- all_submitted = false ;
1388
- package_state.Invalid (PackageValidationResult::PCKG_MEMPOOL_ERROR,
1389
- strprintf (" BUG! Adding to mempool failed: %s" , ws.m_ptx ->GetHash ().ToString ()));
1390
- }
1366
+ // Remove the transaction from the mempool.
1367
+ if (!m_subpackage.m_changeset ) m_subpackage.m_changeset = m_pool.GetChangeSet ();
1368
+ m_subpackage.m_changeset ->StageRemoval (m_pool.GetIter (ws.m_ptx ->GetHash ()).value ());
1369
+ }
1370
+ }
1371
+ if (!all_submitted) {
1372
+ Assume (m_subpackage.m_changeset );
1373
+ // This code should be unreachable; it's here as belt-and-suspenders
1374
+ // to try to ensure we have no consensus-invalid transactions in the
1375
+ // mempool.
1376
+ m_subpackage.m_changeset ->Apply ();
1377
+ m_subpackage.m_changeset .reset ();
1378
+ return false ;
1391
1379
}
1392
1380
1393
1381
std::vector<Wtxid> all_package_wtxids;
@@ -1430,7 +1418,8 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef
1430
1418
AssertLockHeld (cs_main);
1431
1419
LOCK (m_pool.cs ); // mempool "read lock" (held through m_pool.m_opts.signals->TransactionAddedToMempool())
1432
1420
1433
- Workspace ws (ptx);
1421
+ std::vector<Workspace> workspaces{Workspace (ptx)};
1422
+ Workspace &ws = workspaces.front ();
1434
1423
const std::vector<Wtxid> single_wtxid{ws.m_ptx ->GetWitnessHash ()};
1435
1424
1436
1425
if (!PreChecks (args, ws)) {
@@ -1478,7 +1467,7 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef
1478
1467
ws.m_base_fees , effective_feerate, single_wtxid);
1479
1468
}
1480
1469
1481
- if (!Finalize (args, ws )) {
1470
+ if (!FinalizeSubpackage (args, workspaces )) {
1482
1471
// The only possible failure reason is fee-related (mempool full).
1483
1472
// Failed for fee reasons. Provide the effective feerate and which txns were included.
1484
1473
Assume (ws.m_state .GetResult () == TxValidationResult::TX_RECONSIDERABLE);
0 commit comments