From 07858b86a404b57bab9744a1e3cd462ddb5bee9b Mon Sep 17 00:00:00 2001 From: glozow Date: Fri, 18 Nov 2022 16:24:08 -0800 Subject: [PATCH] Merge bitcoin/bitcoin#17786: refactor: Nuke policy/fees->mempool circular dependencies --- src/Makefile.am | 1 + src/bench/mempool_eviction.cpp | 1 + src/bench/mempool_stress.cpp | 1 + src/bench/rpc_mempool.cpp | 1 + src/net_processing.cpp | 1 + src/node/interfaces.cpp | 1 + src/policy/fees.cpp | 2 +- src/policy/rbf.cpp | 184 ++++++++++++++++++++++++ src/rpc/mempool.cpp | 1 + src/test/fuzz/policy_estimator.cpp | 1 + src/test/fuzz/util/mempool.cpp | 27 ++++ src/test/util/setup_common.cpp | 1 + src/txmempool.cpp | 36 ----- src/txmempool.h | 124 +--------------- src/txmempool_entry.h | 178 +++++++++++++++++++++++ src/validation.cpp | 1 + test/lint/lint-circular-dependencies.py | 1 - test/sanitizer_suppressions/ubsan | 2 + 18 files changed, 403 insertions(+), 161 deletions(-) create mode 100644 src/policy/rbf.cpp create mode 100644 src/test/fuzz/util/mempool.cpp create mode 100644 src/txmempool_entry.h diff --git a/src/Makefile.am b/src/Makefile.am index d62df14146fd..39a8f823ea80 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -372,6 +372,7 @@ BITCOIN_CORE_H = \ torcontrol.h \ txdb.h \ txmempool.h \ + txmempool_entry.h \ txorphanage.h \ undo.h \ unordered_lru_cache.h \ diff --git a/src/bench/mempool_eviction.cpp b/src/bench/mempool_eviction.cpp index b9cfdf3914f9..c994e1c53448 100644 --- a/src/bench/mempool_eviction.cpp +++ b/src/bench/mempool_eviction.cpp @@ -6,6 +6,7 @@ #include #include #include +#include static void AddTx(const CTransactionRef& tx, const CAmount& nFee, CTxMemPool& pool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, pool.cs) diff --git a/src/bench/mempool_stress.cpp b/src/bench/mempool_stress.cpp index 4c35b6592fcf..3c80190f0dda 100644 --- a/src/bench/mempool_stress.cpp +++ b/src/bench/mempool_stress.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include diff --git a/src/bench/rpc_mempool.cpp b/src/bench/rpc_mempool.cpp index ef989b286d20..55139a4185c5 100644 --- a/src/bench/rpc_mempool.cpp +++ b/src/bench/rpc_mempool.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 9176e393987e..c821b07bc2e2 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 5f6026b9426a..690247bba78b 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp index e0586637450a..1da950fef0ef 100644 --- a/src/policy/fees.cpp +++ b/src/policy/fees.cpp @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/policy/rbf.cpp b/src/policy/rbf.cpp new file mode 100644 index 000000000000..994e13dd5668 --- /dev/null +++ b/src/policy/rbf.cpp @@ -0,0 +1,184 @@ +// Copyright (c) 2016-2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool) +{ + AssertLockHeld(pool.cs); + + CTxMemPool::setEntries ancestors; + + // First check the transaction itself. + if (SignalsOptInRBF(tx)) { + return RBFTransactionState::REPLACEABLE_BIP125; + } + + // If this transaction is not in our mempool, then we can't be sure + // we will know about all its inputs. + if (!pool.exists(GenTxid::Txid(tx.GetHash()))) { + return RBFTransactionState::UNKNOWN; + } + + // If all the inputs have nSequence >= maxint-1, it still might be + // signaled for RBF if any unconfirmed parents have signaled. + std::string dummy; + CTxMemPoolEntry entry = *pool.mapTx.find(tx.GetHash()); + pool.CalculateMemPoolAncestors(entry, ancestors, CTxMemPool::Limits::NoLimits(), dummy, false); + + for (CTxMemPool::txiter it : ancestors) { + if (SignalsOptInRBF(it->GetTx())) { + return RBFTransactionState::REPLACEABLE_BIP125; + } + } + return RBFTransactionState::FINAL; +} + +RBFTransactionState IsRBFOptInEmptyMempool(const CTransaction& tx) +{ + // If we don't have a local mempool we can only check the transaction itself. + return SignalsOptInRBF(tx) ? RBFTransactionState::REPLACEABLE_BIP125 : RBFTransactionState::UNKNOWN; +} + +std::optional GetEntriesForConflicts(const CTransaction& tx, + CTxMemPool& pool, + const CTxMemPool::setEntries& iters_conflicting, + CTxMemPool::setEntries& all_conflicts) +{ + AssertLockHeld(pool.cs); + const uint256 txid = tx.GetHash(); + uint64_t nConflictingCount = 0; + for (const auto& mi : iters_conflicting) { + nConflictingCount += mi->GetCountWithDescendants(); + // Rule #5: don't consider replacing more than MAX_REPLACEMENT_CANDIDATES + // entries from the mempool. This potentially overestimates the number of actual + // descendants (i.e. if multiple conflicts share a descendant, it will be counted multiple + // times), but we just want to be conservative to avoid doing too much work. + if (nConflictingCount > MAX_REPLACEMENT_CANDIDATES) { + return strprintf("rejecting replacement %s; too many potential replacements (%d > %d)\n", + txid.ToString(), + nConflictingCount, + MAX_REPLACEMENT_CANDIDATES); + } + } + // Calculate the set of all transactions that would have to be evicted. + for (CTxMemPool::txiter it : iters_conflicting) { + pool.CalculateDescendants(it, all_conflicts); + } + return std::nullopt; +} + +std::optional HasNoNewUnconfirmed(const CTransaction& tx, + const CTxMemPool& pool, + const CTxMemPool::setEntries& iters_conflicting) +{ + AssertLockHeld(pool.cs); + std::set parents_of_conflicts; + for (const auto& mi : iters_conflicting) { + for (const CTxIn& txin : mi->GetTx().vin) { + parents_of_conflicts.insert(txin.prevout.hash); + } + } + + for (unsigned int j = 0; j < tx.vin.size(); j++) { + // Rule #2: We don't want to accept replacements that require low feerate junk to be + // mined first. Ideally we'd keep track of the ancestor feerates and make the decision + // based on that, but for now requiring all new inputs to be confirmed works. + // + // Note that if you relax this to make RBF a little more useful, this may break the + // CalculateMempoolAncestors RBF relaxation which subtracts the conflict count/size from the + // descendant limit. + if (!parents_of_conflicts.count(tx.vin[j].prevout.hash)) { + // Rather than check the UTXO set - potentially expensive - it's cheaper to just check + // if the new input refers to a tx that's in the mempool. + if (pool.exists(GenTxid::Txid(tx.vin[j].prevout.hash))) { + return strprintf("replacement %s adds unconfirmed input, idx %d", + tx.GetHash().ToString(), j); + } + } + } + return std::nullopt; +} + +std::optional EntriesAndTxidsDisjoint(const CTxMemPool::setEntries& ancestors, + const std::set& direct_conflicts, + const uint256& txid) +{ + for (CTxMemPool::txiter ancestorIt : ancestors) { + const uint256& hashAncestor = ancestorIt->GetTx().GetHash(); + if (direct_conflicts.count(hashAncestor)) { + return strprintf("%s spends conflicting transaction %s", + txid.ToString(), + hashAncestor.ToString()); + } + } + return std::nullopt; +} + +std::optional PaysMoreThanConflicts(const CTxMemPool::setEntries& iters_conflicting, + CFeeRate replacement_feerate, + const uint256& txid) +{ + for (const auto& mi : iters_conflicting) { + // Don't allow the replacement to reduce the feerate of the mempool. + // + // We usually don't want to accept replacements with lower feerates than what they replaced + // as that would lower the feerate of the next block. Requiring that the feerate always be + // increased is also an easy-to-reason about way to prevent DoS attacks via replacements. + // + // We only consider the feerates of transactions being directly replaced, not their indirect + // descendants. While that does mean high feerate children are ignored when deciding whether + // or not to replace, we do require the replacement to pay more overall fees too, mitigating + // most cases. + CFeeRate original_feerate(mi->GetModifiedFee(), mi->GetTxSize()); + if (replacement_feerate <= original_feerate) { + return strprintf("rejecting replacement %s; new feerate %s <= old feerate %s", + txid.ToString(), + replacement_feerate.ToString(), + original_feerate.ToString()); + } + } + return std::nullopt; +} + +std::optional PaysForRBF(CAmount original_fees, + CAmount replacement_fees, + size_t replacement_vsize, + CFeeRate relay_fee, + const uint256& txid) +{ + // Rule #3: The replacement fees must be greater than or equal to fees of the + // transactions it replaces, otherwise the bandwidth used by those conflicting transactions + // would not be paid for. + if (replacement_fees < original_fees) { + return strprintf("rejecting replacement %s, less fees than conflicting txs; %s < %s", + txid.ToString(), FormatMoney(replacement_fees), FormatMoney(original_fees)); + } + + // Rule #4: The new transaction must pay for its own bandwidth. Otherwise, we have a DoS + // vector where attackers can cause a transaction to be replaced (and relayed) repeatedly by + // increasing the fee by tiny amounts. + CAmount additional_fees = replacement_fees - original_fees; + if (additional_fees < relay_fee.GetFee(replacement_vsize)) { + return strprintf("rejecting replacement %s, not enough additional fees to relay; %s < %s", + txid.ToString(), + FormatMoney(additional_fees), + FormatMoney(relay_fee.GetFee(replacement_vsize))); + } + return std::nullopt; +} diff --git a/src/rpc/mempool.cpp b/src/rpc/mempool.cpp index adaf7eee581a..a5ea966536a6 100644 --- a/src/rpc/mempool.cpp +++ b/src/rpc/mempool.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/src/test/fuzz/policy_estimator.cpp b/src/test/fuzz/policy_estimator.cpp index 3d3d41db0349..dcbe81e85fbd 100644 --- a/src/test/fuzz/policy_estimator.cpp +++ b/src/test/fuzz/policy_estimator.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include diff --git a/src/test/fuzz/util/mempool.cpp b/src/test/fuzz/util/mempool.cpp new file mode 100644 index 000000000000..ac83f6ca219a --- /dev/null +++ b/src/test/fuzz/util/mempool.cpp @@ -0,0 +1,27 @@ +// Copyright (c) 2022 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include +#include +#include +#include + +#include + +CTxMemPoolEntry ConsumeTxMemPoolEntry(FuzzedDataProvider& fuzzed_data_provider, const CTransaction& tx) noexcept +{ + // Avoid: + // policy/feerate.cpp:28:34: runtime error: signed integer overflow: 34873208148477500 * 1000 cannot be represented in type 'long' + // + // Reproduce using CFeeRate(348732081484775, 10).GetFeePerK() + const CAmount fee{ConsumeMoney(fuzzed_data_provider, /*max=*/std::numeric_limits::max() / CAmount{100'000})}; + assert(MoneyRange(fee)); + const int64_t time = fuzzed_data_provider.ConsumeIntegral(); + const unsigned int entry_height = fuzzed_data_provider.ConsumeIntegral(); + const bool spends_coinbase = fuzzed_data_provider.ConsumeBool(); + const unsigned int sig_op_cost = fuzzed_data_provider.ConsumeIntegralInRange(0, MAX_BLOCK_SIGOPS_COST); + return CTxMemPoolEntry{MakeTransactionRef(tx), fee, time, entry_height, spends_coinbase, sig_op_cost, {}}; +} diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 5d657d1c4d15..f178096b1f3c 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include diff --git a/src/txmempool.cpp b/src/txmempool.cpp index febec3d957ad..1fe8f6e95c3b 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -48,42 +48,6 @@ bool TestLockPointValidity(CChain& active_chain, const LockPoints& lp) return true; } -CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& tx, CAmount fee, - int64_t time, unsigned int entry_height, - bool spends_coinbase, int64_t sigops_count, LockPoints lp) - : tx{tx}, - nFee{fee}, - nTxSize(tx->GetTotalSize()), - nUsageSize{RecursiveDynamicUsage(tx)}, - nTime{time}, - entryHeight{entry_height}, - spendsCoinbase{spends_coinbase}, - sigOpCount{sigops_count}, - m_modified_fee{nFee}, - lockPoints{lp}, - nSizeWithDescendants{GetTxSize()}, - nModFeesWithDescendants{nFee}, - nSizeWithAncestors{GetTxSize()}, - nModFeesWithAncestors{nFee}, - nSigOpCountWithAncestors{sigOpCount} {} - -void CTxMemPoolEntry::UpdateModifiedFee(CAmount newFeeDelta) -{ - nModFeesWithDescendants = SaturatingAdd(nModFeesWithDescendants, newFeeDelta); - nModFeesWithAncestors = SaturatingAdd(nModFeesWithAncestors, newFeeDelta); - m_modified_fee = SaturatingAdd(m_modified_fee, newFeeDelta); -} - -void CTxMemPoolEntry::UpdateLockPoints(const LockPoints& lp) -{ - lockPoints = lp; -} - -size_t CTxMemPoolEntry::GetTxSize() const -{ - return GetVirtualTransactionSize(nTxSize, sigOpCount); -} - void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap& cachedDescendants, const std::set& setExclude, std::set& descendants_to_remove, uint64_t ancestor_size_limit, uint64_t ancestor_count_limit) diff --git a/src/txmempool.h b/src/txmempool.h index e28b6c856ff6..1502eb7117e6 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -52,134 +53,11 @@ using CBLSLazyPublicKey = CBLSLazyWrapper; /** Fake height value used in Coin to signify they are only in the memory pool (since 0.8) */ static const uint32_t MEMPOOL_HEIGHT = 0x7FFFFFFF; -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}; -}; - /** * Test whether the LockPoints height and time are still valid on the current chain */ bool TestLockPointValidity(CChain& active_chain, const LockPoints& lp) EXCLUSIVE_LOCKS_REQUIRED(cs_main); -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 - * (nCountWithDescendants, 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: - const CTransactionRef tx; - mutable Parents m_parents; - mutable Children m_children; - const CAmount nFee; //!< Cached to avoid expensive parent-transaction lookups - const size_t nTxSize; //!< ... and avoid recomputing tx size - const size_t nUsageSize; //!< ... and total memory usage - const int64_t nTime; //!< Local time when entering the mempool - const unsigned int entryHeight; //!< Chain height when entering the mempool - const bool spendsCoinbase; //!< keep track of transactions that spend a coinbase - const int64_t sigOpCount; //!< Legacy sig ops plus P2SH sig op count - CAmount m_modified_fee; //!< Used for determining the priority of the transaction for mining in a block - 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. - uint64_t nCountWithDescendants{1}; //!< number of descendant transactions - uint64_t nSizeWithDescendants; //!< ... and size - CAmount nModFeesWithDescendants; //!< ... and total fees (all including us) - - // Analogous statistics for ancestor transactions - uint64_t nCountWithAncestors{1}; - uint64_t nSizeWithAncestors; - CAmount nModFeesWithAncestors; - int64_t nSigOpCountWithAncestors; - -public: - CTxMemPoolEntry(const CTransactionRef& tx, CAmount fee, - int64_t time, unsigned int entry_height, - bool spends_coinbase, - int64_t sigops_count, 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; - std::chrono::seconds GetTime() const { return std::chrono::seconds{nTime}; } - unsigned int GetHeight() const { return entryHeight; } - int64_t GetSigOpCount() const { return sigOpCount; } - 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(int64_t modifySize, CAmount modifyFee, int64_t modifyCount); - // Adjusts the ancestor state - void UpdateAncestorState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount, int64_t modifySigOps); - // Updates the modified fees with descendants/ancestors. - void UpdateModifiedFee(CAmount newFeeDelta); - // Update the LockPoints after a reorg - 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 GetSigOpCountWithAncestors() const { return nSigOpCountWithAncestors; } - - 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 vTxHashesIdx; //!< Index in mempool's vTxHashes - - // If this is a proTx, this will be the hash of the key for which this ProTx was valid - mutable uint256 validForProTxKey; - mutable bool isKeyChangeProTx{false}; - mutable Epoch::Marker m_epoch_marker; //!< epoch when last touched, useful for graph algorithms -}; - // extracts a transaction hash from CTxMemPoolEntry or CTransactionRef struct mempoolentry_txid { diff --git a/src/txmempool_entry.h b/src/txmempool_entry.h new file mode 100644 index 000000000000..c4d9bea5b5cd --- /dev/null +++ b/src/txmempool_entry.h @@ -0,0 +1,178 @@ +// Copyright (c) 2009-2022 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_TXMEMPOOL_ENTRY_H +#define BITCOIN_TXMEMPOOL_ENTRY_H + +#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}; +}; + +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 + * (nCountWithDescendants, 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: + const CTransactionRef tx; + mutable Parents m_parents; + mutable Children m_children; + const CAmount nFee; //!< Cached to avoid expensive parent-transaction lookups + const size_t nTxSize; //!< ... and avoid recomputing tx size + const size_t nUsageSize; //!< ... and total memory usage + const int64_t nTime; //!< Local time when entering the mempool + const unsigned int entryHeight; //!< Chain height when entering the mempool + const bool spendsCoinbase; //!< keep track of transactions that spend a coinbase + const int64_t sigOpCount; //!< Legacy sig ops plus P2SH sig op count + CAmount m_modified_fee; //!< Used for determining the priority of the transaction for mining in a block + 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. + uint64_t nCountWithDescendants{1}; //!< number of descendant transactions + uint64_t nSizeWithDescendants; //!< ... and size + CAmount nModFeesWithDescendants; //!< ... and total fees (all including us) + + // Analogous statistics for ancestor transactions + uint64_t nCountWithAncestors{1}; + uint64_t nSizeWithAncestors; + CAmount nModFeesWithAncestors; + int64_t nSigOpCountWithAncestors; + +public: + CTxMemPoolEntry(const CTransactionRef& tx, CAmount fee, + int64_t time, unsigned int entry_height, + bool spends_coinbase, + int64_t sigops_count, LockPoints lp) + : tx{tx}, + nFee{fee}, + nTxSize(tx->GetTotalSize()), + nUsageSize{RecursiveDynamicUsage(tx)}, + nTime{time}, + entryHeight{entry_height}, + spendsCoinbase{spends_coinbase}, + sigOpCount{sigops_count}, + m_modified_fee{nFee}, + lockPoints{lp}, + nSizeWithDescendants{GetTxSize()}, + nModFeesWithDescendants{nFee}, + nSizeWithAncestors{GetTxSize()}, + nModFeesWithAncestors{nFee}, + nSigOpCountWithAncestors{sigOpCount} {} + + const CTransaction& GetTx() const { return *this->tx; } + CTransactionRef GetSharedTx() const { return this->tx; } + const CAmount& GetFee() const { return nFee; } + size_t GetTxSize() const + { + return GetVirtualTransactionSize(nTxSize, sigOpCount); + } + std::chrono::seconds GetTime() const { return std::chrono::seconds{nTime}; } + unsigned int GetHeight() const { return entryHeight; } + int64_t GetSigOpCount() const { return sigOpCount; } + 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(int64_t modifySize, CAmount modifyFee, int64_t modifyCount); + // Adjusts the ancestor state + void UpdateAncestorState(int64_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) + { + 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 GetSigOpCountWithAncestors() const { return nSigOpCountWithAncestors; } + + 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 vTxHashesIdx; //!< Index in mempool's vTxHashes + + // If this is a proTx, this will be the hash of the key for which this ProTx was valid + mutable uint256 validForProTxKey; + mutable bool isKeyChangeProTx{false}; + mutable Epoch::Marker m_epoch_marker; //!< epoch when last touched, useful for graph algorithms +}; + +#endif // BITCOIN_TXMEMPOOL_ENTRY_H diff --git a/src/validation.cpp b/src/validation.cpp index 7686968416c2..0862f0c0b32e 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include diff --git a/test/lint/lint-circular-dependencies.py b/test/lint/lint-circular-dependencies.py index 87a6f11e6fcd..6f5474676770 100755 --- a/test/lint/lint-circular-dependencies.py +++ b/test/lint/lint-circular-dependencies.py @@ -14,7 +14,6 @@ EXPECTED_CIRCULAR_DEPENDENCIES = ( "chainparamsbase -> util/system -> chainparamsbase", "node/blockstorage -> validation -> node/blockstorage", - "policy/fees -> txmempool -> policy/fees", "qt/addresstablemodel -> qt/walletmodel -> qt/addresstablemodel", "qt/recentrequeststablemodel -> qt/walletmodel -> qt/recentrequeststablemodel", "qt/transactiontablemodel -> qt/walletmodel -> qt/transactiontablemodel", diff --git a/test/sanitizer_suppressions/ubsan b/test/sanitizer_suppressions/ubsan index 1d1f2ad352b5..d52097daa118 100644 --- a/test/sanitizer_suppressions/ubsan +++ b/test/sanitizer_suppressions/ubsan @@ -50,6 +50,7 @@ unsigned-integer-overflow:prevector.h unsigned-integer-overflow:pubkey.h unsigned-integer-overflow:EvalScript unsigned-integer-overflow:txmempool.cpp +unsigned-integer-overflow:txmempool_entry.h unsigned-integer-overflow:util/strencodings.cpp unsigned-integer-overflow:xoroshiro128plusplus.h implicit-integer-sign-change:addrman.h @@ -64,6 +65,7 @@ implicit-integer-sign-change:script/bitcoinconsensus.cpp implicit-integer-sign-change:script/interpreter.cpp implicit-integer-sign-change:serialize.h implicit-integer-sign-change:txmempool.cpp +implicit-integer-sign-change:txmempool_entry.h implicit-integer-sign-change:util/strencodings.cpp implicit-integer-sign-change:util/strencodings.h implicit-integer-sign-change:validation.cpp