// Copyright (c) 2019-2023 The Zcash developers // Distributed under the MIT software license, see the accompanying // file COPYING or https://www.opensource.org/licenses/mit-license.php . #ifndef ZCASH_MEMPOOL_LIMIT_H #define ZCASH_MEMPOOL_LIMIT_H #include #include #include #include #include "logging.h" #include "random.h" #include "primitives/transaction.h" #include "policy/fees.h" #include "uint256.h" #include "util/time.h" #include "weighted_map.h" const size_t DEFAULT_MEMPOOL_TOTAL_COST_LIMIT = 80000000; const int64_t DEFAULT_MEMPOOL_EVICTION_MEMORY_MINUTES = 60; const size_t EVICTION_MEMORY_ENTRIES = 40000; const int64_t MIN_TX_COST = 4000; const int64_t LOW_FEE_PENALTY = 16000; // This class keeps track of transactions which have been recently evicted from the mempool // in order to prevent them from being re-accepted for a given amount of time. class RecentlyEvictedList { const CClock* const clock; const size_t capacity; const int64_t timeToKeep; // Pairs of txid and time (seconds since epoch) std::deque> txIdsAndTimes; std::set txIdSet; void pruneList(); public: RecentlyEvictedList(const CClock* clock_, size_t capacity_, int64_t timeToKeep_) : clock(clock_), capacity(capacity_), timeToKeep(timeToKeep_) { assert(capacity <= EVICTION_MEMORY_ENTRIES); } RecentlyEvictedList(const CClock* clock_, int64_t timeToKeep_) : RecentlyEvictedList(clock_, EVICTION_MEMORY_ENTRIES, timeToKeep_) {} void add(const uint256& txId); bool contains(const uint256& txId); }; // The mempool of a node holds a set of transactions. Each transaction has a *cost*, // which is an integer defined as: // max(memory usage in bytes, 4000) // // Each transaction also has an *eviction weight*, which is *cost* + *fee_penalty*, // where *fee_penalty* is 16000 if the transaction pays a fee less than the // conventional fee, otherwise 0. // Calculate cost and eviction weight based on the memory usage and fee. std::pair MempoolCostAndEvictionWeight(const CTransaction& tx, const CAmount& fee); class MempoolLimitTxSet { WeightedMap txmap; int64_t capacity; int64_t cost; public: MempoolLimitTxSet(int64_t capacity_) : capacity(capacity_), cost(0) { assert(capacity >= 0); } int64_t getTotalWeight() const { return txmap.getTotalWeight(); } bool empty() const { return txmap.empty(); } void add(const uint256& txId, int64_t txCost, int64_t txWeight) { if (txmap.add(txId, txCost, txWeight)) { cost += txCost; } } void remove(const uint256& txId) { cost -= txmap.remove(txId).value_or(0); } // If the total cost limit has not been exceeded, return std::nullopt. Otherwise, // pick a transaction at random with probability proportional to its eviction weight; // remove and return that transaction's txid. std::optional maybeDropRandom() { if (cost <= capacity) { return std::nullopt; } LogPrint("mempool", "Mempool cost limit exceeded (cost=%d, limit=%d)\n", cost, capacity); assert(!txmap.empty()); auto [txId, txCost, txWeight] = txmap.takeRandom().value(); cost -= txCost; LogPrint("mempool", "Evicting transaction (txid=%s, cost=%d, weight=%d)\n", txId.ToString(), txCost, txWeight); return txId; } }; #endif // ZCASH_MEMPOOL_LIMIT_H