// Copyright (c) 2009-2022 The Limenka developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef LIMENKA_KERNEL_MEMPOOL_ENTRY_H #define LIMENKA_KERNEL_MEMPOOL_ENTRY_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class CBlockIndex; struct LockPoints { // Will be set to the blockchain height and median time past // values that would be necessary to satisfy all relative locktime // constraints (BIP68) of this tx given our view of block chain history int height{0}; int64_t time{0}; // As long as the current chain descends from the highest height block // containing one of the inputs used in the calculation, then the cached // values are still valid even after a reorg. CBlockIndex* maxInputBlock{nullptr}; }; enum MemPool_SPK_State { MSS_UNSEEN = 0, MSS_SPENT = 1, // .second MSS_CREATED = 2, // .first MSS_BOTH = 3, }; typedef std::map SPKStates_t; struct CompareIteratorByHash { // SFINAE for T where T is either a pointer type (e.g., a txiter) or a reference_wrapper // (e.g. a wrapped CTxMemPoolEntry&) template bool operator()(const std::reference_wrapper& a, const std::reference_wrapper& b) const { return a.get().GetTx().GetHash() < b.get().GetTx().GetHash(); } template bool operator()(const T& a, const T& b) const { return a->GetTx().GetHash() < b->GetTx().GetHash(); } }; /** \class CTxMemPoolEntry * * CTxMemPoolEntry stores data about the corresponding transaction, as well * as data about all in-mempool transactions that depend on the transaction * ("descendant" transactions). * * When a new entry is added to the mempool, we update the descendant state * (m_count_with_descendants, nSizeWithDescendants, and nModFeesWithDescendants) for * all ancestors of the newly added transaction. * */ class CTxMemPoolEntry { public: typedef std::reference_wrapper CTxMemPoolEntryRef; // two aliases, should the types ever diverge typedef std::set Parents; typedef std::set Children; private: CTxMemPoolEntry(const CTxMemPoolEntry&) = default; struct ExplicitCopyTag { explicit ExplicitCopyTag() = default; }; const CTransactionRef tx; mutable Parents m_parents; mutable Children m_children; const CAmount nFee; //!< Cached to avoid expensive parent-transaction lookups const int32_t nTxWeight; //!< ... and avoid recomputing tx weight (also used for GetTxSize()) const size_t nUsageSize; //!< ... and total memory usage const int64_t nTime; //!< Local time when entering the mempool const uint64_t entry_sequence; //!< Sequence number used to determine whether this transaction is too recent for relay const int64_t sigOpCost; //!< Total sigop cost const int32_t m_extra_weight; //!< Policy-only additional transaction weight beyond nTxWeight const size_t nModSize; //!< Cached modified size for priority const double entryPriority; //!< Priority when entering the mempool const unsigned int entryHeight; //!< Chain height when entering the mempool double cachedPriority; //!< Last calculated priority unsigned int cachedHeight; //!< Height at which priority was last calculated CAmount inChainInputValue; //!< Sum of all txin values that are already in blockchain const bool spendsCoinbase; //!< keep track of transactions that spend a coinbase CAmount m_modified_fee; //!< Used for determining the priority of the transaction for mining in a block mutable LockPoints lockPoints; //!< Track the height and time at which tx was final // Information about descendants of this transaction that are in the // mempool; if we remove this transaction we must remove all of these // descendants as well. int64_t m_count_with_descendants{1}; //!< number of descendant transactions // Using int64_t instead of int32_t to avoid signed integer overflow issues. int64_t nSizeWithDescendants; //!< ... and size CAmount nModFeesWithDescendants; //!< ... and total fees (all including us) // Analogous statistics for ancestor transactions int64_t m_count_with_ancestors{1}; // Using int64_t instead of int32_t to avoid signed integer overflow issues. int64_t nSizeWithAncestors; CAmount nModFeesWithAncestors; int64_t nSigOpCostWithAncestors; public: CTxMemPoolEntry(const CTransactionRef& tx, CAmount fee, int64_t time, unsigned int entry_height, uint64_t entry_sequence, CoinAgeCache coin_age_cache, bool spends_coinbase, int32_t extra_weight, int64_t sigops_cost, LockPoints lp) : tx{tx}, nFee{fee}, nTxWeight{GetTransactionWeight(*tx)}, nUsageSize{RecursiveDynamicUsage(tx)}, nTime{time}, entry_sequence{entry_sequence}, sigOpCost{sigops_cost}, m_extra_weight{extra_weight}, nModSize{CalculateModifiedSize(*tx, GetTxSize())}, entryPriority{ComputePriority2(coin_age_cache.inputs_coin_age, nModSize)}, entryHeight{entry_height}, cachedPriority{entryPriority}, // Since entries arrive *after* the tip's height, their entry priority is for the height+1 cachedHeight{entry_height + 1}, inChainInputValue{coin_age_cache.in_chain_input_value}, spendsCoinbase{spends_coinbase}, m_modified_fee{nFee}, lockPoints{lp}, nSizeWithDescendants{GetTxSize()}, nModFeesWithDescendants{nFee}, nSizeWithAncestors{GetTxSize()}, nModFeesWithAncestors{nFee}, nSigOpCostWithAncestors{sigOpCost} { // For confidential transactions the transparent-domain input // value beyond the fee legitimately crosses into the // confidential domain (mint); the kernel balance enforces // conservation there. if (GetCTKernelOutputIndex(*tx) == NO_CT_KERNEL_OUTPUT) { CAmount nValueIn = tx->GetValueOut() + nFee; assert(inChainInputValue <= nValueIn); } } CTxMemPoolEntry(ExplicitCopyTag, const CTxMemPoolEntry& entry) : CTxMemPoolEntry(entry) {} CTxMemPoolEntry& operator=(const CTxMemPoolEntry&) = delete; CTxMemPoolEntry(CTxMemPoolEntry&&) = delete; CTxMemPoolEntry& operator=(CTxMemPoolEntry&&) = delete; static constexpr ExplicitCopyTag ExplicitCopy{}; const CTransaction& GetTx() const { return *this->tx; } CTransactionRef GetSharedTx() const { return this->tx; } double GetStartingPriority() const {return entryPriority; } CoinAgeCache GetInternalCoinAgeCache() const { return { .inputs_coin_age = static_cast(ReversePriority2(cachedPriority, nModSize)), .in_chain_input_value = inChainInputValue, }; } /** * Fast calculation of priority as update from cached value, but only valid if * currentHeight is greater than last height it was recalculated. */ double GetPriority(unsigned int currentHeight) const; /** * Recalculate the cached priority as of currentHeight and adjust inChainInputValue by * valueInCurrentBlock which represents input that was just added to or removed from the blockchain. */ void UpdateCachedPriority(unsigned int currentHeight, CAmount valueInCurrentBlock); const CAmount& GetFee() const { return nFee; } int32_t GetTxSize() const { return GetVirtualTransactionSize(nTxWeight + m_extra_weight, sigOpCost, ::nBytesPerSigOp); } int32_t GetTxWeight() const { return nTxWeight; } std::chrono::seconds GetTime() const { return std::chrono::seconds{nTime}; } unsigned int GetHeight() const { return entryHeight; } uint64_t GetSequence() const { return entry_sequence; } int32_t GetExtraWeight() const { return m_extra_weight; } int64_t GetSigOpCost() const { return sigOpCost; } CAmount GetModifiedFee() const { return m_modified_fee; } size_t DynamicMemoryUsage() const { return nUsageSize; } const LockPoints& GetLockPoints() const { return lockPoints; } // Adjusts the descendant state. void UpdateDescendantState(int32_t modifySize, CAmount modifyFee, int64_t modifyCount); // Adjusts the ancestor state void UpdateAncestorState(int32_t modifySize, CAmount modifyFee, int64_t modifyCount, int64_t modifySigOps); // Updates the modified fees with descendants/ancestors. void UpdateModifiedFee(CAmount fee_diff) { nModFeesWithDescendants = SaturatingAdd(nModFeesWithDescendants, fee_diff); nModFeesWithAncestors = SaturatingAdd(nModFeesWithAncestors, fee_diff); m_modified_fee = SaturatingAdd(m_modified_fee, fee_diff); } // Update the LockPoints after a reorg void UpdateLockPoints(const LockPoints& lp) const { lockPoints = lp; } uint64_t GetCountWithDescendants() const { return m_count_with_descendants; } int64_t GetSizeWithDescendants() const { return nSizeWithDescendants; } CAmount GetModFeesWithDescendants() const { return nModFeesWithDescendants; } bool GetSpendsCoinbase() const { return spendsCoinbase; } uint64_t GetCountWithAncestors() const { return m_count_with_ancestors; } int64_t GetSizeWithAncestors() const { return nSizeWithAncestors; } CAmount GetModFeesWithAncestors() const { return nModFeesWithAncestors; } int64_t GetSigOpCostWithAncestors() const { return nSigOpCostWithAncestors; } const Parents& GetMemPoolParentsConst() const { return m_parents; } const Children& GetMemPoolChildrenConst() const { return m_children; } Parents& GetMemPoolParents() const { return m_parents; } Children& GetMemPoolChildren() const { return m_children; } mutable size_t idx_randomized; //!< Index in mempool's txns_randomized mutable Epoch::Marker m_epoch_marker; //!< epoch when last touched, useful for graph algorithms SPKStates_t mapSPK; }; using CTxMemPoolEntryRef = CTxMemPoolEntry::CTxMemPoolEntryRef; struct TransactionInfo { const CTransactionRef m_tx; /* The fee the transaction paid */ const CAmount m_fee; /** * The virtual transaction size. * * This is a policy field which considers the sigop cost of the * transaction as well as its weight, and reinterprets it as bytes. * * It is the primary metric by which the mining algorithm selects * transactions. */ const int64_t m_virtual_transaction_size; /* The block height the transaction entered the mempool */ const unsigned int txHeight; TransactionInfo(const CTransactionRef& tx, const CAmount& fee, const int64_t vsize, const unsigned int height) : m_tx{tx}, m_fee{fee}, m_virtual_transaction_size{vsize}, txHeight{height} {} }; struct RemovedMempoolTransactionInfo { TransactionInfo info; explicit RemovedMempoolTransactionInfo(const CTxMemPoolEntry& entry) : info{entry.GetSharedTx(), entry.GetFee(), entry.GetTxSize(), entry.GetHeight()} {} }; struct NewMempoolTransactionInfo { TransactionInfo info; /* * This boolean indicates whether the transaction was added * without enforcing mempool fee limits. */ const ignore_rejects_type m_ignore_rejects; /* This boolean indicates whether the transaction is part of a package. */ const bool m_submitted_in_package; /* * This boolean indicates whether the blockchain is up to date when the * transaction is added to the mempool. */ const bool m_chainstate_is_current; /* Indicates whether the transaction has unconfirmed parents. */ const bool m_has_no_mempool_parents; explicit NewMempoolTransactionInfo(const CTransactionRef& tx, const CAmount& fee, const int64_t vsize, const unsigned int height, const ignore_rejects_type& ignore_rejects, const bool submitted_in_package, const bool chainstate_is_current, const bool has_no_mempool_parents) : info{tx, fee, vsize, height}, m_ignore_rejects{ignore_rejects}, m_submitted_in_package{submitted_in_package}, m_chainstate_is_current{chainstate_is_current}, m_has_no_mempool_parents{has_no_mempool_parents} {} }; #endif // LIMENKA_KERNEL_MEMPOOL_ENTRY_H