Coverage Report

Created: 2026-09-14 20:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/tmp/bitcoin/src/policy/fees/mempool_estimator.h
Line
Count
Source
1
// Copyright (c) The Bitcoin Core developers
2
// Distributed under the MIT software license. See the accompanying
3
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5
#ifndef BITCOIN_POLICY_FEES_MEMPOOL_ESTIMATOR_H
6
#define BITCOIN_POLICY_FEES_MEMPOOL_ESTIMATOR_H
7
8
#include <primitives/transaction.h>
9
#include <sync.h>
10
#include <threadsafety.h>
11
#include <uint256.h>
12
#include <util/expected.h>
13
#include <util/feefrac.h>
14
#include <util/fees.h>
15
#include <util/fs.h>
16
#include <util/time.h>
17
18
#include <chrono>
19
#include <memory>
20
#include <optional>
21
#include <span>
22
#include <vector>
23
24
class CBlock;
25
class AutoFile;
26
class ChainstateManager;
27
class CTxMemPool;
28
29
struct RemovedMempoolTransactionInfo;
30
31
// Fee rate estimate for confirmation target above this is not reliable,
32
// as mempool conditions are likely to change.
33
constexpr int MEMPOOL_FEE_ESTIMATOR_MAX_TARGET{2};
34
constexpr std::chrono::seconds CACHE_LIFE{7};
35
36
// Constants for mempool sanity checks.
37
constexpr size_t MEMPOOL_HEALTH_WINDOW_BLOCKS = 6;
38
constexpr double MEMPOOL_REPRESENTATION_THRESHOLD = 0.75;
39
40
//! Weight statistics for a recently mined block, used to assess mempool coverage.
41
struct MinedBlockStats {
42
    //! Block height.
43
    uint64_t m_height{0};
44
    //! Weight of mempool transactions removed for this block (excluding coinbase).
45
    uint64_t m_removed_block_txs_weight{0};
46
    //! Total non-coinbase transaction weight in the block.
47
    uint64_t m_block_weight{0};
48
};
49
50
/**
51
 * MemPoolFeeRateEstimatorCache holds a cache of recent fee rate estimates.
52
 * A cached fee rate is only provided while it is not older than CACHE_LIFE
53
 * and the chain tip has not changed.
54
 */
55
class MemPoolFeeRateEstimatorCache
56
{
57
public:
58
1.07k
    MemPoolFeeRateEstimatorCache() = default;
59
    MemPoolFeeRateEstimatorCache(const MemPoolFeeRateEstimatorCache&) = delete;
60
    MemPoolFeeRateEstimatorCache& operator=(const MemPoolFeeRateEstimatorCache&) = delete;
61
    /** Returns true if the cache is empty or older than CACHE_LIFE. */
62
    bool IsStale() const;
63
    struct FeeRateEstimate {
64
        FeePerVSize m_conservative;
65
        FeePerVSize m_economical;
66
    };
67
    /** Returns cached estimates if not stale and computed on tip_hash, nullopt otherwise. */
68
    std::optional<FeeRateEstimate> GetCachedEstimate(const uint256& tip_hash) const;
69
    /** Update the cache with new estimates computed on tip_hash. */
70
    void Update(FeePerVSize conservative, FeePerVSize economical, const uint256& tip_hash);
71
    /** Clear cached fee rate estimates. */
72
    void Clear();
73
74
private:
75
    std::optional<FeeRateEstimate> m_fee_rate_estimation;
76
    uint256 m_tip_hash;
77
    NodeClock::time_point m_last_updated{};
78
};
79
80
/**
81
 * Estimate the fee rate required for a transaction to be included in the next block.
82
 *
83
 * Uses Bitcoin Core's block-building algorithm to generate a block template from the mempool,
84
 * then calculates percentile fee rates from the selected chunks: the 75th percentile is returned
85
 * as the economical estimate and the 50th percentile as the conservative estimate.
86
 */
