LCOV - code coverage report
Current view: top level - src/policy/fees - mempool_estimator.cpp (source / functions) Coverage Total Hit
Test: fuzz_coverage.info Lines: 34.8 % 207 72
Test Date: 2026-09-05 05:35:01 Functions: 36.4 % 22 8
Branches: 15.9 % 340 54

             Branch data     Line data    Source code
       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                 :             : #include <policy/fees/mempool_estimator.h>
       6                 :             : 
       7                 :             : #include <logging.h>
       8                 :             : #include <node/miner.h>
       9                 :             : #include <policy/feerate.h>
      10                 :             : #include <policy/policy.h>
      11                 :             : #include <primitives/block.h>
      12                 :             : #include <serialize.h>
      13                 :             : #include <streams.h>
      14                 :             : #include <sync.h>
      15                 :             : #include <tinyformat.h>
      16                 :             : #include <txmempool.h>
      17                 :             : #include <util/check.h>
      18                 :             : #include <util/feefrac.h>
      19                 :             : #include <util/fees.h>
      20                 :             : #include <util/fs.h>
      21                 :             : #include <util/overflow.h>
      22                 :             : #include <util/syserror.h>
      23                 :             : #include <validation.h>
      24                 :             : 
      25                 :             : #include <algorithm>
      26                 :             : #include <iterator>
      27                 :             : #include <numeric>
      28                 :             : #include <optional>
      29                 :             : #include <string>
      30                 :             : #include <string_view>
      31                 :             : #include <system_error>
      32                 :             : #include <utility>
      33                 :             : 
      34                 :             : constexpr int CURRENT_MEMPOOL_ESTIMATOR_VERSION{1};
      35                 :             : 
      36                 :             : namespace {
      37                 :             : struct MinedBlockStatsFormatter {
      38                 :             :     template <typename Stream>
      39                 :           2 :     void Ser(Stream& s, const MinedBlockStats& v)
      40                 :             :     {
      41                 :           2 :         s << v.m_height << v.m_removed_block_txs_weight << v.m_block_weight;
      42                 :           2 :     }
      43                 :             :     template <typename Stream>
      44                 :         311 :     void Unser(Stream& s, MinedBlockStats& v)
      45                 :             :     {
      46                 :         311 :         s >> v.m_height >> v.m_removed_block_txs_weight >> v.m_block_weight;
      47                 :         304 :     }
      48                 :             : };
      49                 :             : 
      50                 :           0 : void AddMinedBlockStats(std::vector<MinedBlockStats>& mined_blocks, MinedBlockStats stats)
      51                 :             : {
      52                 :           0 :     const auto stale_begin{std::find_if(mined_blocks.begin(), mined_blocks.end(), [&](const MinedBlockStats& block) {
      53         [ #  # ]:           0 :         return block.m_height >= stats.m_height;
      54                 :             :     })};
      55         [ #  # ]:           0 :     const auto stale_count{std::distance(stale_begin, mined_blocks.end())};
      56         [ #  # ]:           0 :     if (stale_count > 0) {
      57         [ #  # ]:           0 :         LogDebug(BCLog::ESTIMATEFEE,
      58                 :             :                  "%s: connected block height=%s discards tracked mined-block stats "
      59                 :             :                  "from height=%s to height=%s; stale_stats=%s",
      60                 :             :                  FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY),
      61                 :             :                  stats.m_height,
      62                 :             :                  stale_begin->m_height,
      63                 :             :                  mined_blocks.back().m_height,
      64                 :             :                  stale_count);
      65                 :             :     }
      66                 :           0 :     mined_blocks.erase(stale_begin, mined_blocks.end());
      67   [ #  #  #  # ]:           0 :     if (!mined_blocks.empty() && mined_blocks.back().m_height + 1 != stats.m_height) {
      68   [ #  #  #  # ]:           0 :         LogDebug(BCLog::ESTIMATEFEE,
      69                 :             :                  "%s: clearing mined-block stats after height gap; tracked_stats=%s "
      70                 :             :                  "expected_height=%s received_height=%s",
      71                 :             :                  FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY),
      72                 :             :                  mined_blocks.size(),
      73                 :             :                  mined_blocks.back().m_height + 1,
      74                 :             :                  stats.m_height);
      75         [ #  # ]:           0 :         mined_blocks.clear();
      76                 :             :     }
      77                 :             : 
      78   [ #  #  #  # ]:           0 :     if (mined_blocks.size() == MEMPOOL_HEALTH_WINDOW_BLOCKS) mined_blocks.erase(mined_blocks.begin());
      79                 :           0 :     mined_blocks.push_back(stats);
      80                 :           0 : }
      81                 :             : 
      82                 :             : struct ActiveTip {
      83                 :             :     int height;
      84                 :             :     uint256 hash;
      85                 :             : };
      86                 :             : 
      87                 :           4 : std::optional<ActiveTip> GetActiveTip(const ChainstateManager& chainman)
      88                 :             : {
      89                 :           4 :     LOCK(::cs_main);
      90         [ +  - ]:           4 :     const CBlockIndex* tip{chainman.ActiveTip()};
      91         [ -  + ]:           4 :     if (!tip) return std::nullopt;
      92                 :           4 :     return ActiveTip{tip->nHeight, tip->GetBlockHash()};
      93                 :           4 : }
      94                 :             : } // namespace
      95                 :             : 
      96                 :           0 : MemPoolFeeRateEstimator::Percentiles MemPoolFeeRateEstimator::CalculateMaxWeightPercentiles(std::span<const FeePerVSize> chunk_feerates)
      97                 :             : {
      98   [ #  #  #  # ]:           0 :     Assume(std::is_sorted(chunk_feerates.begin(), chunk_feerates.end(), [](const auto& a, const auto& b) { return ByRatio{a} > ByRatio{b}; }));
      99                 :           0 :     constexpr int64_t total_weight{DEFAULT_BLOCK_MAX_WEIGHT};
     100                 :           0 :     const int64_t p50_weight{total_weight / 2};
     101                 :           0 :     const int64_t p75_weight{total_weight * 3 / 4};
     102                 :           0 :     Percentiles percentiles{};
     103                 :           0 :     int64_t accumulated_weight{0};
     104         [ #  # ]:           0 :     for (const auto& curr_feerate : chunk_feerates) {
     105                 :           0 :         accumulated_weight += int64_t{curr_feerate.size} * WITNESS_SCALE_FACTOR;
     106   [ #  #  #  # ]:           0 :         if (accumulated_weight >= p50_weight && percentiles.p50.IsEmpty()) {
     107                 :           0 :             percentiles.p50 = curr_feerate;
     108                 :             :         }
     109         [ #  # ]:           0 :         if (accumulated_weight >= p75_weight && percentiles.p75.IsEmpty()) {
     110                 :           0 :             percentiles.p75 = curr_feerate;
     111                 :           0 :             break;
     112                 :             :         }
     113                 :             :     }
     114                 :           0 :     return percentiles;
     115                 :             : }
     116                 :             : 
     117                 :           0 : bool MemPoolFeeRateEstimatorCache::IsStale() const
     118                 :             : {
     119   [ #  #  #  # ]:           0 :     return !m_fee_rate_estimation || (m_last_updated + CACHE_LIFE) < NodeClock::now();
     120                 :             : }
     121                 :             : 
     122                 :             : std::optional<MemPoolFeeRateEstimatorCache::FeeRateEstimate>
     123                 :           0 : MemPoolFeeRateEstimatorCache::GetCachedEstimate(const uint256& tip_hash) const
     124                 :             : {
     125   [ #  #  #  # ]:           0 :     if (IsStale() || tip_hash != m_tip_hash) return std::nullopt;
     126                 :           0 :     return m_fee_rate_estimation;
     127                 :             : }
     128                 :             : 
     129                 :           0 : void MemPoolFeeRateEstimatorCache::Update(FeePerVSize conservative, FeePerVSize economical, const uint256& tip_hash)
     130                 :             : {
     131         [ #  # ]:           0 :     m_fee_rate_estimation = {conservative, economical};
     132                 :           0 :     m_tip_hash = tip_hash;
     133                 :           0 :     m_last_updated = NodeClock::now();
     134                 :           0 : }
     135                 :             : 
     136                 :           7 : void MemPoolFeeRateEstimatorCache::Clear()
     137                 :             : {
     138         [ -  + ]:           7 :     m_fee_rate_estimation.reset();
     139                 :           7 :     m_tip_hash.SetNull();
     140                 :           7 :     m_last_updated = {};
     141                 :           7 : }
     142                 :             : 
     143                 :             : //! Build the error result for a failed mempool fee rate estimation.
     144                 :           0 : static util::Unexpected<FeeRateEstimationError> EstimationError(std::string error)
     145                 :             : {
     146                 :           0 :     return EstimationError(FeeRateEstimatorType::MEMPOOL_POLICY, MEMPOOL_FEE_ESTIMATOR_MAX_TARGET, std::move(error));
     147                 :             : }
     148                 :             : 
     149                 :           0 : static std::optional<std::string_view> MempoolHealthError(MemPoolFeeRateEstimator::MempoolHealth health)
     150                 :             : {
     151   [ #  #  #  # ]:           0 :     switch (health) {
     152                 :           0 :     case MemPoolFeeRateEstimator::MempoolHealth::INSUFFICIENT_DATA:
     153                 :           0 :         return "Not enough recent block data for fee rate estimation";
     154                 :           0 :     case MemPoolFeeRateEstimator::MempoolHealth::LOW_COVERAGE:
     155                 :           0 :         return "Mempool is unreliable for fee rate estimation";
     156                 :           0 :     case MemPoolFeeRateEstimator::MempoolHealth::HEALTHY:
     157                 :           0 :         return std::nullopt;
     158                 :             :     }
     159                 :           0 :     Assume(false);
     160                 :             :     return std::nullopt;
     161                 :             : }
     162                 :             : 
     163                 :         154 : MemPoolFeeRateEstimator::MemPoolFeeRateEstimator(fs::path mempool_estimator_file_path,
     164                 :             :                                                  const CTxMemPool& mempool,
     165                 :         154 :                                                  ChainstateManager& chainman)
     166                 :         154 :     : m_mempool(mempool),
     167                 :         154 :       m_chainman(chainman),
     168                 :         154 :       m_mempool_estimator_file_path(std::move(mempool_estimator_file_path))
     169                 :             : {
     170         [ +  - ]:         154 :     ReadFromDisk();
     171                 :         154 : }
     172                 :             : 
     173                 :         154 : void MemPoolFeeRateEstimator::ReadFromDisk()
     174                 :             : {
     175                 :         308 :     AutoFile file{fsbridge::fopen(m_mempool_estimator_file_path, "rb")};
     176         [ +  - ]:         154 :     if (file.IsNull()) {
     177   [ +  -  -  +  :         154 :         LogDebug(BCLog::ESTIMATEFEE, "%s: %s does not exist. Continuing anyway",
          -  -  -  -  -  
                      - ]
     178                 :             :                  FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY),
     179                 :             :                  fs::PathToString(m_mempool_estimator_file_path));
     180                 :         154 :         return;
     181                 :             :     }
     182   [ #  #  #  # ]:           0 :     if (Read(file)) {
     183   [ #  #  #  #  :           0 :         LogDebug(BCLog::ESTIMATEFEE, "%s: mined-block stats successfully read from %s.",
          #  #  #  #  #  
                      # ]
     184                 :             :                  FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY),
     185                 :             :                  fs::PathToString(m_mempool_estimator_file_path));
     186                 :             :     }
     187                 :         154 : }
     188                 :             : 
     189                 :         216 : bool MemPoolFeeRateEstimator::Read(AutoFile& file)
     190                 :             : {
     191                 :         216 :     try {
     192                 :         216 :         int version_required;
     193         [ +  + ]:         216 :         file >> version_required;
     194         [ +  + ]:          29 :         if (version_required != CURRENT_MEMPOOL_ESTIMATOR_VERSION) {
     195   [ +  -  +  - ]:         216 :             LogWarning("%s: file version not supported; continuing anyway",
     196                 :             :                        FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY));
     197                 :             :             return false;
     198                 :             :         }
     199                 :             :         // Stage into a local buffer and commit to the member only after validation passes.
     200                 :          22 :         std::vector<MinedBlockStats> blocks;
     201         [ +  + ]:          22 :         file >> Using<VectorFormatter<MinedBlockStatsFormatter>>(blocks);
     202                 :          14 :         uint256 tip_hash;
     203         [ +  + ]:          14 :         file >> tip_hash;
     204   [ -  +  +  + ]:          13 :         if (blocks.size() > MEMPOOL_HEALTH_WINDOW_BLOCKS) {
     205   [ +  -  +  - ]:           1 :             LogWarning("%s: Number of previously mined blocks read exceeds the maximum of %s; ignoring file",
     206                 :             :                        FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY),
     207                 :             :                        MEMPOOL_HEALTH_WINDOW_BLOCKS);
     208                 :             :             return false;
     209                 :             :         }
     210         [ +  + ]:          14 :         for (size_t i = 1; i < blocks.size(); ++i) {
     211         [ +  + ]:           5 :             const uint64_t expected_height{SaturatingAdd(blocks[i - 1].m_height, uint64_t{1})};
     212         [ +  + ]:           5 :             if (blocks[i].m_height != expected_height) {
     213   [ +  -  +  - ]:           6 :                 LogWarning("%s: Non-consecutive block heights read, expected height %s but found %s; ignoring file",
     214                 :             :                            FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY),
     215                 :             :                            expected_height, blocks[i].m_height);
     216                 :             :                 return false;
     217                 :             :             }
     218                 :             :         }
     219         [ +  + ]:           9 :         if (!blocks.empty()) {
     220                 :           4 :             const auto& last_block{blocks.back()};
     221         [ +  - ]:           4 :             const std::optional<ActiveTip> active_tip{GetActiveTip(m_chainman)};
     222         [ -  + ]:           4 :             if (!active_tip) {
     223   [ #  #  #  #  :           0 :                 LogWarning("%s: Mined-block stats read end at height %s block %s, but there is no active chain tip; ignoring file",
                   #  # ]
     224                 :             :                            FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY),
     225                 :             :                            last_block.m_height, tip_hash.ToString());
     226                 :           0 :                 return false;
     227                 :             :             }
     228   [ +  +  +  + ]:           4 :             if (last_block.m_height != static_cast<uint64_t>(active_tip->height) || tip_hash != active_tip->hash) {
     229   [ +  -  +  -  :           2 :                 LogWarning("%s: Mined-block stats read end at height %s block %s, but the active chain tip is height %s block %s; ignoring file",
             +  -  +  - ]
     230                 :             :                            FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY),
     231                 :             :                            last_block.m_height, tip_hash.ToString(),
     232                 :             :                            active_tip->height, active_tip->hash.ToString());
     233                 :           2 :                 return false;
     234                 :             :             }
     235                 :             :         }
     236         [ +  - ]:           7 :         LOCK(cs);
     237                 :           7 :         m_prev_mined_blocks = std::move(blocks);
     238                 :           7 :         m_mined_blocks_tip_hash = tip_hash;
     239         [ +  - ]:           7 :         m_cache.Clear();
     240         [ -  + ]:         209 :     } catch (const std::exception&) {
     241   [ +  -  +  - ]:         196 :         LogWarning("%s: Unable to read mined-block stats from stream (non-fatal)",
     242                 :             :                    FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY));
     243                 :         196 :         return false;
     244                 :         196 :     }
     245                 :           7 :     return true;
     246                 :             : }
     247                 :             : 
     248                 :           7 : bool MemPoolFeeRateEstimator::Write(AutoFile& file) const
     249                 :             : {
     250                 :           7 :     try {
     251         [ +  - ]:           7 :         LOCK(cs);
     252         [ +  + ]:           7 :         file << CURRENT_MEMPOOL_ESTIMATOR_VERSION;
     253         [ +  - ]:           6 :         file << Using<VectorFormatter<MinedBlockStatsFormatter>>(m_prev_mined_blocks);
     254   [ +  -  +  - ]:          12 :         file << m_mined_blocks_tip_hash;
     255         [ -  + ]:           1 :     } catch (const std::exception&) {
     256                 :           1 :         return false;
     257                 :           1 :     }
     258                 :           6 :     return true;
     259                 :             : }
     260                 :             : 
     261                 :           0 : void MemPoolFeeRateEstimator::FlushMinedBlockStats()
     262                 :             : {
     263         [ #  # ]:           0 :     if (!m_mempool_estimator_file_path.parent_path().empty()) {
     264                 :           0 :         std::error_code error;
     265         [ #  # ]:           0 :         fs::create_directories(m_mempool_estimator_file_path.parent_path(), error);
     266         [ #  # ]:           0 :         if (error) {
     267   [ #  #  #  #  :           0 :             LogWarning("%s: failed to create mempool policy estimator directory %s: %s. Continuing anyway",
             #  #  #  # ]
     268                 :             :                        FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY),
     269                 :             :                        fs::PathToString(m_mempool_estimator_file_path.parent_path()), error.message());
     270                 :           0 :             return;
     271                 :             :         }
     272                 :             :     }
     273                 :           0 :     AutoFile file{fsbridge::fopen(m_mempool_estimator_file_path, "wb")};
     274         [ #  # ]:           0 :     if (file.IsNull()) {
     275   [ #  #  #  #  :           0 :         LogWarning("%s: unable to open %s for writing. Continuing anyway",
                   #  # ]
     276                 :             :                    FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY),
     277                 :             :                    fs::PathToString(m_mempool_estimator_file_path));
     278                 :           0 :         return;
     279                 :             :     }
     280   [ #  #  #  # ]:           0 :     if (!Write(file)) {
     281   [ #  #  #  #  :           0 :         LogWarning("%s: Unable to write mined-block stats to %s (non-fatal)",
                   #  # ]
     282                 :             :                    FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY),
     283                 :             :                    fs::PathToString(m_mempool_estimator_file_path));
     284                 :             :     }
     285   [ #  #  #  # ]:           0 :     if (file.fclose() != 0) {
     286   [ #  #  #  #  :           0 :         LogWarning("Failed to close mempool policy estimator file %s: %s. Continuing anyway.",
                   #  # ]
     287                 :             :                    fs::PathToString(m_mempool_estimator_file_path), SysErrorString(errno));
     288                 :           0 :         return;
     289                 :             :     }
     290   [ #  #  #  #  :           0 :     LogDebug(BCLog::ESTIMATEFEE, "%s: mined-block stats flushed to %s.",
          #  #  #  #  #  
                      # ]
     291                 :             :              FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY),
     292                 :             :              fs::PathToString(m_mempool_estimator_file_path));
     293                 :           0 : }
     294                 :             : 
     295                 :             : 
     296                 :           0 : void MemPoolFeeRateEstimator::MempoolTxsRemovedForBlock(const std::shared_ptr<const CBlock>& block,
     297                 :             :                                                         const std::vector<RemovedMempoolTransactionInfo>& txs_removed_for_block,
     298                 :             :                                                         unsigned int block_height)
     299                 :             : {
     300                 :           0 :     LOCK(cs);
     301         [ #  # ]:           0 :     Assert(!block->vtx.empty());
     302                 :             :     // Accumulate total block weight and removed mempool tx weight, both excluding the coinbase.
     303                 :           0 :     const auto get_tx_weight = [](const CTransactionRef& tx) {
     304                 :           0 :         return static_cast<uint64_t>(GetTransactionWeight(*tx));
     305                 :             :     };
     306                 :             :     // Skip vtx[0], which is the coinbase.
     307                 :           0 :     const uint64_t block_weight = std::accumulate(std::next(block->vtx.begin()), block->vtx.end(), uint64_t{0},
     308                 :           0 :                                                   [&](uint64_t acc, const CTransactionRef& tx) {
     309                 :           0 :                                                       return acc + get_tx_weight(tx);
     310                 :             :                                                   });
     311                 :           0 :     const uint64_t removed_weight = std::accumulate(
     312                 :             :         txs_removed_for_block.begin(), txs_removed_for_block.end(), uint64_t{0},
     313                 :           0 :         [&](uint64_t acc, const RemovedMempoolTransactionInfo& tx) {
     314                 :           0 :             return acc + get_tx_weight(tx.info.m_tx);
     315                 :             :         });
     316         [ #  # ]:           0 :     AddMinedBlockStats(m_prev_mined_blocks, {block_height, removed_weight, block_weight});
     317         [ #  # ]:           0 :     m_mined_blocks_tip_hash = block->GetHash();
     318         [ #  # ]:           0 :     m_cache.Clear();
     319                 :           0 : }
     320                 :             : 
     321                 :             : // Require at least one block worth of activity across the window before using
     322                 :             : // the coverage ratio as a representative mempool health signal.
     323                 :             : static constexpr uint64_t MIN_REPRESENTATIVE_WINDOW_WEIGHT{DEFAULT_BLOCK_MAX_WEIGHT};
     324                 :             : 
     325                 :           0 : MemPoolFeeRateEstimator::MempoolHealth MemPoolFeeRateEstimator::GetMempoolHealth() const
     326                 :             : {
     327                 :           0 :     LOCK(cs);
     328         [ #  # ]:           0 :     const auto estimator_name{FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY)};
     329   [ #  #  #  # ]:           0 :     if (m_prev_mined_blocks.size() < MEMPOOL_HEALTH_WINDOW_BLOCKS) {
     330   [ #  #  #  #  :           0 :         LogDebug(BCLog::ESTIMATEFEE, "%s: mempool health check failed; tracked_blocks=%s required_blocks=%s",
             #  #  #  # ]
     331                 :             :                  estimator_name, m_prev_mined_blocks.size(), MEMPOOL_HEALTH_WINDOW_BLOCKS);
     332                 :           0 :         return MempoolHealth::INSUFFICIENT_DATA;
     333                 :             :     }
     334                 :           0 :     uint64_t total_block_weight{0};
     335                 :           0 :     uint64_t total_removed_weight{0};
     336                 :           0 :     uint64_t expected_height{m_prev_mined_blocks.front().m_height};
     337         [ #  # ]:           0 :     for (const auto& block : m_prev_mined_blocks) {
     338         [ #  # ]:           0 :         Assume(block.m_height == expected_height);
     339                 :           0 :         ++expected_height;
     340                 :           0 :         total_block_weight += block.m_block_weight;
     341                 :           0 :         total_removed_weight += block.m_removed_block_txs_weight;
     342                 :             :     }
     343                 :             :     // Too little block activity for the coverage ratio to be meaningful; skip it.
     344         [ #  # ]:           0 :     if (total_block_weight < MIN_REPRESENTATIVE_WINDOW_WEIGHT) {
     345   [ #  #  #  #  :           0 :         LogDebug(BCLog::ESTIMATEFEE, "%s: mempool health check passed; low activity, total_block_weight=%s minimum=%s",
                   #  # ]
     346                 :             :                  estimator_name, total_block_weight, MIN_REPRESENTATIVE_WINDOW_WEIGHT);
     347                 :           0 :         return MempoolHealth::HEALTHY;
     348                 :             :     }
     349                 :           0 :     const double representation_ratio = static_cast<double>(total_removed_weight) / total_block_weight;
     350   [ #  #  #  #  :           0 :     LogDebug(BCLog::ESTIMATEFEE,
             #  #  #  # ]
     351                 :             :              "%s: mempool health check %s; removed_weight=%s total_block_weight=%s "
     352                 :             :              "coverage=%.2f required_coverage=%.2f",
     353                 :             :              estimator_name,
     354                 :             :              representation_ratio >= MEMPOOL_REPRESENTATION_THRESHOLD ? "passed" : "failed",
     355                 :             :              total_removed_weight,
     356                 :             :              total_block_weight,
     357                 :             :              representation_ratio,
     358                 :             :              MEMPOOL_REPRESENTATION_THRESHOLD);
     359         [ #  # ]:           0 :     return representation_ratio >= MEMPOOL_REPRESENTATION_THRESHOLD ? MempoolHealth::HEALTHY : MempoolHealth::LOW_COVERAGE;
     360                 :           0 : }
     361                 :             : 
     362                 :           0 : util::Expected<FeeRateEstimation, FeeRateEstimationError> MemPoolFeeRateEstimator::EstimateFeeRate(bool conservative) const
     363                 :             : {
     364                 :           0 :     constexpr auto estimator_type{FeeRateEstimatorType::MEMPOOL_POLICY};
     365         [ #  # ]:           0 :     if (!m_mempool.GetLoadTried()) {
     366                 :           0 :         return EstimationError(strprintf("%s: Mempool not loaded yet, no fee rate estimate available", FeeRateEstimatorTypeToString(estimator_type)));
     367                 :             :     }
     368         [ #  # ]:           0 :     if (auto error{MempoolHealthError(GetMempoolHealth())}) {
     369                 :           0 :         return EstimationError(strprintf("%s: %s", FeeRateEstimatorTypeToString(estimator_type), *error));
     370                 :             :     }
     371                 :             :     // The estimator lock is not held while building a block template, so
     372                 :             :     // in a rare edge case concurrent callers may duplicate work.
     373                 :             :     //
     374                 :             :     // Cached fee rate estimates are tagged with the chain tip they were computed on
     375                 :             :     // and only served from the cache while that tip is current.
     376                 :             :     //
     377                 :             :     // The fee rate estimate returned directly below may still reflect a tip that went
     378                 :             :     // stale during the call; that is an accepted tradeoff of not holding
     379                 :             :     // locks across block assembly.
     380                 :           0 :     {
     381   [ #  #  #  #  :           0 :         const uint256 tip_hash{WITH_LOCK(::cs_main, return Assume(m_chainman.CurrentChainstate().m_chain.Tip())->GetBlockHash())};
                   #  # ]
     382                 :           0 :         LOCK(cs);
     383         [ #  # ]:           0 :         const auto cached_estimate = m_cache.GetCachedEstimate(tip_hash);
     384         [ #  # ]:           0 :         if (cached_estimate) {
     385                 :           0 :             const auto cached_feerate{
     386         [ #  # ]:           0 :                 conservative ? cached_estimate->m_conservative : cached_estimate->m_economical};
     387         [ #  # ]:           0 :             return FeeRateEstimation{estimator_type, cached_feerate, MEMPOOL_FEE_ESTIMATOR_MAX_TARGET};
     388                 :             :         }
     389                 :           0 :     }
     390                 :           0 :     node::BlockCreateOptions options;
     391                 :           0 :     options.test_block_validity = false;
     392   [ #  #  #  #  :           0 :     const auto blocktemplate = WITH_LOCK(::cs_main, return (node::BlockAssembler{m_chainman.CurrentChainstate(), &m_mempool, options}).CreateNewBlock());
             #  #  #  # ]
     393   [ #  #  #  #  :           0 :     if (!blocktemplate) return EstimationError(strprintf("%s: Failed to create block template for fee rate estimation", FeeRateEstimatorTypeToString(estimator_type)));
                   #  # ]
     394                 :             :     // Sort again because the rounding up when converting from weight to vsize may cause slight misorder.
     395                 :           0 :     std::sort(blocktemplate->m_package_feerates.begin(), blocktemplate->m_package_feerates.end(), [](const auto& a, const auto& b) { return ByRatio{a} > ByRatio{b}; });
           [ #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
             #  #  #  #  
                      # ]
     396   [ #  #  #  # ]:           0 :     const auto percentiles = CalculateMaxWeightPercentiles(blocktemplate->m_package_feerates);
     397                 :             :     // Fall back to a relayable floor (the higher of the min relay fee and the current
     398                 :             :     // mempool min fee) for any percentile the mempool was too sparse to fill.
     399   [ #  #  #  # ]:           0 :     const FeePerVSize floor{std::max(m_mempool.m_opts.min_relay_feerate, m_mempool.GetMinFee()).GetFeePerVSize()};
     400         [ #  # ]:           0 :     const FeePerVSize p50{percentiles.p50.IsEmpty() ? floor : percentiles.p50};
     401         [ #  # ]:           0 :     const FeePerVSize p75{percentiles.p75.IsEmpty() ? floor : percentiles.p75};
     402   [ #  #  #  # ]:           0 :     WITH_LOCK(cs, m_cache.Update(p50, p75, blocktemplate->block.hashPrevBlock));
     403                 :           0 :     LogDebug(BCLog::ESTIMATEFEE, "%s: conservative/economical fee rate: %s/%s %s/kvB",
           [ #  #  #  #  
          #  #  #  #  #  
                #  #  # ]
     404                 :             :              FeeRateEstimatorTypeToString(estimator_type), CFeeRate(p50).GetFeePerK(),
     405                 :             :              CFeeRate(p75).GetFeePerK(), CURRENCY_ATOM);
     406         [ #  # ]:           0 :     return FeeRateEstimation{estimator_type, conservative ? p50 : p75, MEMPOOL_FEE_ESTIMATOR_MAX_TARGET};
     407                 :           0 : }
        

Generated by: LCOV version 2.5.0-full