Changeset View
Changeset View
Standalone View
Standalone View
src/txmempool.cpp
Show First 20 Lines • Show All 263 Lines • ▼ Show 20 Lines | bool CTxMemPool::CalculateMemPoolAncestors( | ||||
} | } | ||||
return CalculateAncestorsAndCheckLimits( | return CalculateAncestorsAndCheckLimits( | ||||
entry.GetTxSize(), /* entry_count */ 1, setAncestors, staged_ancestors, | entry.GetTxSize(), /* entry_count */ 1, setAncestors, staged_ancestors, | ||||
limitAncestorCount, limitAncestorSize, limitDescendantCount, | limitAncestorCount, limitAncestorSize, limitDescendantCount, | ||||
limitDescendantSize, errString); | limitDescendantSize, errString); | ||||
} | } | ||||
void CTxMemPool::UpdateParentsOf(bool add, txiter it, | void CTxMemPool::UpdateParentsOf(bool add, txiter it) { | ||||
const setEntries *setAncestors) { | |||||
// add or remove this tx as a child of each parent | // add or remove this tx as a child of each parent | ||||
for (const CTxMemPoolEntry &parent : it->GetMemPoolParentsConst()) { | for (const CTxMemPoolEntry &parent : it->GetMemPoolParentsConst()) { | ||||
UpdateChild(mapTx.iterator_to(parent), it, add); | UpdateChild(mapTx.iterator_to(parent), it, add); | ||||
} | } | ||||
// Remove this after wellington | |||||
if (setAncestors && !wellingtonLatched) { | |||||
const int64_t updateCount = (add ? 1 : -1); | |||||
const int64_t updateSize = updateCount * it->GetTxSize(); | |||||
const int64_t updateSigChecks = updateCount * it->GetSigChecks(); | |||||
const Amount updateFee = updateCount * it->GetModifiedFee(); | |||||
for (txiter ancestorIt : *setAncestors) { | |||||
mapTx.modify(ancestorIt, | |||||
update_descendant_state(updateSize, updateFee, | |||||
updateCount, updateSigChecks)); | |||||
} | |||||
} | |||||
} | |||||
void CTxMemPool::UpdateEntryForAncestors(txiter it, | |||||
const setEntries *setAncestors) { | |||||
if (!setAncestors || wellingtonLatched) { | |||||
return; | |||||
} | |||||
int64_t updateCount = setAncestors->size(); | |||||
int64_t updateSize = 0; | |||||
int64_t updateSigChecks = 0; | |||||
Amount updateFee = Amount::zero(); | |||||
for (txiter ancestorIt : *setAncestors) { | |||||
updateSize += ancestorIt->GetTxSize(); | |||||
updateFee += ancestorIt->GetModifiedFee(); | |||||
updateSigChecks += ancestorIt->GetSigChecks(); | |||||
} | |||||
mapTx.modify(it, update_ancestor_state(updateSize, updateFee, updateCount, | |||||
updateSigChecks)); | |||||
} | } | ||||
void CTxMemPool::UpdateChildrenForRemoval(txiter it) { | void CTxMemPool::UpdateChildrenForRemoval(txiter it) { | ||||
const CTxMemPoolEntry::Children &children = it->GetMemPoolChildrenConst(); | const CTxMemPoolEntry::Children &children = it->GetMemPoolChildrenConst(); | ||||
for (const CTxMemPoolEntry &updateIt : children) { | for (const CTxMemPoolEntry &updateIt : children) { | ||||
UpdateParent(mapTx.iterator_to(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) { | ||||
if (!wellingtonLatched) { | |||||
// remove this branch after wellington | |||||
// slow quadratic branch, only for pre-activation compatibility | |||||
// For each entry, walk back all ancestors and decrement size associated | |||||
// with this transaction. | |||||
if (updateDescendants) { | |||||
// updateDescendants should be true whenever we're not recursively | |||||
// 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 | |||||
// CTxMemPool::Parents 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) { | |||||
setEntries setDescendants; | |||||
CalculateDescendants(removeIt, setDescendants); | |||||
setDescendants.erase(removeIt); // don't update state for self | |||||
int64_t modifySize = -int64_t(removeIt->GetTxSize()); | |||||
Amount modifyFee = -1 * removeIt->GetModifiedFee(); | |||||
int modifySigChecks = -removeIt->GetSigChecks(); | |||||
for (txiter dit : setDescendants) { | |||||
mapTx.modify(dit, | |||||
update_ancestor_state(modifySize, modifyFee, | |||||
-1, modifySigChecks)); | |||||
} | |||||
} | |||||
} | |||||
for (txiter removeIt : entriesToRemove) { | |||||
setEntries setAncestors; | |||||
const CTxMemPoolEntry &entry = *removeIt; | |||||
std::string dummy; | |||||
// 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 state, then using true or false should both be | |||||
// correct, though false should be a bit faster. | |||||
CalculateMemPoolAncestors(entry, setAncestors, nNoLimit, nNoLimit, | |||||
nNoLimit, nNoLimit, dummy, false); | |||||
// Note that UpdateParentsOf severs the child links that point to | |||||
// removeIt in the entries for the parents of removeIt. | |||||
UpdateParentsOf(false, removeIt, &setAncestors); | |||||
} | |||||
} else { | |||||
for (txiter removeIt : entriesToRemove) { | for (txiter removeIt : entriesToRemove) { | ||||
// Note that UpdateParentsOf severs the child links that point to | // Note that UpdateParentsOf 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. | ||||
UpdateParentsOf(false, removeIt); | UpdateParentsOf(false, removeIt); | ||||
} | } | ||||
} | |||||
// After updating all the parent links, we can now sever the link between | // After updating all the parent links, 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 | ||||
// CTxMemPoolEntry::m_parents for each direct child of a transaction being | // CTxMemPoolEntry::m_parents for each direct child of a transaction being | ||||
// removed). | // removed). | ||||
for (txiter removeIt : entriesToRemove) { | for (txiter removeIt : entriesToRemove) { | ||||
UpdateChildrenForRemoval(removeIt); | UpdateChildrenForRemoval(removeIt); | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 81 Lines • ▼ Show 20 Lines | void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entryIn, | ||||
// guaranteed that a new transaction arriving will not have any children, | // guaranteed that a new transaction arriving will not have any children, | ||||
// because such children would be orphans. | // because such children would be orphans. | ||||
// Update ancestors with information about this tx | // Update ancestors with information about this tx | ||||
for (const auto &pit : GetIterSet(setParentTransactions)) { | for (const auto &pit : GetIterSet(setParentTransactions)) { | ||||
UpdateParent(newit, pit, true); | UpdateParent(newit, pit, true); | ||||
} | } | ||||
const setEntries *pSetEntries = wellingtonLatched ? nullptr : &setAncestors; | UpdateParentsOf(true, newit); | ||||
UpdateParentsOf(true, newit, pSetEntries); | |||||
UpdateEntryForAncestors(newit, pSetEntries); | |||||
nTransactionsUpdated++; | nTransactionsUpdated++; | ||||
totalTxSize += entry.GetTxSize(); | totalTxSize += entry.GetTxSize(); | ||||
m_total_fee += entry.GetFee(); | m_total_fee += entry.GetFee(); | ||||
} | } | ||||
void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason) { | void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason) { | ||||
// We increment mempool sequence value no matter removal reason | // We increment mempool sequence value no matter removal reason | ||||
▲ Show 20 Lines • Show All 225 Lines • ▼ Show 20 Lines | for (const CTxMemPoolEntry &entry : mapTx.get<entry_id>()) { | ||||
// Verify ancestor state is correct. | // Verify ancestor state is correct. | ||||
setEntries setAncestors; | setEntries setAncestors; | ||||
std::string dummy; | std::string dummy; | ||||
const bool ok = CalculateMemPoolAncestors( | const bool ok = CalculateMemPoolAncestors( | ||||
entry, setAncestors, nNoLimit, nNoLimit, nNoLimit, nNoLimit, dummy); | entry, setAncestors, nNoLimit, nNoLimit, nNoLimit, nNoLimit, dummy); | ||||
assert(ok); | assert(ok); | ||||
if (!wellingtonLatched) { | |||||
uint64_t nCountCheck = setAncestors.size() + 1; | |||||
uint64_t nSizeCheck = entry.GetTxSize(); | |||||
Amount nFeesCheck = entry.GetModifiedFee(); | |||||
int64_t nSigChecksCheck = entry.GetSigChecks(); | |||||
for (txiter ancestorIt : setAncestors) { | |||||
nSizeCheck += ancestorIt->GetTxSize(); | |||||
nFeesCheck += ancestorIt->GetModifiedFee(); | |||||
nSigChecksCheck += ancestorIt->GetSigChecks(); | |||||
} | |||||
assert(entry.GetCountWithAncestors() == nCountCheck); | |||||
assert(entry.GetSizeWithAncestors() == nSizeCheck); | |||||
assert(entry.GetSigChecksWithAncestors() == nSigChecksCheck); | |||||
assert(entry.GetModFeesWithAncestors() == nFeesCheck); | |||||
} | |||||
// all ancestors should have entryId < this tx's entryId | // all ancestors should have entryId < this tx's entryId | ||||
for (const auto &ancestor : setAncestors) { | for (const auto &ancestor : setAncestors) { | ||||
assert(ancestor->GetEntryId() < entry.GetEntryId()); | assert(ancestor->GetEntryId() < entry.GetEntryId()); | ||||
} | } | ||||
// Check children against mapNextTx | // Check children against mapNextTx | ||||
CTxMemPoolEntry::Children setChildrenCheck; | CTxMemPoolEntry::Children setChildrenCheck; | ||||
auto iter = mapNextTx.lower_bound(COutPoint(entry.GetTx().GetId(), 0)); | auto iter = mapNextTx.lower_bound(COutPoint(entry.GetTx().GetId(), 0)); | ||||
Show All 9 Lines | for (const CTxMemPoolEntry &entry : mapTx.get<entry_id>()) { | ||||
child_sizes += childIt->GetTxSize(); | child_sizes += childIt->GetTxSize(); | ||||
child_sigChecks += childIt->GetSigChecks(); | child_sigChecks += childIt->GetSigChecks(); | ||||
} | } | ||||
} | } | ||||
assert(setChildrenCheck.size() == | assert(setChildrenCheck.size() == | ||||
entry.GetMemPoolChildrenConst().size()); | entry.GetMemPoolChildrenConst().size()); | ||||
assert(std::equal(setChildrenCheck.begin(), setChildrenCheck.end(), | assert(std::equal(setChildrenCheck.begin(), setChildrenCheck.end(), | ||||
entry.GetMemPoolChildrenConst().begin(), comp)); | entry.GetMemPoolChildrenConst().begin(), comp)); | ||||
if (!wellingtonLatched) { | |||||
// Also check to make sure size is greater than sum with immediate | |||||
// children. Just a sanity check, not definitive that this calc is | |||||
// correct... | |||||
assert(entry.GetSizeWithDescendants() >= | |||||
child_sizes + entry.GetTxSize()); | |||||
assert(entry.GetSigChecksWithDescendants() >= | |||||
child_sigChecks + entry.GetSigChecks()); | |||||
} | |||||
// Not used. CheckTxInputs() should always pass | // Not used. CheckTxInputs() should always pass | ||||
TxValidationState dummy_state; | TxValidationState dummy_state; | ||||
Amount txfee{Amount::zero()}; | Amount txfee{Amount::zero()}; | ||||
assert(!tx.IsCoinBase()); | assert(!tx.IsCoinBase()); | ||||
assert(Consensus::CheckTxInputs(tx, dummy_state, mempoolDuplicate, | assert(Consensus::CheckTxInputs(tx, dummy_state, mempoolDuplicate, | ||||
spendheight, txfee)); | spendheight, txfee)); | ||||
for (const auto &input : tx.vin) { | for (const auto &input : tx.vin) { | ||||
▲ Show 20 Lines • Show All 95 Lines • ▼ Show 20 Lines | void CTxMemPool::PrioritiseTransaction(const TxId &txid, | ||||
{ | { | ||||
LOCK(cs); | LOCK(cs); | ||||
Amount &delta = mapDeltas[txid]; | Amount &delta = mapDeltas[txid]; | ||||
delta += nFeeDelta; | delta += nFeeDelta; | ||||
txiter it = mapTx.find(txid); | txiter it = mapTx.find(txid); | ||||
if (it != mapTx.end()) { | if (it != mapTx.end()) { | ||||
mapTx.modify( | mapTx.modify( | ||||
it, [&delta](CTxMemPoolEntry &e) { e.UpdateFeeDelta(delta); }); | it, [&delta](CTxMemPoolEntry &e) { e.UpdateFeeDelta(delta); }); | ||||
// Remove after wellington | |||||
if (!wellingtonLatched) { | |||||
// Now update all ancestors' modified fees with descendants | |||||
setEntries setAncestors; | |||||
std::string dummy; | |||||
CalculateMemPoolAncestors(*it, setAncestors, nNoLimit, nNoLimit, | |||||
nNoLimit, nNoLimit, dummy, false); | |||||
for (txiter ancestorIt : setAncestors) { | |||||
mapTx.modify(ancestorIt, | |||||
update_descendant_state(0, nFeeDelta, 0, 0)); | |||||
} | |||||
// Now update all descendants' modified fees with ancestors | |||||
setEntries setDescendants; | |||||
CalculateDescendants(it, setDescendants); | |||||
setDescendants.erase(it); | |||||
for (txiter descendantIt : setDescendants) { | |||||
mapTx.modify(descendantIt, | |||||
update_ancestor_state(0, nFeeDelta, 0, 0)); | |||||
} | |||||
} | |||||
++nTransactionsUpdated; | ++nTransactionsUpdated; | ||||
} | } | ||||
} | } | ||||
LogPrintf("PrioritiseTransaction: %s fee += %s\n", txid.ToString(), | LogPrintf("PrioritiseTransaction: %s fee += %s\n", txid.ToString(), | ||||
FormatMoney(nFeeDelta)); | FormatMoney(nFeeDelta)); | ||||
} | } | ||||
void CTxMemPool::ApplyDelta(const TxId &txid, Amount &nFeeDelta) const { | void CTxMemPool::ApplyDelta(const TxId &txid, Amount &nFeeDelta) const { | ||||
▲ Show 20 Lines • Show All 144 Lines • ▼ Show 20 Lines | void CTxMemPool::LimitSize(CCoinsViewCache &coins_cache, size_t limit, | ||||
TrimToSize(limit, &vNoSpendsRemaining); | TrimToSize(limit, &vNoSpendsRemaining); | ||||
for (const COutPoint &removed : vNoSpendsRemaining) { | for (const COutPoint &removed : vNoSpendsRemaining) { | ||||
coins_cache.Uncache(removed); | coins_cache.Uncache(removed); | ||||
} | } | ||||
} | } | ||||
void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry) { | void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry) { | ||||
setEntries setAncestors; | setEntries setAncestors; | ||||
if (!wellingtonLatched) { | |||||
std::string dummy; | |||||
CalculateMemPoolAncestors(entry, setAncestors, nNoLimit, nNoLimit, | |||||
nNoLimit, 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); | ||||
CTxMemPoolEntry::Children s; | CTxMemPoolEntry::Children s; | ||||
if (add && entry->GetMemPoolChildren().insert(*child).second) { | if (add && entry->GetMemPoolChildren().insert(*child).second) { | ||||
cachedInnerUsage += memusage::IncrementalDynamicUsage(s); | cachedInnerUsage += memusage::IncrementalDynamicUsage(s); | ||||
▲ Show 20 Lines • Show All 83 Lines • ▼ Show 20 Lines | void CTxMemPool::TrimToSize(size_t sizelimit, | ||||
if (maxFeeRateRemoved > CFeeRate(Amount::zero())) { | if (maxFeeRateRemoved > CFeeRate(Amount::zero())) { | ||||
LogPrint(BCLog::MEMPOOL, | LogPrint(BCLog::MEMPOOL, | ||||
"Removed %u txn, rolling minimum fee bumped to %s\n", | "Removed %u txn, rolling minimum fee bumped to %s\n", | ||||
nTxnRemoved, maxFeeRateRemoved.ToString()); | nTxnRemoved, maxFeeRateRemoved.ToString()); | ||||
} | } | ||||
} | } | ||||
/// Remove after wellington; after wellington activates this will be inaccurate | |||||
uint64_t CTxMemPool::CalculateDescendantMaximum(txiter entry) const { | |||||
// find parent with highest descendant count | |||||
std::vector<txiter> candidates; | |||||
setEntries counted; | |||||
candidates.push_back(entry); | |||||
uint64_t maximum = 0; | |||||
while (candidates.size()) { | |||||
txiter candidate = candidates.back(); | |||||
candidates.pop_back(); | |||||
if (!counted.insert(candidate).second) { | |||||
continue; | |||||
} | |||||
const CTxMemPoolEntry::Parents &parents = | |||||
candidate->GetMemPoolParentsConst(); | |||||
if (parents.size() == 0) { | |||||
maximum = std::max(maximum, candidate->GetCountWithDescendants()); | |||||
} else { | |||||
for (const CTxMemPoolEntry &i : parents) { | |||||
candidates.push_back(mapTx.iterator_to(i)); | |||||
} | |||||
} | |||||
} | |||||
return maximum; | |||||
} | |||||
void CTxMemPool::GetTransactionAncestry(const TxId &txid, size_t &ancestors, | |||||
size_t &descendants, | |||||
size_t *const ancestorsize, | |||||
Amount *const ancestorfees) const { | |||||
LOCK(cs); | |||||
auto it = mapTx.find(txid); | |||||
ancestors = descendants = 0; | |||||
if (it != mapTx.end()) { | |||||
ancestors = it->GetCountWithAncestors(); | |||||
if (ancestorsize) { | |||||
*ancestorsize = it->GetSizeWithAncestors(); | |||||
} | |||||
if (ancestorfees) { | |||||
*ancestorfees = it->GetModFeesWithAncestors(); | |||||
} | |||||
descendants = CalculateDescendantMaximum(it); | |||||
} | |||||
} | |||||
bool CTxMemPool::IsLoaded() const { | bool CTxMemPool::IsLoaded() const { | ||||
LOCK(cs); | LOCK(cs); | ||||
return m_is_loaded; | return m_is_loaded; | ||||
} | } | ||||
void CTxMemPool::SetIsLoaded(bool loaded) { | void CTxMemPool::SetIsLoaded(bool loaded) { | ||||
LOCK(cs); | LOCK(cs); | ||||
m_is_loaded = loaded; | m_is_loaded = loaded; | ||||
▲ Show 20 Lines • Show All 174 Lines • Show Last 20 Lines |