比特幣源碼解析(6) - 數據結構 - 交易池

0x00 摘要

我們知道當交易被廣播並且被礦工接收到時,礦工就會把交易加入到本地的交易池當中,每個礦工又會對自己的交易池設置相應的限制,來保證交易數量不會過多,礦工在打包交易到區塊中時,也會根據一定的優先順序來選擇交易,從而讓自己能獲得儘量多的交易費。 
對於交易池主要介紹兩個結構CTxMemPoolEntryCTxMemPool,第一個是交易池中每一個元素的基本結構,第二個是整個交易池包含的所有信息。

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);
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章