Skip to content

Commit 56484f0

Browse files
glozowmurchandamus
andcommitted
[mempool] find connected mempool entries with GatherClusters(…)
We limit GatherClusters’s result to a maximum of 500 transactions as clusters can be made arbitrarily large by third parties. Co-authored-by: Murch <murch@murch.one>
1 parent 68e484a commit 56484f0

File tree

2 files changed

+55
-2
lines changed

2 files changed

+55
-2
lines changed

src/txmempool.cpp

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <validationinterface.h>
2525

2626
#include <cmath>
27+
#include <numeric>
2728
#include <optional>
2829
#include <string_view>
2930
#include <utility>
@@ -898,6 +899,19 @@ CTxMemPool::setEntries CTxMemPool::GetIterSet(const std::set<uint256>& hashes) c
898899
return ret;
899900
}
900901

902+
std::vector<CTxMemPool::txiter> CTxMemPool::GetIterVec(const std::vector<uint256>& txids) const
903+
{
904+
AssertLockHeld(cs);
905+
std::vector<txiter> ret;
906+
ret.reserve(txids.size());
907+
for (const auto& txid : txids) {
908+
const auto it{GetIter(txid)};
909+
if (!it) return {};
910+
ret.push_back(*it);
911+
}
912+
return ret;
913+
}
914+
901915
bool CTxMemPool::HasNoInputsOf(const CTransaction &tx) const
902916
{
903917
for (unsigned int i = 0; i < tx.vin.size(); i++)
@@ -1127,7 +1141,6 @@ void CTxMemPool::SetLoadTried(bool load_tried)
11271141
m_load_tried = load_tried;
11281142
}
11291143

1130-
11311144
std::string RemovalReasonToString(const MemPoolRemovalReason& r) noexcept
11321145
{
11331146
switch (r) {
@@ -1140,3 +1153,30 @@ std::string RemovalReasonToString(const MemPoolRemovalReason& r) noexcept
11401153
}
11411154
assert(false);
11421155
}
1156+
1157+
std::vector<CTxMemPool::txiter> CTxMemPool::GatherClusters(const std::vector<uint256>& txids) const
1158+
{
1159+
AssertLockHeld(cs);
1160+
std::vector<txiter> clustered_txs{GetIterVec(txids)};
1161+
// Use epoch: visiting an entry means we have added it to the clustered_txs vector. It does not
1162+
// necessarily mean the entry has been processed.
1163+
WITH_FRESH_EPOCH(m_epoch);
1164+
for (const auto& it : clustered_txs) {
1165+
visited(it);
1166+
}
1167+
// i = index of where the list of entries to process starts
1168+
for (size_t i{0}; i < clustered_txs.size(); ++i) {
1169+
// DoS protection: if there are 500 or more entries to process, just quit.
1170+
if (clustered_txs.size() > 500) return {};
1171+
const txiter& tx_iter = clustered_txs.at(i);
1172+
for (const auto& entries : {tx_iter->GetMemPoolParentsConst(), tx_iter->GetMemPoolChildrenConst()}) {
1173+
for (const CTxMemPoolEntry& entry : entries) {
1174+
const auto entry_it = mapTx.iterator_to(entry);
1175+
if (!visited(entry_it)) {
1176+
clustered_txs.push_back(entry_it);
1177+
}
1178+
}
1179+
}
1180+
}
1181+
return clustered_txs;
1182+
}

src/txmempool.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -522,9 +522,16 @@ class CTxMemPool
522522
/** Returns an iterator to the given hash, if found */
523523
std::optional<txiter> GetIter(const uint256& txid) const EXCLUSIVE_LOCKS_REQUIRED(cs);
524524

525-
/** Translate a set of hashes into a set of pool iterators to avoid repeated lookups */
525+
/** Translate a set of hashes into a set of pool iterators to avoid repeated lookups.
526+
* Does not require that all of the hashes correspond to actual transactions in the mempool,
527+
* only returns the ones that exist. */
526528
setEntries GetIterSet(const std::set<uint256>& hashes) const EXCLUSIVE_LOCKS_REQUIRED(cs);
527529

530+
/** Translate a list of hashes into a list of mempool iterators to avoid repeated lookups.
531+
* The nth element in txids becomes the nth element in the returned vector. If any of the txids
532+
* don't actually exist in the mempool, returns an empty vector. */
533+
std::vector<txiter> GetIterVec(const std::vector<uint256>& txids) const EXCLUSIVE_LOCKS_REQUIRED(cs);
534+
528535
/** Remove a set of transactions from the mempool.
529536
* If a transaction is in this set, then all in-mempool descendants must
530537
* also be in the set, unless this transaction is being removed for being
@@ -585,6 +592,12 @@ class CTxMemPool
585592
const Limits& limits,
586593
bool fSearchForParents = true) const EXCLUSIVE_LOCKS_REQUIRED(cs);
587594

595+
/** Collect the entire cluster of connected transactions for each transaction in txids.
596+
* All txids must correspond to transaction entries in the mempool, otherwise this returns an
597+
* empty vector. This call will also exit early and return an empty vector if it collects 500 or
598+
* more transactions as a DoS protection. */
599+
std::vector<txiter> GatherClusters(const std::vector<uint256>& txids) const EXCLUSIVE_LOCKS_REQUIRED(cs);
600+
588601
/** Calculate all in-mempool ancestors of a set of transactions not already in the mempool and
589602
* check ancestor and descendant limits. Heuristics are used to estimate the ancestor and
590603
* descendant count of all entries if the package were to be added to the mempool. The limits

0 commit comments

Comments
 (0)