Changeset View
Changeset View
Standalone View
Standalone View
src/txmempool.cpp
Show First 20 Lines • Show All 73 Lines • ▼ Show 20 Lines | 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 setMemPoolChildren 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<uint256> &setExclude) { | const std::set<txhash_t> &setExclude) { | ||||
setEntries stageEntries, setAllDescendants; | setEntries stageEntries, setAllDescendants; | ||||
stageEntries = GetMemPoolChildren(updateIt); | stageEntries = GetMemPoolChildren(updateIt); | ||||
while (!stageEntries.empty()) { | while (!stageEntries.empty()) { | ||||
const txiter cit = *stageEntries.begin(); | const txiter cit = *stageEntries.begin(); | ||||
setAllDescendants.insert(cit); | setAllDescendants.insert(cit); | ||||
stageEntries.erase(cit); | stageEntries.erase(cit); | ||||
const setEntries &setChildren = GetMemPoolChildren(cit); | const setEntries &setChildren = GetMemPoolChildren(cit); | ||||
Show All 12 Lines | while (!stageEntries.empty()) { | ||||
} | } | ||||
} | } | ||||
// setAllDescendants now contains all in-mempool descendants of updateIt. | // setAllDescendants 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; | ||||
Amount modifyFee = 0; | Amount modifyFee = 0; | ||||
int64_t modifyCount = 0; | int64_t modifyCount = 0; | ||||
for (txiter cit : setAllDescendants) { | for (txiter cit : setAllDescendants) { | ||||
if (!setExclude.count(cit->GetTx().GetId())) { | if (!setExclude.count(cit->GetTx().GetHash())) { | ||||
modifySize += cit->GetTxSize(); | modifySize += cit->GetTxSize(); | ||||
modifyFee += cit->GetModifiedFee(); | modifyFee += cit->GetModifiedFee(); | ||||
modifyCount++; | modifyCount++; | ||||
cachedDescendants[updateIt].insert(cit); | cachedDescendants[updateIt].insert(cit); | ||||
// Update ancestor state for each descendant | // Update ancestor state for each descendant | ||||
mapTx.modify(cit, | mapTx.modify(cit, | ||||
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)); | ||||
} | } | ||||
// vHashesToUpdate is the set of transaction hashes from a disconnected block | // vHashesToUpdate is the set of transaction hashes from a disconnected block | ||||
// which has been re-added to the mempool. For each entry, look for descendants | // which has been re-added to the mempool. For each entry, look for descendants | ||||
// that are outside hashesToUpdate, and add fee/size information for such | // that are outside hashesToUpdate, and add fee/size information for such | ||||
// descendants to the parent. For each such descendant, also update the ancestor | // descendants to the parent. For each such descendant, also update the ancestor | ||||
// state to include the parent. | // state to include the parent. | ||||
void CTxMemPool::UpdateTransactionsFromBlock( | void CTxMemPool::UpdateTransactionsFromBlock( | ||||
const std::vector<uint256> &vHashesToUpdate) { | const std::vector<txhash_t> &vHashesToUpdate) { | ||||
LOCK(cs); | LOCK(cs); | ||||
// For each entry in vHashesToUpdate, store the set of in-mempool, but not | // For each entry in vHashesToUpdate, store the set of in-mempool, but not | ||||
// in-vHashesToUpdate transactions, so that we don't have to recalculate | // in-vHashesToUpdate transactions, so that we don't have to recalculate | ||||
// descendants when we come across a previously seen entry. | // descendants when we come across a previously seen entry. | ||||
cacheMap mapMemPoolDescendantsToUpdate; | cacheMap mapMemPoolDescendantsToUpdate; | ||||
// Use a set for lookups into vHashesToUpdate (these entries are already | // Use a set for lookups into vHashesToUpdate (these entries are already | ||||
// accounted for in the state of their ancestors) | // accounted for in the state of their ancestors) | ||||
std::set<uint256> setAlreadyIncluded(vHashesToUpdate.begin(), | std::set<txhash_t> setAlreadyIncluded(vHashesToUpdate.begin(), | ||||
vHashesToUpdate.end()); | vHashesToUpdate.end()); | ||||
// Iterate in reverse, so that whenever we are looking at at a transaction | // Iterate in reverse, so that whenever we are looking at 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 | // setMemPoolChildren will be updated, an assumption made in | ||||
// UpdateForDescendants. | // UpdateForDescendants. | ||||
for (const uint256 &hash : boost::adaptors::reverse(vHashesToUpdate)) { | for (const txhash_t &txhash : boost::adaptors::reverse(vHashesToUpdate)) { | ||||
// we cache the in-mempool children to avoid duplicate updates | // we cache the in-mempool children to avoid duplicate updates | ||||
setEntries setChildren; | setEntries setChildren; | ||||
// calculate children from mapNextTx | // calculate children from mapNextTx | ||||
txiter it = mapTx.find(hash); | txiter it = mapTx.find(txhash); | ||||
if (it == mapTx.end()) { | if (it == mapTx.end()) { | ||||
continue; | continue; | ||||
} | } | ||||
auto iter = mapNextTx.lower_bound(COutPoint(hash, 0)); | |||||
unspentid_t unspentid = it->GetTx().Getunspentid(); | |||||
auto iter = mapNextTx.lower_bound(COutPoint(unspentid, 0)); | |||||
// First calculate the children, and update setMemPoolChildren to | // First calculate the children, and update setMemPoolChildren to | ||||
// include them, and update their setMemPoolParents to include this tx. | // include them, and update their setMemPoolParents to include this tx. | ||||
for (; iter != mapNextTx.end() && iter->first->hash == hash; ++iter) { | for (; iter != mapNextTx.end() && iter->first->unspentid == unspentid; | ||||
const uint256 &childHash = iter->second->GetId(); | ++iter) { | ||||
const txhash_t &childHash = iter->second->GetHash(); | |||||
txiter childIter = mapTx.find(childHash); | txiter childIter = mapTx.find(childHash); | ||||
assert(childIter != mapTx.end()); | assert(childIter != mapTx.end()); | ||||
// We can skip updating entries we've encountered before or that are | // We can skip updating entries we've encountered before or that are | ||||
// in the block (which are already accounted for). | // in the block (which are already accounted for). | ||||
if (setChildren.insert(childIter).second && | if (setChildren.insert(childIter).second && | ||||
!setAlreadyIncluded.count(childHash)) { | !setAlreadyIncluded.count(childHash)) { | ||||
UpdateChild(it, childIter, true); | UpdateChild(it, childIter, true); | ||||
UpdateParent(childIter, it, true); | UpdateParent(childIter, it, true); | ||||
Show All 14 Lines | bool CTxMemPool::CalculateMemPoolAncestors( | ||||
setEntries parentHashes; | setEntries parentHashes; | ||||
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 (unsigned int i = 0; i < tx.vin.size(); i++) { | for (unsigned int i = 0; i < tx.vin.size(); i++) { | ||||
txiter piter = mapTx.find(tx.vin[i].prevout.hash); | txiter piter = findByunspentid(tx.vin[i].prevout.unspentid); | ||||
if (piter != mapTx.end()) { | if (piter != mapTx.end()) { | ||||
parentHashes.insert(piter); | parentHashes.insert(piter); | ||||
if (parentHashes.size() + 1 > limitAncestorCount) { | if (parentHashes.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; | ||||
} | } | ||||
Show All 14 Lines | while (!parentHashes.empty()) { | ||||
setAncestors.insert(stageit); | setAncestors.insert(stageit); | ||||
parentHashes.erase(stageit); | parentHashes.erase(stageit); | ||||
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().GetHash().ToString(), limitDescendantSize); | ||||
return false; | return false; | ||||
} else if (stageit->GetCountWithDescendants() + 1 > | } else if (stageit->GetCountWithDescendants() + 1 > | ||||
limitDescendantCount) { | 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().GetHash().ToString(), | ||||
limitDescendantCount); | limitDescendantCount); | ||||
return false; | return false; | ||||
} else if (totalSizeWithAncestors > limitAncestorSize) { | } else 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; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 163 Lines • ▼ Show 20 Lines | unsigned int CTxMemPool::GetTransactionsUpdated() const { | ||||
return nTransactionsUpdated; | return nTransactionsUpdated; | ||||
} | } | ||||
void CTxMemPool::AddTransactionsUpdated(unsigned int n) { | void CTxMemPool::AddTransactionsUpdated(unsigned int n) { | ||||
LOCK(cs); | LOCK(cs); | ||||
nTransactionsUpdated += n; | nTransactionsUpdated += n; | ||||
} | } | ||||
bool CTxMemPool::addUnchecked(const uint256 &hash, const CTxMemPoolEntry &entry, | bool CTxMemPool::addUnchecked(const txhash_t &txhash, | ||||
const CTxMemPoolEntry &entry, | |||||
setEntries &setAncestors, bool validFeeEstimate) { | setEntries &setAncestors, bool validFeeEstimate) { | ||||
NotifyEntryAdded(entry.GetSharedTx()); | NotifyEntryAdded(entry.GetSharedTx()); | ||||
// 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. | ||||
LOCK(cs); | LOCK(cs); | ||||
indexed_transaction_set::iterator newit = mapTx.insert(entry).first; | indexed_transaction_set::iterator newit = mapTx.insert(entry).first; | ||||
mapLinks.insert(make_pair(newit, TxLinks())); | 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. | ||||
std::map<uint256, std::pair<double, Amount>>::const_iterator pos = | std::map<txhash_t, std::pair<double, Amount>>::const_iterator pos = | ||||
mapDeltas.find(hash); | mapDeltas.find(txhash); | ||||
if (pos != mapDeltas.end()) { | if (pos != mapDeltas.end()) { | ||||
const std::pair<double, Amount> &deltas = pos->second; | const std::pair<double, Amount> &deltas = pos->second; | ||||
if (deltas.second != 0) { | if (deltas.second != 0) { | ||||
mapTx.modify(newit, update_fee_delta(deltas.second)); | mapTx.modify(newit, update_fee_delta(deltas.second)); | ||||
} | } | ||||
} | } | ||||
// Update cachedInnerUsage to include contained transaction's usage. | // Update cachedInnerUsage to include contained transaction's usage. | ||||
// (When we update the entry for in-mempool parents, memory usage will be | // (When we update the entry for in-mempool parents, memory usage will be | ||||
// further updated.) | // further updated.) | ||||
cachedInnerUsage += entry.DynamicMemoryUsage(); | cachedInnerUsage += entry.DynamicMemoryUsage(); | ||||
const CTransaction &tx = newit->GetTx(); | const CTransaction &tx = newit->GetTx(); | ||||
std::set<uint256> setParentTransactions; | std::set<unspentid_t> 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, &tx)); | mapNextTx.insert(std::make_pair(&tx.vin[i].prevout, &tx)); | ||||
setParentTransactions.insert(tx.vin[i].prevout.hash); | setParentTransactions.insert(tx.vin[i].prevout.unspentid); | ||||
} | } | ||||
// Don't bother worrying about child transactions of this one. Normal case | // Don't bother worrying about child transactions of this one. Normal case | ||||
// of a new transaction arriving is that there can't be any children, | // of a new transaction arriving is that there can't be any children, | ||||
// because such children would be orphans. An exception to that is if a | // because such children would be orphans. An exception to that is if a | ||||
// transaction enters that used to be in a block. In that case, our | // transaction enters that used to be in a block. In that case, our | ||||
// disconnect block logic will call UpdateTransactionsFromBlock to clean up | // disconnect block logic will call UpdateTransactionsFromBlock to clean up | ||||
// the mess we're leaving here. | // the mess we're leaving here. | ||||
// Update ancestors with information about this tx | // Update ancestors with information about this tx | ||||
for (const uint256 &phash : setParentTransactions) { | for (const unspentid_t &phash : setParentTransactions) { | ||||
txiter pit = mapTx.find(phash); | auto pit = findByunspentid(phash); | ||||
if (pit != mapTx.end()) { | if (pit != mapTx.end()) { | ||||
UpdateParent(newit, pit, true); | UpdateParent(newit, pit, true); | ||||
} | } | ||||
} | } | ||||
UpdateAncestorsOf(true, newit, setAncestors); | UpdateAncestorsOf(true, newit, setAncestors); | ||||
UpdateEntryForAncestors(newit, setAncestors); | UpdateEntryForAncestors(newit, setAncestors); | ||||
nTransactionsUpdated++; | nTransactionsUpdated++; | ||||
totalTxSize += entry.GetTxSize(); | totalTxSize += entry.GetTxSize(); | ||||
minerPolicyEstimator->processTransaction(entry, validFeeEstimate); | minerPolicyEstimator->processTransaction(entry, validFeeEstimate); | ||||
vTxHashes.emplace_back(tx.GetHash(), newit); | vTxHashes.emplace_back(tx.GetHash(), newit); | ||||
newit->vTxHashesIdx = vTxHashes.size() - 1; | newit->vTxHashesIdx = vTxHashes.size() - 1; | ||||
return true; | return true; | ||||
} | } | ||||
void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason) { | void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason) { | ||||
NotifyEntryRemoved(it->GetSharedTx(), reason); | NotifyEntryRemoved(it->GetSharedTx(), reason); | ||||
const uint256 txid = it->GetTx().GetId(); | const txhash_t txhash = it->GetTx().GetHash(); | ||||
for (const CTxIn &txin : it->GetTx().vin) { | for (const CTxIn &txin : it->GetTx().vin) { | ||||
mapNextTx.erase(txin.prevout); | mapNextTx.erase(txin.prevout); | ||||
} | } | ||||
if (vTxHashes.size() > 1) { | if (vTxHashes.size() > 1) { | ||||
vTxHashes[it->vTxHashesIdx] = std::move(vTxHashes.back()); | vTxHashes[it->vTxHashesIdx] = std::move(vTxHashes.back()); | ||||
vTxHashes[it->vTxHashesIdx].second->vTxHashesIdx = it->vTxHashesIdx; | vTxHashes[it->vTxHashesIdx].second->vTxHashesIdx = it->vTxHashesIdx; | ||||
vTxHashes.pop_back(); | vTxHashes.pop_back(); | ||||
if (vTxHashes.size() * 2 < vTxHashes.capacity()) | if (vTxHashes.size() * 2 < vTxHashes.capacity()) | ||||
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(mapLinks[it].parents) + | ||||
memusage::DynamicUsage(mapLinks[it].children); | memusage::DynamicUsage(mapLinks[it].children); | ||||
mapLinks.erase(it); | mapLinks.erase(it); | ||||
mapTx.erase(it); | mapTx.erase(it); | ||||
nTransactionsUpdated++; | nTransactionsUpdated++; | ||||
minerPolicyEstimator->removeTx(txid); | minerPolicyEstimator->removeTx(txhash); | ||||
} | } | ||||
// 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 | // setMemPoolChildren is correct for tx and all descendants. Also assumes that | ||||
// if an entry is in setDescendants already, then all in-mempool descendants of | // if an entry is in setDescendants already, then all in-mempool descendants of | ||||
// it are already in setDescendants as well, so that we can save time by not | // it are already in setDescendants as well, so that we can save time by not | ||||
// iterating over those entries. | // iterating over those entries. | ||||
Show All 21 Lines | |||||
} | } | ||||
void CTxMemPool::removeRecursive(const CTransaction &origTx, | void CTxMemPool::removeRecursive(const CTransaction &origTx, | ||||
MemPoolRemovalReason reason) { | MemPoolRemovalReason reason) { | ||||
// Remove transaction from memory pool | // Remove transaction from memory pool | ||||
{ | { | ||||
LOCK(cs); | LOCK(cs); | ||||
setEntries txToRemove; | setEntries txToRemove; | ||||
txiter origit = mapTx.find(origTx.GetId()); | txiter origit = mapTx.find(origTx.GetHash()); | ||||
if (origit != mapTx.end()) { | if (origit != mapTx.end()) { | ||||
txToRemove.insert(origit); | txToRemove.insert(origit); | ||||
} else { | } else { | ||||
// When recursively removing but origTx isn't in the mempool be sure | // When recursively removing but origTx isn't in the mempool be sure | ||||
// to remove any children that are in the pool. This can happen | // to remove any children that are in the pool. This can happen | ||||
// during chain re-orgs if origTx isn't re-accepted into the mempool | // during chain re-orgs if origTx isn't re-accepted into the mempool | ||||
// for any reason. | // for any reason. | ||||
for (unsigned int i = 0; i < origTx.vout.size(); i++) { | for (unsigned int i = 0; i < origTx.vout.size(); i++) { | ||||
auto it = mapNextTx.find(COutPoint(origTx.GetId(), i)); | auto it = mapNextTx.find(COutPoint(origTx.Getunspentid(), i)); | ||||
if (it == mapNextTx.end()) continue; | if (it == mapNextTx.end()) continue; | ||||
txiter nextit = mapTx.find(it->second->GetId()); | txiter nextit = mapTx.find(it->second->GetHash()); | ||||
assert(nextit != mapTx.end()); | assert(nextit != mapTx.end()); | ||||
txToRemove.insert(nextit); | txToRemove.insert(nextit); | ||||
} | } | ||||
} | } | ||||
setEntries setAllRemoves; | setEntries setAllRemoves; | ||||
for (txiter it : txToRemove) { | for (txiter it : txToRemove) { | ||||
CalculateDescendants(it, setAllRemoves); | CalculateDescendants(it, setAllRemoves); | ||||
} | } | ||||
Show All 22 Lines | for (indexed_transaction_set::const_iterator it = mapTx.begin(); | ||||
!CheckSequenceLocks(tx, flags, &lp, validLP)) { | !CheckSequenceLocks(tx, flags, &lp, validLP)) { | ||||
// Note if CheckSequenceLocks fails the LockPoints may still be | // Note if CheckSequenceLocks fails the LockPoints may still be | ||||
// invalid. So it's critical that we remove the tx and not depend on | // invalid. So it's critical that we remove the tx and not depend on | ||||
// the LockPoints. | // the LockPoints. | ||||
txToRemove.insert(it); | txToRemove.insert(it); | ||||
} else if (it->GetSpendsCoinbase()) { | } else if (it->GetSpendsCoinbase()) { | ||||
for (const CTxIn &txin : tx.vin) { | for (const CTxIn &txin : tx.vin) { | ||||
indexed_transaction_set::const_iterator it2 = | indexed_transaction_set::const_iterator it2 = | ||||
mapTx.find(txin.prevout.hash); | findByunspentid(txin.prevout.unspentid); | ||||
if (it2 != mapTx.end()) { | if (it2 != mapTx.end()) { | ||||
continue; | continue; | ||||
} | } | ||||
const Coin &coin = pcoins->AccessCoin(txin.prevout); | const Coin &coin = pcoins->AccessCoin(txin.prevout); | ||||
if (nCheckFrequency != 0) assert(!coin.IsSpent()); | if (nCheckFrequency != 0) assert(!coin.IsSpent()); | ||||
if (coin.IsSpent() || | if (coin.IsSpent() || | ||||
(coin.IsCoinBase() && | (coin.IsCoinBase() && | ||||
Show All 18 Lines | |||||
void CTxMemPool::removeConflicts(const CTransaction &tx) { | void CTxMemPool::removeConflicts(const CTransaction &tx) { | ||||
// Remove transactions which depend on inputs of tx, recursively | // Remove transactions which depend on inputs of tx, recursively | ||||
LOCK(cs); | LOCK(cs); | ||||
for (const CTxIn &txin : tx.vin) { | for (const CTxIn &txin : tx.vin) { | ||||
auto it = mapNextTx.find(txin.prevout); | auto it = mapNextTx.find(txin.prevout); | ||||
if (it != mapNextTx.end()) { | if (it != mapNextTx.end()) { | ||||
const CTransaction &txConflict = *it->second; | const CTransaction &txConflict = *it->second; | ||||
if (txConflict != tx) { | if (txConflict != tx) { | ||||
ClearPrioritisation(txConflict.GetId()); | ClearPrioritisation(txConflict.GetHash()); | ||||
removeRecursive(txConflict, MemPoolRemovalReason::CONFLICT); | removeRecursive(txConflict, MemPoolRemovalReason::CONFLICT); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* Called when a block is connected. Removes from mempool and updates the miner | * Called when a block is connected. Removes from mempool and updates the miner | ||||
* fee estimator. | * fee estimator. | ||||
*/ | */ | ||||
void CTxMemPool::removeForBlock(const std::vector<CTransactionRef> &vtx, | void CTxMemPool::removeForBlock(const std::vector<CTransactionRef> &vtx, | ||||
unsigned int nBlockHeight) { | unsigned int nBlockHeight) { | ||||
LOCK(cs); | LOCK(cs); | ||||
std::vector<const CTxMemPoolEntry *> entries; | std::vector<const CTxMemPoolEntry *> entries; | ||||
for (const auto &tx : vtx) { | for (const auto &tx : vtx) { | ||||
uint256 txid = tx->GetId(); | txhash_t txhash = tx->GetHash(); | ||||
indexed_transaction_set::iterator i = mapTx.find(txid); | indexed_transaction_set::iterator i = mapTx.find(txhash); | ||||
if (i != mapTx.end()) entries.push_back(&*i); | if (i != mapTx.end()) entries.push_back(&*i); | ||||
} | } | ||||
// Before the txs in the new block have been removed from the mempool, | // Before the txs in the new block have been removed from the mempool, | ||||
// update policy estimates | // update policy estimates | ||||
minerPolicyEstimator->processBlock(nBlockHeight, entries); | minerPolicyEstimator->processBlock(nBlockHeight, entries); | ||||
for (const auto &tx : vtx) { | for (const auto &tx : vtx) { | ||||
txiter it = mapTx.find(tx->GetId()); | txiter it = mapTx.find(tx->GetHash()); | ||||
if (it != mapTx.end()) { | if (it != mapTx.end()) { | ||||
setEntries stage; | setEntries stage; | ||||
stage.insert(it); | stage.insert(it); | ||||
RemoveStaged(stage, true, MemPoolRemovalReason::BLOCK); | RemoveStaged(stage, true, MemPoolRemovalReason::BLOCK); | ||||
} | } | ||||
removeConflicts(*tx); | removeConflicts(*tx); | ||||
ClearPrioritisation(tx->GetId()); | ClearPrioritisation(tx->GetHash()); | ||||
} | } | ||||
lastRollingFeeUpdate = GetTime(); | lastRollingFeeUpdate = GetTime(); | ||||
blockSinceLastRollingFeeBump = true; | blockSinceLastRollingFeeBump = true; | ||||
} | } | ||||
void CTxMemPool::_clear() { | void CTxMemPool::_clear() { | ||||
mapLinks.clear(); | mapLinks.clear(); | ||||
mapTx.clear(); | mapTx.clear(); | ||||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | for (indexed_transaction_set::const_iterator it = mapTx.begin(); | ||||
bool fDependsWait = false; | bool fDependsWait = false; | ||||
setEntries setParentCheck; | setEntries setParentCheck; | ||||
int64_t parentSizes = 0; | int64_t parentSizes = 0; | ||||
int64_t parentSigOpCount = 0; | int64_t parentSigOpCount = 0; | ||||
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.hash); | findByunspentid(txin.prevout.unspentid); | ||||
if (it2 != mapTx.end()) { | if (it2 != mapTx.end()) { | ||||
const CTransaction &tx2 = it2->GetTx(); | const CTransaction &tx2 = it2->GetTx(); | ||||
assert(tx2.vout.size() > txin.prevout.n && | assert(tx2.vout.size() > txin.prevout.n && | ||||
!tx2.vout[txin.prevout.n].IsNull()); | !tx2.vout[txin.prevout.n].IsNull()); | ||||
fDependsWait = true; | fDependsWait = true; | ||||
if (setParentCheck.insert(it2).second) { | if (setParentCheck.insert(it2).second) { | ||||
parentSizes += it2->GetTxSize(); | parentSizes += it2->GetTxSize(); | ||||
parentSigOpCount += it2->GetSigOpCount(); | parentSigOpCount += it2->GetSigOpCount(); | ||||
Show All 28 Lines | for (indexed_transaction_set::const_iterator it = mapTx.begin(); | ||||
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; | CTxMemPool::setEntries setChildrenCheck; | ||||
auto iter = mapNextTx.lower_bound(COutPoint(it->GetTx().GetId(), 0)); | auto iter = | ||||
mapNextTx.lower_bound(COutPoint(it->GetTx().Getunspentid(), 0)); | |||||
int64_t childSizes = 0; | int64_t childSizes = 0; | ||||
for (; iter != mapNextTx.end() && | for (; iter != mapNextTx.end() && | ||||
iter->first->hash == it->GetTx().GetId(); | iter->first->unspentid == it->GetTx().Getunspentid(); | ||||
++iter) { | ++iter) { | ||||
txiter childit = mapTx.find(iter->second->GetId()); | txiter childit = mapTx.find(iter->second->GetHash()); | ||||
assert(childit != | assert(childit != | ||||
mapTx.end()); // mapNextTx points to in-mempool transactions | mapTx.end()); // mapNextTx points to in-mempool transactions | ||||
if (setChildrenCheck.insert(childit).second) { | if (setChildrenCheck.insert(childit).second) { | ||||
childSizes += childit->GetTxSize(); | childSizes += childit->GetTxSize(); | ||||
} | } | ||||
} | } | ||||
assert(setChildrenCheck == GetMemPoolChildren(it)); | assert(setChildrenCheck == GetMemPoolChildren(it)); | ||||
// Also check to make sure size is greater than sum with immediate | // Also check to make sure size is greater than sum with immediate | ||||
Show All 27 Lines | while (!waitingOnDependants.empty()) { | ||||
Consensus::CheckTxInputs(entry->GetTx(), state, | Consensus::CheckTxInputs(entry->GetTx(), state, | ||||
mempoolDuplicate, nSpendHeight); | mempoolDuplicate, nSpendHeight); | ||||
assert(fCheckResult); | assert(fCheckResult); | ||||
UpdateCoins(entry->GetTx(), mempoolDuplicate, 1000000); | UpdateCoins(entry->GetTx(), mempoolDuplicate, 1000000); | ||||
stepsSinceLastRemove = 0; | stepsSinceLastRemove = 0; | ||||
} | } | ||||
} | } | ||||
for (auto it = mapNextTx.cbegin(); it != mapNextTx.cend(); it++) { | for (auto it = mapNextTx.cbegin(); it != mapNextTx.cend(); it++) { | ||||
uint256 txid = it->second->GetId(); | txhash_t txhash = it->second->GetHash(); | ||||
indexed_transaction_set::const_iterator it2 = mapTx.find(txid); | indexed_transaction_set::const_iterator it2 = mapTx.find(txhash); | ||||
const CTransaction &tx = it2->GetTx(); | const CTransaction &tx = it2->GetTx(); | ||||
assert(it2 != mapTx.end()); | assert(it2 != mapTx.end()); | ||||
assert(&tx == it->second); | assert(&tx == it->second); | ||||
} | } | ||||
assert(totalTxSize == checkTotal); | assert(totalTxSize == checkTotal); | ||||
assert(innerUsage == cachedInnerUsage); | assert(innerUsage == cachedInnerUsage); | ||||
} | } | ||||
bool CTxMemPool::CompareDepthAndScore(const uint256 &hasha, | bool CTxMemPool::CompareDepthAndScore(const txhash_t &hasha, | ||||
const uint256 &hashb) { | const txhash_t &hashb) { | ||||
LOCK(cs); | LOCK(cs); | ||||
indexed_transaction_set::const_iterator i = mapTx.find(hasha); | indexed_transaction_set::const_iterator i = mapTx.find(hasha); | ||||
if (i == mapTx.end()) return false; | if (i == mapTx.end()) return false; | ||||
indexed_transaction_set::const_iterator j = mapTx.find(hashb); | indexed_transaction_set::const_iterator j = mapTx.find(hashb); | ||||
if (j == mapTx.end()) return true; | if (j == mapTx.end()) return true; | ||||
uint64_t counta = i->GetCountWithAncestors(); | uint64_t counta = i->GetCountWithAncestors(); | ||||
uint64_t countb = j->GetCountWithAncestors(); | uint64_t countb = j->GetCountWithAncestors(); | ||||
if (counta == countb) { | if (counta == countb) { | ||||
Show All 28 Lines | CTxMemPool::GetSortedDepthAndScore() const { | ||||
for (indexed_transaction_set::iterator mi = mapTx.begin(); | for (indexed_transaction_set::iterator mi = mapTx.begin(); | ||||
mi != mapTx.end(); ++mi) { | mi != mapTx.end(); ++mi) { | ||||
iters.push_back(mi); | iters.push_back(mi); | ||||
} | } | ||||
std::sort(iters.begin(), iters.end(), DepthAndScoreComparator()); | std::sort(iters.begin(), iters.end(), DepthAndScoreComparator()); | ||||
return iters; | return iters; | ||||
} | } | ||||
void CTxMemPool::queryHashes(std::vector<uint256> &vtxid) { | void CTxMemPool::queryHashes(std::vector<txhash_t> &vtxhash) { | ||||
LOCK(cs); | LOCK(cs); | ||||
auto iters = GetSortedDepthAndScore(); | auto iters = GetSortedDepthAndScore(); | ||||
vtxid.clear(); | vtxhash.clear(); | ||||
vtxid.reserve(mapTx.size()); | vtxhash.reserve(mapTx.size()); | ||||
for (auto it : iters) { | for (auto it : iters) { | ||||
vtxid.push_back(it->GetTx().GetId()); | vtxhash.push_back(it->GetTx().GetHash()); | ||||
} | } | ||||
} | } | ||||
static TxMempoolInfo | static TxMempoolInfo | ||||
GetInfo(CTxMemPool::indexed_transaction_set::const_iterator it) { | GetInfo(CTxMemPool::indexed_transaction_set::const_iterator it) { | ||||
return TxMempoolInfo{it->GetSharedTx(), it->GetTime(), | return TxMempoolInfo{it->GetSharedTx(), it->GetTime(), | ||||
CFeeRate(it->GetFee(), it->GetTxSize()), | CFeeRate(it->GetFee(), it->GetTxSize()), | ||||
it->GetModifiedFee() - it->GetFee()}; | it->GetModifiedFee() - it->GetFee()}; | ||||
} | } | ||||
std::vector<TxMempoolInfo> CTxMemPool::infoAll() const { | std::vector<TxMempoolInfo> CTxMemPool::infoAll() const { | ||||
LOCK(cs); | LOCK(cs); | ||||
auto iters = GetSortedDepthAndScore(); | auto iters = GetSortedDepthAndScore(); | ||||
std::vector<TxMempoolInfo> ret; | std::vector<TxMempoolInfo> ret; | ||||
ret.reserve(mapTx.size()); | ret.reserve(mapTx.size()); | ||||
for (auto it : iters) { | for (auto it : iters) { | ||||
ret.push_back(GetInfo(it)); | ret.push_back(GetInfo(it)); | ||||
} | } | ||||
return ret; | return ret; | ||||
} | } | ||||
CTransactionRef CTxMemPool::get(const uint256 &txid) const { | CTransactionRef CTxMemPool::get(const unspentid_t &unspentid) const { | ||||
LOCK(cs); | |||||
indexed_transaction_set::const_iterator i = findByunspentid(unspentid); | |||||
if (i == mapTx.end()) return nullptr; | |||||
return i->GetSharedTx(); | |||||
} | |||||
CTransactionRef CTxMemPool::get(const txhash_t &txhash) const { | |||||
LOCK(cs); | LOCK(cs); | ||||
indexed_transaction_set::const_iterator i = mapTx.find(txid); | indexed_transaction_set::const_iterator i = mapTx.find(txhash); | ||||
if (i == mapTx.end()) return nullptr; | if (i == mapTx.end()) return nullptr; | ||||
return i->GetSharedTx(); | return i->GetSharedTx(); | ||||
} | } | ||||
TxMempoolInfo CTxMemPool::info(const uint256 &txid) const { | TxMempoolInfo CTxMemPool::info(const txhash_t &txhash) const { | ||||
LOCK(cs); | LOCK(cs); | ||||
indexed_transaction_set::const_iterator i = mapTx.find(txid); | indexed_transaction_set::const_iterator i = mapTx.find(txhash); | ||||
if (i == mapTx.end()) return TxMempoolInfo(); | if (i == mapTx.end()) return TxMempoolInfo(); | ||||
return GetInfo(i); | return GetInfo(i); | ||||
} | } | ||||
CFeeRate CTxMemPool::estimateFee(int nBlocks) const { | CFeeRate CTxMemPool::estimateFee(int nBlocks) const { | ||||
LOCK(cs); | LOCK(cs); | ||||
return minerPolicyEstimator->estimateFee(nBlocks); | return minerPolicyEstimator->estimateFee(nBlocks); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | bool CTxMemPool::ReadFeeEstimates(CAutoFile &filein) { | ||||
} catch (const std::exception &) { | } catch (const std::exception &) { | ||||
LogPrintf("CTxMemPool::ReadFeeEstimates(): unable to read policy " | LogPrintf("CTxMemPool::ReadFeeEstimates(): unable to read policy " | ||||
"estimator data (non-fatal)\n"); | "estimator data (non-fatal)\n"); | ||||
return false; | return false; | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
void CTxMemPool::PrioritiseTransaction(const uint256 hash, | void CTxMemPool::PrioritiseTransaction(const txhash_t txhash, | ||||
const std::string strHash, | const std::string strHash, | ||||
double dPriorityDelta, | double dPriorityDelta, | ||||
const Amount nFeeDelta) { | const Amount nFeeDelta) { | ||||
{ | { | ||||
LOCK(cs); | LOCK(cs); | ||||
std::pair<double, Amount> &deltas = mapDeltas[hash]; | std::pair<double, Amount> &deltas = mapDeltas[txhash]; | ||||
deltas.first += dPriorityDelta; | deltas.first += dPriorityDelta; | ||||
deltas.second += nFeeDelta; | deltas.second += nFeeDelta; | ||||
txiter it = mapTx.find(hash); | txiter it = mapTx.find(txhash); | ||||
if (it != mapTx.end()) { | if (it != mapTx.end()) { | ||||
mapTx.modify(it, update_fee_delta(deltas.second)); | mapTx.modify(it, update_fee_delta(deltas.second)); | ||||
// Now update all ancestors' modified fees with descendants | // Now update all ancestors' modified fees with descendants | ||||
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, false); | nNoLimit, nNoLimit, dummy, false); | ||||
Show All 10 Lines | void CTxMemPool::PrioritiseTransaction(const txhash_t txhash, | ||||
update_ancestor_state(0, nFeeDelta, 0, 0)); | update_ancestor_state(0, nFeeDelta, 0, 0)); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
LogPrintf("PrioritiseTransaction: %s priority += %f, fee += %d\n", strHash, | LogPrintf("PrioritiseTransaction: %s priority += %f, fee += %d\n", strHash, | ||||
dPriorityDelta, FormatMoney(nFeeDelta)); | dPriorityDelta, FormatMoney(nFeeDelta)); | ||||
} | } | ||||
void CTxMemPool::ApplyDeltas(const uint256 hash, double &dPriorityDelta, | void CTxMemPool::ApplyDeltas(const txhash_t txhash, double &dPriorityDelta, | ||||
Amount nFeeDelta) const { | Amount nFeeDelta) const { | ||||
LOCK(cs); | LOCK(cs); | ||||
std::map<uint256, std::pair<double, Amount>>::const_iterator pos = | std::map<txhash_t, std::pair<double, Amount>>::const_iterator pos = | ||||
mapDeltas.find(hash); | mapDeltas.find(txhash); | ||||
if (pos == mapDeltas.end()) return; | if (pos == mapDeltas.end()) return; | ||||
const std::pair<double, Amount> &deltas = pos->second; | const std::pair<double, Amount> &deltas = pos->second; | ||||
dPriorityDelta += deltas.first; | dPriorityDelta += deltas.first; | ||||
nFeeDelta += deltas.second; | nFeeDelta += deltas.second; | ||||
} | } | ||||
void CTxMemPool::ClearPrioritisation(const uint256 hash) { | void CTxMemPool::ClearPrioritisation(const txhash_t txhash) { | ||||
LOCK(cs); | LOCK(cs); | ||||
mapDeltas.erase(hash); | mapDeltas.erase(txhash); | ||||
} | } | ||||
bool CTxMemPool::HasNoInputsOf(const CTransaction &tx) const { | bool CTxMemPool::HasNoInputsOf(const CTransaction &tx) const { | ||||
for (unsigned int i = 0; i < tx.vin.size(); i++) | for (unsigned int i = 0; i < tx.vin.size(); i++) | ||||
if (exists(tx.vin[i].prevout.hash)) return false; | if (exists(tx.vin[i].prevout.unspentid)) return false; | ||||
return true; | return true; | ||||
} | } | ||||
CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView *baseIn, | CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView *baseIn, | ||||
const CTxMemPool &mempoolIn) | const CTxMemPool &mempoolIn) | ||||
: CCoinsViewBacked(baseIn), mempool(mempoolIn) {} | : CCoinsViewBacked(baseIn), mempool(mempoolIn) {} | ||||
bool CCoinsViewMemPool::GetCoin(const COutPoint &outpoint, Coin &coin) const { | bool CCoinsViewMemPool::GetCoin(const COutPoint &outpoint, Coin &coin) const { | ||||
// If an entry in the mempool exists, always return that one, as it's | // If an entry in the mempool exists, always return that one, as it's | ||||
// guaranteed to never conflict with the underlying cache, and it cannot | // guaranteed to never conflict with the underlying cache, and it cannot | ||||
// have pruned entries (as it contains full) transactions. First checking | // have pruned entries (as it contains full) transactions. First checking | ||||
// the underlying cache risks returning a pruned entry instead. | // the underlying cache risks returning a pruned entry instead. | ||||
CTransactionRef ptx = mempool.get(outpoint.hash); | CTransactionRef ptx = mempool.get(outpoint.unspentid); | ||||
if (ptx) { | if (ptx) { | ||||
if (outpoint.n < ptx->vout.size()) { | if (outpoint.n < ptx->vout.size()) { | ||||
coin = Coin(ptx->vout[outpoint.n], MEMPOOL_HEIGHT, false); | coin = Coin(ptx->vout[outpoint.n], MEMPOOL_HEIGHT, false); | ||||
return true; | return true; | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
Show All 38 Lines | int CTxMemPool::Expire(int64_t time) { | ||||
setEntries stage; | setEntries stage; | ||||
for (txiter removeit : toremove) { | for (txiter removeit : toremove) { | ||||
CalculateDescendants(removeit, stage); | CalculateDescendants(removeit, stage); | ||||
} | } | ||||
RemoveStaged(stage, false, MemPoolRemovalReason::EXPIRY); | RemoveStaged(stage, false, MemPoolRemovalReason::EXPIRY); | ||||
return stage.size(); | return stage.size(); | ||||
} | } | ||||
bool CTxMemPool::addUnchecked(const uint256 &hash, const CTxMemPoolEntry &entry, | bool CTxMemPool::addUnchecked(const txhash_t &txhash, | ||||
const CTxMemPoolEntry &entry, | |||||
bool validFeeEstimate) { | bool validFeeEstimate) { | ||||
LOCK(cs); | LOCK(cs); | ||||
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(entry, setAncestors, nNoLimit, nNoLimit, nNoLimit, | CalculateMemPoolAncestors(entry, setAncestors, nNoLimit, nNoLimit, nNoLimit, | ||||
nNoLimit, dummy); | nNoLimit, dummy); | ||||
return addUnchecked(hash, entry, setAncestors, validFeeEstimate); | return addUnchecked(txhash, entry, setAncestors, validFeeEstimate); | ||||
} | } | ||||
void CTxMemPool::UpdateChild(txiter entry, txiter child, bool add) { | void CTxMemPool::UpdateChild(txiter entry, txiter child, bool add) { | ||||
setEntries s; | setEntries s; | ||||
if (add && mapLinks[entry].children.insert(child).second) { | if (add && mapLinks[entry].children.insert(child).second) { | ||||
cachedInnerUsage += memusage::IncrementalDynamicUsage(s); | cachedInnerUsage += memusage::IncrementalDynamicUsage(s); | ||||
} else if (!add && mapLinks[entry].children.erase(child)) { | } else if (!add && mapLinks[entry].children.erase(child)) { | ||||
cachedInnerUsage -= memusage::IncrementalDynamicUsage(s); | cachedInnerUsage -= memusage::IncrementalDynamicUsage(s); | ||||
▲ Show 20 Lines • Show All 92 Lines • ▼ Show 20 Lines | while (!mapTx.empty() && DynamicMemoryUsage() > sizelimit) { | ||||
for (txiter iter : stage) { | for (txiter iter : stage) { | ||||
txn.push_back(iter->GetTx()); | txn.push_back(iter->GetTx()); | ||||
} | } | ||||
} | } | ||||
RemoveStaged(stage, false, MemPoolRemovalReason::SIZELIMIT); | RemoveStaged(stage, false, MemPoolRemovalReason::SIZELIMIT); | ||||
if (pvNoSpendsRemaining) { | if (pvNoSpendsRemaining) { | ||||
for (const CTransaction &tx : txn) { | for (const CTransaction &tx : txn) { | ||||
for (const CTxIn &txin : tx.vin) { | for (const CTxIn &txin : tx.vin) { | ||||
if (exists(txin.prevout.hash)) { | if (exists(txin.prevout.unspentid)) { | ||||
continue; | continue; | ||||
} | } | ||||
if (!mapNextTx.count(txin.prevout)) { | if (!mapNextTx.count(txin.prevout)) { | ||||
pvNoSpendsRemaining->push_back(txin.prevout); | pvNoSpendsRemaining->push_back(txin.prevout); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if (maxFeeRateRemoved > CFeeRate(0)) | if (maxFeeRateRemoved > CFeeRate(0)) | ||||
LogPrint("mempool", | LogPrint("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()); | ||||
} | } | ||||
bool CTxMemPool::TransactionWithinChainLimit(const uint256 &txid, | bool CTxMemPool::TransactionWithinChainLimit(const txhash_t &txhash, | ||||
size_t chainLimit) const { | size_t chainLimit) const { | ||||
LOCK(cs); | LOCK(cs); | ||||
auto it = mapTx.find(txid); | auto it = mapTx.find(txhash); | ||||
return it == mapTx.end() || (it->GetCountWithAncestors() < chainLimit && | return it == mapTx.end() || (it->GetCountWithAncestors() < chainLimit && | ||||
it->GetCountWithDescendants() < chainLimit); | it->GetCountWithDescendants() < chainLimit); | ||||
} | } | ||||
SaltedTxidHasher::SaltedTxidHasher() | SaltedTxHasher::SaltedTxHasher() | ||||
: k0(GetRand(std::numeric_limits<uint64_t>::max())), | : k0(GetRand(std::numeric_limits<uint64_t>::max())), | ||||
k1(GetRand(std::numeric_limits<uint64_t>::max())) {} | k1(GetRand(std::numeric_limits<uint64_t>::max())) {} |