Skip to content

Commit 20d8936

Browse files
glozowinstagibbs
authored andcommitted
[refactor] make some members MemPoolAccept-wide
No change in behavior. For single transaction acceptance, this is a simple refactor: Workspace::m_all_conflicting Workspace::m_conflicting_fees Workspace::m_conflicting_size Workspace::m_replaced_transactions are now grouped under a new SubPackageState struct that is a member of MemPoolAccept. And local variables m_total_vsize and m_total_modified_fees are now SubpackageState members so they can be accessed from PackageMempoolChecks. We want these to be package-wide variables because - Transactions could conflict with the same tx (just not the same prevout), or their conflicts could share descendants. - We want to compare conflicts with the package fee rather than individual transaction fee. We reset these MemPoolAccept-wide fields for each subpackage evaluation to not cause state leaking, similar to temporary coins.
1 parent cbbfe71 commit 20d8936

File tree

1 file changed

+64
-39
lines changed

1 file changed

+64
-39
lines changed

src/validation.cpp

Lines changed: 64 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -621,18 +621,11 @@ class MemPoolAccept
621621
/** Iterators to mempool entries that this transaction directly conflicts with or may
622622
* replace via sibling eviction. */
623623
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;
627624
/** All mempool ancestors of this transaction. */
628625
CTxMemPool::setEntries m_ancestors;
629626
/** Mempool entry constructed for this transaction. Constructed in PreChecks() but not
630627
* inserted into the mempool until Finalize(). */
631628
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;
636629
/** Whether RBF-related data structures (m_conflicts, m_iters_conflicting, m_all_conflicting,
637630
* m_replaced_transactions) include a sibling in addition to txns with conflicting inputs. */
638631
bool m_sibling_eviction{false};
@@ -644,10 +637,6 @@ class MemPoolAccept
644637
CAmount m_base_fees;
645638
/** Base fees + any fee delta set by the user with prioritisetransaction. */
646639
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};
651640

