Skip to content

Commit 802214c

Browse files
committed
Introduce mempool changesets
Introduce the CTxMemPool::ChangeSet, a wrapper for creating (potential) new mempool entries and removing conflicts.
1 parent 87d92fa commit 802214c

File tree

3 files changed

+80
-21
lines changed

3 files changed

+80
-21
lines changed

src/txmempool.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1369,3 +1369,23 @@ util::Result<std::pair<std::vector<FeeFrac>, std::vector<FeeFrac>>> CTxMemPool::
13691369
std::sort(new_chunks.begin(), new_chunks.end(), std::greater());
13701370
return std::make_pair(old_chunks, new_chunks);
13711371
}
1372+
1373+
CTxMemPool::ChangeSet::TxHandle CTxMemPool::ChangeSet::StageAddition(const CTransactionRef& tx, const CAmount fee, int64_t time, unsigned int entry_height, uint64_t entry_sequence, bool spends_coinbase, int64_t sigops_cost, LockPoints lp)
1374+
{
1375+
auto newit = m_to_add.emplace(tx, fee, time, entry_height, entry_sequence, spends_coinbase, sigops_cost, lp).first;
1376+
m_entry_vec.push_back(newit);
1377+
return newit;
1378+
}
1379+
1380+
void CTxMemPool::ChangeSet::Apply()
1381+
{
1382+
LOCK(m_pool->cs);
1383+
m_pool->RemoveStaged(m_to_remove, false, MemPoolRemovalReason::REPLACED);
1384+
for (size_t i=0; i<m_entry_vec.size(); ++i) {
1385+
auto tx_entry = m_entry_vec[i];
1386+
m_pool->addUnchecked(*tx_entry);
1387+
}
1388+
m_to_add.clear();
1389+
m_to_remove.clear();
1390+
m_entry_vec.clear();
1391+
}

src/txmempool.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,37 @@ class CTxMemPool
816816
assert(m_epoch.guarded()); // verify guard even when it==nullopt
817817
return !it || visited(*it);
818818
}
819+
820+
class ChangeSet {
821+
public:
822+
explicit ChangeSet(CTxMemPool* pool) : m_pool(pool) {}
823+
~ChangeSet() = default;
824+
825+
ChangeSet(const ChangeSet&) = delete;
826+
ChangeSet& operator=(const ChangeSet&) = delete;
827+
828+
using TxHandle = CTxMemPool::txiter;
829+
830+
TxHandle StageAddition(const CTransactionRef& tx, const CAmount fee, int64_t time, unsigned int entry_height, uint64_t entry_sequence, bool spends_coinbase, int64_t sigops_cost, LockPoints lp);
831+
void StageRemoval(CTxMemPool::txiter it) { m_to_remove.insert(it); }
832+
833+
util::Result<CTxMemPool::setEntries> CalculateMemPoolAncestors(TxHandle tx, const Limits& limits)
834+
{
835+
LOCK(m_pool->cs);
836+
auto ret{m_pool->CalculateMemPoolAncestors(*tx, limits)};
837+
return ret;
838+
}
839+
840+
void Apply() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
841+
842+
private:
843+
CTxMemPool* m_pool;
844+
CTxMemPool::indexed_transaction_set m_to_add;
845+
std::vector<CTxMemPool::txiter> m_entry_vec; // track the added transactions' insertion order
846+
CTxMemPool::setEntries m_to_remove;
847+
};
848+
849+
std::unique_ptr<ChangeSet> GetChangeSet() EXCLUSIVE_LOCKS_REQUIRED(cs) { return std::make_unique<ChangeSet>(this); }
819850
};
820851

821852
/**

src/validation.cpp

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -633,9 +633,9 @@ class MemPoolAccept
633633
CTxMemPool::setEntries m_iters_conflicting;
634634
/** All mempool ancestors of this transaction. */
635635
CTxMemPool::setEntries m_ancestors;
636-
/** Mempool entry constructed for this transaction. Constructed in PreChecks() but not
637-
* inserted into the mempool until Finalize(). */
638-
std::unique_ptr<CTxMemPoolEntry> m_entry;
636+
/* Changeset representing adding a transaction and removing its conflicts. */
637+
std::unique_ptr<CTxMemPool::ChangeSet> m_changeset;
638+
CTxMemPool::ChangeSet::TxHandle m_tx_handle;
639639
/** Whether RBF-related data structures (m_conflicts, m_iters_conflicting, m_all_conflicting,
640640
* m_replaced_transactions) include a sibling in addition to txns with conflicting inputs. */
641641
bool m_sibling_eviction{false};
@@ -780,7 +780,6 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
780780

