Browse Source

mempool: add notification for added/removed entries

Add notification signals to make it possible to subscribe to mempool
changes:

- NotifyEntryAdded(CTransactionRef)>
- NotifyEntryRemoved(CTransactionRef, MemPoolRemovalReason)>

Also add a mempool removal reason enumeration, which is passed to the
removed notification based on why the transaction was removed from
the mempool.
0.14
Wladimir J. van der Laan 9 years ago committed by Alex Morcos
parent
commit
ff25c32392
  1. 23
      src/txmempool.cpp
  2. 25
      src/txmempool.h
  3. 4
      src/validation.cpp

23
src/txmempool.cpp

@ -393,6 +393,7 @@ void CTxMemPool::AddTransactionsUpdated(unsigned int n)
bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, setEntries &setAncestors, bool validFeeEstimate) bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, setEntries &setAncestors, bool validFeeEstimate)
{ {
NotifyEntryAdded(entry.GetSharedTx());
// Add to memory pool without checking anything. // Add to memory pool without checking anything.
// Used by main.cpp AcceptToMemoryPool(), which DOES do // Used by main.cpp AcceptToMemoryPool(), which DOES do
// all the appropriate checks. // all the appropriate checks.
@ -449,8 +450,9 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
return true; return true;
} }
void CTxMemPool::removeUnchecked(txiter it) void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
{ {
NotifyEntryRemoved(it->GetSharedTx(), reason);
const uint256 hash = it->GetTx().GetHash(); const uint256 hash = it->GetTx().GetHash();
BOOST_FOREACH(const CTxIn& txin, it->GetTx().vin) BOOST_FOREACH(const CTxIn& txin, it->GetTx().vin)
mapNextTx.erase(txin.prevout); mapNextTx.erase(txin.prevout);
@ -502,7 +504,7 @@ void CTxMemPool::CalculateDescendants(txiter entryit, setEntries &setDescendants
} }
} }
void CTxMemPool::removeRecursive(const CTransaction &origTx) void CTxMemPool::removeRecursive(const CTransaction &origTx, MemPoolRemovalReason reason)
{ {
// Remove transaction from memory pool // Remove transaction from memory pool
{ {
@ -529,7 +531,8 @@ void CTxMemPool::removeRecursive(const CTransaction &origTx)
BOOST_FOREACH(txiter it, txToRemove) { BOOST_FOREACH(txiter it, txToRemove) {
CalculateDescendants(it, setAllRemoves); CalculateDescendants(it, setAllRemoves);
} }
RemoveStaged(setAllRemoves, false);
RemoveStaged(setAllRemoves, false, reason);
} }
} }
@ -567,7 +570,7 @@ void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMem
for (txiter it : txToRemove) { for (txiter it : txToRemove) {
CalculateDescendants(it, setAllRemoves); CalculateDescendants(it, setAllRemoves);
} }
RemoveStaged(setAllRemoves, false); RemoveStaged(setAllRemoves, false, MemPoolRemovalReason::REORG);
} }
void CTxMemPool::removeConflicts(const CTransaction &tx) void CTxMemPool::removeConflicts(const CTransaction &tx)
@ -581,7 +584,7 @@ void CTxMemPool::removeConflicts(const CTransaction &tx)
if (txConflict != tx) if (txConflict != tx)
{ {
ClearPrioritisation(txConflict.GetHash()); ClearPrioritisation(txConflict.GetHash());
removeRecursive(txConflict); removeRecursive(txConflict, MemPoolRemovalReason::CONFLICT);
} }
} }
} }
@ -610,7 +613,7 @@ void CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx, unsigne
if (it != mapTx.end()) { if (it != mapTx.end()) {
setEntries stage; setEntries stage;
stage.insert(it); stage.insert(it);
RemoveStaged(stage, true); RemoveStaged(stage, true, MemPoolRemovalReason::BLOCK);
} }
removeConflicts(*tx); removeConflicts(*tx);
ClearPrioritisation(tx->GetHash()); ClearPrioritisation(tx->GetHash());
@ -989,11 +992,11 @@ size_t CTxMemPool::DynamicMemoryUsage() const {
return memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 15 * sizeof(void*)) * mapTx.size() + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + memusage::DynamicUsage(mapLinks) + memusage::DynamicUsage(vTxHashes) + cachedInnerUsage; return memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 15 * sizeof(void*)) * mapTx.size() + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + memusage::DynamicUsage(mapLinks) + memusage::DynamicUsage(vTxHashes) + cachedInnerUsage;
} }
void CTxMemPool::RemoveStaged(setEntries &stage, bool updateDescendants) { void CTxMemPool::RemoveStaged(setEntries &stage, bool updateDescendants, MemPoolRemovalReason reason) {
AssertLockHeld(cs); AssertLockHeld(cs);
UpdateForRemoveFromMempool(stage, updateDescendants); UpdateForRemoveFromMempool(stage, updateDescendants);
BOOST_FOREACH(const txiter& it, stage) { BOOST_FOREACH(const txiter& it, stage) {
removeUnchecked(it); removeUnchecked(it, reason);
} }
} }
@ -1009,7 +1012,7 @@ int CTxMemPool::Expire(int64_t time) {
BOOST_FOREACH(txiter removeit, toremove) { BOOST_FOREACH(txiter removeit, toremove) {
CalculateDescendants(removeit, stage); CalculateDescendants(removeit, stage);
} }
RemoveStaged(stage, false); RemoveStaged(stage, false, MemPoolRemovalReason::EXPIRY);
return stage.size(); return stage.size();
} }
@ -1118,7 +1121,7 @@ void CTxMemPool::TrimToSize(size_t sizelimit, std::vector<uint256>* pvNoSpendsRe
BOOST_FOREACH(txiter iter, stage) BOOST_FOREACH(txiter iter, stage)
txn.push_back(iter->GetTx()); txn.push_back(iter->GetTx());
} }
RemoveStaged(stage, false); RemoveStaged(stage, false, MemPoolRemovalReason::SIZELIMIT);
if (pvNoSpendsRemaining) { if (pvNoSpendsRemaining) {
BOOST_FOREACH(const CTransaction& tx, txn) { BOOST_FOREACH(const CTransaction& tx, txn) {
BOOST_FOREACH(const CTxIn& txin, tx.vin) { BOOST_FOREACH(const CTxIn& txin, tx.vin) {

25
src/txmempool.h

@ -25,6 +25,8 @@
#include "boost/multi_index/ordered_index.hpp" #include "boost/multi_index/ordered_index.hpp"
#include "boost/multi_index/hashed_index.hpp" #include "boost/multi_index/hashed_index.hpp"
#include <boost/signals2/signal.hpp>
class CAutoFile; class CAutoFile;
class CBlockIndex; class CBlockIndex;
@ -333,6 +335,19 @@ struct TxMempoolInfo
int64_t nFeeDelta; int64_t nFeeDelta;
}; };
/** Reason why a transaction was removed from the mempool,
* this is passed to the notification signal.
*/
enum class MemPoolRemovalReason {
UNKNOWN = 0, //! Manually removed or unknown reason
EXPIRY, //! Expired from mempool
SIZELIMIT, //! Removed in size limiting
REORG, //! Removed for reorganization
BLOCK, //! Removed for block
CONFLICT, //! Removed for conflict with in-block transaction
REPLACED //! Removed for replacement
};
/** /**
* CTxMemPool stores valid-according-to-the-current-best-chain transactions * CTxMemPool stores valid-according-to-the-current-best-chain transactions
* that may be included in the next block. * that may be included in the next block.
@ -521,10 +536,11 @@ public:
bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, bool validFeeEstimate = true); bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, bool validFeeEstimate = true);
bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, setEntries &setAncestors, bool validFeeEstimate = true); bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, setEntries &setAncestors, bool validFeeEstimate = true);
void removeRecursive(const CTransaction &tx); void removeRecursive(const CTransaction &tx, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN);
void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags); void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags);
void removeConflicts(const CTransaction &tx); void removeConflicts(const CTransaction &tx);
void removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight); void removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight);
void clear(); void clear();
void _clear(); //lock free void _clear(); //lock free
bool CompareDepthAndScore(const uint256& hasha, const uint256& hashb); bool CompareDepthAndScore(const uint256& hasha, const uint256& hashb);
@ -551,7 +567,7 @@ public:
* Set updateDescendants to true when removing a tx that was in a block, so * Set updateDescendants to true when removing a tx that was in a block, so
* that any in-mempool descendants have their ancestor state updated. * that any in-mempool descendants have their ancestor state updated.
*/ */
void RemoveStaged(setEntries &stage, bool updateDescendants); void RemoveStaged(setEntries &stage, bool updateDescendants, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN);
/** When adding transactions from a disconnected block back to the mempool, /** When adding transactions from a disconnected block back to the mempool,
* new mempool entries may have children in the mempool (which is generally * new mempool entries may have children in the mempool (which is generally
@ -647,6 +663,9 @@ public:
size_t DynamicMemoryUsage() const; size_t DynamicMemoryUsage() const;
boost::signals2::signal<void (CTransactionRef)> NotifyEntryAdded;
boost::signals2::signal<void (CTransactionRef, MemPoolRemovalReason)> NotifyEntryRemoved;
private: private:
/** UpdateForDescendants is used by UpdateTransactionsFromBlock to update /** UpdateForDescendants is used by UpdateTransactionsFromBlock to update
* the descendants for a single transaction that has been added to the * the descendants for a single transaction that has been added to the
@ -683,7 +702,7 @@ private:
* transactions in a chain before we've updated all the state for the * transactions in a chain before we've updated all the state for the
* removal. * removal.
*/ */
void removeUnchecked(txiter entry); void removeUnchecked(txiter entry, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN);
}; };
/** /**

4
src/validation.cpp

@ -956,7 +956,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
if (plTxnReplaced) if (plTxnReplaced)
plTxnReplaced->push_back(it->GetSharedTx()); plTxnReplaced->push_back(it->GetSharedTx());
} }
pool.RemoveStaged(allConflicting, false); pool.RemoveStaged(allConflicting, false, MemPoolRemovalReason::REPLACED);
// This transaction should only count for fee estimation if // This transaction should only count for fee estimation if
// the node is not behind and it is not dependent on any other // the node is not behind and it is not dependent on any other
@ -2166,7 +2166,7 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara
// ignore validation errors in resurrected transactions // ignore validation errors in resurrected transactions
CValidationState stateDummy; CValidationState stateDummy;
if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, it, false, NULL, NULL, true)) { if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, it, false, NULL, NULL, true)) {
mempool.removeRecursive(tx); mempool.removeRecursive(tx, MemPoolRemovalReason::REORG);
} else if (mempool.exists(tx.GetHash())) { } else if (mempool.exists(tx.GetHash())) {
vHashUpdate.push_back(tx.GetHash()); vHashUpdate.push_back(tx.GetHash());
} }

Loading…
Cancel
Save