Changeset View
Changeset View
Standalone View
Standalone View
src/txmempool.cpp
Show First 20 Lines • Show All 630 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); | LOCK(cs); | ||||
DisconnectedBlockTransactions disconnectpool; | DisconnectedBlockTransactions disconnectpool; | ||||
disconnectpool.addForBlock(vtx); | 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(); | ||||
indexed_transaction_set::iterator i = mapTx.find(txid); | indexed_transaction_set::iterator i = mapTx.find(txid); | ||||
if (i != mapTx.end()) { | if (i != mapTx.end()) { | ||||
▲ Show 20 Lines • Show All 631 Lines • ▼ Show 20 Lines | void CTxMemPool::SetIsLoaded(bool loaded) { | ||||
LOCK(cs); | LOCK(cs); | ||||
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) { | const std::vector<CTransactionRef> &vtx, CTxMemPool &pool) { | ||||
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 All 35 Lines | for (const auto &tx : reverse_iterate(vtx)) { | ||||
} | } | ||||
} | } | ||||
// 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); | pool.removeRecursive(**it, MemPoolRemovalReason::REORG); | ||||
removeEntry(it); | removeEntry(it); | ||||
} | } | ||||
} | } | ||||
void DisconnectedBlockTransactions::importMempool(CTxMemPool &pool) { | void DisconnectedBlockTransactions::importMempool(CTxMemPool &pool) { | ||||
// 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 | ||||
Show All 11 Lines | std::vector<CTransactionRef> vtx; | ||||
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); | 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) { | |||||
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 | ||||
// previously seen in a block. | // previously seen in a block. | ||||
for (const CTransactionRef &tx : | for (const CTransactionRef &tx : | ||||
reverse_iterate(queuedTx.get<insertion_order>())) { | reverse_iterate(queuedTx.get<insertion_order>())) { | ||||
// ignore validation errors in resurrected transactions | // ignore validation errors in resurrected transactions | ||||
TxValidationState stateDummy; | TxValidationState stateDummy; | ||||
if (!fAddToMempool || tx->IsCoinBase() || | if (!fAddToMempool || tx->IsCoinBase() || | ||||
!AcceptToMemoryPool(config, g_mempool, stateDummy, tx, | !AcceptToMemoryPool(config, pool, stateDummy, tx, | ||||
true /* bypass_limits */, | true /* bypass_limits */, | ||||
Amount::zero() /* nAbsurdFee */)) { | Amount::zero() /* nAbsurdFee */)) { | ||||
// If the transaction doesn't make it in to the mempool, remove any | // If the transaction doesn't make it in to the mempool, remove any | ||||
// transactions that depend on it (which would now be orphans). | // transactions that depend on it (which would now be orphans). | ||||
g_mempool.removeRecursive(*tx, MemPoolRemovalReason::REORG); | pool.removeRecursive(*tx, MemPoolRemovalReason::REORG); | ||||
} else if (g_mempool.exists(tx->GetId())) { | } else if (pool.exists(tx->GetId())) { | ||||
txidsUpdate.push_back(tx->GetId()); | txidsUpdate.push_back(tx->GetId()); | ||||
} | } | ||||
} | } | ||||
queuedTx.clear(); | queuedTx.clear(); | ||||
// AcceptToMemoryPool/addUnchecked all assume that new mempool entries have | // AcceptToMemoryPool/addUnchecked all assume that new mempool entries have | ||||
// no in-mempool children, which is generally not true when adding | // no in-mempool children, which is generally not true when adding | ||||
// previously-confirmed transactions back to the mempool. | // previously-confirmed transactions back to the mempool. | ||||
// UpdateTransactionsFromBlock finds descendants of any transactions in the | // UpdateTransactionsFromBlock finds descendants of any transactions in the | ||||
// disconnectpool that were added back and cleans up the mempool state. | // disconnectpool that were added back and cleans up the mempool state. | ||||
g_mempool.UpdateTransactionsFromBlock(txidsUpdate); | pool.UpdateTransactionsFromBlock(txidsUpdate); | ||||
// We also need to remove any now-immature transactions | // We also need to remove any now-immature transactions | ||||
g_mempool.removeForReorg(config, pcoinsTip.get(), | pool.removeForReorg(config, pcoinsTip.get(), | ||||
::ChainActive().Tip()->nHeight + 1, | ::ChainActive().Tip()->nHeight + 1, | ||||
STANDARD_LOCKTIME_VERIFY_FLAGS); | STANDARD_LOCKTIME_VERIFY_FLAGS); | ||||
// Re-limit mempool size, in case we added any transactions | // Re-limit mempool size, in case we added any transactions | ||||
g_mempool.LimitSize( | pool.LimitSize(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * | ||||
gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, | 1000000, | ||||
std::chrono::hours{ | std::chrono::hours{ | ||||
gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)}); | gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)}); | ||||
} | } |