781781
// Alias what we need out of ws
782782
TxValidationState& state = ws.m_state;
783-
std::unique_ptr<CTxMemPoolEntry>& entry = ws.m_entry;
784783

785784
if (!CheckTransaction(tx, state)) {
786785
return false; // state filled in by CheckTransaction
@@ -909,9 +908,10 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
909908
// Set entry_sequence to 0 when bypass_limits is used; this allows txs from a block
910909
// reorg to be marked earlier than any child txs that were already in the mempool.
911910
const uint64_t entry_sequence = bypass_limits ? 0 : m_pool.GetSequence();
912-
entry.reset(new CTxMemPoolEntry(ptx, ws.m_base_fees, nAcceptTime, m_active_chainstate.m_chain.Height(), entry_sequence,
913-
fSpendsCoinbase, nSigOpsCost, lock_points.value()));
914-
ws.m_vsize = entry->GetTxSize();
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());
913+
914+
ws.m_vsize = ws.m_tx_handle->GetTxSize();
915915

916916
// Enforces 0-fee for dust transactions, no incentive to be mined alone
917917
if (m_pool.m_opts.require_standard) {
@@ -983,7 +983,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
983983
maybe_rbf_limits.descendant_size_vbytes += conflict->GetSizeWithDescendants();
984984
}
985985

986-
if (auto ancestors{m_pool.CalculateMemPoolAncestors(*entry, maybe_rbf_limits)}) {
986+
if (auto ancestors{ws.m_changeset->CalculateMemPoolAncestors(ws.m_tx_handle, maybe_rbf_limits)}) {
987987
ws.m_ancestors = std::move(*ancestors);
988988
} else {
989989
// If CalculateMemPoolAncestors fails second time, we want the original error string.
@@ -1015,7 +1015,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
10151015
if (ws.m_vsize > EXTRA_DESCENDANT_TX_SIZE_LIMIT || ws.m_ptx->version == TRUC_VERSION) {
10161016
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "too-long-mempool-chain", error_message);
10171017
}
1018-
if (auto ancestors_retry{m_pool.CalculateMemPoolAncestors(*entry, cpfp_carve_out_limits)}) {
1018+
if (auto ancestors_retry{ws.m_changeset->CalculateMemPoolAncestors(ws.m_tx_handle, cpfp_carve_out_limits)}) {
10191019
ws.m_ancestors = std::move(*ancestors_retry);
10201020
} else {
10211021
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "too-long-mempool-chain", error_message);
@@ -1114,6 +1114,11 @@ bool MemPoolAccept::ReplacementChecks(Workspace& ws)
11141114
return state.Invalid(TxValidationResult::TX_RECONSIDERABLE,
11151115
strprintf("insufficient fee%s", ws.m_sibling_eviction ? " (including sibling eviction)" : ""), *err_string);
11161116
}
1117+
1118+
// Add all the to-be-removed transactions to the changeset.
1119+
for (auto it : m_subpackage.m_all_conflicts) {
1120+
ws.m_changeset->StageRemoval(it);
1121+
}
11171122
return true;
11181123
}
11191124

@@ -1173,7 +1178,9 @@ bool MemPoolAccept::PackageMempoolChecks(const std::vector<CTransactionRef>& txn
11731178
"package RBF failed: too many potential replacements", *err_string);
11741179
}
11751180

1181+
11761182
for (CTxMemPool::txiter it : m_subpackage.m_all_conflicts) {
1183+
parent_ws.m_changeset->StageRemoval(it);
11771184
m_subpackage.m_conflicting_fees += it->GetModifiedFee();
11781185
m_subpackage.m_conflicting_size += it->GetTxSize();
11791186
}
@@ -1283,7 +1290,6 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
12831290
const uint256& hash = ws.m_hash;
12841291
TxValidationState& state = ws.m_state;
12851292
const bool bypass_limits = args.m_bypass_limits;
1286-
std::unique_ptr<CTxMemPoolEntry>& entry = ws.m_entry;
12871293

