Changeset View
Changeset View
Standalone View
Standalone View
src/txmempool.cpp
Show First 20 Lines • Show All 128 Lines • ▼ Show 20 Lines | |||||
// txidsToUpdate is the set of transaction hashes from a disconnected block | // txidsToUpdate 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 txidsToUpdate, and add fee/size information for such | // that are outside txidsToUpdate, 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<TxId> &txidsToUpdate) { | const std::vector<TxId> &txidsToUpdate) { | ||||
LOCK(cs); | AssertLockHeld(cs); | ||||
// For each entry in txidsToUpdate, store the set of in-mempool, but not | // For each entry in txidsToUpdate, store the set of in-mempool, but not | ||||
// in-txidsToUpdate transactions, so that we don't have to recalculate | // in-txidsToUpdate 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 txidsToUpdate (these entries are already | // Use a set for lookups into txidsToUpdate (these entries are already | ||||
// accounted for in the state of their ancestors) | // accounted for in the state of their ancestors) | ||||
std::set<TxId> setAlreadyIncluded(txidsToUpdate.begin(), | std::set<TxId> setAlreadyIncluded(txidsToUpdate.begin(), | ||||
▲ Show 20 Lines • Show All 375 Lines • ▼ Show 20 Lines | while (!stage.empty()) { | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
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); | AssertLockHeld(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 to | // When recursively removing but origTx isn't in the mempool be sure to | ||||
// remove any children that are in the pool. This can happen during | // remove any children that are in the pool. This can happen during | ||||
// chain re-orgs if origTx isn't re-accepted into the mempool for any | // chain re-orgs if origTx isn't re-accepted into the mempool for any | ||||
Show All 18 Lines | void CTxMemPool::removeRecursive(const CTransaction &origTx, | ||||
RemoveStaged(setAllRemoves, false, reason); | RemoveStaged(setAllRemoves, false, reason); | ||||
} | } | ||||
void CTxMemPool::removeForReorg(const Config &config, | void CTxMemPool::removeForReorg(const Config &config, | ||||
const CCoinsViewCache *pcoins, | 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); | AssertLockHeld(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); | ||||
CValidationState state; | CValidationState state; | ||||
▲ Show 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
/** | /** | ||||
* 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); | AssertLockHeld(cs); | ||||
DisconnectedBlockTransactions disconnectpool; | DisconnectedBlockTransactions disconnectpool; | ||||
disconnectpool.addForBlock(vtx); | disconnectpool.addForBlock(vtx); | ||||
std::vector<const CTxMemPoolEntry *> entries; | std::vector<const CTxMemPoolEntry *> entries; | ||||
for (const CTransactionRef &tx : | for (const CTransactionRef &tx : | ||||
reverse_iterate(disconnectpool.GetQueuedTx().get<insertion_order>())) { | reverse_iterate(disconnectpool.GetQueuedTx().get<insertion_order>())) { | ||||
const TxId &txid = tx->GetId(); | const TxId &txid = tx->GetId(); | ||||
▲ Show 20 Lines • Show All 425 Lines • ▼ Show 20 Lines | void CTxMemPool::RemoveStaged(setEntries &stage, bool updateDescendants, | ||||
AssertLockHeld(cs); | AssertLockHeld(cs); | ||||
UpdateForRemoveFromMempool(stage, updateDescendants); | UpdateForRemoveFromMempool(stage, updateDescendants); | ||||
for (txiter it : stage) { | for (txiter it : stage) { | ||||
removeUnchecked(it, reason); | removeUnchecked(it, reason); | ||||
} | } | ||||
} | } | ||||
int CTxMemPool::Expire(std::chrono::seconds time) { | int CTxMemPool::Expire(std::chrono::seconds time) { | ||||
LOCK(cs); | AssertLockHeld(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(); | ||||
} | } | ||||
void CTxMemPool::LimitSize(size_t limit, std::chrono::seconds age) { | void CTxMemPool::LimitSize(size_t limit, std::chrono::seconds age) | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs) { | |||||
int expired = Expire(GetTime<std::chrono::seconds>() - age); | int expired = Expire(GetTime<std::chrono::seconds>() - age); | ||||
if (expired != 0) { | if (expired != 0) { | ||||
LogPrint(BCLog::MEMPOOL, | LogPrint(BCLog::MEMPOOL, | ||||
"Expired %i transactions from the memory pool\n", expired); | "Expired %i transactions from the memory pool\n", expired); | ||||
} | } | ||||
std::vector<COutPoint> vNoSpendsRemaining; | std::vector<COutPoint> vNoSpendsRemaining; | ||||
TrimToSize(limit, &vNoSpendsRemaining); | TrimToSize(limit, &vNoSpendsRemaining); | ||||
▲ Show 20 Lines • Show All 73 Lines • ▼ Show 20 Lines | void CTxMemPool::trackPackageRemoved(const CFeeRate &rate) { | ||||
if ((rate.GetFeePerK() / SATOSHI) > rollingMinimumFeeRate) { | if ((rate.GetFeePerK() / SATOSHI) > rollingMinimumFeeRate) { | ||||
rollingMinimumFeeRate = rate.GetFeePerK() / SATOSHI; | rollingMinimumFeeRate = rate.GetFeePerK() / SATOSHI; | ||||
blockSinceLastRollingFeeBump = false; | blockSinceLastRollingFeeBump = false; | ||||
} | } | ||||
} | } | ||||
void CTxMemPool::TrimToSize(size_t sizelimit, | void CTxMemPool::TrimToSize(size_t sizelimit, | ||||
std::vector<COutPoint> *pvNoSpendsRemaining) { | std::vector<COutPoint> *pvNoSpendsRemaining) { | ||||
LOCK(cs); | AssertLockHeld(cs); | ||||
unsigned nTxnRemoved = 0; | unsigned nTxnRemoved = 0; | ||||
CFeeRate maxFeeRateRemoved(Amount::zero()); | CFeeRate maxFeeRateRemoved(Amount::zero()); | ||||
while (!mapTx.empty() && DynamicMemoryUsage() > sizelimit) { | while (!mapTx.empty() && DynamicMemoryUsage() > sizelimit) { | ||||
indexed_transaction_set::index<descendant_score>::type::iterator it = | indexed_transaction_set::index<descendant_score>::type::iterator it = | ||||
mapTx.get<descendant_score>().begin(); | mapTx.get<descendant_score>().begin(); | ||||
// We set the new mempool min fee to the feerate of the removed set, | // We set the new mempool min fee to the feerate of the removed set, | ||||
▲ Show 20 Lines • Show All 134 Lines • ▼ Show 20 Lines | for (const auto &tx : reverse_iterate(vtx)) { | ||||
// And we make sure ancestors are covered. | // And we make sure ancestors are covered. | ||||
for (const CTxIn &in : ptx->vin) { | for (const CTxIn &in : ptx->vin) { | ||||
parents.insert(in.prevout.GetTxId()); | parents.insert(in.prevout.GetTxId()); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
LOCK(::g_mempool.cs); | |||||
Fabien: I don't think this is correct.
There are only 3 callsites (tests excepted):
- In… | |||||
// Keep the size under control. | // Keep the size under control. | ||||
while (DynamicMemoryUsage() > MAX_DISCONNECTED_TX_POOL_SIZE) { | while (DynamicMemoryUsage() > MAX_DISCONNECTED_TX_POOL_SIZE) { | ||||
// Drop the earliest entry, and remove its children from the | // Drop the earliest entry, and remove its children from the | ||||
// mempool. | // mempool. | ||||
auto it = queuedTx.get<insertion_order>().begin(); | auto it = queuedTx.get<insertion_order>().begin(); | ||||
g_mempool.removeRecursive(**it, MemPoolRemovalReason::REORG); | g_mempool.removeRecursive(**it, MemPoolRemovalReason::REORG); | ||||
removeEntry(it); | removeEntry(it); | ||||
} | } | ||||
Show All 32 Lines | void DisconnectedBlockTransactions::importMempool(CTxMemPool &pool) { | ||||
// disconnected | // disconnected | ||||
while (DynamicMemoryUsage() > MAX_DISCONNECTED_TX_POOL_SIZE) { | while (DynamicMemoryUsage() > MAX_DISCONNECTED_TX_POOL_SIZE) { | ||||
// Drop the earliest entry which, by definition, has no children | // Drop the earliest entry which, by definition, has no children | ||||
removeEntry(queuedTx.get<insertion_order>().begin()); | removeEntry(queuedTx.get<insertion_order>().begin()); | ||||
} | } | ||||
} | } | ||||
void DisconnectedBlockTransactions::updateMempoolForReorg(const Config &config, | void DisconnectedBlockTransactions::updateMempoolForReorg(const Config &config, | ||||
bool fAddToMempool) { | bool fAddToMempool) | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::g_mempool.cs) { | |||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
std::vector<TxId> txidsUpdate; | std::vector<TxId> txidsUpdate; | ||||
// disconnectpool's insertion_order index sorts the entries from oldest to | // disconnectpool's insertion_order index sorts the entries from oldest to | ||||
// newest, but the oldest entry will be the last tx from the latest mined | // newest, but the oldest entry will be the last tx from the latest mined | ||||
// block that was disconnected. | // block that was disconnected. | ||||
// Iterate disconnectpool in reverse, so that we add transactions back to | // Iterate disconnectpool in reverse, so that we add transactions back to | ||||
// the mempool starting with the earliest transaction that had been | // the mempool starting with the earliest transaction that had been | ||||
Show All 38 Lines |
I don't think this is correct.
There are only 3 callsites (tests excepted):
So if you need the lock here then either I am missing a callsite or there is a problem somewhere else that cause the lock not the be held as expected.