比特币源码解析(6)
来源:互联网 发布:如何注销淘宝达人账号 编辑:程序博客网 时间:2024/05/27 20:17
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};
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);};
阅读全文
2 0
- 比特币源码解析(6)
- 比特币源码解析(1)
- 比特币源码解析(2)
- 比特币源码解析(3)
- 比特币源码解析(4)
- 比特币源码解析(5)
- 比特币源码解析(7)
- 比特币源码解析(8)
- 比特币源码解析(9)
- 比特币源码解析(11)
- 比特币源码解析(12)
- 比特币源码解析(13)
- 比特币源码解析(14)
- 比特币源码解析(15)
- 比特币源码解析(16)
- 比特币源码解析(17)
- 比特币源码解析(18)
- 比特币源码解析(19)
- 字符设备驱动,平台设备驱动,设备驱动模型,sysfs的比较
- 【设计模式 四】适配器模式
- (十五)evbuffers缓冲区(上)
- [中等] UVa OJ 804 Petri Net Simulation
- 使用python爬虫爬取迅雷侠下载,呵呵,你懂得
- 比特币源码解析(6)
- UVA 465
- yii规则编写
- groovy开发环境安装小记(idea)
- 使用 ipmitool 实现 Linux 系统下对服务器的 ipmi 管理
- STL(十四)multimap多重映照容器
- composer 安装laravel 框架
- ROC与AUC
- Linux入门命令