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); | ||||
TxValidationState state; | TxValidationState 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, *this); | disconnectpool.addForBlock(vtx, *this); | ||||
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 426 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++; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 91 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 84 Lines • ▼ Show 20 Lines | void CTxMemPool::SetIsLoaded(bool loaded) { | ||||
m_is_loaded = loaded; | m_is_loaded = loaded; | ||||
} | } | ||||
/** Maximum bytes for transactions to store for processing during reorg */ | /** Maximum bytes for transactions to store for processing during reorg */ | ||||
static const size_t MAX_DISCONNECTED_TX_POOL_SIZE = 20 * DEFAULT_MAX_BLOCK_SIZE; | static const size_t MAX_DISCONNECTED_TX_POOL_SIZE = 20 * DEFAULT_MAX_BLOCK_SIZE; | ||||
void DisconnectedBlockTransactions::addForBlock( | void DisconnectedBlockTransactions::addForBlock( | ||||
const std::vector<CTransactionRef> &vtx, CTxMemPool &pool) { | const std::vector<CTransactionRef> &vtx, CTxMemPool &pool) { | ||||
AssertLockHeld(pool.cs); | |||||
for (const auto &tx : reverse_iterate(vtx)) { | for (const auto &tx : reverse_iterate(vtx)) { | ||||
// If we already added it, just skip. | // If we already added it, just skip. | ||||
auto it = queuedTx.find(tx->GetId()); | auto it = queuedTx.find(tx->GetId()); | ||||
if (it != queuedTx.end()) { | if (it != queuedTx.end()) { | ||||
continue; | continue; | ||||
} | } | ||||
// Insert the transaction into the pool. | // Insert the transaction into the pool. | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | while (DynamicMemoryUsage() > MAX_DISCONNECTED_TX_POOL_SIZE) { | ||||
// mempool. | // mempool. | ||||
auto it = queuedTx.get<insertion_order>().begin(); | auto it = queuedTx.get<insertion_order>().begin(); | ||||
pool.removeRecursive(**it, MemPoolRemovalReason::REORG); | pool.removeRecursive(**it, MemPoolRemovalReason::REORG); | ||||
removeEntry(it); | removeEntry(it); | ||||
} | } | ||||
} | } | ||||
void DisconnectedBlockTransactions::importMempool(CTxMemPool &pool) { | void DisconnectedBlockTransactions::importMempool(CTxMemPool &pool) { | ||||
AssertLockHeld(pool.cs); | |||||
// addForBlock's algorithm sorts a vector of transactions back into | // addForBlock's algorithm sorts a vector of transactions back into | ||||
// topological order. We use it in a separate object to create a valid | // topological order. We use it in a separate object to create a valid | ||||
// ordering of all mempool transactions, which we then splice in front of | // ordering of all mempool transactions, which we then splice in front of | ||||
// the current queuedTx. This results in a valid sequence of transactions to | // the current queuedTx. This results in a valid sequence of transactions to | ||||
// be reprocessed in updateMempoolForReorg. | // be reprocessed in updateMempoolForReorg. | ||||
// We create vtx in order of the entry_time index to facilitate for | // We create vtx in order of the entry_time index to facilitate for | ||||
// addForBlocks (which iterates in reverse order), as vtx probably end in | // addForBlocks (which iterates in reverse order), as vtx probably end in | ||||
// the correct ordering for queuedTx. | // the correct ordering for queuedTx. | ||||
std::vector<CTransactionRef> vtx; | std::vector<CTransactionRef> vtx; | ||||
{ | |||||
LOCK(pool.cs); | |||||
vtx.reserve(pool.mapTx.size()); | vtx.reserve(pool.mapTx.size()); | ||||
for (const CTxMemPoolEntry &e : pool.mapTx.get<entry_time>()) { | for (const CTxMemPoolEntry &e : pool.mapTx.get<entry_time>()) { | ||||
vtx.push_back(e.GetSharedTx()); | vtx.push_back(e.GetSharedTx()); | ||||
} | } | ||||
pool.clear(); | pool.clear(); | ||||
} | |||||
// Use addForBlocks to sort the transactions and then splice them in front | // Use addForBlocks to sort the transactions and then splice them in front | ||||
// of queuedTx | // of queuedTx | ||||
DisconnectedBlockTransactions orderedTxnPool; | DisconnectedBlockTransactions orderedTxnPool; | ||||
orderedTxnPool.addForBlock(vtx, pool); | orderedTxnPool.addForBlock(vtx, pool); | ||||
cachedInnerUsage += orderedTxnPool.cachedInnerUsage; | cachedInnerUsage += orderedTxnPool.cachedInnerUsage; | ||||
queuedTx.get<insertion_order>().splice( | queuedTx.get<insertion_order>().splice( | ||||
queuedTx.get<insertion_order>().begin(), | queuedTx.get<insertion_order>().begin(), | ||||
orderedTxnPool.queuedTx.get<insertion_order>()); | orderedTxnPool.queuedTx.get<insertion_order>()); | ||||
// We limit memory usage because we can't know if more blocks will be | // We limit memory usage because we can't know if more blocks will be | ||||
// 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, | ||||
CTxMemPool &pool) { | CTxMemPool &pool) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
AssertLockHeld(pool.cs); | |||||
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 | ||||
// previously seen in a block. | // previously seen in a block. | ||||
Show All 36 Lines |