mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-02-16 02:18:53 +00:00
91504cbe0de2b74ef1aa2709761aaf0597ec66a2 rpc: `SyncWithValidationInterfaceQueue` on fee estimation RPC's (ismaelsadeeq) 714523918ba2b853fc69bee6b04a33ba0c828bf5 tx fees, policy: CBlockPolicyEstimator update from `CValidationInterface` notifications (ismaelsadeeq) dff5ad3b9944cbb56126ba37a8da180d1327ba39 CValidationInterface: modify the parameter of `TransactionAddedToMempool` (ismaelsadeeq) 91532bd38223d7d04166e05de11d0d0b55e60f13 tx fees, policy: update `CBlockPolicyEstimator::processBlock` parameter (ismaelsadeeq) bfcd401368fc0dc43827a8969a37b7e038d5ca79 CValidationInterface, mempool: add new callback to `CValidationInterface` (ismaelsadeeq) 0889e07987294d4ef2814abfca16d8e2a0c5f541 tx fees, policy: cast with static_cast instead of C-Style cast (ismaelsadeeq) a0e3eb7549d2ba4dd3af12b9ce65e29158f59078 tx fees, policy: bugfix: move `removeTx` into reason != `BLOCK` condition (ismaelsadeeq) Pull request description: This is an attempt to #11775 This Pr will enable fee estimator to listen to ValidationInterface notifications to process new transactions added and removed from the mempool. This PR includes the following changes: - Added a new callback to the Validation Interface `MempoolTransactionsRemovedForConnectedBlock`, which notifies listeners about the transactions that have been removed due to a new block being connected, along with the height at which the transactions were removed. - Modified the `TransactionAddedToMempool` callback parameter to include additional information about the transaction needed for fee estimation. - Updated `CBlockPolicyEstimator` to process transactions using` CTransactionRef` instead of `CTxMempoolEntry.` - Implemented the `CValidationInterface` interface in `CBlockPolicyEstimater` and overridden the `TransactionAddedToMempool`, `TransactionRemovedFromMempool`, and `MempoolTransactionsRemovedForConnectedBlock` methods to receive updates from their notifications. Prior to this PR, the fee estimator updates from the mempool, i.e whenever a new block is connected all transactions in the block that are in our mempool are going to be removed using the `removeForBlock` function in `txmempool.cpp`. This removal triggered updates to the fee estimator. As a result, the fee estimator would block mempool's `cs` until it finished updating every time a new block was connected. Instead of being blocked only on mempool tx removal, we were blocking on both tx removal and fee estimator updating. If we want to further improve fee estimation, or add heavy-calulation steps to it, it is currently not viable as we would be slowing down block relay in the process This PR is smaller in terms of the changes made compared to #11775, as it focuses solely on enabling fee estimator updates from the validationInterface/cscheduler thread notifications. I have not split the validation interface because, as I understand it, the rationale behind the split in #11775 was to have `MempoolInterface` signals come from the mempool and `CValidationInterface` events come from validation. I believe this separation can be achieved in a separate refactoring PR when the need arises. Also left out some commits from #11775 - Some refactoring which are no longer needed. - Handle reorgs much better in fee estimator. - Track witness hash malleation in fee estimator I believe they are a separate change that can come in a follow-up after this. ACKs for top commit: achow101: ACK 91504cbe0de2b74ef1aa2709761aaf0597ec66a2 TheCharlatan: Re-ACK 91504cbe0de2b74ef1aa2709761aaf0597ec66a2 willcl-ark: ACK 91504cbe0de2b74ef1aa2709761aaf0597ec66a2 Tree-SHA512: 846dfb9da57a8a42458827b8975722d153907fe6302ad65748d74f311e1925557ad951c3d95fe71fb90ddcc8a3710c45abb343ab86b88780871cb9c38c72c7b1
253 lines
10 KiB
C++
253 lines
10 KiB
C++
// 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_KERNEL_MEMPOOL_ENTRY_H
|
|
#define BITCOIN_KERNEL_MEMPOOL_ENTRY_H
|
|
|
|
#include <consensus/amount.h>
|
|
#include <consensus/validation.h>
|
|
#include <core_memusage.h>
|
|
#include <policy/policy.h>
|
|
#include <policy/settings.h>
|
|
#include <primitives/transaction.h>
|
|
#include <util/epochguard.h>
|
|
#include <util/overflow.h>
|
|
|
|
#include <chrono>
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <set>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
|
|
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<T>
|
|
// (e.g. a wrapped CTxMemPoolEntry&)
|
|
template <typename T>
|
|
bool operator()(const std::reference_wrapper<T>& a, const std::reference_wrapper<T>& b) const
|
|
{
|
|
return a.get().GetTx().GetHash() < b.get().GetTx().GetHash();
|
|
}
|
|
template <typename T>
|
|
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<const CTxMemPoolEntry> CTxMemPoolEntryRef;
|
|
// two aliases, should the types ever diverge
|
|
typedef std::set<CTxMemPoolEntryRef, CompareIteratorByHash> Parents;
|
|
typedef std::set<CTxMemPoolEntryRef, CompareIteratorByHash> 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 unsigned int entryHeight; //!< Chain height when entering the mempool
|
|
const bool spendsCoinbase; //!< keep track of transactions that spend a coinbase
|
|
const int64_t sigOpCost; //!< Total sigop cost
|
|
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,
|
|
bool spends_coinbase,
|
|
int64_t sigops_cost, LockPoints lp)
|
|
: tx{tx},
|
|
nFee{fee},
|
|
nTxWeight{GetTransactionWeight(*tx)},
|
|
nUsageSize{RecursiveDynamicUsage(tx)},
|
|
nTime{time},
|
|
entry_sequence{entry_sequence},
|
|
entryHeight{entry_height},
|
|
spendsCoinbase{spends_coinbase},
|
|
sigOpCost{sigops_cost},
|
|
m_modified_fee{nFee},
|
|
lockPoints{lp},
|
|
nSizeWithDescendants{GetTxSize()},
|
|
nModFeesWithDescendants{nFee},
|
|
nSizeWithAncestors{GetTxSize()},
|
|
nModFeesWithAncestors{nFee},
|
|
nSigOpCostWithAncestors{sigOpCost} {}
|
|
|
|
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; }
|
|
const CAmount& GetFee() const { return nFee; }
|
|
int32_t GetTxSize() const
|
|
{
|
|
return GetVirtualTransactionSize(nTxWeight, 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; }
|
|
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
|
|
};
|
|
|
|
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 bool m_from_disconnected_block;
|
|
/* 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 bool from_disconnected_block, const bool submitted_in_package,
|
|
const bool chainstate_is_current,
|
|
const bool has_no_mempool_parents)
|
|
: info{tx, fee, vsize, height},
|
|
m_from_disconnected_block{from_disconnected_block},
|
|
m_submitted_in_package{submitted_in_package},
|
|
m_chainstate_is_current{chainstate_is_current},
|
|
m_has_no_mempool_parents{has_no_mempool_parents} {}
|
|
};
|
|
|
|
#endif // BITCOIN_KERNEL_MEMPOOL_ENTRY_H
|