Changeset View
Changeset View
Standalone View
Standalone View
src/txmempool.h
Show First 20 Lines • Show All 676 Lines • ▼ Show 20 Lines | public: | ||||
* Remove transactions from the mempool until its dynamic size is <= | * Remove transactions from the mempool until its dynamic size is <= | ||||
* sizelimit. pvNoSpendsRemaining, if set, will be populated with the list | * sizelimit. pvNoSpendsRemaining, if set, will be populated with the list | ||||
* of outpoints which are not in mempool which no longer have any spends in | * of outpoints which are not in mempool which no longer have any spends in | ||||
* this mempool. | * this mempool. | ||||
*/ | */ | ||||
void TrimToSize(size_t sizelimit, | void TrimToSize(size_t sizelimit, | ||||
std::vector<COutPoint> *pvNoSpendsRemaining = nullptr); | std::vector<COutPoint> *pvNoSpendsRemaining = nullptr); | ||||
/** Expire all transaction (and their dependencies) in the mempool older | /** | ||||
* than time. Return the number of removed transactions. */ | * Expire all transaction (and their dependencies) in the mempool older than | ||||
* time. Return the number of removed transactions. | |||||
*/ | |||||
int Expire(int64_t time); | int Expire(int64_t time); | ||||
/** Returns false if the transaction is in the mempool and not within the | /** | ||||
* chain limit specified. */ | * Reduce the size of the mempool by expiring and then trimming the mempool. | ||||
*/ | |||||
void LimitSize(size_t limit, unsigned long age); | |||||
/** | |||||
* Returns false if the transaction is in the mempool and not within the | |||||
* chain limit specified. | |||||
*/ | |||||
bool TransactionWithinChainLimit(const uint256 &txid, | bool TransactionWithinChainLimit(const uint256 &txid, | ||||
size_t chainLimit) const; | size_t chainLimit) const; | ||||
unsigned long size() { | unsigned long size() { | ||||
LOCK(cs); | LOCK(cs); | ||||
return mapTx.size(); | return mapTx.size(); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | private: | ||||
* already reflected in the parent state). | * already reflected in the parent state). | ||||
* | * | ||||
* cachedDescendants will be updated with the descendants of the transaction | * cachedDescendants will be updated with the descendants of the transaction | ||||
* being updated, so that future invocations don't need to walk the same | * being updated, so that future invocations don't need to walk the same | ||||
* transaction again, if encountered in another transaction chain. | * transaction again, if encountered in another transaction chain. | ||||
*/ | */ | ||||
void UpdateForDescendants(txiter updateIt, cacheMap &cachedDescendants, | void UpdateForDescendants(txiter updateIt, cacheMap &cachedDescendants, | ||||
const std::set<uint256> &setExclude); | const std::set<uint256> &setExclude); | ||||
/** Update ancestors of hash to add/remove it as a descendant transaction. | /** | ||||
* Update ancestors of hash to add/remove it as a descendant transaction. | |||||
*/ | */ | ||||
void UpdateAncestorsOf(bool add, txiter hash, setEntries &setAncestors); | void UpdateAncestorsOf(bool add, txiter hash, setEntries &setAncestors); | ||||
/** Set ancestor state for an entry */ | /** Set ancestor state for an entry */ | ||||
void UpdateEntryForAncestors(txiter it, const setEntries &setAncestors); | void UpdateEntryForAncestors(txiter it, const setEntries &setAncestors); | ||||
/** | /** | ||||
* For each transaction being removed, update ancestors and any direct | * For each transaction being removed, update ancestors and any direct | ||||
* children. If updateDescendants is true, then also update in-mempool | * children. If updateDescendants is true, then also update in-mempool | ||||
* descendants' ancestor state. | * descendants' ancestor state. | ||||
Show All 40 Lines | bool operator()(const TxCoinAgePriority &a, const TxCoinAgePriority &b) { | ||||
return CompareTxMemPoolEntryByScore()(*(b.second), *(a.second)); | return CompareTxMemPoolEntryByScore()(*(b.second), *(a.second)); | ||||
} | } | ||||
return a.first < b.first; | return a.first < b.first; | ||||
} | } | ||||
}; | }; | ||||
/** | /** | ||||
* DisconnectedBlockTransactions | * DisconnectedBlockTransactions | ||||
* | |||||
* During the reorg, it's desirable to re-add previously confirmed transactions | * During the reorg, it's desirable to re-add previously confirmed transactions | ||||
* to the mempool, so that anything not re-confirmed in the new chain is | * to the mempool, so that anything not re-confirmed in the new chain is | ||||
* available to be mined. However, it's more efficient to wait until the reorg | * available to be mined. However, it's more efficient to wait until the reorg | ||||
* is complete and process all still-unconfirmed transactions at that time, | * is complete and process all still-unconfirmed transactions at that time, | ||||
* since we expect most confirmed transactions to (typically) still be | * since we expect most confirmed transactions to (typically) still be | ||||
* confirmed in the new chain, and re-accepting to the memory pool is expensive | * confirmed in the new chain, and re-accepting to the memory pool is expensive | ||||
* (and therefore better to not do in the middle of reorg-processing). | * (and therefore better to not do in the middle of reorg-processing). | ||||
* Instead, store the disconnected transactions (in order!) as we go, remove any | * Instead, store the disconnected transactions (in order!) as we go, remove any | ||||
* that are included in blocks in the new chain, and then process the remaining | * that are included in blocks in the new chain, and then process the remaining | ||||
* still-unconfirmed transactions at the end. | * still-unconfirmed transactions at the end. | ||||
*/ | */ | ||||
// multi_index tag names | // multi_index tag names | ||||
struct txid_index {}; | struct txid_index {}; | ||||
struct insertion_order {}; | struct insertion_order {}; | ||||
struct DisconnectedBlockTransactions { | class DisconnectedBlockTransactions { | ||||
private: | |||||
typedef boost::multi_index_container< | typedef boost::multi_index_container< | ||||
CTransactionRef, boost::multi_index::indexed_by< | CTransactionRef, boost::multi_index::indexed_by< | ||||
// sorted by txid | // sorted by txid | ||||
boost::multi_index::hashed_unique< | boost::multi_index::hashed_unique< | ||||
boost::multi_index::tag<txid_index>, | boost::multi_index::tag<txid_index>, | ||||
mempoolentry_txid, SaltedTxidHasher>, | mempoolentry_txid, SaltedTxidHasher>, | ||||
// sorted by order in the blockchain | // sorted by order in the blockchain | ||||
boost::multi_index::sequenced< | boost::multi_index::sequenced< | ||||
boost::multi_index::tag<insertion_order>>>> | boost::multi_index::tag<insertion_order>>>> | ||||
indexed_disconnected_transactions; | indexed_disconnected_transactions; | ||||
indexed_disconnected_transactions queuedTx; | |||||
uint64_t cachedInnerUsage = 0; | |||||
public: | |||||
// It's almost certainly a logic bug if we don't clear out queuedTx before | // It's almost certainly a logic bug if we don't clear out queuedTx before | ||||
// destruction, as we add to it while disconnecting blocks, and then we | // destruction, as we add to it while disconnecting blocks, and then we | ||||
// need to re-process remaining transactions to ensure mempool consistency. | // need to re-process remaining transactions to ensure mempool consistency. | ||||
// For now, assert() that we've emptied out this object on destruction. | // For now, assert() that we've emptied out this object on destruction. | ||||
// This assert() can always be removed if the reorg-processing code were | // This assert() can always be removed if the reorg-processing code were | ||||
// to be refactored such that this assumption is no longer true (for | // to be refactored such that this assumption is no longer true (for | ||||
// instance if there was some other way we cleaned up the mempool after a | // instance if there was some other way we cleaned up the mempool after a | ||||
// reorg, besides draining this object). | // reorg, besides draining this object). | ||||
~DisconnectedBlockTransactions() { assert(queuedTx.empty()); } | ~DisconnectedBlockTransactions() { assert(queuedTx.empty()); } | ||||
indexed_disconnected_transactions queuedTx; | |||||
uint64_t cachedInnerUsage = 0; | |||||
// Estimate the overhead of queuedTx to be 6 pointers + an allocation, as | // Estimate the overhead of queuedTx to be 6 pointers + an allocation, as | ||||
// no exact formula for boost::multi_index_contained is implemented. | // no exact formula for boost::multi_index_contained is implemented. | ||||
size_t DynamicMemoryUsage() const { | size_t DynamicMemoryUsage() const { | ||||
return memusage::MallocUsage(sizeof(CTransactionRef) + | return memusage::MallocUsage(sizeof(CTransactionRef) + | ||||
6 * sizeof(void *)) * | 6 * sizeof(void *)) * | ||||
queuedTx.size() + | queuedTx.size() + | ||||
cachedInnerUsage; | cachedInnerUsage; | ||||
} | } | ||||
// Add entries for a block while reconstructing the topological ordering so | |||||
// they can be added back to the mempool simply. | |||||
void addForBlock(const std::vector<CTransactionRef> &vtx); | |||||
void addTransaction(const CTransactionRef &tx) { | void addTransaction(const CTransactionRef &tx) { | ||||
queuedTx.insert(tx); | queuedTx.insert(tx); | ||||
cachedInnerUsage += RecursiveDynamicUsage(tx); | cachedInnerUsage += RecursiveDynamicUsage(tx); | ||||
} | } | ||||
// Remove entries based on txid_index, and update memory usage. | // Remove entries based on txid_index, and update memory usage. | ||||
void removeForBlock(const std::vector<CTransactionRef> &vtx) { | void removeForBlock(const std::vector<CTransactionRef> &vtx) { | ||||
// Short-circuit in the common case of a block being added to the tip | // Short-circuit in the common case of a block being added to the tip | ||||
if (queuedTx.empty()) { | if (queuedTx.empty()) { | ||||
return; | return; | ||||
} | } | ||||
for (auto const &tx : vtx) { | for (auto const &tx : vtx) { | ||||
auto it = queuedTx.find(tx->GetHash()); | auto it = queuedTx.find(tx->GetId()); | ||||
if (it != queuedTx.end()) { | if (it != queuedTx.end()) { | ||||
cachedInnerUsage -= RecursiveDynamicUsage(*it); | cachedInnerUsage -= RecursiveDynamicUsage(*it); | ||||
queuedTx.erase(it); | queuedTx.erase(it); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// Remove an entry by insertion_order index, and update memory usage. | // Remove an entry by insertion_order index, and update memory usage. | ||||
void removeEntry(indexed_disconnected_transactions::index< | void removeEntry(indexed_disconnected_transactions::index< | ||||
insertion_order>::type::iterator entry) { | insertion_order>::type::iterator entry) { | ||||
cachedInnerUsage -= RecursiveDynamicUsage(*entry); | cachedInnerUsage -= RecursiveDynamicUsage(*entry); | ||||
queuedTx.get<insertion_order>().erase(entry); | queuedTx.get<insertion_order>().erase(entry); | ||||
} | } | ||||
void clear() { | void clear() { | ||||
cachedInnerUsage = 0; | cachedInnerUsage = 0; | ||||
queuedTx.clear(); | queuedTx.clear(); | ||||
} | } | ||||
/** | |||||
* Make mempool consistent after a reorg, by re-adding or recursively | |||||
* erasing disconnected block transactions from the mempool, and also | |||||
* removing any other transactions from the mempool that are no longer valid | |||||
* given the new tip/height. | |||||
* | |||||
* Note: we assume that disconnectpool only contains transactions that are | |||||
* NOT confirmed in the current chain nor already in the mempool (otherwise, | |||||
* in-mempool descendants of such transactions would be removed). | |||||
* | |||||
* Passing fAddToMempool=false will skip trying to add the transactions | |||||
* back, and instead just erase from the mempool as needed. | |||||
*/ | |||||
void updateMempoolForReorg(const Config &config, bool fAddToMempool); | |||||
}; | }; | ||||
#endif // BITCOIN_TXMEMPOOL_H | #endif // BITCOIN_TXMEMPOOL_H |