652641
/** If we're doing package validation (i.e. m_package_feerates=true), the "effective"
653642
* package feerate of this transaction is the total fees divided by the total size of
@@ -725,9 +714,39 @@ class MemPoolAccept
725714

726715
Chainstate& m_active_chainstate;
727716

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+
}
731750
};
732751

733752
bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
@@ -1036,7 +1055,8 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
10361055
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-spends-conflicting-tx", *err_string);
10371056
}
10381057

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();
10401060
return true;
10411061
}
10421062

@@ -1068,24 +1088,25 @@ bool MemPoolAccept::ReplacementChecks(Workspace& ws)
10681088
}
10691089

10701090
// 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)}) {
10721092
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY,
10731093
strprintf("too many potential replacements%s", ws.m_sibling_eviction ? " (including sibling eviction)" : ""), *err_string);
10741094
}
10751095
// 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)}) {
10771097
// Sibling eviction is only done for v3 transactions, which cannot have multiple ancestors.
10781098
Assume(!ws.m_sibling_eviction);
10791099
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY,
10801100
strprintf("replacement-adds-unconfirmed%s", ws.m_sibling_eviction ? " (including sibling eviction)" : ""), *err_string);
10811101
}
1102+
10821103
// Check if it's economically rational to mine this transaction rather than the ones it
10831104
// 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();
10871108
}
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,
10891110
m_pool.m_opts.incremental_relay_feerate, hash)}) {
10901111
// Even though this is a fee-related failure, this result is TX_MEMPOOL_POLICY, not
10911112
// TX_RECONSIDERABLE, because it cannot be bypassed using package validation.
@@ -1184,19 +1205,18 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
11841205
const uint256& hash = ws.m_hash;
11851206
TxValidationState& state = ws.m_state;
11861207
const bool bypass_limits = args.m_bypass_limits;
1187-
11881208
std::unique_ptr<CTxMemPoolEntry>& entry = ws.m_entry;
11891209

11901210
// 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)
11921212
{
11931213
LogPrint(BCLog::MEMPOOL, "replacing tx %s (wtxid=%s) with %s (wtxid=%s) for %s additional fees, %d delta bytes\n",
11941214
it->GetTx().GetHash().ToString(),
11951215
it->GetTx().GetWitnessHash().ToString(),
11961216
hash.ToString(),
11971217
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);
12001220
TRACE7(mempool, replaced,
12011221
it->GetTx().GetHash().data(),
12021222
it->GetTxSize(),
@@ -1206,9 +1226,12 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
12061226
entry->GetTxSize(),
12071227
entry->GetFee()
12081228
);
1209-
ws.m_replaced_transactions.push_back(it->GetSharedTx());
1229+
m_subpackage.m_replaced_transactions.push_back(it->GetSharedTx());
12101230
}
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();
12121235
// Store transaction in memory
12131236
m_pool.addUnchecked(*entry, ws.m_ancestors);
12141237

@@ -1294,7 +1317,7 @@ bool MemPoolAccept::SubmitPackage(const ATMPArgs& args, std::vector<Workspace>&
12941317
const auto effective_feerate_wtxids = args.m_package_feerates ? all_package_wtxids :
12951318
std::vector<Wtxid>{ws.m_ptx->GetWitnessHash()};
12961319
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,
12981321
ws.m_base_fees, effective_feerate, effective_feerate_wtxids));
12991322
if (!m_pool.m_opts.signals) continue;
13001323
const CTransaction& tx = *ws.m_ptx;
@@ -1330,7 +1353,7 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef
13301353
return MempoolAcceptResult::Failure(ws.m_state);
13311354
}
13321355

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);
13341357

13351358
// Perform the inexpensive checks first and avoid hashing and signature verification unless
13361359
// those checks pass, to mitigate CPU exhaustion denial-of-service attacks.
@@ -1341,7 +1364,7 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef
13411364
const CFeeRate effective_feerate{ws.m_modified_fees, static_cast<uint32_t>(ws.m_vsize)};
13421365
// Tx was accepted, but not added
13431366
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,
13451368
ws.m_base_fees, effective_feerate, single_wtxid);
13461369
}
13471370

@@ -1362,7 +1385,7 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef
13621385
m_pool.m_opts.signals->TransactionAddedToMempool(tx_info, m_pool.GetAndIncrementSequence());
13631386
}
13641387

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,
13661389
effective_feerate, single_wtxid);
13671390
}
13681391

@@ -1407,6 +1430,7 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
14071430
// replacements, we don't need to track the coins spent. Note that this logic will need to be
14081431
// updated if package replace-by-fee is allowed in the future.
14091432
assert(!args.m_allow_replacement);
1433+
assert(!m_subpackage.m_rbf);
14101434
m_viewmempool.PackageAddTransaction(ws.m_ptx);
14111435
}
14121436

@@ -1428,26 +1452,26 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
14281452
// a child that is below mempool minimum feerate. To avoid these behaviors, callers of
14291453
// AcceptMultipleTransactions need to restrict txns topology (e.g. to ancestor sets) and check
14301454
// 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},
14321456
[](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},
14341458
[](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);
14361460
std::vector<Wtxid> all_package_wtxids;
14371461
all_package_wtxids.reserve(workspaces.size());
14381462
std::transform(workspaces.cbegin(), workspaces.cend(), std::back_inserter(all_package_wtxids),
14391463
[](const auto& ws) { return ws.m_ptx->GetWitnessHash(); });
14401464
TxValidationState placeholder_state;
14411465
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)) {
14431467
package_state.Invalid(PackageValidationResult::PCKG_TX, "transaction failed");
14441468
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)}});
14461470
}
14471471

14481472
// Apply package mempool ancestor/descendant limits. Skip if there is only one transaction,
14491473
// 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)) {
14511475
return PackageMempoolAcceptResult(package_state, std::move(results));
14521476
}
14531477

@@ -1465,7 +1489,7 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
14651489
const auto effective_feerate_wtxids = args.m_package_feerates ? all_package_wtxids :
14661490
std::vector<Wtxid>{ws.m_ptx->GetWitnessHash()};
14671491
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),
14691493
ws.m_vsize, ws.m_base_fees, effective_feerate,
14701494
effective_feerate_wtxids));
14711495
}
@@ -1530,7 +1554,8 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptSubPackage(const std::vector<CTr
15301554

15311555
// Clean up m_view and m_viewmempool so that other subpackage evaluations don't have access to
15321556
// 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();
15341559

15351560
return result;
15361561
}

0 commit comments

Comments
 (0)