Changeset View
Changeset View
Standalone View
Standalone View
src/txmempool.cpp
Show First 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
double CTxMemPoolEntry::GetPriority(unsigned int currentHeight) const { | double CTxMemPoolEntry::GetPriority(unsigned int currentHeight) const { | ||||
double deltaPriority = double((currentHeight - entryHeight) * | double deltaPriority = double((currentHeight - entryHeight) * | ||||
inChainInputValue.GetSatoshis()) / | inChainInputValue.GetSatoshis()) / | ||||
nModSize; | nModSize; | ||||
double dResult = entryPriority + deltaPriority; | double dResult = entryPriority + deltaPriority; | ||||
// This should only happen if it was called with a height below entry height | // This should only happen if it was called with a height below entry height | ||||
if (dResult < 0) dResult = 0; | if (dResult < 0) { | ||||
dResult = 0; | |||||
} | |||||
return dResult; | return dResult; | ||||
} | } | ||||
void CTxMemPoolEntry::UpdateFeeDelta(Amount newFeeDelta) { | void CTxMemPoolEntry::UpdateFeeDelta(Amount newFeeDelta) { | ||||
nModFeesWithDescendants += newFeeDelta - feeDelta; | nModFeesWithDescendants += newFeeDelta - feeDelta; | ||||
nModFeesWithAncestors += newFeeDelta - feeDelta; | nModFeesWithAncestors += newFeeDelta - feeDelta; | ||||
feeDelta = newFeeDelta; | feeDelta = newFeeDelta; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 112 Lines • ▼ Show 20 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 (const CTxIn &in : tx.vin) { | ||||
txiter piter = mapTx.find(tx.vin[i].prevout.hash); | txiter piter = mapTx.find(in.prevout.hash); | ||||
if (piter != mapTx.end()) { | if (piter == mapTx.end()) { | ||||
continue; | |||||
} | |||||
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; | ||||
} | } | ||||
} | } | ||||
} | |||||
} 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); | parentHashes = GetMemPoolParents(it); | ||||
} | } | ||||
size_t totalSizeWithAncestors = entry.GetTxSize(); | size_t totalSizeWithAncestors = entry.GetTxSize(); | ||||
while (!parentHashes.empty()) { | while (!parentHashes.empty()) { | ||||
txiter stageit = *parentHashes.begin(); | txiter stageit = *parentHashes.begin(); | ||||
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().GetId().ToString(), limitDescendantSize); | ||||
return false; | return false; | ||||
} else 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; | ||||
} else 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 setEntries &setMemPoolParents = GetMemPoolParents(stageit); | ||||
for (const txiter &phash : setMemPoolParents) { | for (const txiter &phash : setMemPoolParents) { | ||||
// If this is a new ancestor, add it. | // If this is a new ancestor, add it. | ||||
▲ Show 20 Lines • Show All 188 Lines • ▼ Show 20 Lines | bool CTxMemPool::addUnchecked(const uint256 &hash, const CTxMemPoolEntry &entry, | ||||
// 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<uint256> setParentTransactions; | ||||
for (unsigned int i = 0; i < tx.vin.size(); i++) { | for (const CTxIn &in : tx.vin) { | ||||
mapNextTx.insert(std::make_pair(&tx.vin[i].prevout, &tx)); | mapNextTx.insert(std::make_pair(&in.prevout, &tx)); | ||||
setParentTransactions.insert(tx.vin[i].prevout.hash); | setParentTransactions.insert(in.prevout.hash); | ||||
} | } | ||||
// 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. | ||||
Show All 23 Lines | void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason) { | ||||
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++; | ||||
Show All 26 Lines | while (!stage.empty()) { | ||||
stage.insert(childiter); | stage.insert(childiter); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
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.GetId()); | ||||
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 | ||||
// to remove any children that are in the pool. This can happen | // remove any children that are in the pool. This can happen during | ||||
// during chain re-orgs if origTx isn't re-accepted into the mempool | // chain re-orgs if origTx isn't re-accepted into the mempool for any | ||||
// for any reason. | // reason. | ||||
for (unsigned int i = 0; i < origTx.vout.size(); i++) { | for (size_t i = 0; i < origTx.vout.size(); i++) { | ||||
auto it = mapNextTx.find(COutPoint(origTx.GetId(), i)); | auto it = mapNextTx.find(COutPoint(origTx.GetId(), i)); | ||||
if (it == mapNextTx.end()) continue; | if (it == mapNextTx.end()) { | ||||
continue; | |||||
} | |||||
txiter nextit = mapTx.find(it->second->GetId()); | txiter nextit = mapTx.find(it->second->GetId()); | ||||
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); | ||||
} | } | ||||
RemoveStaged(setAllRemoves, false, reason); | RemoveStaged(setAllRemoves, false, reason); | ||||
} | } | ||||
} | |||||
void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, | void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, | ||||
unsigned int nMemPoolHeight, int flags) { | unsigned int nMemPoolHeight, int flags) { | ||||
// Remove transactions spending a coinbase which are now immature and | // Remove transactions spending a coinbase which are now immature and | ||||
// no-longer-final transactions | // no-longer-final transactions. | ||||
LOCK(cs); | LOCK(cs); | ||||
setEntries txToRemove; | setEntries txToRemove; | ||||
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++) { | ||||
const CTransaction &tx = it->GetTx(); | const CTransaction &tx = it->GetTx(); | ||||
LockPoints lp = it->GetLockPoints(); | LockPoints lp = it->GetLockPoints(); | ||||
bool validLP = TestLockPointValidity(&lp); | bool validLP = TestLockPointValidity(&lp); | ||||
Show All 10 Lines | for (indexed_transaction_set::const_iterator it = mapTx.begin(); | ||||
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); | mapTx.find(txin.prevout.hash); | ||||
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() && | ||||
int64_t(nMemPoolHeight) - coin.GetHeight() < | int64_t(nMemPoolHeight) - coin.GetHeight() < | ||||
COINBASE_MATURITY)) { | COINBASE_MATURITY)) { | ||||
txToRemove.insert(it); | txToRemove.insert(it); | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
Show All 31 Lines | |||||
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(); | uint256 txid = tx->GetId(); | ||||
indexed_transaction_set::iterator i = mapTx.find(txid); | indexed_transaction_set::iterator i = mapTx.find(txid); | ||||
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->GetId()); | ||||
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->GetId()); | ||||
} | } | ||||
lastRollingFeeUpdate = GetTime(); | lastRollingFeeUpdate = GetTime(); | ||||
blockSinceLastRollingFeeBump = true; | blockSinceLastRollingFeeBump = true; | ||||
} | } | ||||
void CTxMemPool::_clear() { | void CTxMemPool::_clear() { | ||||
mapLinks.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; | ||||
++nTransactionsUpdated; | ++nTransactionsUpdated; | ||||
} | } | ||||
void CTxMemPool::clear() { | void CTxMemPool::clear() { | ||||
LOCK(cs); | LOCK(cs); | ||||
_clear(); | _clear(); | ||||
} | } | ||||
void CTxMemPool::check(const CCoinsViewCache *pcoins) const { | void CTxMemPool::check(const CCoinsViewCache *pcoins) const { | ||||
if (nCheckFrequency == 0) return; | if (nCheckFrequency == 0) { | ||||
return; | |||||
} | |||||
if (GetRand(std::numeric_limits<uint32_t>::max()) >= nCheckFrequency) | if (GetRand(std::numeric_limits<uint32_t>::max()) >= nCheckFrequency) { | ||||
return; | return; | ||||
} | |||||
LogPrint("mempool", "Checking mempool with %u transactions and %u inputs\n", | LogPrint("mempool", "Checking mempool with %u transactions and %u inputs\n", | ||||
(unsigned int)mapTx.size(), (unsigned int)mapNextTx.size()); | (unsigned int)mapTx.size(), (unsigned int)mapNextTx.size()); | ||||
uint64_t checkTotal = 0; | uint64_t checkTotal = 0; | ||||
uint64_t innerUsage = 0; | uint64_t innerUsage = 0; | ||||
CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache *>(pcoins)); | CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache *>(pcoins)); | ||||
▲ Show 20 Lines • Show All 66 Lines • ▼ Show 20 Lines | for (indexed_transaction_set::const_iterator it = mapTx.begin(); | ||||
// 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().GetId(), 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->hash == it->GetTx().GetId(); | ||||
++iter) { | ++iter) { | ||||
txiter childit = mapTx.find(iter->second->GetId()); | txiter childit = mapTx.find(iter->second->GetId()); | ||||
assert(childit != | // mapNextTx points to in-mempool transactions | ||||
mapTx.end()); // mapNextTx points to in-mempool transactions | assert(childit != mapTx.end()); | ||||
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 | ||||
// 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() >= childSizes + it->GetTxSize()); | assert(it->GetSizeWithDescendants() >= childSizes + it->GetTxSize()); | ||||
if (fDependsWait) | if (fDependsWait) { | ||||
waitingOnDependants.push_back(&(*it)); | waitingOnDependants.push_back(&(*it)); | ||||
else { | } else { | ||||
CValidationState state; | CValidationState state; | ||||
bool fCheckResult = tx.IsCoinBase() || | bool fCheckResult = tx.IsCoinBase() || | ||||
Consensus::CheckTxInputs( | Consensus::CheckTxInputs( | ||||
tx, state, mempoolDuplicate, nSpendHeight); | tx, state, mempoolDuplicate, nSpendHeight); | ||||
assert(fCheckResult); | assert(fCheckResult); | ||||
UpdateCoins(tx, mempoolDuplicate, 1000000); | UpdateCoins(tx, mempoolDuplicate, 1000000); | ||||
} | } | ||||
} | } | ||||
unsigned int stepsSinceLastRemove = 0; | unsigned int stepsSinceLastRemove = 0; | ||||
while (!waitingOnDependants.empty()) { | while (!waitingOnDependants.empty()) { | ||||
const CTxMemPoolEntry *entry = waitingOnDependants.front(); | const CTxMemPoolEntry *entry = waitingOnDependants.front(); | ||||
waitingOnDependants.pop_front(); | waitingOnDependants.pop_front(); | ||||
CValidationState state; | CValidationState state; | ||||
if (!mempoolDuplicate.HaveInputs(entry->GetTx())) { | if (!mempoolDuplicate.HaveInputs(entry->GetTx())) { | ||||
waitingOnDependants.push_back(entry); | waitingOnDependants.push_back(entry); | ||||
stepsSinceLastRemove++; | stepsSinceLastRemove++; | ||||
assert(stepsSinceLastRemove < waitingOnDependants.size()); | assert(stepsSinceLastRemove < waitingOnDependants.size()); | ||||
} else { | } else { | ||||
bool fCheckResult = | bool fCheckResult = | ||||
entry->GetTx().IsCoinBase() || | entry->GetTx().IsCoinBase() || | ||||
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(); | uint256 txid = it->second->GetId(); | ||||
indexed_transaction_set::const_iterator it2 = mapTx.find(txid); | indexed_transaction_set::const_iterator it2 = mapTx.find(txid); | ||||
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 uint256 &hasha, | ||||
const uint256 &hashb) { | const uint256 &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) { | ||||
return CompareTxMemPoolEntryByScore()(*i, *j); | return CompareTxMemPoolEntryByScore()(*i, *j); | ||||
} | } | ||||
return counta < countb; | return counta < countb; | ||||
} | } | ||||
Show All 14 Lines | |||||
} // namespace | } // namespace | ||||
std::vector<CTxMemPool::indexed_transaction_set::const_iterator> | std::vector<CTxMemPool::indexed_transaction_set::const_iterator> | ||||
CTxMemPool::GetSortedDepthAndScore() const { | CTxMemPool::GetSortedDepthAndScore() const { | ||||
std::vector<indexed_transaction_set::const_iterator> iters; | std::vector<indexed_transaction_set::const_iterator> iters; | ||||
AssertLockHeld(cs); | AssertLockHeld(cs); | ||||
iters.reserve(mapTx.size()); | iters.reserve(mapTx.size()); | ||||
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<uint256> &vtxid) { | ||||
LOCK(cs); | LOCK(cs); | ||||
auto iters = GetSortedDepthAndScore(); | auto iters = GetSortedDepthAndScore(); | ||||
Show All 23 Lines | std::vector<TxMempoolInfo> CTxMemPool::infoAll() const { | ||||
} | } | ||||
return ret; | return ret; | ||||
} | } | ||||
CTransactionRef CTxMemPool::get(const uint256 &txid) const { | CTransactionRef CTxMemPool::get(const uint256 &txid) const { | ||||
LOCK(cs); | LOCK(cs); | ||||
indexed_transaction_set::const_iterator i = mapTx.find(txid); | indexed_transaction_set::const_iterator i = mapTx.find(txid); | ||||
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 uint256 &txid) const { | ||||
LOCK(cs); | LOCK(cs); | ||||
indexed_transaction_set::const_iterator i = mapTx.find(txid); | indexed_transaction_set::const_iterator i = mapTx.find(txid); | ||||
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); | ||||
} | } | ||||
CFeeRate CTxMemPool::estimateSmartFee(int nBlocks, | CFeeRate CTxMemPool::estimateSmartFee(int nBlocks, | ||||
Show All 28 Lines | bool CTxMemPool::WriteFeeEstimates(CAutoFile &fileout) const { | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool CTxMemPool::ReadFeeEstimates(CAutoFile &filein) { | bool CTxMemPool::ReadFeeEstimates(CAutoFile &filein) { | ||||
try { | try { | ||||
int nVersionRequired, nVersionThatWrote; | int nVersionRequired, nVersionThatWrote; | ||||
filein >> nVersionRequired >> nVersionThatWrote; | filein >> nVersionRequired >> nVersionThatWrote; | ||||
if (nVersionRequired > CLIENT_VERSION) | if (nVersionRequired > CLIENT_VERSION) { | ||||
return error("CTxMemPool::ReadFeeEstimates(): up-version (%d) fee " | return error("CTxMemPool::ReadFeeEstimates(): up-version (%d) fee " | ||||
"estimate file", | "estimate file", | ||||
nVersionRequired); | nVersionRequired); | ||||
} | |||||
LOCK(cs); | LOCK(cs); | ||||
minerPolicyEstimator->Read(filein, nVersionThatWrote); | minerPolicyEstimator->Read(filein, nVersionThatWrote); | ||||
} 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; | ||||
Show All 16 Lines | void CTxMemPool::PrioritiseTransaction(const uint256 hash, | ||||
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); | ||||
for (txiter ancestorIt : setAncestors) { | for (txiter ancestorIt : setAncestors) { | ||||
mapTx.modify(ancestorIt, | mapTx.modify(ancestorIt, | ||||
update_descendant_state(0, nFeeDelta, 0)); | update_descendant_state(0, nFeeDelta, 0)); | ||||
} | } | ||||
// Now update all descendants' modified fees with ancestors | // Now update all descendants' modified fees with ancestors | ||||
setEntries setDescendants; | setEntries setDescendants; | ||||
CalculateDescendants(it, setDescendants); | CalculateDescendants(it, setDescendants); | ||||
setDescendants.erase(it); | setDescendants.erase(it); | ||||
for (txiter descendantIt : setDescendants) { | for (txiter descendantIt : setDescendants) { | ||||
mapTx.modify(descendantIt, | mapTx.modify(descendantIt, | ||||
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 uint256 hash, double &dPriorityDelta, | ||||
Amount &nFeeDelta) const { | Amount &nFeeDelta) const { | ||||
LOCK(cs); | LOCK(cs); | ||||
std::map<uint256, std::pair<double, Amount>>::const_iterator pos = | std::map<uint256, std::pair<double, Amount>>::const_iterator pos = | ||||
mapDeltas.find(hash); | mapDeltas.find(hash); | ||||
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 uint256 hash) { | ||||
LOCK(cs); | LOCK(cs); | ||||
mapDeltas.erase(hash); | mapDeltas.erase(hash); | ||||
} | } | ||||
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 (const CTxIn &in : tx.vin) { | ||||
if (exists(tx.vin[i].prevout.hash)) return false; | if (exists(in.prevout.hash)) { | ||||
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 { | ||||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | int CTxMemPool::Expire(int64_t time) { | ||||
LOCK(cs); | LOCK(cs); | ||||
indexed_transaction_set::index<entry_time>::type::iterator it = | indexed_transaction_set::index<entry_time>::type::iterator it = | ||||
mapTx.get<entry_time>().begin(); | mapTx.get<entry_time>().begin(); | ||||
setEntries toremove; | setEntries toremove; | ||||
while (it != mapTx.get<entry_time>().end() && it->GetTime() < time) { | while (it != mapTx.get<entry_time>().end() && it->GetTime() < time) { | ||||
toremove.insert(mapTx.project<0>(it)); | toremove.insert(mapTx.project<0>(it)); | ||||
it++; | it++; | ||||
} | } | ||||
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 uint256 &hash, const CTxMemPoolEntry &entry, | ||||
bool validFeeEstimate) { | bool validFeeEstimate) { | ||||
LOCK(cs); | LOCK(cs); | ||||
setEntries setAncestors; | setEntries setAncestors; | ||||
Show All 35 Lines | CTxMemPool::GetMemPoolChildren(txiter entry) const { | ||||
assert(entry != mapTx.end()); | assert(entry != mapTx.end()); | ||||
txlinksMap::const_iterator it = mapLinks.find(entry); | txlinksMap::const_iterator it = mapLinks.find(entry); | ||||
assert(it != mapLinks.end()); | assert(it != mapLinks.end()); | ||||
return it->second.children; | 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(Amount(int64_t(rollingMinimumFeeRate))); | return CFeeRate(Amount(int64_t(rollingMinimumFeeRate))); | ||||
} | |||||
int64_t time = GetTime(); | int64_t time = GetTime(); | ||||
if (time > lastRollingFeeUpdate + 10) { | if (time > lastRollingFeeUpdate + 10) { | ||||
double halflife = ROLLING_FEE_HALFLIFE; | double halflife = ROLLING_FEE_HALFLIFE; | ||||
if (DynamicMemoryUsage() < sizelimit / 4) | if (DynamicMemoryUsage() < sizelimit / 4) { | ||||
halflife /= 4; | halflife /= 4; | ||||
else if (DynamicMemoryUsage() < sizelimit / 2) | } else if (DynamicMemoryUsage() < sizelimit / 2) { | ||||
halflife /= 2; | halflife /= 2; | ||||
} | |||||
rollingMinimumFeeRate = | rollingMinimumFeeRate = | ||||
rollingMinimumFeeRate / | rollingMinimumFeeRate / | ||||
pow(2.0, (time - lastRollingFeeUpdate) / halflife); | pow(2.0, (time - lastRollingFeeUpdate) / halflife); | ||||
lastRollingFeeUpdate = time; | lastRollingFeeUpdate = time; | ||||
if (rollingMinimumFeeRate < | if (rollingMinimumFeeRate < | ||||
(double)incrementalRelayFee.GetFeePerK().GetSatoshis() / 2) { | (double)incrementalRelayFee.GetFeePerK().GetSatoshis() / 2) { | ||||
▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | while (!mapTx.empty() && DynamicMemoryUsage() > sizelimit) { | ||||
if (!mapNextTx.count(txin.prevout)) { | if (!mapNextTx.count(txin.prevout)) { | ||||
pvNoSpendsRemaining->push_back(txin.prevout); | pvNoSpendsRemaining->push_back(txin.prevout); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if (maxFeeRateRemoved > CFeeRate(Amount(0))) | if (maxFeeRateRemoved > CFeeRate(Amount(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 uint256 &txid, | ||||
size_t chainLimit) const { | size_t chainLimit) const { | ||||
LOCK(cs); | LOCK(cs); | ||||
auto it = mapTx.find(txid); | auto it = mapTx.find(txid); | ||||
return it == mapTx.end() || (it->GetCountWithAncestors() < chainLimit && | return it == mapTx.end() || (it->GetCountWithAncestors() < chainLimit && | ||||
it->GetCountWithDescendants() < chainLimit); | it->GetCountWithDescendants() < chainLimit); | ||||
} | } | ||||
SaltedTxidHasher::SaltedTxidHasher() | SaltedTxidHasher::SaltedTxidHasher() | ||||
: 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())) {} |