Changeset View
Changeset View
Standalone View
Standalone View
src/txmempool.h
Show First 20 Lines • Show All 226 Lines • ▼ Show 20 Lines | struct update_lock_points { | ||||
void operator()(CTxMemPoolEntry &e) { e.UpdateLockPoints(lp); } | void operator()(CTxMemPoolEntry &e) { e.UpdateLockPoints(lp); } | ||||
private: | private: | ||||
const LockPoints &lp; | const LockPoints &lp; | ||||
}; | }; | ||||
// extracts a TxMemPoolEntry's transaction hash | // extracts a TxMemPoolEntry's transaction hash | ||||
struct mempoolentry_txid { | struct mempoolentry_txhash { | ||||
typedef uint256 result_type; | typedef txhash_t result_type; | ||||
result_type operator()(const CTxMemPoolEntry &entry) const { | result_type operator()(const CTxMemPoolEntry &entry) const { | ||||
return entry.GetTx().GetId(); | return entry.GetTx().GetHash(); | ||||
} | |||||
}; | |||||
// extracts a TxMemPoolEntry's unspentid | |||||
struct mempoolentry_unspentid { | |||||
typedef unspentid_t result_type; | |||||
result_type operator()(const CTxMemPoolEntry &entry) const { | |||||
return entry.GetTx().Getunspentid(); | |||||
} | } | ||||
}; | }; | ||||
/** \class CompareTxMemPoolEntryByDescendantScore | /** \class CompareTxMemPoolEntryByDescendantScore | ||||
* | * | ||||
* Sort an entry by max(score/size of entry's tx, score/size with all | * Sort an entry by max(score/size of entry's tx, score/size with all | ||||
* descendants). | * descendants). | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
class CompareTxMemPoolEntryByScore { | class CompareTxMemPoolEntryByScore { | ||||
public: | public: | ||||
bool operator()(const CTxMemPoolEntry &a, const CTxMemPoolEntry &b) { | bool operator()(const CTxMemPoolEntry &a, const CTxMemPoolEntry &b) { | ||||
double f1 = | double f1 = | ||||
double(b.GetTxSize() * a.GetModFeesWithDescendants().GetSatoshis()); | double(b.GetTxSize() * a.GetModFeesWithDescendants().GetSatoshis()); | ||||
double f2 = double(a.GetTxSize() * b.GetModifiedFee().GetSatoshis()); | double f2 = double(a.GetTxSize() * b.GetModifiedFee().GetSatoshis()); | ||||
if (f1 == f2) { | if (f1 == f2) { | ||||
return b.GetTx().GetId() < a.GetTx().GetId(); | return b.GetTx().GetHash() < a.GetTx().GetHash(); | ||||
} | } | ||||
return f1 > f2; | return f1 > f2; | ||||
} | } | ||||
}; | }; | ||||
class CompareTxMemPoolEntryByEntryTime { | class CompareTxMemPoolEntryByEntryTime { | ||||
public: | public: | ||||
bool operator()(const CTxMemPoolEntry &a, const CTxMemPoolEntry &b) { | bool operator()(const CTxMemPoolEntry &a, const CTxMemPoolEntry &b) { | ||||
Show All 10 Lines | bool operator()(const CTxMemPoolEntry &a, const CTxMemPoolEntry &b) { | ||||
double bFees = double(b.GetModFeesWithAncestors().GetSatoshis()); | double bFees = double(b.GetModFeesWithAncestors().GetSatoshis()); | ||||
double bSize = b.GetSizeWithAncestors(); | double bSize = b.GetSizeWithAncestors(); | ||||
// Avoid division by rewriting (a/b > c/d) as (a*d > c*b). | // Avoid division by rewriting (a/b > c/d) as (a*d > c*b). | ||||
double f1 = aFees * bSize; | double f1 = aFees * bSize; | ||||
double f2 = aSize * bFees; | double f2 = aSize * bFees; | ||||
if (f1 == f2) { | if (f1 == f2) { | ||||
return a.GetTx().GetId() < b.GetTx().GetId(); | return a.GetTx().GetHash() < b.GetTx().GetHash(); | ||||
} | } | ||||
return f1 > f2; | return f1 > f2; | ||||
} | } | ||||
}; | }; | ||||
// Multi_index tag names | // Multi_index tag names | ||||
struct unspentid_idx {}; | |||||
struct descendant_score {}; | struct descendant_score {}; | ||||
struct entry_time {}; | struct entry_time {}; | ||||
struct mining_score {}; | struct mining_score {}; | ||||
struct ancestor_score {}; | struct ancestor_score {}; | ||||
class CBlockPolicyEstimator; | class CBlockPolicyEstimator; | ||||
/** | /** | ||||
Show All 29 Lines | enum class MemPoolRemovalReason { | ||||
//! Removed for block | //! Removed for block | ||||
BLOCK, | BLOCK, | ||||
//! Removed for conflict with in-block transaction | //! Removed for conflict with in-block transaction | ||||
CONFLICT, | CONFLICT, | ||||
//! Removed for replacement | //! Removed for replacement | ||||
REPLACED | REPLACED | ||||
}; | }; | ||||
class SaltedTxidHasher { | class SaltedTxHasher { | ||||
private: | private: | ||||
/** Salt */ | /** Salt */ | ||||
const uint64_t k0, k1; | const uint64_t k0, k1; | ||||
public: | public: | ||||
SaltedTxidHasher(); | SaltedTxHasher(); | ||||
size_t operator()(const uint256 &txid) const { | size_t operator()(const uint256 &hash) const { | ||||
return SipHashUint256(k0, k1, txid); | return SipHashUint256(k0, k1, hash); | ||||
} | } | ||||
}; | }; | ||||
/** | /** | ||||
* CTxMemPool stores valid-according-to-the-current-best-chain transactions that | * CTxMemPool stores valid-according-to-the-current-best-chain transactions that | ||||
* may be included in the next block. | * may be included in the next block. | ||||
* | * | ||||
* Transactions are added when they are seen on the network (or created by the | * Transactions are added when they are seen on the network (or created by the | ||||
▲ Show 20 Lines • Show All 93 Lines • ▼ Show 20 Lines | private: | ||||
void trackPackageRemoved(const CFeeRate &rate); | void trackPackageRemoved(const CFeeRate &rate); | ||||
public: | public: | ||||
// public only for testing | // public only for testing | ||||
static const int ROLLING_FEE_HALFLIFE = 60 * 60 * 12; | static const int ROLLING_FEE_HALFLIFE = 60 * 60 * 12; | ||||
typedef boost::multi_index_container< | typedef boost::multi_index_container< | ||||
CTxMemPoolEntry, boost::multi_index::indexed_by< | CTxMemPoolEntry, boost::multi_index::indexed_by< | ||||
// sorted by txid | // sorted by hash | ||||
boost::multi_index::hashed_unique< | boost::multi_index::hashed_unique< | ||||
mempoolentry_txid, SaltedTxidHasher>, | mempoolentry_txhash, SaltedTxHasher>, | ||||
// sorted by unspentid | |||||
boost::multi_index::hashed_unique< | |||||
boost::multi_index::tag<unspentid_idx>, | |||||
mempoolentry_unspentid, SaltedTxHasher>, | |||||
// sorted by fee rate | // sorted by fee rate | ||||
boost::multi_index::ordered_non_unique< | boost::multi_index::ordered_non_unique< | ||||
boost::multi_index::tag<descendant_score>, | boost::multi_index::tag<descendant_score>, | ||||
boost::multi_index::identity<CTxMemPoolEntry>, | boost::multi_index::identity<CTxMemPoolEntry>, | ||||
CompareTxMemPoolEntryByDescendantScore>, | CompareTxMemPoolEntryByDescendantScore>, | ||||
// sorted by entry time | // sorted by entry time | ||||
boost::multi_index::ordered_non_unique< | boost::multi_index::ordered_non_unique< | ||||
boost::multi_index::tag<entry_time>, | boost::multi_index::tag<entry_time>, | ||||
Show All 11 Lines | typedef boost::multi_index_container< | ||||
CompareTxMemPoolEntryByAncestorFee>>> | CompareTxMemPoolEntryByAncestorFee>>> | ||||
indexed_transaction_set; | indexed_transaction_set; | ||||
mutable CCriticalSection cs; | mutable CCriticalSection cs; | ||||
indexed_transaction_set mapTx; | indexed_transaction_set mapTx; | ||||
typedef indexed_transaction_set::nth_index<0>::type::iterator txiter; | typedef indexed_transaction_set::nth_index<0>::type::iterator txiter; | ||||
//!< All tx hashes/entries in mapTx, in random order | //!< All tx hashes/entries in mapTx, in random order | ||||
std::vector<std::pair<uint256, txiter>> vTxHashes; | std::vector<std::pair<txhash_t, txiter>> vTxHashes; | ||||
struct CompareIteratorByHash { | struct CompareIteratorByHash { | ||||
bool operator()(const txiter &a, const txiter &b) const { | bool operator()(const txiter &a, const txiter &b) const { | ||||
return a->GetTx().GetId() < b->GetTx().GetId(); | return a->GetTx().GetHash() < b->GetTx().GetHash(); | ||||
} | } | ||||
}; | }; | ||||
typedef std::set<txiter, CompareIteratorByHash> setEntries; | typedef std::set<txiter, CompareIteratorByHash> setEntries; | ||||
const setEntries &GetMemPoolParents(txiter entry) const; | const setEntries &GetMemPoolParents(txiter entry) const; | ||||
const setEntries &GetMemPoolChildren(txiter entry) const; | const setEntries &GetMemPoolChildren(txiter entry) const; | ||||
private: | private: | ||||
typedef std::map<txiter, setEntries, CompareIteratorByHash> cacheMap; | typedef std::map<txiter, setEntries, CompareIteratorByHash> cacheMap; | ||||
struct TxLinks { | struct TxLinks { | ||||
setEntries parents; | setEntries parents; | ||||
setEntries children; | setEntries children; | ||||
}; | }; | ||||
typedef std::map<txiter, TxLinks, CompareIteratorByHash> txlinksMap; | typedef std::map<txiter, TxLinks, CompareIteratorByHash> txlinksMap; | ||||
txlinksMap mapLinks; | txlinksMap mapLinks; | ||||
void UpdateParent(txiter entry, txiter parent, bool add); | void UpdateParent(txiter entry, txiter parent, bool add); | ||||
void UpdateChild(txiter entry, txiter child, bool add); | void UpdateChild(txiter entry, txiter child, bool add); | ||||
std::vector<indexed_transaction_set::const_iterator> | std::vector<indexed_transaction_set::const_iterator> | ||||
GetSortedDepthAndScore() const; | GetSortedDepthAndScore() const; | ||||
typedef indexed_transaction_set::index<unspentid_idx>::type | |||||
mapTxByunspentid; | |||||
txiter findByunspentid(const unspentid_t &unspentid) const { | |||||
mapTxByunspentid::iterator pit = | |||||
mapTx.get<unspentid_idx>().find(unspentid); | |||||
if (pit == mapTx.get<unspentid_idx>().end()) { | |||||
return mapTx.end(); | |||||
} else { | |||||
return mapTx.find(pit->GetTx().GetHash()); | |||||
} | |||||
} | |||||
public: | public: | ||||
indirectmap<COutPoint, const CTransaction *> mapNextTx; | indirectmap<COutPoint, const CTransaction *> mapNextTx; | ||||
std::map<uint256, std::pair<double, Amount>> mapDeltas; | std::map<txhash_t, std::pair<double, Amount>> mapDeltas; | ||||
/** Create a new CTxMemPool. | /** Create a new CTxMemPool. | ||||
*/ | */ | ||||
CTxMemPool(const CFeeRate &_minReasonableRelayFee); | CTxMemPool(const CFeeRate &_minReasonableRelayFee); | ||||
~CTxMemPool(); | ~CTxMemPool(); | ||||
/** | /** | ||||
* If sanity-checking is turned on, check makes sure the pool is consistent | * If sanity-checking is turned on, check makes sure the pool is consistent | ||||
* (does not contain two transactions that spend the same inputs, all inputs | * (does not contain two transactions that spend the same inputs, all inputs | ||||
* are in the mapNextTx array). If sanity-checking is turned off, check does | * are in the mapNextTx array). If sanity-checking is turned off, check does | ||||
* nothing. | * nothing. | ||||
*/ | */ | ||||
void check(const CCoinsViewCache *pcoins) const; | void check(const CCoinsViewCache *pcoins) const; | ||||
void setSanityCheck(double dFrequency = 1.0) { | void setSanityCheck(double dFrequency = 1.0) { | ||||
nCheckFrequency = dFrequency * 4294967295.0; | nCheckFrequency = dFrequency * 4294967295.0; | ||||
} | } | ||||
// addUnchecked must updated state for all ancestors of a given transaction, | // addUnchecked must updated state for all ancestors of a given transaction, | ||||
// to track size/count of descendant transactions. First version of | // to track size/count of descendant transactions. First version of | ||||
// addUnchecked can be used to have it call CalculateMemPoolAncestors(), and | // addUnchecked can be used to have it call CalculateMemPoolAncestors(), and | ||||
// then invoke the second version. | // then invoke the second version. | ||||
bool addUnchecked(const uint256 &hash, const CTxMemPoolEntry &entry, | bool addUnchecked(const txhash_t &txhash, const CTxMemPoolEntry &entry, | ||||
bool validFeeEstimate = true); | bool validFeeEstimate = true); | ||||
bool addUnchecked(const uint256 &hash, const CTxMemPoolEntry &entry, | bool addUnchecked(const txhash_t &txhash, const CTxMemPoolEntry &entry, | ||||
setEntries &setAncestors, bool validFeeEstimate = true); | setEntries &setAncestors, bool validFeeEstimate = true); | ||||
void removeRecursive( | void removeRecursive( | ||||
const CTransaction &tx, | const CTransaction &tx, | ||||
MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN); | MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN); | ||||
void removeForReorg(const CCoinsViewCache *pcoins, | void removeForReorg(const CCoinsViewCache *pcoins, | ||||
unsigned int nMemPoolHeight, int flags); | unsigned int nMemPoolHeight, int flags); | ||||
void removeConflicts(const CTransaction &tx); | void removeConflicts(const CTransaction &tx); | ||||
void removeForBlock(const std::vector<CTransactionRef> &vtx, | void removeForBlock(const std::vector<CTransactionRef> &vtx, | ||||
unsigned int nBlockHeight); | unsigned int nBlockHeight); | ||||
void clear(); | void clear(); | ||||
// lock free | // lock free | ||||
void _clear(); | void _clear(); | ||||
bool CompareDepthAndScore(const uint256 &hasha, const uint256 &hashb); | bool CompareDepthAndScore(const txhash_t &hasha, const txhash_t &hashb); | ||||
void queryHashes(std::vector<uint256> &vtxid); | void queryHashes(std::vector<txhash_t> &vtxhash); | ||||
bool isSpent(const COutPoint &outpoint); | bool isSpent(const COutPoint &outpoint); | ||||
unsigned int GetTransactionsUpdated() const; | unsigned int GetTransactionsUpdated() const; | ||||
void AddTransactionsUpdated(unsigned int n); | void AddTransactionsUpdated(unsigned int n); | ||||
/** | /** | ||||
* Check that none of this transactions inputs are in the mempool, and thus | * Check that none of this transactions inputs are in the mempool, and thus | ||||
* the tx is not dependent on other mempool transactions to be included in a | * the tx is not dependent on other mempool transactions to be included in a | ||||
* block. | * block. | ||||
*/ | */ | ||||
bool HasNoInputsOf(const CTransaction &tx) const; | bool HasNoInputsOf(const CTransaction &tx) const; | ||||
/** Affect CreateNewBlock prioritisation of transactions */ | /** Affect CreateNewBlock prioritisation of transactions */ | ||||
void PrioritiseTransaction(const uint256 hash, const std::string strHash, | void PrioritiseTransaction(const txhash_t txhash, const std::string strHash, | ||||
double dPriorityDelta, const Amount nFeeDelta); | double dPriorityDelta, const Amount nFeeDelta); | ||||
void ApplyDeltas(const uint256 hash, double &dPriorityDelta, | void ApplyDeltas(const txhash_t txhash, double &dPriorityDelta, | ||||
Amount nFeeDelta) const; | Amount nFeeDelta) const; | ||||
void ClearPrioritisation(const uint256 hash); | void ClearPrioritisation(const txhash_t txhash); | ||||
public: | public: | ||||
/** | /** | ||||
* Remove a set of transactions from the mempool. If a transaction is in | * Remove a set of transactions from the mempool. If a transaction is in | ||||
* this set, then all in-mempool descendants must also be in the set, unless | * this set, then all in-mempool descendants must also be in the set, unless | ||||
* this transaction is being removed for being in a block. Set | * this transaction is being removed for being in a block. Set | ||||
* updateDescendants to true when removing a tx that was in a block, so that | * updateDescendants to true when removing a tx that was in a block, so that | ||||
* any in-mempool descendants have their ancestor state updated. | * any in-mempool descendants have their ancestor state updated. | ||||
*/ | */ | ||||
void | void | ||||
RemoveStaged(setEntries &stage, bool updateDescendants, | RemoveStaged(setEntries &stage, bool updateDescendants, | ||||
MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN); | 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 | ||||
* not the case when otherwise adding transactions). | * not the case when otherwise adding transactions). | ||||
* UpdateTransactionsFromBlock() will find child transactions and update the | * UpdateTransactionsFromBlock() will find child transactions and update the | ||||
* descendant state for each transaction in hashesToUpdate (excluding any | * descendant state for each transaction in hashesToUpdate (excluding any | ||||
* child transactions present in hashesToUpdate, which are already accounted | * child transactions present in hashesToUpdate, which are already accounted | ||||
* for). Note: hashesToUpdate should be the set of transactions from the | * for). Note: hashesToUpdate should be the set of transactions from the | ||||
* disconnected block that have been accepted back into the mempool. | * disconnected block that have been accepted back into the mempool. | ||||
*/ | */ | ||||
void | void | ||||
UpdateTransactionsFromBlock(const std::vector<uint256> &hashesToUpdate); | UpdateTransactionsFromBlock(const std::vector<txhash_t> &hashesToUpdate); | ||||
/** | /** | ||||
* Try to calculate all in-mempool ancestors of entry. | * Try to calculate all in-mempool ancestors of entry. | ||||
* (these are all calculated including the tx itself) | * (these are all calculated including the tx itself) | ||||
* limitAncestorCount = max number of ancestors | * limitAncestorCount = max number of ancestors | ||||
* limitAncestorSize = max size of ancestors | * limitAncestorSize = max size of ancestors | ||||
* limitDescendantCount = max number of descendants any ancestor can have | * limitDescendantCount = max number of descendants any ancestor can have | ||||
* limitDescendantSize = max size of descendants any ancestor can have | * limitDescendantSize = max size of descendants any ancestor can have | ||||
Show All 33 Lines | 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 | /** Expire all transaction (and their dependencies) in the mempool older | ||||
* than time. Return the number of removed transactions. */ | * 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 | /** Returns false if the transaction is in the mempool and not within the | ||||
* chain limit specified. */ | * chain limit specified. */ | ||||
bool TransactionWithinChainLimit(const uint256 &txid, | bool TransactionWithinChainLimit(const txhash_t &txhash, | ||||
size_t chainLimit) const; | size_t chainLimit) const; | ||||
unsigned long size() { | unsigned long size() { | ||||
LOCK(cs); | LOCK(cs); | ||||
return mapTx.size(); | return mapTx.size(); | ||||
} | } | ||||
uint64_t GetTotalTxSize() { | uint64_t GetTotalTxSize() { | ||||
LOCK(cs); | LOCK(cs); | ||||
return totalTxSize; | return totalTxSize; | ||||
} | } | ||||
bool exists(uint256 hash) const { | bool exists(txhash_t txhash) const { | ||||
LOCK(cs); | LOCK(cs); | ||||
return mapTx.count(hash) != 0; | return mapTx.count(txhash) != 0; | ||||
} | } | ||||
bool exists(const COutPoint &outpoint) const { | bool exists(const COutPoint &outpoint) const { | ||||
LOCK(cs); | LOCK(cs); | ||||
auto it = mapTx.find(outpoint.hash); | auto it = findByunspentid(outpoint.unspentid); | ||||
return it != mapTx.end() && outpoint.n < it->GetTx().vout.size(); | return it != mapTx.end() && outpoint.n < it->GetTx().vout.size(); | ||||
} | } | ||||
CTransactionRef get(const uint256 &hash) const; | bool exists(unspentid_t unspentid) const { | ||||
TxMempoolInfo info(const uint256 &hash) const; | LOCK(cs); | ||||
return (mapTx.get<unspentid_idx>().count(unspentid) != 0); | |||||
} | |||||
CTransactionRef get(const txhash_t &txhash) const; | |||||
CTransactionRef get(const unspentid_t &unspentid) const; | |||||
TxMempoolInfo info(const txhash_t &txhash) const; | |||||
std::vector<TxMempoolInfo> infoAll() const; | std::vector<TxMempoolInfo> infoAll() const; | ||||
/** | /** | ||||
* Estimate fee rate needed to get into the next nBlocks. If no answer can | * Estimate fee rate needed to get into the next nBlocks. If no answer can | ||||
* be given at nBlocks, return an estimate at the lowest number of blocks | * be given at nBlocks, return an estimate at the lowest number of blocks | ||||
* where one can be given. | * where one can be given. | ||||
*/ | */ | ||||
CFeeRate estimateSmartFee(int nBlocks, | CFeeRate estimateSmartFee(int nBlocks, | ||||
Show All 33 Lines | private: | ||||
* the mempool after the transaction being updated and hence their state is | * the mempool after the transaction being updated and hence their state is | ||||
* 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<txhash_t> &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 | ||||
▲ Show 20 Lines • Show All 47 Lines • Show Last 20 Lines |