Stop tracking parents/children outside of txgraph
This commit is contained in:
1 parent
88672e205b
commit
84de685cf7
3 files changed
+14
-110
No files matched your search
@@ -67,16 +67,11 @@ class CTxMemPoolEntry : public TxGraph::Ref
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef std::reference_wrapper<const CTxMemPoolEntry> CTxMemPoolEntryRef;
|
typedef std::reference_wrapper<const CTxMemPoolEntry> CTxMemPoolEntryRef;
|
||||||
// two aliases, should the types ever diverge
|
|
||||||
typedef std::set<CTxMemPoolEntryRef, CompareIteratorByHash> Parents;
|
|
||||||
typedef std::set<CTxMemPoolEntryRef, CompareIteratorByHash> Children;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CTxMemPoolEntry(const CTxMemPoolEntry&) = delete;
|
CTxMemPoolEntry(const CTxMemPoolEntry&) = delete;
|
||||||
|
|
||||||
const CTransactionRef tx;
|
const CTransactionRef tx;
|
||||||
mutable Parents m_parents;
|
|
||||||
mutable Children m_children;
|
|
||||||
const CAmount nFee; //!< Cached to avoid expensive parent-transaction lookups
|
const CAmount nFee; //!< Cached to avoid expensive parent-transaction lookups
|
||||||
const int32_t nTxWeight; //!< ... and avoid recomputing tx weight (also used for GetTxSize())
|
const int32_t nTxWeight; //!< ... and avoid recomputing tx weight (also used for GetTxSize())
|
||||||
const size_t nUsageSize; //!< ... and total memory usage
|
const size_t nUsageSize; //!< ... and total memory usage
|
||||||
@@ -142,11 +137,6 @@ public:
|
|||||||
|
|
||||||
bool GetSpendsCoinbase() const { return spendsCoinbase; }
|
bool GetSpendsCoinbase() const { return spendsCoinbase; }
|
||||||
|
|
||||||
const Parents& GetMemPoolParentsConst() const { return m_parents; }
|
|
||||||
const Children& GetMemPoolChildrenConst() const { return m_children; }
|
|
||||||
Parents& GetMemPoolParents() const { return m_parents; }
|
|
||||||
Children& GetMemPoolChildren() const { return m_children; }
|
|
||||||
|
|
||||||
mutable size_t idx_randomized; //!< Index in mempool's txns_randomized
|
mutable size_t idx_randomized; //!< Index in mempool's txns_randomized
|
||||||
mutable Epoch::Marker m_epoch_marker; //!< epoch when last touched, useful for graph algorithms
|
mutable Epoch::Marker m_epoch_marker; //!< epoch when last touched, useful for graph algorithms
|
||||||
};
|
};
|
||||||
|
|||||||
+14
-88
@@ -90,10 +90,6 @@ void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<Txid>& vHashesToU
|
|||||||
{
|
{
|
||||||
AssertLockHeld(cs);
|
AssertLockHeld(cs);
|
||||||
|
|
||||||
// Use a set for lookups into vHashesToUpdate (these entries are already
|
|
||||||
// accounted for in the state of their ancestors)
|
|
||||||
std::set<Txid> setAlreadyIncluded(vHashesToUpdate.begin(), vHashesToUpdate.end());
|
|
||||||
|
|
||||||
// Iterate in reverse, so that whenever we are looking at a transaction
|
// Iterate in reverse, so that whenever we are looking at a transaction
|
||||||
// we are sure that all in-mempool descendants have already been processed.
|
// we are sure that all in-mempool descendants have already been processed.
|
||||||
for (const Txid& hash : vHashesToUpdate | std::views::reverse) {
|
for (const Txid& hash : vHashesToUpdate | std::views::reverse) {
|
||||||
@@ -103,20 +99,10 @@ void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<Txid>& vHashesToU
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto iter = mapNextTx.lower_bound(COutPoint(hash, 0));
|
auto iter = mapNextTx.lower_bound(COutPoint(hash, 0));
|
||||||
// First calculate the children, and update CTxMemPoolEntry::m_children to
|
|
||||||
// include them, and update their CTxMemPoolEntry::m_parents to include this tx.
|
|
||||||
// we cache the in-mempool children to avoid duplicate updates
|
|
||||||
{
|
{
|
||||||
WITH_FRESH_EPOCH(m_epoch);
|
|
||||||
for (; iter != mapNextTx.end() && iter->first->hash == hash; ++iter) {
|
for (; iter != mapNextTx.end() && iter->first->hash == hash; ++iter) {
|
||||||
txiter childIter = iter->second;
|
txiter childIter = iter->second;
|
||||||
assert(childIter != mapTx.end());
|
assert(childIter != mapTx.end());
|
||||||
// We can skip updating entries we've encountered before or that
|
|
||||||
// are in the block (which are already accounted for).
|
|
||||||
if (!visited(childIter) && !setAlreadyIncluded.count(iter->second->GetTx().GetHash())) {
|
|
||||||
UpdateChild(it, childIter, true);
|
|
||||||
UpdateParent(childIter, it, true);
|
|
||||||
}
|
|
||||||
// Add dependencies that are discovered between transactions in the
|
// Add dependencies that are discovered between transactions in the
|
||||||
// block and transactions that were in the mempool to txgraph.
|
// block and transactions that were in the mempool to txgraph.
|
||||||
m_txgraph->AddDependency(/*parent=*/*it, /*child=*/*childIter);
|
m_txgraph->AddDependency(/*parent=*/*it, /*child=*/*childIter);
|
||||||
@@ -158,8 +144,6 @@ CTxMemPool::setEntries CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEnt
|
|||||||
const CTransaction &tx = entry.GetTx();
|
const CTransaction &tx = entry.GetTx();
|
||||||
|
|
||||||
// Get parents of this transaction that are in the mempool
|
// Get parents of this transaction that are in the mempool
|
||||||
// GetMemPoolParents() is only valid for entries in the mempool, so we
|
|
||||||
// iterate mapTx to find parents.
|
|
||||||
for (unsigned int i = 0; i < tx.vin.size(); i++) {
|
for (unsigned int i = 0; i < tx.vin.size(); i++) {
|
||||||
std::optional<txiter> piter = GetIter(tx.vin[i].prevout.hash);
|
std::optional<txiter> piter = GetIter(tx.vin[i].prevout.hash);
|
||||||
if (piter) {
|
if (piter) {
|
||||||
@@ -177,38 +161,6 @@ CTxMemPool::setEntries CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEnt
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CTxMemPool::UpdateAncestorsOf(bool add, txiter it)
|
|
||||||
{
|
|
||||||
const CTxMemPoolEntry::Parents& parents = it->GetMemPoolParentsConst();
|
|
||||||
// add or remove this tx as a child of each parent
|
|
||||||
for (const CTxMemPoolEntry& parent : parents) {
|
|
||||||
UpdateChild(mapTx.iterator_to(parent), it, add);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CTxMemPool::UpdateChildrenForRemoval(txiter it)
|
|
||||||
{
|
|
||||||
const CTxMemPoolEntry::Children& children = it->GetMemPoolChildrenConst();
|
|
||||||
for (const CTxMemPoolEntry& updateIt : children) {
|
|
||||||
UpdateParent(mapTx.iterator_to(updateIt), it, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CTxMemPool::UpdateForRemoveFromMempool(const setEntries &entriesToRemove, bool updateDescendants)
|
|
||||||
{
|
|
||||||
for (txiter removeIt : entriesToRemove) {
|
|
||||||
// Note that UpdateAncestorsOf severs the child links that point to
|
|
||||||
// removeIt in the entries for the parents of removeIt.
|
|
||||||
UpdateAncestorsOf(false, removeIt);
|
|
||||||
}
|
|
||||||
// After updating all the ancestor sizes, we can now sever the link between each
|
|
||||||
// transaction being removed and any mempool children (ie, update CTxMemPoolEntry::m_parents
|
|
||||||
// for each direct child of a transaction being removed).
|
|
||||||
for (txiter removeIt : entriesToRemove) {
|
|
||||||
UpdateChildrenForRemoval(removeIt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static CTxMemPool::Options&& Flatten(CTxMemPool::Options&& opts, bilingual_str& error)
|
static CTxMemPool::Options&& Flatten(CTxMemPool::Options&& opts, bilingual_str& error)
|
||||||
{
|
{
|
||||||
opts.check_ratio = std::clamp<int>(opts.check_ratio, 0, 1'000'000);
|
opts.check_ratio = std::clamp<int>(opts.check_ratio, 0, 1'000'000);
|
||||||
@@ -271,10 +223,8 @@ void CTxMemPool::addNewTransaction(CTxMemPool::txiter newit)
|
|||||||
cachedInnerUsage += entry.DynamicMemoryUsage();
|
cachedInnerUsage += entry.DynamicMemoryUsage();
|
||||||
|
|
||||||
const CTransaction& tx = newit->GetTx();
|
const CTransaction& tx = newit->GetTx();
|
||||||
std::set<Txid> setParentTransactions;
|
|
||||||
for (unsigned int i = 0; i < tx.vin.size(); i++) {
|
for (unsigned int i = 0; i < tx.vin.size(); i++) {
|
||||||
mapNextTx.insert(std::make_pair(&tx.vin[i].prevout, newit));
|
mapNextTx.insert(std::make_pair(&tx.vin[i].prevout, newit));
|
||||||
setParentTransactions.insert(tx.vin[i].prevout.hash);
|
|
||||||
}
|
}
|
||||||
// Don't bother worrying about child transactions of this one.
|
// Don't bother worrying about child transactions of this one.
|
||||||
// Normal case of a new transaction arriving is that there can't be any
|
// Normal case of a new transaction arriving is that there can't be any
|
||||||
@@ -283,12 +233,6 @@ void CTxMemPool::addNewTransaction(CTxMemPool::txiter newit)
|
|||||||
// In that case, our disconnect block logic will call UpdateTransactionsFromBlock
|
// In that case, our disconnect block logic will call UpdateTransactionsFromBlock
|
||||||
// to clean up the mess we're leaving here.
|
// to clean up the mess we're leaving here.
|
||||||
|
|
||||||
// Update ancestors with information about this tx
|
|
||||||
for (const auto& pit : GetIterSet(setParentTransactions)) {
|
|
||||||
UpdateParent(newit, pit, true);
|
|
||||||
}
|
|
||||||
UpdateAncestorsOf(true, newit);
|
|
||||||
|
|
||||||
nTransactionsUpdated++;
|
nTransactionsUpdated++;
|
||||||
totalTxSize += entry.GetTxSize();
|
totalTxSize += entry.GetTxSize();
|
||||||
m_total_fee += entry.GetFee();
|
m_total_fee += entry.GetFee();
|
||||||
@@ -344,7 +288,6 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
|
|||||||
totalTxSize -= it->GetTxSize();
|
totalTxSize -= it->GetTxSize();
|
||||||
m_total_fee -= it->GetFee();
|
m_total_fee -= it->GetFee();
|
||||||
cachedInnerUsage -= it->DynamicMemoryUsage();
|
cachedInnerUsage -= it->DynamicMemoryUsage();
|
||||||
cachedInnerUsage -= memusage::DynamicUsage(it->GetMemPoolParentsConst()) + memusage::DynamicUsage(it->GetMemPoolChildrenConst());
|
|
||||||
mapTx.erase(it);
|
mapTx.erase(it);
|
||||||
nTransactionsUpdated++;
|
nTransactionsUpdated++;
|
||||||
}
|
}
|
||||||
@@ -483,8 +426,8 @@ void CTxMemPool::check(const CCoinsViewCache& active_coins_tip, int64_t spendhei
|
|||||||
check_total_fee += it->GetFee();
|
check_total_fee += it->GetFee();
|
||||||
innerUsage += it->DynamicMemoryUsage();
|
innerUsage += it->DynamicMemoryUsage();
|
||||||
const CTransaction& tx = it->GetTx();
|
const CTransaction& tx = it->GetTx();
|
||||||
innerUsage += memusage::DynamicUsage(it->GetMemPoolParentsConst()) + memusage::DynamicUsage(it->GetMemPoolChildrenConst());
|
std::set<CTxMemPoolEntry::CTxMemPoolEntryRef, CompareIteratorByHash> setParentCheck;
|
||||||
CTxMemPoolEntry::Parents setParentCheck;
|
std::set<CTxMemPoolEntry::CTxMemPoolEntryRef, CompareIteratorByHash> setParentsStored;
|
||||||
for (const CTxIn &txin : tx.vin) {
|
for (const CTxIn &txin : tx.vin) {
|
||||||
// Check that every mempool transaction's inputs refer to available coins, or other mempool tx's.
|
// Check that every mempool transaction's inputs refer to available coins, or other mempool tx's.
|
||||||
indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.hash);
|
indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.hash);
|
||||||
@@ -507,19 +450,26 @@ void CTxMemPool::check(const CCoinsViewCache& active_coins_tip, int64_t spendhei
|
|||||||
auto comp = [](const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) -> bool {
|
auto comp = [](const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) -> bool {
|
||||||
return a.GetTx().GetHash() == b.GetTx().GetHash();
|
return a.GetTx().GetHash() == b.GetTx().GetHash();
|
||||||
};
|
};
|
||||||
assert(setParentCheck.size() == it->GetMemPoolParentsConst().size());
|
for (auto &txentry : GetParents(*it)) {
|
||||||
assert(std::equal(setParentCheck.begin(), setParentCheck.end(), it->GetMemPoolParentsConst().begin(), comp));
|
setParentsStored.insert(dynamic_cast<const CTxMemPoolEntry&>(txentry.get()));
|
||||||
|
}
|
||||||
|
assert(setParentCheck.size() == setParentsStored.size());
|
||||||
|
assert(std::equal(setParentCheck.begin(), setParentCheck.end(), setParentsStored.begin(), comp));
|
||||||
|
|
||||||
// Check children against mapNextTx
|
// Check children against mapNextTx
|
||||||
CTxMemPoolEntry::Children setChildrenCheck;
|
std::set<CTxMemPoolEntry::CTxMemPoolEntryRef, CompareIteratorByHash> setChildrenCheck;
|
||||||
|
std::set<CTxMemPoolEntry::CTxMemPoolEntryRef, CompareIteratorByHash> setChildrenStored;
|
||||||
auto iter = mapNextTx.lower_bound(COutPoint(it->GetTx().GetHash(), 0));
|
auto iter = mapNextTx.lower_bound(COutPoint(it->GetTx().GetHash(), 0));
|
||||||
for (; iter != mapNextTx.end() && iter->first->hash == it->GetTx().GetHash(); ++iter) {
|
for (; iter != mapNextTx.end() && iter->first->hash == it->GetTx().GetHash(); ++iter) {
|
||||||
txiter childit = iter->second;
|
txiter childit = iter->second;
|
||||||
assert(childit != mapTx.end()); // mapNextTx points to in-mempool transactions
|
assert(childit != mapTx.end()); // mapNextTx points to in-mempool transactions
|
||||||
setChildrenCheck.insert(*childit);
|
setChildrenCheck.insert(*childit);
|
||||||
}
|
}
|
||||||
assert(setChildrenCheck.size() == it->GetMemPoolChildrenConst().size());
|
for (auto &txentry : GetChildren(*it)) {
|
||||||
assert(std::equal(setChildrenCheck.begin(), setChildrenCheck.end(), it->GetMemPoolChildrenConst().begin(), comp));
|
setChildrenStored.insert(dynamic_cast<const CTxMemPoolEntry&>(txentry.get()));
|
||||||
|
}
|
||||||
|
assert(setChildrenCheck.size() == setChildrenStored.size());
|
||||||
|
assert(std::equal(setChildrenCheck.begin(), setChildrenCheck.end(), setChildrenStored.begin(), comp));
|
||||||
|
|
||||||
TxValidationState dummy_state; // Not used. CheckTxInputs() should always pass
|
TxValidationState dummy_state; // Not used. CheckTxInputs() should always pass
|
||||||
CAmount txfee = 0;
|
CAmount txfee = 0;
|
||||||
@@ -777,7 +727,6 @@ void CTxMemPool::RemoveUnbroadcastTx(const Txid& txid, const bool unchecked) {
|
|||||||
|
|
||||||
void CTxMemPool::RemoveStaged(setEntries &stage, bool updateDescendants, MemPoolRemovalReason reason) {
|
void CTxMemPool::RemoveStaged(setEntries &stage, bool updateDescendants, MemPoolRemovalReason reason) {
|
||||||
AssertLockHeld(cs);
|
AssertLockHeld(cs);
|
||||||
UpdateForRemoveFromMempool(stage, updateDescendants);
|
|
||||||
for (txiter it : stage) {
|
for (txiter it : stage) {
|
||||||
removeUnchecked(it, reason);
|
removeUnchecked(it, reason);
|
||||||
}
|
}
|
||||||
@@ -812,28 +761,6 @@ int CTxMemPool::Expire(std::chrono::seconds time)
|
|||||||
return stage.size();
|
return stage.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CTxMemPool::UpdateChild(txiter entry, txiter child, bool add)
|
|
||||||
{
|
|
||||||
AssertLockHeld(cs);
|
|
||||||
CTxMemPoolEntry::Children s;
|
|
||||||
if (add && entry->GetMemPoolChildren().insert(*child).second) {
|
|
||||||
cachedInnerUsage += memusage::IncrementalDynamicUsage(s);
|
|
||||||
} else if (!add && entry->GetMemPoolChildren().erase(*child)) {
|
|
||||||
cachedInnerUsage -= memusage::IncrementalDynamicUsage(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CTxMemPool::UpdateParent(txiter entry, txiter parent, bool add)
|
|
||||||
{
|
|
||||||
AssertLockHeld(cs);
|
|
||||||
CTxMemPoolEntry::Parents s;
|
|
||||||
if (add && entry->GetMemPoolParents().insert(*parent).second) {
|
|
||||||
cachedInnerUsage += memusage::IncrementalDynamicUsage(s);
|
|
||||||
} else if (!add && entry->GetMemPoolParents().erase(*parent)) {
|
|
||||||
cachedInnerUsage -= memusage::IncrementalDynamicUsage(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CFeeRate CTxMemPool::GetMinFee(size_t sizelimit) const {
|
CFeeRate CTxMemPool::GetMinFee(size_t sizelimit) const {
|
||||||
LOCK(cs);
|
LOCK(cs);
|
||||||
if (!blockSinceLastRollingFeeBump || rollingMinimumFeeRate == 0)
|
if (!blockSinceLastRollingFeeBump || rollingMinimumFeeRate == 0)
|
||||||
@@ -900,7 +827,6 @@ void CTxMemPool::TrimToSize(size_t sizelimit, std::vector<COutPoint>* pvNoSpends
|
|||||||
for (auto ref : worst_chunk) {
|
for (auto ref : worst_chunk) {
|
||||||
stage.insert(mapTx.iterator_to(static_cast<const CTxMemPoolEntry&>(*ref)));
|
stage.insert(mapTx.iterator_to(static_cast<const CTxMemPoolEntry&>(*ref)));
|
||||||
}
|
}
|
||||||
UpdateForRemoveFromMempool(stage, false);
|
|
||||||
for (auto e : stage) {
|
for (auto e : stage) {
|
||||||
removeUnchecked(e, MemPoolRemovalReason::SIZELIMIT);
|
removeUnchecked(e, MemPoolRemovalReason::SIZELIMIT);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -296,9 +296,6 @@ private:
|
|||||||
typedef std::map<txiter, setEntries, CompareIteratorByHash> cacheMap;
|
typedef std::map<txiter, setEntries, CompareIteratorByHash> cacheMap;
|
||||||
|
|
||||||
|
|
||||||
void UpdateParent(txiter entry, txiter parent, bool add) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
|
||||||
void UpdateChild(txiter entry, txiter child, bool add) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
|
||||||
|
|
||||||
std::vector<indexed_transaction_set::const_iterator> GetSortedScoreWithTopology() const EXCLUSIVE_LOCKS_REQUIRED(cs);
|
std::vector<indexed_transaction_set::const_iterator> GetSortedScoreWithTopology() const EXCLUSIVE_LOCKS_REQUIRED(cs);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -584,15 +581,6 @@ private:
|
|||||||
*/
|
*/
|
||||||
void RemoveStaged(setEntries& stage, bool updateDescendants, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
void RemoveStaged(setEntries& stage, bool updateDescendants, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
||||||
|
|
||||||
/** Update ancestors of hash to add/remove it as a descendant transaction. */
|
|
||||||
void UpdateAncestorsOf(bool add, txiter hash) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
|
||||||
/** For each transaction being removed, update ancestors and any direct children.
|
|
||||||
* If updateDescendants is true, then also update in-mempool descendants'
|
|
||||||
* ancestor state. */
|
|
||||||
void UpdateForRemoveFromMempool(const setEntries &entriesToRemove, bool updateDescendants) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
|
||||||
/** Sever link between specified transaction and direct children. */
|
|
||||||
void UpdateChildrenForRemoval(txiter entry) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
|
||||||
|
|
||||||
/** Before calling removeUnchecked for a given transaction,
|
/** Before calling removeUnchecked for a given transaction,
|
||||||
* UpdateForRemoveFromMempool must be called on the entire (dependent) set
|
* UpdateForRemoveFromMempool must be called on the entire (dependent) set
|
||||||
* of transactions being removed at the same time. We use each
|
* of transactions being removed at the same time. We use each
|
||||||
|
|||||||
Reference in new issue
Block a user