Changeset View
Changeset View
Standalone View
Standalone View
src/txmempool.cpp
Show First 20 Lines • Show All 69 Lines • ▼ Show 20 Lines | void CTxMemPoolEntry::UpdateFeeDelta(Amount newFeeDelta) { | ||||
feeDelta = newFeeDelta; | feeDelta = newFeeDelta; | ||||
} | } | ||||
void CTxMemPoolEntry::UpdateLockPoints(const LockPoints &lp) { | void CTxMemPoolEntry::UpdateLockPoints(const LockPoints &lp) { | ||||
lockPoints = lp; | lockPoints = lp; | ||||
} | } | ||||
// Update the given tx for any in-mempool descendants. | // Update the given tx for any in-mempool descendants. | ||||
// Assumes that setMemPoolChildren is correct for the given tx and all | // Assumes that CTxMemPool::m_children is correct for the given tx and all | ||||
// descendants. | // descendants. | ||||
void CTxMemPool::UpdateForDescendants(txiter updateIt, | void CTxMemPool::UpdateForDescendants(txiter updateIt, | ||||
cacheMap &cachedDescendants, | cacheMap &cachedDescendants, | ||||
const std::set<TxId> &setExclude) { | const std::set<TxId> &setExclude) { | ||||
setEntries stageEntries, setAllDescendants; | CTxMemPoolEntry::Children stageEntries, descendants; | ||||
stageEntries = GetMemPoolChildren(updateIt); | stageEntries = updateIt->GetMemPoolChildrenConst(); | ||||
while (!stageEntries.empty()) { | while (!stageEntries.empty()) { | ||||
const txiter cit = *stageEntries.begin(); | const CTxMemPoolEntry &descendant = *stageEntries.begin(); | ||||
setAllDescendants.insert(cit); | descendants.insert(descendant); | ||||
stageEntries.erase(cit); | stageEntries.erase(descendant); | ||||
const setEntries &setChildren = GetMemPoolChildren(cit); | const CTxMemPoolEntry::Children &children = | ||||
for (txiter childEntry : setChildren) { | descendant.GetMemPoolChildrenConst(); | ||||
cacheMap::iterator cacheIt = cachedDescendants.find(childEntry); | for (const CTxMemPoolEntry &childEntry : children) { | ||||
cacheMap::iterator cacheIt = | |||||
cachedDescendants.find(mapTx.iterator_to(childEntry)); | |||||
if (cacheIt != cachedDescendants.end()) { | if (cacheIt != cachedDescendants.end()) { | ||||
// We've already calculated this one, just add the entries for | // We've already calculated this one, just add the entries for | ||||
// this set but don't traverse again. | // this set but don't traverse again. | ||||
for (txiter cacheEntry : cacheIt->second) { | for (txiter cacheEntry : cacheIt->second) { | ||||
setAllDescendants.insert(cacheEntry); | descendants.insert(*cacheEntry); | ||||
} | } | ||||
} else if (!setAllDescendants.count(childEntry)) { | } else if (!descendants.count(childEntry)) { | ||||
// Schedule for later processing | // Schedule for later processing | ||||
stageEntries.insert(childEntry); | stageEntries.insert(childEntry); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// setAllDescendants now contains all in-mempool descendants of updateIt. | // descendants now contains all in-mempool descendants of updateIt. | ||||
// Update and add to cached descendant map | // Update and add to cached descendant map | ||||
int64_t modifySize = 0; | int64_t modifySize = 0; | ||||
int64_t modifyCount = 0; | int64_t modifyCount = 0; | ||||
Amount modifyFee = Amount::zero(); | Amount modifyFee = Amount::zero(); | ||||
int64_t modifySigOpCount = 0; | int64_t modifySigOpCount = 0; | ||||
for (txiter cit : setAllDescendants) { | for (const CTxMemPoolEntry &descendant : descendants) { | ||||
if (!setExclude.count(cit->GetTx().GetId())) { | if (!setExclude.count(descendant.GetTx().GetId())) { | ||||
modifySize += cit->GetTxSize(); | modifySize += descendant.GetTxSize(); | ||||
modifyFee += cit->GetModifiedFee(); | modifyFee += descendant.GetModifiedFee(); | ||||
modifyCount++; | modifyCount++; | ||||
modifySigOpCount += cit->GetSigOpCount(); | modifySigOpCount += descendant.GetSigOpCount(); | ||||
cachedDescendants[updateIt].insert(cit); | cachedDescendants[updateIt].insert(mapTx.iterator_to(descendant)); | ||||
// Update ancestor state for each descendant | // Update ancestor state for each descendant | ||||
mapTx.modify(cit, | mapTx.modify(mapTx.iterator_to(descendant), | ||||
update_ancestor_state(updateIt->GetTxSize(), | update_ancestor_state(updateIt->GetTxSize(), | ||||
updateIt->GetModifiedFee(), 1, | updateIt->GetModifiedFee(), 1, | ||||
updateIt->GetSigOpCount())); | updateIt->GetSigOpCount())); | ||||
} | } | ||||
} | } | ||||
mapTx.modify(updateIt, | mapTx.modify(updateIt, | ||||
update_descendant_state(modifySize, modifyFee, modifyCount, | update_descendant_state(modifySize, modifyFee, modifyCount, | ||||
modifySigOpCount)); | modifySigOpCount)); | ||||
Show All 15 Lines | void CTxMemPool::UpdateTransactionsFromBlock( | ||||
// Use a set for lookups into txidsToUpdate (these entries are already | // Use a set for lookups into txidsToUpdate (these entries are already | ||||
// accounted for in the state of their ancestors) | // accounted for in the state of their ancestors) | ||||
std::set<TxId> setAlreadyIncluded(txidsToUpdate.begin(), | std::set<TxId> setAlreadyIncluded(txidsToUpdate.begin(), | ||||
txidsToUpdate.end()); | txidsToUpdate.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. | ||||
// This maximizes the benefit of the descendant cache and guarantees that | // This maximizes the benefit of the descendant cache and guarantees that | ||||
// setMemPoolChildren will be updated, an assumption made in | // CTxMemPool::m_children will be updated, an assumption made in | ||||
// UpdateForDescendants. | // UpdateForDescendants. | ||||
for (const TxId &txid : reverse_iterate(txidsToUpdate)) { | for (const TxId &txid : reverse_iterate(txidsToUpdate)) { | ||||
// calculate children from mapNextTx | // calculate children from mapNextTx | ||||
txiter it = mapTx.find(txid); | txiter it = mapTx.find(txid); | ||||
if (it == mapTx.end()) { | if (it == mapTx.end()) { | ||||
continue; | continue; | ||||
} | } | ||||
auto iter = mapNextTx.lower_bound(COutPoint(txid, 0)); | auto iter = mapNextTx.lower_bound(COutPoint(txid, 0)); | ||||
// First calculate the children, and update setMemPoolChildren to | // First calculate the children, and update CTxMemPool::m_children to | ||||
// include them, and update their setMemPoolParents to include this tx. | // include them, and update their CTxMemPoolEntry::m_parents to include | ||||
// we cache the in-mempool children to avoid duplicate updates | // this tx. we cache the in-mempool children to avoid duplicate updates | ||||
{ | { | ||||
const auto epoch = GetFreshEpoch(); | const auto epoch = GetFreshEpoch(); | ||||
for (; iter != mapNextTx.end() && iter->first->GetTxId() == txid; | for (; iter != mapNextTx.end() && iter->first->GetTxId() == txid; | ||||
++iter) { | ++iter) { | ||||
const TxId &childTxId = iter->second->GetId(); | const TxId &childTxId = iter->second->GetId(); | ||||
txiter childIter = mapTx.find(childTxId); | txiter childIter = mapTx.find(childTxId); | ||||
assert(childIter != mapTx.end()); | assert(childIter != mapTx.end()); | ||||
// We can skip updating entries we've encountered before or that | // We can skip updating entries we've encountered before or that | ||||
Show All 10 Lines | void CTxMemPool::UpdateTransactionsFromBlock( | ||||
} | } | ||||
} | } | ||||
bool CTxMemPool::CalculateMemPoolAncestors( | bool CTxMemPool::CalculateMemPoolAncestors( | ||||
const CTxMemPoolEntry &entry, setEntries &setAncestors, | const CTxMemPoolEntry &entry, setEntries &setAncestors, | ||||
uint64_t limitAncestorCount, uint64_t limitAncestorSize, | uint64_t limitAncestorCount, uint64_t limitAncestorSize, | ||||
uint64_t limitDescendantCount, uint64_t limitDescendantSize, | uint64_t limitDescendantCount, uint64_t limitDescendantSize, | ||||
std::string &errString, bool fSearchForParents /* = true */) const { | std::string &errString, bool fSearchForParents /* = true */) const { | ||||
setEntries parentHashes; | CTxMemPoolEntry::Parents staged_ancestors; | ||||
const CTransaction &tx = entry.GetTx(); | const CTransaction &tx = entry.GetTx(); | ||||
if (fSearchForParents) { | if (fSearchForParents) { | ||||
// 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 | // GetMemPoolParents() is only valid for entries in the mempool, so we | ||||
// iterate mapTx to find parents. | // iterate mapTx to find parents. | ||||
for (const CTxIn &in : tx.vin) { | for (const CTxIn &in : tx.vin) { | ||||
std::optional<txiter> piter = GetIter(in.prevout.GetTxId()); | std::optional<txiter> piter = GetIter(in.prevout.GetTxId()); | ||||
if (!piter) { | if (!piter) { | ||||
continue; | continue; | ||||
} | } | ||||
parentHashes.insert(*piter); | staged_ancestors.insert(**piter); | ||||
if (parentHashes.size() + 1 > limitAncestorCount) { | if (staged_ancestors.size() + 1 > limitAncestorCount) { | ||||
errString = | errString = | ||||
strprintf("too many unconfirmed parents [limit: %u]", | strprintf("too many unconfirmed parents [limit: %u]", | ||||
limitAncestorCount); | limitAncestorCount); | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
} else { | } else { | ||||
// If we're not searching for parents, we require this to be an entry in | // If we're not searching for parents, we require this to be an entry in | ||||
// the mempool already. | // the mempool already. | ||||
txiter it = mapTx.iterator_to(entry); | txiter it = mapTx.iterator_to(entry); | ||||
parentHashes = GetMemPoolParents(it); | staged_ancestors = it->GetMemPoolParentsConst(); | ||||
} | } | ||||
size_t totalSizeWithAncestors = entry.GetTxSize(); | size_t totalSizeWithAncestors = entry.GetTxSize(); | ||||
while (!parentHashes.empty()) { | while (!staged_ancestors.empty()) { | ||||
txiter stageit = *parentHashes.begin(); | const CTxMemPoolEntry &stage = staged_ancestors.begin()->get(); | ||||
txiter stageit = mapTx.iterator_to(stage); | |||||
setAncestors.insert(stageit); | setAncestors.insert(stageit); | ||||
parentHashes.erase(stageit); | staged_ancestors.erase(stage); | ||||
totalSizeWithAncestors += stageit->GetTxSize(); | totalSizeWithAncestors += stageit->GetTxSize(); | ||||
if (stageit->GetSizeWithDescendants() + entry.GetTxSize() > | if (stageit->GetSizeWithDescendants() + entry.GetTxSize() > | ||||
limitDescendantSize) { | limitDescendantSize) { | ||||
errString = strprintf( | errString = strprintf( | ||||
"exceeds descendant size limit for tx %s [limit: %u]", | "exceeds descendant size limit for tx %s [limit: %u]", | ||||
stageit->GetTx().GetId().ToString(), limitDescendantSize); | stageit->GetTx().GetId().ToString(), limitDescendantSize); | ||||
return false; | return false; | ||||
} | } | ||||
if (stageit->GetCountWithDescendants() + 1 > limitDescendantCount) { | if (stageit->GetCountWithDescendants() + 1 > limitDescendantCount) { | ||||
errString = strprintf("too many descendants for tx %s [limit: %u]", | errString = strprintf("too many descendants for tx %s [limit: %u]", | ||||
stageit->GetTx().GetId().ToString(), | stageit->GetTx().GetId().ToString(), | ||||
limitDescendantCount); | limitDescendantCount); | ||||
return false; | return false; | ||||
} | } | ||||
if (totalSizeWithAncestors > limitAncestorSize) { | if (totalSizeWithAncestors > limitAncestorSize) { | ||||
errString = strprintf("exceeds ancestor size limit [limit: %u]", | errString = strprintf("exceeds ancestor size limit [limit: %u]", | ||||
limitAncestorSize); | limitAncestorSize); | ||||
return false; | return false; | ||||
} | } | ||||
const setEntries &setMemPoolParents = GetMemPoolParents(stageit); | const CTxMemPoolEntry::Parents &parents = | ||||
for (txiter phash : setMemPoolParents) { | stageit->GetMemPoolParentsConst(); | ||||
for (const CTxMemPoolEntry &parent : parents) { | |||||
txiter parent_it = mapTx.iterator_to(parent); | |||||
// If this is a new ancestor, add it. | // If this is a new ancestor, add it. | ||||
if (setAncestors.count(phash) == 0) { | if (setAncestors.count(parent_it) == 0) { | ||||
parentHashes.insert(phash); | staged_ancestors.insert(parent); | ||||
} | } | ||||
if (parentHashes.size() + setAncestors.size() + 1 > | if (staged_ancestors.size() + setAncestors.size() + 1 > | ||||
limitAncestorCount) { | limitAncestorCount) { | ||||
errString = | errString = | ||||
strprintf("too many unconfirmed ancestors [limit: %u]", | strprintf("too many unconfirmed ancestors [limit: %u]", | ||||
limitAncestorCount); | limitAncestorCount); | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
void CTxMemPool::UpdateAncestorsOf(bool add, txiter it, | void CTxMemPool::UpdateAncestorsOf(bool add, txiter it, | ||||
setEntries &setAncestors) { | setEntries &setAncestors) { | ||||
setEntries parentIters = GetMemPoolParents(it); | CTxMemPoolEntry::Parents parents = it->GetMemPoolParents(); | ||||
// add or remove this tx as a child of each parent | // add or remove this tx as a child of each parent | ||||
for (txiter piter : parentIters) { | for (const CTxMemPoolEntry &parent : parents) { | ||||
UpdateChild(piter, it, add); | UpdateChild(mapTx.iterator_to(parent), it, add); | ||||
} | } | ||||
const int64_t updateCount = (add ? 1 : -1); | const int64_t updateCount = (add ? 1 : -1); | ||||
const int64_t updateSize = updateCount * it->GetTxSize(); | const int64_t updateSize = updateCount * it->GetTxSize(); | ||||
const int64_t updateSigOpCount = updateCount * it->GetSigOpCount(); | const int64_t updateSigOpCount = updateCount * it->GetSigOpCount(); | ||||
const Amount updateFee = updateCount * it->GetModifiedFee(); | const Amount updateFee = updateCount * it->GetModifiedFee(); | ||||
for (txiter ancestorIt : setAncestors) { | for (txiter ancestorIt : setAncestors) { | ||||
mapTx.modify(ancestorIt, | mapTx.modify(ancestorIt, | ||||
update_descendant_state(updateSize, updateFee, updateCount, | update_descendant_state(updateSize, updateFee, updateCount, | ||||
Show All 13 Lines | for (txiter ancestorIt : setAncestors) { | ||||
updateFee += ancestorIt->GetModifiedFee(); | updateFee += ancestorIt->GetModifiedFee(); | ||||
updateSigOpsCount += ancestorIt->GetSigOpCount(); | updateSigOpsCount += ancestorIt->GetSigOpCount(); | ||||
} | } | ||||
mapTx.modify(it, update_ancestor_state(updateSize, updateFee, updateCount, | mapTx.modify(it, update_ancestor_state(updateSize, updateFee, updateCount, | ||||
updateSigOpsCount)); | updateSigOpsCount)); | ||||
} | } | ||||
void CTxMemPool::UpdateChildrenForRemoval(txiter it) { | void CTxMemPool::UpdateChildrenForRemoval(txiter it) { | ||||
const setEntries &setMemPoolChildren = GetMemPoolChildren(it); | const CTxMemPoolEntry::Children &children = it->GetMemPoolChildrenConst(); | ||||
for (txiter updateIt : setMemPoolChildren) { | for (const CTxMemPoolEntry &updateIt : children) { | ||||
UpdateParent(updateIt, it, false); | UpdateParent(mapTx.iterator_to(updateIt), it, false); | ||||
} | } | ||||
} | } | ||||
void CTxMemPool::UpdateForRemoveFromMempool(const setEntries &entriesToRemove, | void CTxMemPool::UpdateForRemoveFromMempool(const setEntries &entriesToRemove, | ||||
bool updateDescendants) { | bool updateDescendants) { | ||||
// For each entry, walk back all ancestors and decrement size associated | // For each entry, walk back all ancestors and decrement size associated | ||||
// with this transaction. | // with this transaction. | ||||
const uint64_t nNoLimit = std::numeric_limits<uint64_t>::max(); | const uint64_t nNoLimit = std::numeric_limits<uint64_t>::max(); | ||||
if (updateDescendants) { | if (updateDescendants) { | ||||
// updateDescendants should be true whenever we're not recursively | // updateDescendants should be true whenever we're not recursively | ||||
// removing a tx and all its descendants, eg when a transaction is | // removing a tx and all its descendants, eg when a transaction is | ||||
// confirmed in a block. Here we only update statistics and not data in | // confirmed in a block. | ||||
// mapLinks (which we need to preserve until we're finished with all | // Here we only update statistics and not data in CTxMemPool::Parents | ||||
// operations that need to traverse the mempool). | // and CTxMemPoolEntry::Children (which we need to preserve until we're | ||||
// finished with all operations that need to traverse the mempool). | |||||
for (txiter removeIt : entriesToRemove) { | for (txiter removeIt : entriesToRemove) { | ||||
setEntries setDescendants; | setEntries setDescendants; | ||||
CalculateDescendants(removeIt, setDescendants); | CalculateDescendants(removeIt, setDescendants); | ||||
setDescendants.erase(removeIt); // don't update state for self | setDescendants.erase(removeIt); // don't update state for self | ||||
int64_t modifySize = -int64_t(removeIt->GetTxSize()); | int64_t modifySize = -int64_t(removeIt->GetTxSize()); | ||||
Amount modifyFee = -1 * removeIt->GetModifiedFee(); | Amount modifyFee = -1 * removeIt->GetModifiedFee(); | ||||
int modifySigOps = -removeIt->GetSigOpCount(); | int modifySigOps = -removeIt->GetSigOpCount(); | ||||
for (txiter dit : setDescendants) { | for (txiter dit : setDescendants) { | ||||
mapTx.modify(dit, update_ancestor_state(modifySize, modifyFee, | mapTx.modify(dit, update_ancestor_state(modifySize, modifyFee, | ||||
-1, modifySigOps)); | -1, modifySigOps)); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
for (txiter removeIt : entriesToRemove) { | for (txiter removeIt : entriesToRemove) { | ||||
setEntries setAncestors; | setEntries setAncestors; | ||||
const CTxMemPoolEntry &entry = *removeIt; | const CTxMemPoolEntry &entry = *removeIt; | ||||
std::string dummy; | std::string dummy; | ||||
// Since this is a tx that is already in the mempool, we can call CMPA | // Since this is a tx that is already in the mempool, we can call CMPA | ||||
// with fSearchForParents = false. If the mempool is in a consistent | // with fSearchForParents = false. If the mempool is in a consistent | ||||
// state, then using true or false should both be correct, though false | // state, then using true or false should both be correct, though false | ||||
// should be a bit faster. | // should be a bit faster. | ||||
// However, if we happen to be in the middle of processing a reorg, then | // However, if we happen to be in the middle of processing a reorg, then | ||||
// the mempool can be in an inconsistent state. In this case, the set of | // the mempool can be in an inconsistent state. In this case, the set | ||||
// ancestors reachable via mapLinks will be the same as the set of | // of ancestors reachable via GetMemPoolParents()/GetMemPoolChildren() | ||||
// ancestors whose packages include this transaction, because when we | // will be the same as the set of ancestors whose packages include this | ||||
// add a new transaction to the mempool in addUnchecked(), we assume it | // transaction, because when we add a new transaction to the mempool in | ||||
// has no children, and in the case of a reorg where that assumption is | // addUnchecked(), we assume it has no children, and in the case of a | ||||
// false, the in-mempool children aren't linked to the in-block tx's | // reorg where that assumption is false, the in-mempool children aren't | ||||
// until UpdateTransactionsFromBlock() is called. So if we're being | // linked to the in-block tx's until UpdateTransactionsFromBlock() is | ||||
// called during a reorg, ie before UpdateTransactionsFromBlock() has | // called. | ||||
// been called, then mapLinks[] will differ from the set of mempool | // So if we're being called during a reorg, ie before | ||||
// parents we'd calculate by searching, and it's important that we use | // UpdateTransactionsFromBlock() has been called, then | ||||
// the mapLinks[] notion of ancestor transactions as the set of things | // GetMemPoolParents()/GetMemPoolChildren() will differ from the set of | ||||
// to update for removal. | // mempool parents we'd calculate by searching, and it's important that | ||||
// we use the cached notion of ancestor transactions as the set of | |||||
// things to update for removal. | |||||
CalculateMemPoolAncestors(entry, setAncestors, nNoLimit, nNoLimit, | CalculateMemPoolAncestors(entry, setAncestors, nNoLimit, nNoLimit, | ||||
nNoLimit, nNoLimit, dummy, false); | nNoLimit, nNoLimit, dummy, false); | ||||
// Note that UpdateAncestorsOf severs the child links that point to | // Note that UpdateAncestorsOf severs the child links that point to | ||||
// removeIt in the entries for the parents of removeIt. | // removeIt in the entries for the parents of removeIt. | ||||
UpdateAncestorsOf(false, removeIt, setAncestors); | UpdateAncestorsOf(false, removeIt, setAncestors); | ||||
} | } | ||||
// After updating all the ancestor sizes, we can now sever the link between | // After updating all the ancestor sizes, we can now sever the link between | ||||
// each transaction being removed and any mempool children (ie, update | // each transaction being removed and any mempool children (ie, update | ||||
// setMemPoolParents for each direct child of a transaction being removed). | // CTxMemPoolEntry::m_parents for each direct child of a transaction being | ||||
// removed). | |||||
for (txiter removeIt : entriesToRemove) { | for (txiter removeIt : entriesToRemove) { | ||||
UpdateChildrenForRemoval(removeIt); | UpdateChildrenForRemoval(removeIt); | ||||
} | } | ||||
} | } | ||||
void CTxMemPoolEntry::UpdateDescendantState(int64_t modifySize, | void CTxMemPoolEntry::UpdateDescendantState(int64_t modifySize, | ||||
Amount modifyFee, | Amount modifyFee, | ||||
int64_t modifyCount, | int64_t modifyCount, | ||||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | void CTxMemPool::AddTransactionsUpdated(unsigned int n) { | ||||
nTransactionsUpdated += n; | nTransactionsUpdated += n; | ||||
} | } | ||||
void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, | void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, | ||||
setEntries &setAncestors) { | setEntries &setAncestors) { | ||||
// Add to memory pool without checking anything. | // Add to memory pool without checking anything. | ||||
// Used by AcceptToMemoryPool(), which DOES do all the appropriate checks. | // Used by AcceptToMemoryPool(), which DOES do all the appropriate checks. | ||||
indexed_transaction_set::iterator newit = mapTx.insert(entry).first; | indexed_transaction_set::iterator newit = mapTx.insert(entry).first; | ||||
mapLinks.insert(make_pair(newit, TxLinks())); | |||||
// Update transaction for any feeDelta created by PrioritiseTransaction | // Update transaction for any feeDelta created by PrioritiseTransaction | ||||
// TODO: refactor so that the fee delta is calculated before inserting into | // TODO: refactor so that the fee delta is calculated before inserting into | ||||
// mapTx. | // mapTx. | ||||
Amount feeDelta = Amount::zero(); | Amount feeDelta = Amount::zero(); | ||||
ApplyDelta(entry.GetTx().GetId(), feeDelta); | ApplyDelta(entry.GetTx().GetId(), feeDelta); | ||||
if (feeDelta != Amount::zero()) { | if (feeDelta != Amount::zero()) { | ||||
mapTx.modify(newit, update_fee_delta(feeDelta)); | mapTx.modify(newit, update_fee_delta(feeDelta)); | ||||
▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | if (vTxHashes.size() > 1) { | ||||
vTxHashes.shrink_to_fit(); | vTxHashes.shrink_to_fit(); | ||||
} | } | ||||
} else { | } else { | ||||
vTxHashes.clear(); | vTxHashes.clear(); | ||||
} | } | ||||
totalTxSize -= it->GetTxSize(); | totalTxSize -= it->GetTxSize(); | ||||
cachedInnerUsage -= it->DynamicMemoryUsage(); | cachedInnerUsage -= it->DynamicMemoryUsage(); | ||||
cachedInnerUsage -= memusage::DynamicUsage(mapLinks[it].parents) + | cachedInnerUsage -= memusage::DynamicUsage(it->GetMemPoolParentsConst()) + | ||||
memusage::DynamicUsage(mapLinks[it].children); | memusage::DynamicUsage(it->GetMemPoolChildrenConst()); | ||||
mapLinks.erase(it); | |||||
mapTx.erase(it); | mapTx.erase(it); | ||||
nTransactionsUpdated++; | nTransactionsUpdated++; | ||||
} | } | ||||
// Calculates descendants of entry that are not already in setDescendants, and | // Calculates descendants of entry that are not already in setDescendants, and | ||||
// adds to setDescendants. Assumes entryit is already a tx in the mempool and | // adds to setDescendants. Assumes entryit is already a tx in the mempool and | ||||
// setMemPoolChildren is correct for tx and all descendants. Also assumes that | // CTxMemPoolEntry::m_children is correct for tx and all descendants. Also | ||||
// if an entry is in setDescendants already, then all in-mempool descendants of | // assumes that if an entry is in setDescendants already, then all in-mempool | ||||
// it are already in setDescendants as well, so that we can save time by not | // descendants of it are already in setDescendants as well, so that we can save | ||||
// iterating over those entries. | // time by not iterating over those entries. | ||||
void CTxMemPool::CalculateDescendants(txiter entryit, | void CTxMemPool::CalculateDescendants(txiter entryit, | ||||
setEntries &setDescendants) const { | setEntries &setDescendants) const { | ||||
setEntries stage; | setEntries stage; | ||||
if (setDescendants.count(entryit) == 0) { | if (setDescendants.count(entryit) == 0) { | ||||
stage.insert(entryit); | stage.insert(entryit); | ||||
} | } | ||||
// Traverse down the children of entry, only adding children that are not | // Traverse down the children of entry, only adding children that are not | ||||
// accounted for in setDescendants already (because those children have | // accounted for in setDescendants already (because those children have | ||||
// either already been walked, or will be walked in this iteration). | // either already been walked, or will be walked in this iteration). | ||||
while (!stage.empty()) { | while (!stage.empty()) { | ||||
txiter it = *stage.begin(); | txiter it = *stage.begin(); | ||||
setDescendants.insert(it); | setDescendants.insert(it); | ||||
stage.erase(it); | stage.erase(it); | ||||
const setEntries &setChildren = GetMemPoolChildren(it); | const CTxMemPoolEntry::Children &children = | ||||
for (txiter childiter : setChildren) { | it->GetMemPoolChildrenConst(); | ||||
for (const CTxMemPoolEntry &child : children) { | |||||
txiter childiter = mapTx.iterator_to(child); | |||||
if (!setDescendants.count(childiter)) { | if (!setDescendants.count(childiter)) { | ||||
stage.insert(childiter); | stage.insert(childiter); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
void CTxMemPool::removeRecursive(const CTransaction &origTx, | void CTxMemPool::removeRecursive(const CTransaction &origTx, | ||||
▲ Show 20 Lines • Show All 134 Lines • ▼ Show 20 Lines | void CTxMemPool::removeForBlock(const std::vector<CTransactionRef> &vtx, | ||||
disconnectpool.clear(); | disconnectpool.clear(); | ||||
lastRollingFeeUpdate = GetTime(); | lastRollingFeeUpdate = GetTime(); | ||||
blockSinceLastRollingFeeBump = true; | blockSinceLastRollingFeeBump = true; | ||||
} | } | ||||
void CTxMemPool::_clear() { | void CTxMemPool::_clear() { | ||||
mapLinks.clear(); | |||||
mapTx.clear(); | mapTx.clear(); | ||||
mapNextTx.clear(); | mapNextTx.clear(); | ||||
vTxHashes.clear(); | vTxHashes.clear(); | ||||
totalTxSize = 0; | totalTxSize = 0; | ||||
cachedInnerUsage = 0; | cachedInnerUsage = 0; | ||||
lastRollingFeeUpdate = GetTime(); | lastRollingFeeUpdate = GetTime(); | ||||
blockSinceLastRollingFeeBump = false; | blockSinceLastRollingFeeBump = false; | ||||
rollingMinimumFeeRate = 0; | rollingMinimumFeeRate = 0; | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | void CTxMemPool::check(const CCoinsViewCache *pcoins) const { | ||||
std::list<const CTxMemPoolEntry *> waitingOnDependants; | std::list<const CTxMemPoolEntry *> waitingOnDependants; | ||||
for (indexed_transaction_set::const_iterator it = mapTx.begin(); | for (indexed_transaction_set::const_iterator it = mapTx.begin(); | ||||
it != mapTx.end(); it++) { | it != mapTx.end(); it++) { | ||||
unsigned int i = 0; | unsigned int i = 0; | ||||
checkTotal += it->GetTxSize(); | checkTotal += it->GetTxSize(); | ||||
innerUsage += it->DynamicMemoryUsage(); | innerUsage += it->DynamicMemoryUsage(); | ||||
const CTransaction &tx = it->GetTx(); | const CTransaction &tx = it->GetTx(); | ||||
txlinksMap::const_iterator linksiter = mapLinks.find(it); | innerUsage += memusage::DynamicUsage(it->GetMemPoolParentsConst()) + | ||||
assert(linksiter != mapLinks.end()); | memusage::DynamicUsage(it->GetMemPoolChildrenConst()); | ||||
const TxLinks &links = linksiter->second; | |||||
innerUsage += memusage::DynamicUsage(links.parents) + | |||||
memusage::DynamicUsage(links.children); | |||||
bool fDependsWait = false; | bool fDependsWait = false; | ||||
setEntries setParentCheck; | CTxMemPoolEntry::Parents setParentCheck; | ||||
for (const CTxIn &txin : tx.vin) { | for (const CTxIn &txin : tx.vin) { | ||||
// Check that every mempool transaction's inputs refer to available | // Check that every mempool transaction's inputs refer to available | ||||
// coins, or other mempool tx's. | // coins, or other mempool tx's. | ||||
indexed_transaction_set::const_iterator it2 = | indexed_transaction_set::const_iterator it2 = | ||||
mapTx.find(txin.prevout.GetTxId()); | mapTx.find(txin.prevout.GetTxId()); | ||||
if (it2 != mapTx.end()) { | if (it2 != mapTx.end()) { | ||||
const CTransaction &tx2 = it2->GetTx(); | const CTransaction &tx2 = it2->GetTx(); | ||||
assert(tx2.vout.size() > txin.prevout.GetN() && | assert(tx2.vout.size() > txin.prevout.GetN() && | ||||
!tx2.vout[txin.prevout.GetN()].IsNull()); | !tx2.vout[txin.prevout.GetN()].IsNull()); | ||||
fDependsWait = true; | fDependsWait = true; | ||||
setParentCheck.insert(it2); | setParentCheck.insert(*it2); | ||||
} else { | } else { | ||||
assert(pcoins->HaveCoin(txin.prevout)); | assert(pcoins->HaveCoin(txin.prevout)); | ||||
} | } | ||||
// Check whether its inputs are marked in mapNextTx. | // Check whether its inputs are marked in mapNextTx. | ||||
auto it3 = mapNextTx.find(txin.prevout); | auto it3 = mapNextTx.find(txin.prevout); | ||||
assert(it3 != mapNextTx.end()); | assert(it3 != mapNextTx.end()); | ||||
assert(it3->first == &txin.prevout); | assert(it3->first == &txin.prevout); | ||||
assert(it3->second == &tx); | assert(it3->second == &tx); | ||||
i++; | i++; | ||||
} | } | ||||
assert(setParentCheck == GetMemPoolParents(it)); | auto comp = [](const CTxMemPoolEntry &a, | ||||
const CTxMemPoolEntry &b) -> bool { | |||||
return a.GetTx().GetId() == b.GetTx().GetId(); | |||||
}; | |||||
assert(setParentCheck.size() == it->GetMemPoolParentsConst().size()); | |||||
assert(std::equal(setParentCheck.begin(), setParentCheck.end(), | |||||
it->GetMemPoolParentsConst().begin(), comp)); | |||||
// Verify ancestor state is correct. | // Verify ancestor state is correct. | ||||
setEntries setAncestors; | setEntries setAncestors; | ||||
uint64_t nNoLimit = std::numeric_limits<uint64_t>::max(); | uint64_t nNoLimit = std::numeric_limits<uint64_t>::max(); | ||||
std::string dummy; | std::string dummy; | ||||
CalculateMemPoolAncestors(*it, setAncestors, nNoLimit, nNoLimit, | CalculateMemPoolAncestors(*it, setAncestors, nNoLimit, nNoLimit, | ||||
nNoLimit, nNoLimit, dummy); | nNoLimit, nNoLimit, dummy); | ||||
uint64_t nCountCheck = setAncestors.size() + 1; | uint64_t nCountCheck = setAncestors.size() + 1; | ||||
uint64_t nSizeCheck = it->GetTxSize(); | uint64_t nSizeCheck = it->GetTxSize(); | ||||
Amount nFeesCheck = it->GetModifiedFee(); | Amount nFeesCheck = it->GetModifiedFee(); | ||||
int64_t nSigOpCheck = it->GetSigOpCount(); | int64_t nSigOpCheck = it->GetSigOpCount(); | ||||
for (txiter ancestorIt : setAncestors) { | for (txiter ancestorIt : setAncestors) { | ||||
nSizeCheck += ancestorIt->GetTxSize(); | nSizeCheck += ancestorIt->GetTxSize(); | ||||
nFeesCheck += ancestorIt->GetModifiedFee(); | nFeesCheck += ancestorIt->GetModifiedFee(); | ||||
nSigOpCheck += ancestorIt->GetSigOpCount(); | nSigOpCheck += ancestorIt->GetSigOpCount(); | ||||
} | } | ||||
assert(it->GetCountWithAncestors() == nCountCheck); | assert(it->GetCountWithAncestors() == nCountCheck); | ||||
assert(it->GetSizeWithAncestors() == nSizeCheck); | assert(it->GetSizeWithAncestors() == nSizeCheck); | ||||
assert(it->GetSigOpCountWithAncestors() == nSigOpCheck); | assert(it->GetSigOpCountWithAncestors() == nSigOpCheck); | ||||
assert(it->GetModFeesWithAncestors() == nFeesCheck); | assert(it->GetModFeesWithAncestors() == nFeesCheck); | ||||
// Check children against mapNextTx | // Check children against mapNextTx | ||||
CTxMemPool::setEntries setChildrenCheck; | CTxMemPoolEntry::Children setChildrenCheck; | ||||
auto iter = mapNextTx.lower_bound(COutPoint(it->GetTx().GetId(), 0)); | auto iter = mapNextTx.lower_bound(COutPoint(it->GetTx().GetId(), 0)); | ||||
uint64_t child_sizes = 0; | uint64_t child_sizes = 0; | ||||
int64_t child_sigop_counts = 0; | int64_t child_sigop_counts = 0; | ||||
for (; iter != mapNextTx.end() && | for (; iter != mapNextTx.end() && | ||||
iter->first->GetTxId() == it->GetTx().GetId(); | iter->first->GetTxId() == it->GetTx().GetId(); | ||||
++iter) { | ++iter) { | ||||
txiter childit = mapTx.find(iter->second->GetId()); | txiter childit = mapTx.find(iter->second->GetId()); | ||||
// mapNextTx points to in-mempool transactions | // mapNextTx points to in-mempool transactions | ||||
assert(childit != mapTx.end()); | assert(childit != mapTx.end()); | ||||
if (setChildrenCheck.insert(childit).second) { | if (setChildrenCheck.insert(*childit).second) { | ||||
child_sizes += childit->GetTxSize(); | child_sizes += childit->GetTxSize(); | ||||
child_sigop_counts += childit->GetSigOpCount(); | child_sigop_counts += childit->GetSigOpCount(); | ||||
} | } | ||||
} | } | ||||
assert(setChildrenCheck == GetMemPoolChildren(it)); | assert(setChildrenCheck.size() == it->GetMemPoolChildrenConst().size()); | ||||
assert(std::equal(setChildrenCheck.begin(), setChildrenCheck.end(), | |||||
it->GetMemPoolChildrenConst().begin(), comp)); | |||||
// Also check to make sure size is greater than sum with immediate | // Also check to make sure size is greater than sum with immediate | ||||
// children. Just a sanity check, not definitive that this calc is | // children. Just a sanity check, not definitive that this calc is | ||||
// correct... | // correct... | ||||
assert(it->GetSizeWithDescendants() >= child_sizes + it->GetTxSize()); | assert(it->GetSizeWithDescendants() >= child_sizes + it->GetTxSize()); | ||||
assert(it->GetSigOpCountWithDescendants() >= | assert(it->GetSigOpCountWithDescendants() >= | ||||
child_sigop_counts + it->GetSigOpCount()); | child_sigop_counts + it->GetSigOpCount()); | ||||
if (fDependsWait) { | if (fDependsWait) { | ||||
▲ Show 20 Lines • Show All 251 Lines • ▼ Show 20 Lines | size_t CTxMemPool::DynamicMemoryUsage() const { | ||||
LOCK(cs); | LOCK(cs); | ||||
// Estimate the overhead of mapTx to be 12 pointers + an allocation, as no | // Estimate the overhead of mapTx to be 12 pointers + an allocation, as no | ||||
// exact formula for boost::multi_index_contained is implemented. | // exact formula for boost::multi_index_contained is implemented. | ||||
return memusage::MallocUsage(sizeof(CTxMemPoolEntry) + | return memusage::MallocUsage(sizeof(CTxMemPoolEntry) + | ||||
12 * sizeof(void *)) * | 12 * sizeof(void *)) * | ||||
mapTx.size() + | mapTx.size() + | ||||
memusage::DynamicUsage(mapNextTx) + | memusage::DynamicUsage(mapNextTx) + | ||||
memusage::DynamicUsage(mapDeltas) + | memusage::DynamicUsage(mapDeltas) + | ||||
memusage::DynamicUsage(mapLinks) + | |||||
memusage::DynamicUsage(vTxHashes) + cachedInnerUsage; | memusage::DynamicUsage(vTxHashes) + cachedInnerUsage; | ||||
} | } | ||||
void CTxMemPool::RemoveUnbroadcastTx(const TxId &txid, const bool unchecked) { | void CTxMemPool::RemoveUnbroadcastTx(const TxId &txid, const bool unchecked) { | ||||
LOCK(cs); | LOCK(cs); | ||||
if (m_unbroadcast_txids.erase(txid)) { | if (m_unbroadcast_txids.erase(txid)) { | ||||
LogPrint( | LogPrint( | ||||
▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry) { | ||||
std::string dummy; | std::string dummy; | ||||
CalculateMemPoolAncestors(entry, setAncestors, nNoLimit, nNoLimit, nNoLimit, | CalculateMemPoolAncestors(entry, setAncestors, nNoLimit, nNoLimit, nNoLimit, | ||||
nNoLimit, dummy); | nNoLimit, dummy); | ||||
return addUnchecked(entry, setAncestors); | return addUnchecked(entry, setAncestors); | ||||
} | } | ||||
void CTxMemPool::UpdateChild(txiter entry, txiter child, bool add) { | void CTxMemPool::UpdateChild(txiter entry, txiter child, bool add) { | ||||
AssertLockHeld(cs); | AssertLockHeld(cs); | ||||
setEntries s; | CTxMemPoolEntry::Children s; | ||||
if (add && mapLinks[entry].children.insert(child).second) { | if (add && entry->GetMemPoolChildren().insert(*child).second) { | ||||
cachedInnerUsage += memusage::IncrementalDynamicUsage(s); | cachedInnerUsage += memusage::IncrementalDynamicUsage(s); | ||||
} else if (!add && mapLinks[entry].children.erase(child)) { | } else if (!add && entry->GetMemPoolChildren().erase(*child)) { | ||||
cachedInnerUsage -= memusage::IncrementalDynamicUsage(s); | cachedInnerUsage -= memusage::IncrementalDynamicUsage(s); | ||||
} | } | ||||
} | } | ||||
void CTxMemPool::UpdateParent(txiter entry, txiter parent, bool add) { | void CTxMemPool::UpdateParent(txiter entry, txiter parent, bool add) { | ||||
AssertLockHeld(cs); | AssertLockHeld(cs); | ||||
setEntries s; | CTxMemPoolEntry::Parents s; | ||||
if (add && mapLinks[entry].parents.insert(parent).second) { | if (add && entry->GetMemPoolParents().insert(*parent).second) { | ||||
cachedInnerUsage += memusage::IncrementalDynamicUsage(s); | cachedInnerUsage += memusage::IncrementalDynamicUsage(s); | ||||
} else if (!add && mapLinks[entry].parents.erase(parent)) { | } else if (!add && entry->GetMemPoolParents().erase(*parent)) { | ||||
cachedInnerUsage -= memusage::IncrementalDynamicUsage(s); | cachedInnerUsage -= memusage::IncrementalDynamicUsage(s); | ||||
} | } | ||||
} | } | ||||
const CTxMemPool::setEntries & | |||||
CTxMemPool::GetMemPoolParents(txiter entry) const { | |||||
assert(entry != mapTx.end()); | |||||
txlinksMap::const_iterator it = mapLinks.find(entry); | |||||
assert(it != mapLinks.end()); | |||||
return it->second.parents; | |||||
} | |||||
const CTxMemPool::setEntries & | |||||
CTxMemPool::GetMemPoolChildren(txiter entry) const { | |||||
assert(entry != mapTx.end()); | |||||
txlinksMap::const_iterator it = mapLinks.find(entry); | |||||
assert(it != mapLinks.end()); | |||||
return it->second.children; | |||||
} | |||||
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) { | ||||
return CFeeRate(int64_t(ceill(rollingMinimumFeeRate)) * SATOSHI); | return CFeeRate(int64_t(ceill(rollingMinimumFeeRate)) * SATOSHI); | ||||
} | } | ||||
int64_t time = GetTime(); | int64_t time = GetTime(); | ||||
if (time > lastRollingFeeUpdate + 10) { | if (time > lastRollingFeeUpdate + 10) { | ||||
▲ Show 20 Lines • Show All 80 Lines • ▼ Show 20 Lines | uint64_t CTxMemPool::CalculateDescendantMaximum(txiter entry) const { | ||||
candidates.push_back(entry); | candidates.push_back(entry); | ||||
uint64_t maximum = 0; | uint64_t maximum = 0; | ||||
while (candidates.size()) { | while (candidates.size()) { | ||||
txiter candidate = candidates.back(); | txiter candidate = candidates.back(); | ||||
candidates.pop_back(); | candidates.pop_back(); | ||||
if (!counted.insert(candidate).second) { | if (!counted.insert(candidate).second) { | ||||
continue; | continue; | ||||
} | } | ||||
const setEntries &parents = GetMemPoolParents(candidate); | const CTxMemPoolEntry::Parents &parents = | ||||
candidate->GetMemPoolParentsConst(); | |||||
if (parents.size() == 0) { | if (parents.size() == 0) { | ||||
maximum = std::max(maximum, candidate->GetCountWithDescendants()); | maximum = std::max(maximum, candidate->GetCountWithDescendants()); | ||||
} else { | } else { | ||||
for (txiter i : parents) { | for (const CTxMemPoolEntry &i : parents) { | ||||
candidates.push_back(i); | candidates.push_back(mapTx.iterator_to(i)); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
return maximum; | return maximum; | ||||
} | } | ||||
void CTxMemPool::GetTransactionAncestry(const TxId &txid, size_t &ancestors, | void CTxMemPool::GetTransactionAncestry(const TxId &txid, size_t &ancestors, | ||||
size_t &descendants) const { | size_t &descendants) const { | ||||
▲ Show 20 Lines • Show All 182 Lines • Show Last 20 Lines |