87
class MemPoolFeeRateEstimator
88
{
89
public:
90
    // Block percentiles fee rate (in sat/vB).
91
    struct Percentiles {
92
        FeePerVSize p50;
93
        FeePerVSize p75;
94
    };
95
96
    MemPoolFeeRateEstimator(fs::path mempool_estimator_file_path,
97
                            const CTxMemPool& mempool,
98
                            ChainstateManager& chainman);
99
1.07k
    ~MemPoolFeeRateEstimator() = default;
100
    /**
101
     * Calculate the 50th and 75th percentile fee rates from block template chunks,
102
     * sorted in descending mining-score order. A percentile is left empty when the
103
     * chunks cannot cover the corresponding fraction of a block.
104
     *
105
     * @param[in] chunk_feerates Block template chunk fee rates sorted by descending mining score.
106
     */
107
    static Percentiles CalculateMaxWeightPercentiles(std::span<const FeePerVSize> chunk_feerates);
108
    util::Expected<FeeRateEstimation, FeeRateEstimationError> EstimateFeeRate(bool conservative) const
109
        EXCLUSIVE_LOCKS_REQUIRED(!cs);
110
    unsigned int MaximumTarget() const
111
4.12k
    {
112
4.12k
        return MEMPOOL_FEE_ESTIMATOR_MAX_TARGET;
113
4.12k
    }
114
115
    std::vector<MinedBlockStats> GetPrevBlockData() const EXCLUSIVE_LOCKS_REQUIRED(!cs)
116
3
    {
117
3
        LOCK(cs);
118
3
        return m_prev_mined_blocks;
119
3
    }
120
121
    void MempoolTxsRemovedForBlock(const std::shared_ptr<const CBlock>& block,
122
                                   const std::vector<RemovedMempoolTransactionInfo>& txs_removed_for_block,
123
                                   unsigned int block_height)
124
        EXCLUSIVE_LOCKS_REQUIRED(!cs);
125
    //! Health of the recent mined-block window for fee rate estimation.
126
    enum class MempoolHealth {
127
        //! Recent blocks represent the mempool well enough to estimate a fee rate.
128
        HEALTHY,
129
        //! Too few recent mined blocks to estimate a fee rate.
130
        INSUFFICIENT_DATA,
131
        //! Recent blocks include too few mempool transactions to estimate a fee rate.
132
        LOW_COVERAGE,
133
    };
134
    MempoolHealth GetMempoolHealth() const EXCLUSIVE_LOCKS_REQUIRED(!cs);
135
    //! Checks if recent mined blocks indicate a healthy mempool state.
136
29
    bool IsMempoolHealthy() const EXCLUSIVE_LOCKS_REQUIRED(!cs) { return GetMempoolHealth() == MempoolHealth::HEALTHY; }
137
    void FlushMinedBlockStats() EXCLUSIVE_LOCKS_REQUIRED(!cs);
138
    //! Deserialize mined-block stats without taking ownership of file.
139
    bool Read(AutoFile& file) EXCLUSIVE_LOCKS_REQUIRED(!cs);
140
    //! Serialize mined-block stats without taking ownership of file.
141
    //! Callers must explicitly close file and check for errors after writing.
142
    bool Write(AutoFile& file) const EXCLUSIVE_LOCKS_REQUIRED(!cs);
143
144
private:
145
    void ReadFromDisk() EXCLUSIVE_LOCKS_REQUIRED(!cs);
146
    //! Tracks weight statistics for the last MEMPOOL_HEALTH_WINDOW_BLOCKS mined blocks.
147
    std::vector<MinedBlockStats> m_prev_mined_blocks GUARDED_BY(cs);
148
    uint256 m_mined_blocks_tip_hash GUARDED_BY(cs);
149
150
    const CTxMemPool& m_mempool;
151
    ChainstateManager& m_chainman;
152
    mutable Mutex cs;
153
    mutable MemPoolFeeRateEstimatorCache m_cache GUARDED_BY(cs);
154
    const fs::path m_mempool_estimator_file_path;
155
};
156
157
#endif // BITCOIN_POLICY_FEES_MEMPOOL_ESTIMATOR_H