12881294
if (!m_subpackage.m_all_conflicts.empty()) Assume(args.m_allow_replacement);
12891295
// Remove conflicting transactions from the mempool
@@ -1296,25 +1302,23 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
12961302
it->GetTxSize(),
12971303
hash.ToString(),
12981304
tx.GetWitnessHash().ToString(),
1299-
entry->GetFee(),
1300-
entry->GetTxSize());
1305+
ws.m_tx_handle->GetFee(),
1306+
ws.m_tx_handle->GetTxSize());
13011307
TRACEPOINT(mempool, replaced,
13021308
it->GetTx().GetHash().data(),
13031309
it->GetTxSize(),
13041310
it->GetFee(),
13051311
std::chrono::duration_cast<std::chrono::duration<std::uint64_t>>(it->GetTime()).count(),
13061312
hash.data(),
1307-
entry->GetTxSize(),
1308-
entry->GetFee()
1313+
ws.m_tx_handle->GetTxSize(),
1314+
ws.m_tx_handle->GetFee()
13091315
);
13101316
m_subpackage.m_replaced_transactions.push_back(it->GetSharedTx());
13111317
}
1312-
m_pool.RemoveStaged(m_subpackage.m_all_conflicts, false, MemPoolRemovalReason::REPLACED);
1318+
ws.m_changeset->Apply();
13131319
// Don't attempt to process the same conflicts repeatedly during subpackage evaluation:
1314-
// they no longer exist on subsequent calls to Finalize() post-RemoveStaged
1320+
// they no longer exist on subsequent calls to Finalize() post-Apply()
13151321
m_subpackage.m_all_conflicts.clear();
1316-
// Store transaction in memory
1317-
m_pool.addUnchecked(*entry, ws.m_ancestors);
13181322

13191323
// trim mempool and check if tx was trimmed
13201324
// If we are validating a package, don't trim here because we could evict a previous transaction
@@ -1359,7 +1363,7 @@ bool MemPoolAccept::SubmitPackage(const ATMPArgs& args, std::vector<Workspace>&
13591363
// Re-calculate mempool ancestors to call addUnchecked(). They may have changed since the
13601364
// last calculation done in PreChecks, since package ancestors have already been submitted.
13611365
{
1362-
auto ancestors{m_pool.CalculateMemPoolAncestors(*ws.m_entry, m_pool.m_opts.limits)};
1366+
auto ancestors{ws.m_changeset->CalculateMemPoolAncestors(ws.m_tx_handle, m_pool.m_opts.limits)};
13631367
if(!ancestors) {
13641368
results.emplace(ws.m_ptx->GetWitnessHash(), MempoolAcceptResult::Failure(ws.m_state));
13651369
// Since PreChecks() and PackageMempoolChecks() both enforce limits, this should never fail.
@@ -1400,6 +1404,8 @@ bool MemPoolAccept::SubmitPackage(const ATMPArgs& args, std::vector<Workspace>&
14001404

14011405
// Add successful results. The returned results may change later if LimitMempoolSize() evicts them.
14021406
for (Workspace& ws : workspaces) {
1407+
auto iter = m_pool.GetIter(ws.m_ptx->GetHash());
1408+
Assume(iter.has_value());
14031409
const auto effective_feerate = args.m_package_feerates ? ws.m_package_feerate :
14041410
CFeeRate{ws.m_modified_fees, static_cast<uint32_t>(ws.m_vsize)};
14051411
const auto effective_feerate_wtxids = args.m_package_feerates ? all_package_wtxids :
@@ -1410,7 +1416,7 @@ bool MemPoolAccept::SubmitPackage(const ATMPArgs& args, std::vector<Workspace>&
14101416
if (!m_pool.m_opts.signals) continue;
14111417
const CTransaction& tx = *ws.m_ptx;
14121418
const auto tx_info = NewMempoolTransactionInfo(ws.m_ptx, ws.m_base_fees,
1413-
ws.m_vsize, ws.m_entry->GetHeight(),
1419+
ws.m_vsize, (*iter)->GetHeight(),
14141420
args.m_bypass_limits, args.m_package_submission,
14151421
IsCurrentForFeeEstimation(m_active_chainstate),
14161422
m_pool.HasNoInputsOf(tx));
@@ -1481,8 +1487,10 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef
14811487

14821488
if (m_pool.m_opts.signals) {
14831489
const CTransaction& tx = *ws.m_ptx;
1490+
auto iter = m_pool.GetIter(tx.GetHash());
1491+
Assume(iter.has_value());
14841492
const auto tx_info = NewMempoolTransactionInfo(ws.m_ptx, ws.m_base_fees,
1485-
ws.m_vsize, ws.m_entry->GetHeight(),
1493+
ws.m_vsize, (*iter)->GetHeight(),
14861494
args.m_bypass_limits, args.m_package_submission,
14871495
IsCurrentForFeeEstimation(m_active_chainstate),
14881496
m_pool.HasNoInputsOf(tx));

0 commit comments

Comments
 (0)