Skip to content

Commit

Permalink
MOVEONLY: DisconnectedBlockTransactions from txmempool to validation
Browse files Browse the repository at this point in the history
This is only used by validation, so it should live there.
  • Loading branch information
glozow committed Sep 1, 2023
1 parent 172eb39 commit 3d66dc5
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 75 deletions.
75 changes: 0 additions & 75 deletions src/txmempool.h
Original file line number Diff line number Diff line change
Expand Up @@ -837,79 +837,4 @@ class CCoinsViewMemPool : public CCoinsViewBacked
void PackageAddTransaction(const CTransactionRef& tx);
};

/**
* DisconnectedBlockTransactions
* 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
* 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,
* since we expect most confirmed transactions to (typically) still be
* 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).
* 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
* still-unconfirmed transactions at the end.
*/
struct DisconnectedBlockTransactions {
uint64_t cachedInnerUsage = 0;
std::list<CTransactionRef> queuedTx;

// 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
// need to re-process remaining transactions to ensure mempool consistency.
// For now, assert() that we've emptied out this object on destruction.
// This assert() can always be removed if the reorg-processing code were
// 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
// reorg, besides draining this object).
~DisconnectedBlockTransactions() { assert(queuedTx.empty()); }

size_t DynamicMemoryUsage() const {
// std::list uses 3 pointers per entry.
return memusage::MallocUsage(sizeof(CTransactionRef) + 3 * sizeof(void*)) * queuedTx.size() + cachedInnerUsage;
}

void addTransaction(const CTransactionRef& tx)
{
queuedTx.push_back(tx);
cachedInnerUsage += RecursiveDynamicUsage(tx);
}

// Remove entries by txid, and update memory usage.
void removeForBlock(const std::vector<CTransactionRef>& vtx)
{
// Short-circuit in the common case of a block being added to the tip
if (queuedTx.empty()) {
return;
}
// Create a set of all block txids.
std::unordered_set<uint256, SaltedTxidHasher> txids;
std::transform(vtx.cbegin(), vtx.cend(), std::inserter(txids, txids.end()), [](const auto& tx) { return tx->GetHash(); });
// Iterate through entire list once, removing any transactions in the block.
auto it = queuedTx.begin();
while (it != queuedTx.end()) {
auto it_next = std::next(it);
if (txids.count((*it)->GetHash()) > 0) {
cachedInnerUsage -= RecursiveDynamicUsage(*it);
queuedTx.erase(it);
}
it = it_next;
}
}

// Remove the earliest-inserted transaction.
void remove_first()
{
cachedInnerUsage -= RecursiveDynamicUsage(queuedTx.front());
queuedTx.pop_front();
}

void clear()
{
cachedInnerUsage = 0;
queuedTx.clear();
}
};

#endif // BITCOIN_TXMEMPOOL_H
75 changes: 75 additions & 0 deletions src/validation.h
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,81 @@ struct PackageMempoolAcceptResult
: m_tx_results{ {wtxid, result} } {}
};

/**
* DisconnectedBlockTransactions
* 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
* 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,
* since we expect most confirmed transactions to (typically) still be
* 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).
* 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
* still-unconfirmed transactions at the end.
*/
struct DisconnectedBlockTransactions {
uint64_t cachedInnerUsage = 0;
std::list<CTransactionRef> queuedTx;

// 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
// need to re-process remaining transactions to ensure mempool consistency.
// For now, assert() that we've emptied out this object on destruction.
// This assert() can always be removed if the reorg-processing code were
// 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
// reorg, besides draining this object).
~DisconnectedBlockTransactions() { assert(queuedTx.empty()); }

size_t DynamicMemoryUsage() const {
// std::list uses 3 pointers per entry.
return memusage::MallocUsage(sizeof(CTransactionRef) + 3 * sizeof(void*)) * queuedTx.size() + cachedInnerUsage;
}

void addTransaction(const CTransactionRef& tx)
{
queuedTx.push_back(tx);
cachedInnerUsage += RecursiveDynamicUsage(tx);
}

// Remove entries by txid, and update memory usage.
void removeForBlock(const std::vector<CTransactionRef>& vtx)
{
// Short-circuit in the common case of a block being added to the tip
if (queuedTx.empty()) {
return;
}
// Create a set of all block txids.
std::unordered_set<uint256, SaltedTxidHasher> txids;
std::transform(vtx.cbegin(), vtx.cend(), std::inserter(txids, txids.end()), [](const auto& tx) { return tx->GetHash(); });
// Iterate through entire list once, removing any transactions in the block.
auto it = queuedTx.begin();
while (it != queuedTx.end()) {
auto it_next = std::next(it);
if (txids.count((*it)->GetHash()) > 0) {
cachedInnerUsage -= RecursiveDynamicUsage(*it);
queuedTx.erase(it);
}
it = it_next;
}
}

// Remove the earliest-inserted transaction.
void remove_first()
{
cachedInnerUsage -= RecursiveDynamicUsage(queuedTx.front());
queuedTx.pop_front();
}

void clear()
{
cachedInnerUsage = 0;
queuedTx.clear();
}
};

/**
* Try to add a transaction to the mempool. This is an internal function and is exposed only for testing.
* Client code should use ChainstateManager::ProcessTransaction()
Expand Down

0 comments on commit 3d66dc5

Please sign in to comment.