0x00 摘要
我們知道當交易被廣播並且被礦工接收到時,礦工就會把交易加入到本地的交易池當中,每個礦工又會對自己的交易池設置相應的限制,來保證交易數量不會過多,礦工在打包交易到區塊中時,也會根據一定的優先順序來選擇交易,從而讓自己能獲得儘量多的交易費。
對於交易池主要介紹兩個結構CTxMemPoolEntry
和CTxMemPool
,第一個是交易池中每一個元素的基本結構,第二個是整個交易池包含的所有信息。
0x01 CTxMemPoolEntry
/**
* CTxMemPoolEntry存儲交易和該交易的所有子孫交易,
* 當一個新的entry添加到mempool中時,我們更新它的所有子孫狀態
* 和祖先狀態
*/
class CTxMemPoolEntry
{
private:
CTransactionRef tx; // 交易引用
CAmount nFee; //交易費用 //!< Cached to avoid expensive parent-transaction lookups
size_t nTxWeight; //!< ... and avoid recomputing tx weight (also used for GetTxSize())
size_t nUsageSize; //大小 //!< ... and total memory usage
int64_t nTime; //時間戳 //!< Local time when entering the mempool
unsigned int entryHeight; //區塊高度 //!< Chain height when entering the mempool
bool spendsCoinbase; //前一個交易是否是CoinBase
int64_t sigOpCost; //!< Total sigop cost
int64_t feeDelta; // 調整交易的優先級
LockPoints lockPoints; //交易最後的所在區塊高度和打包的時間
// 子孫交易信息,如果我們移除一個交易,必須同時移除它的所有子孫交易
uint64_t nCountWithDescendants; // 子孫交易的數量
uint64_t nSizeWithDescendants; // 大小
CAmount nModFeesWithDescendants; // 費用和,包括當前交易
// 祖先交易信息
uint64_t nCountWithAncestors;
uint64_t nSizeWithAncestors;
CAmount nModFeesWithAncestors;
int64_t nSigOpCostWithAncestors;
public:
CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee,
int64_t _nTime, unsigned int _entryHeight,
bool spendsCoinbase,
int64_t nSigOpsCost, LockPoints lp);
const CTransaction& GetTx() const { return *this->tx; }
CTransactionRef GetSharedTx() const { return this->tx; }
const CAmount& GetFee() const { return nFee; }
size_t GetTxSize() const;
size_t GetTxWeight() const { return nTxWeight; }
int64_t GetTime() const { return nTime; }
unsigned int GetHeight() const { return entryHeight; }
int64_t GetSigOpCost() const { return sigOpCost; }
int64_t GetModifiedFee() const { return nFee + feeDelta; }
size_t DynamicMemoryUsage() const { return nUsageSize; }
const LockPoints& GetLockPoints() const { return lockPoints; }
// 更新子孫狀態
void UpdateDescendantState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount);
// 更新祖先狀態
void UpdateAncestorState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount, int modifySigOps);
// 更新feeDelta,並且修改子孫交易費用
void UpdateFeeDelta(int64_t feeDelta);
// 更新LockPoint
void UpdateLockPoints(const LockPoints& lp);
uint64_t GetCountWithDescendants() const { return nCountWithDescendants; }
uint64_t GetSizeWithDescendants() const { return nSizeWithDescendants; }
CAmount GetModFeesWithDescendants() const { return nModFeesWithDescendants; }
bool GetSpendsCoinbase() const { return spendsCoinbase; }
uint64_t GetCountWithAncestors() const { return nCountWithAncestors; }
uint64_t GetSizeWithAncestors() const { return nSizeWithAncestors; }
CAmount GetModFeesWithAncestors() const { return nModFeesWithAncestors; }
int64_t GetSigOpCostWithAncestors() const { return nSigOpCostWithAncestors; }
mutable size_t vTxHashesIdx; //!< Index in mempool's vTxHashes
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
0x02 CTxMemPool
/**
* 交易內存池,保存所有在當前主鏈上有效的交易。
* 當交易在網絡上廣播之後,就會被加進交易池。
* 但並不是所有的交易都會被加入,
* 例如交易費太小的,或者“雙花”的交易或者非標準交易。
* 內存池中通過一個boost::multi_index類型的變量mapTx來排序所有交易,
* 按照下面四個標準:
* -交易hash
* -交易費(包括所有子孫交易)
* -在mempool中的時間
* -挖礦分數
* 爲了保證交易費的正確性,當新交易被加進mempool時,我們必須更新
* 該交易的所有祖先交易信息,而這個操作可能會導致處理速度變慢,
* 所以必須對更需祖先的數量進行限制。
*/
class CTxMemPool
{
private:
uint32_t nCheckFrequency; //表示在2^32時間內檢查的次數
unsigned int nTransactionsUpdated; //!< Used by getblocktemplate to trigger CreateNewBlock() invocation
CBlockPolicyEstimator* minerPolicyEstimator;
uint64_t totalTxSize; //所有mempool中交易的虛擬大小,不包括見證數據
uint64_t cachedInnerUsage; //map中元素使用的動態內存大小之和
mutable int64_t lastRollingFeeUpdate;
mutable bool blockSinceLastRollingFeeBump;
mutable double rollingMinimumFeeRate; //進入pool需要的最小費用
void trackPackageRemoved(const CFeeRate& rate);
public:
static const int ROLLING_FEE_HALFLIFE = 60 * 60 * 12; // public only for testing
typedef boost::multi_index_container<
CTxMemPoolEntry,
boost::multi_index::indexed_by<
// sorted by txid, 首先根據交易hash排
boost::multi_index::hashed_unique<mempoolentry_txid, SaltedTxidHasher>,
// sorted by fee rate,然後是費用
boost::multi_index::ordered_non_unique<
boost::multi_index::tag<descendant_score>,
boost::multi_index::identity<CTxMemPoolEntry>,
CompareTxMemPoolEntryByDescendantScore
>,
// sorted by entry time,然後時間
boost::multi_index::ordered_non_unique<
boost::multi_index::tag<entry_time>,
boost::multi_index::identity<CTxMemPoolEntry>,
CompareTxMemPoolEntryByEntryTime
>,
// sorted by score (for mining prioritization), 分數
boost::multi_index::ordered_unique<
boost::multi_index::tag<mining_score>,
boost::multi_index::identity<CTxMemPoolEntry>,
CompareTxMemPoolEntryByScore
>,
// sorted by fee rate with ancestors, 祖先交易費
boost::multi_index::ordered_non_unique<
boost::multi_index::tag<ancestor_score>,
boost::multi_index::identity<CTxMemPoolEntry>,
CompareTxMemPoolEntryByAncestorFee
>
>
> indexed_transaction_set;
mutable CCriticalSection cs;
indexed_transaction_set mapTx;
typedef indexed_transaction_set::nth_index<0>::type::iterator txiter;
std::vector<std::pair<uint256, txiter> > vTxHashes; //所有交易見證數據的哈希
struct CompareIteratorByHash {
bool operator()(const txiter &a, const txiter &b) const {
return a->GetTx().GetHash() < b->GetTx().GetHash();
}
};
typedef std::set<txiter, CompareIteratorByHash> setEntries;
const setEntries & GetMemPoolParents(txiter entry) const;
const setEntries & GetMemPoolChildren(txiter entry) const;
private:
typedef std::map<txiter, setEntries, CompareIteratorByHash> cacheMap;
struct TxLinks {
setEntries parents;
setEntries children;
};
typedef std::map<txiter, TxLinks, CompareIteratorByHash> txlinksMap;
txlinksMap mapLinks;
void UpdateParent(txiter entry, txiter parent, bool add);
void UpdateChild(txiter entry, txiter child, bool add);
std::vector<indexed_transaction_set::const_iterator> GetSortedDepthAndScore() const;
public:
indirectmap<COutPoint, const CTransaction*> mapNextTx;
std::map<uint256, CAmount> mapDeltas;
/** 創建新的mempool
*/
explicit CTxMemPool(CBlockPolicyEstimator* estimator = nullptr);
/**
* 如果開啓了sanity-check,那麼check函數將會保證pool的一致性,
* 即不包含雙花交易,所有的輸入都在mapNextTx數組中。
* 如果關閉了sanity-check,那麼check函數什麼都不做
*/
void check(const CCoinsViewCache *pcoins) const;
void setSanityCheck(double dFrequency = 1.0) { nCheckFrequency = dFrequency * 4294967295.0; }
/**
* addUnchecked函數必須首先更新交易的祖先交易狀態,
* 第一個addUnchecked函數可以用來調用CalculateMemPoolAncestors(),
* 然後調用第二個addUnchecked
*/
bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, bool validFeeEstimate = true);
bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, setEntries &setAncestors, bool validFeeEstimate = true);
void removeRecursive(const CTransaction &tx, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN);
void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags);
void removeConflicts(const CTransaction &tx);
void removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight);
void clear();
void _clear(); //lock free
bool CompareDepthAndScore(const uint256& hasha, const uint256& hashb);
void queryHashes(std::vector<uint256>& vtxid);
bool isSpent(const COutPoint& outpoint);
unsigned int GetTransactionsUpdated() const;
void AddTransactionsUpdated(unsigned int n);
/**
* 檢查交易的輸入是否在當前的mempool中
*/
bool HasNoInputsOf(const CTransaction& tx) const;
/** 調整 CreateNewBlock 時交易的優先級 */
void PrioritiseTransaction(const uint256& hash, const CAmount& nFeeDelta);
void ApplyDelta(const uint256 hash, CAmount &nFeeDelta) const;
void ClearPrioritisation(const uint256 hash);
public:
/**
* 從mempool中移除一個交易集合,
* 如果一個交易在這個集合中,那麼它的所有子孫交易都必須在集合中,
* 除非該交易已經被打包到區塊中。
* 如果要移除一個已經被打包到區塊中的交易,
* 那麼要把updateDescendants設爲true,
* 從而更新mempool中所有子孫節點的祖先信息
*/
void RemoveStaged(setEntries &stage, bool updateDescendants, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN);
/**
* 從競爭失敗的Block中更新交易信息到mempool
*/
void UpdateTransactionsFromBlock(const std::vector<uint256> &vHashesToUpdate);
/** 計算mempool中所有entry的祖先
* limitAncestorCount = 最大祖先數量
* limitAncestorSize = 最大祖先交易大小
* limitDescendantCount = 任意祖先的最大子孫數量
* limitDescendantSize = 任意祖先的最大子孫大小
* errString = 超過了任何limit限制的錯誤提示
* fSearchForParents = 是否在mempool中搜索交易的輸入,
* 或者從mapLinks中查找,對於不在mempool中的entry必須設爲true
*/
bool CalculateMemPoolAncestors(const CTxMemPoolEntry &entry, setEntries &setAncestors, uint64_t limitAncestorCount, uint64_t limitAncestorSize, uint64_t limitDescendantCount, uint64_t limitDescendantSize, std::string &errString, bool fSearchForParents = true) const;
/** Populate setDescendants with all in-mempool descendants of hash.
* Assumes that setDescendants includes all in-mempool descendants of anything
* already in it. */
void CalculateDescendants(txiter it, setEntries &setDescendants);
/**
* 返回進入mempool需要的最小交易費,
* incrementalRelayFee變量用來限制feerate降到0所需的時間。
*/
CFeeRate GetMinFee(size_t sizelimit) const;
/**
* 移除所有動態大小超過sizelimit的交易,
* 如果傳入了pvNoSpendsRemaining,那麼將返回不在mempool中並且也沒有
* 任何輸出在mempool的交易列表
*/
void TrimToSize(size_t sizelimit, std::vector<COutPoint>* pvNoSpendsRemaining=nullptr);
/**
* 移除所有在time之前的交易和它的子孫交易,
* 返回移除的數量
*/
int Expire(int64_t time);
/** 如果交易不滿足chain limit,返回false*/
bool TransactionWithinChainLimit(const uint256& txid, size_t chainLimit) const;
unsigned long size()
{
LOCK(cs);
return mapTx.size();
}
uint64_t GetTotalTxSize() const
{
LOCK(cs);
return totalTxSize;
}
bool exists(uint256 hash) const
{
LOCK(cs);
return (mapTx.count(hash) != 0);
}
CTransactionRef get(const uint256& hash) const;
TxMempoolInfo info(const uint256& hash) const;
std::vector<TxMempoolInfo> infoAll() const;
size_t DynamicMemoryUsage() const;
boost::signals2::signal<void (CTransactionRef)> NotifyEntryAdded;
boost::signals2::signal<void (CTransactionRef, MemPoolRemovalReason)> NotifyEntryRemoved;
private:
/** UpdateForDescendants 是被 UpdateTransactionsFromBlock 調用,
* 用來更新被加入pool中的單個交易的子孫節節點;
* setExclude 是內存池中不用更新的子孫交易集合 (because any descendants in
* setExclude were added to the mempool after the transaction being
* updated and hence their state is already reflected in the parent
* state).
*
* 當子孫交易被更新時,cachedDescendants也同時更新
*/
void UpdateForDescendants(txiter updateIt,
cacheMap &cachedDescendants,
const std::set<uint256> &setExclude);
/** Update ancestors of hash to add/remove it as a descendant transaction. */
void UpdateAncestorsOf(bool add, txiter hash, setEntries &setAncestors);
/** 設置一個entry的祖先 */
void UpdateEntryForAncestors(txiter it, const setEntries &setAncestors);
/** 對於每一個要移除的交易,更新它的祖先和直接的兒子。
* 如果updateDescendants 設爲 true, 那麼還同時更新mempool中子孫的祖先狀態
*/
void UpdateForRemoveFromMempool(const setEntries &entriesToRemove, bool updateDescendants);
/** Sever link between specified transaction and direct children. */
void UpdateChildrenForRemoval(txiter entry);
/** 對於一個特定的交易,調用 removeUnchecked 之前,
* 必須爲同時爲要移除的交易集合調用UpdateForRemoveFromMempool。
* 我們使用每個CTxMemPoolEntry中的setMemPoolParents來遍歷
* 要移除交易的祖先,這樣能保證我們更新的正確性。
*/
void removeUnchecked(txiter entry, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN);
};