LCOV - code coverage report
Current view: top level - src/policy/fees - mempool_estimator.cpp (source / functions) Coverage Total Hit
Test: total_coverage.info Lines: 92.5 % 214 198
Test Date: 2026-10-11 08:27:57 Functions: 100.0 % 23 23
Branches: 56.0 % 350 196

             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                 :        3444 :     void Ser(Stream& s, const MinedBlockStats& v)
      40                 :             :     {
      41                 :        3444 :         s << v.m_height << v.m_removed_block_txs_weight << v.m_block_weight;
      42                 :        3444 :     }
      43                 :             :     template <typename Stream>
      44                 :        1559 :     void Unser(Stream& s, MinedBlockStats& v)
      45                 :             :     {
      46                 :        1559 :         s >> v.m_height >> v.m_removed_block_txs_weight >> v.m_block_weight;
      47                 :        1559 :     }
      48                 :             : };
      49                 :             : 
      50                 :      103894 : void AddMinedBlockStats(std::vector<MinedBlockStats>& mined_blocks, MinedBlockStats stats)
      51                 :             : {
      52                 :      103894 :     const auto stale_begin{std::find_if(mined_blocks.begin(), mined_blocks.end(), [&](const MinedBlockStats& block) {
      53         [ +  + ]:      612116 :         return block.m_height >= stats.m_height;
      54                 :             :     })};
      55         [ +  + ]:      103894 :     const auto stale_count{std::distance(stale_begin, mined_blocks.end())};
      56         [ +  + ]:      103894 :     if (stale_count > 0) {
      57         [ +  - ]:         286 :         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                 :      103894 :     mined_blocks.erase(stale_begin, mined_blocks.end());
      67   [ +  +  +  + ]:      103894 :     if (!mined_blocks.empty() && mined_blocks.back().m_height + 1 != stats.m_height) {
      68   [ +  -  -  + ]:           7 :         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         [ +  - ]:           7 :         mined_blocks.clear();
      76                 :             :     }
      77                 :             : 
      78   [ -  +  +  + ]:      103894 :     if (mined_blocks.size() == MEMPOOL_HEALTH_WINDOW_BLOCKS) mined_blocks.erase(mined_blocks.begin());
      79                 :      103894 :     mined_blocks.push_back(stats);
      80                 :      103894 : }
      81                 :             : 
      82                 :             : struct ActiveTip {
      83                 :             :     int height;
      84                 :             :     uint256 hash;
      85                 :             : };
      86                 :             : 
      87                 :         418 : std::optional<ActiveTip> GetActiveTip(const ChainstateManager& chainman)
      88                 :             : {
      89                 :         418 :     LOCK(::cs_main);
      90         [ +  - ]:         418 :     const CBlockIndex* tip{chainman.ActiveTip()};
      91         [ +  + ]:         418 :     if (!tip) return std::nullopt;
      92                 :         400 :     return ActiveTip{tip->nHeight, tip->GetBlockHash()};
      93                 :         418 : }
      94                 :             : } // namespace
      95                 :             : 
      96                 :         465 : MemPoolFeeRateEstimator::Percentiles MemPoolFeeRateEstimator::CalculateMaxWeightPercentiles(std::span<const FeePerVSize> chunk_feerates)
      97                 :             : {
      98         [ -  + ]:       41246 :     Assume(std::is_sorted(chunk_feerates.begin(), chunk_feerates.end(), [](const auto& a, const auto& b) { return ByRatio{a} > ByRatio{b}; }));
      99                 :         465 :     constexpr int64_t total_weight{DEFAULT_BLOCK_MAX_WEIGHT};
     100                 :         465 :     const int64_t p50_weight{total_weight / 2};
     101                 :         465 :     const int64_t p75_weight{total_weight * 3 / 4};
     102                 :         465 :     Percentiles percentiles{};
     103                 :         465 :     int64_t accumulated_weight{0};
     104         [ +  + ]:       37270 :     for (const auto& curr_feerate : chunk_feerates) {
     105                 :       36811 :         accumulated_weight += int64_t{curr_feerate.size} * WITNESS_SCALE_FACTOR;
     106   [ +  +  +  + ]:       36811 :         if (accumulated_weight >= p50_weight && percentiles.p50.IsEmpty()) {
     107                 :           9 :             percentiles.p50 = curr_feerate;
     108                 :             :         }
     109         [ +  + ]:       36811 :         if (accumulated_weight >= p75_weight && percentiles.p75.IsEmpty()) {
     110                 :           6 :             percentiles.p75 = curr_feerate;
     111                 :           6 :             break;
     112                 :             :         }
     113                 :             :     }
     114                 :         465 :     return percentiles;
     115                 :             : }
     116                 :             : 
     117                 :        3173 : bool MemPoolFeeRateEstimatorCache::IsStale() const
     118                 :             : {
     119   [ +  +  +  + ]:        3173 :     return !m_fee_rate_estimation || (m_last_updated + CACHE_LIFE) < NodeClock::now();
     120                 :             : }
     121                 :             : 
     122                 :             : std::optional<MemPoolFeeRateEstimatorCache::FeeRateEstimate>
     123                 :        3170 : MemPoolFeeRateEstimatorCache::GetCachedEstimate(const uint256& tip_hash) const
     124                 :             : {
     125   [ +  +  +  + ]:        3170 :     if (IsStale() || tip_hash != m_tip_hash) return std::nullopt;
     126                 :        2706 :     return m_fee_rate_estimation;
     127                 :             : }
     128                 :             : 
     129                 :         462 : void MemPoolFeeRateEstimatorCache::Update(FeePerVSize conservative, FeePerVSize economical, const uint256& tip_hash)
     130                 :             : {
     131         [ +  + ]:         462 :     m_fee_rate_estimation = {conservative, economical};
     132                 :         462 :     m_tip_hash = tip_hash;
     133                 :         462 :     m_last_updated = NodeClock::now();
     134                 :         462 : }
     135                 :             : 
     136                 :      104486 : void MemPoolFeeRateEstimatorCache::Clear()
     137                 :             : {
     138         [ +  + ]:      104486 :     m_fee_rate_estimation.reset();
     139                 :      104486 :     m_tip_hash.SetNull();
     140                 :      104486 :     m_last_updated = {};
     141                 :      104486 : }
     142                 :             : 
     143                 :             : //! Build the error result for a failed mempool fee rate estimation.
     144                 :          15 : static util::Unexpected<FeeRateEstimationError> EstimationError(std::string error)
     145                 :             : {
     146                 :          15 :     return EstimationError(FeeRateEstimatorType::MEMPOOL_POLICY, MEMPOOL_FEE_ESTIMATOR_MAX_TARGET, std::move(error));
     147                 :             : }
     148                 :             : 
     149                 :        3180 : static std::optional<std::string_view> MempoolHealthError(MemPoolFeeRateEstimator::MempoolHealth health)
     150                 :             : {
     151   [ +  +  +  - ]:        3180 :     switch (health) {
     152                 :          11 :     case MemPoolFeeRateEstimator::MempoolHealth::INSUFFICIENT_DATA:
     153                 :          11 :         return "Not enough recent block data for fee rate estimation";
     154                 :           3 :     case MemPoolFeeRateEstimator::MempoolHealth::LOW_COVERAGE:
     155                 :           3 :         return "Mempool is unreliable for fee rate estimation";
     156                 :        3166 :     case MemPoolFeeRateEstimator::MempoolHealth::HEALTHY:
     157                 :        3166 :         return std::nullopt;
     158                 :             :     }
     159                 :           0 :     Assume(false);
     160                 :           0 :     return std::nullopt;
     161                 :             : }
     162                 :             : 
     163                 :        1147 : MemPoolFeeRateEstimator::MemPoolFeeRateEstimator(fs::path mempool_estimator_file_path,
     164                 :             :                                                  const CTxMemPool& mempool,
     165                 :        1147 :                                                  ChainstateManager& chainman)
     166                 :        1147 :     : m_mempool(mempool),
     167                 :        1147 :       m_chainman(chainman),
     168                 :        1147 :       m_mempool_estimator_file_path(std::move(mempool_estimator_file_path))
     169                 :             : {
     170         [ +  - ]:        1147 :     ReadFromDisk();
     171                 :        1147 : }
     172                 :             : 
     173                 :        1147 : void MemPoolFeeRateEstimator::ReadFromDisk()
     174                 :             : {
     175                 :        2294 :     AutoFile file{fsbridge::fopen(m_mempool_estimator_file_path, "rb")};
     176         [ +  + ]:        1147 :     if (file.IsNull()) {
     177   [ +  -  +  -  :        1068 :         LogDebug(BCLog::ESTIMATEFEE, "%s: %s does not exist. Continuing anyway",
          -  +  +  -  +  
                      - ]
     178                 :             :                  FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY),
     179                 :             :                  fs::PathToString(m_mempool_estimator_file_path));
     180                 :         534 :         return;
     181                 :             :     }
     182   [ +  -  +  + ]:         613 :     if (Read(file)) {
     183   [ +  -  +  +  :        1181 :         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                 :        1147 : }
     188                 :             : 
     189                 :         613 : bool MemPoolFeeRateEstimator::Read(AutoFile& file)
     190                 :             : {
     191                 :         613 :     try {
     192                 :         613 :         int version_required;
     193         [ +  - ]:         613 :         file >> version_required;
     194         [ -  + ]:         613 :         if (version_required != CURRENT_MEMPOOL_ESTIMATOR_VERSION) {
     195   [ -  -  -  - ]:         613 :             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                 :         613 :         std::vector<MinedBlockStats> blocks;
     201         [ +  - ]:         613 :         file >> Using<VectorFormatter<MinedBlockStatsFormatter>>(blocks);
     202                 :         613 :         uint256 tip_hash;
     203         [ +  - ]:         613 :         file >> tip_hash;
     204   [ -  +  -  + ]:         613 :         if (blocks.size() > MEMPOOL_HEALTH_WINDOW_BLOCKS) {
     205   [ #  #  #  # ]:           0 :             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         [ +  + ]:        1754 :         for (size_t i = 1; i < blocks.size(); ++i) {
     211         [ -  + ]:        1141 :             const uint64_t expected_height{SaturatingAdd(blocks[i - 1].m_height, uint64_t{1})};
     212         [ -  + ]:        1141 :             if (blocks[i].m_height != expected_height) {
     213   [ -  -  -  - ]:          21 :                 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         [ +  + ]:         613 :         if (!blocks.empty()) {
     220                 :         418 :             const auto& last_block{blocks.back()};
     221         [ +  - ]:         418 :             const std::optional<ActiveTip> active_tip{GetActiveTip(m_chainman)};
     222         [ +  + ]:         418 :             if (!active_tip) {
     223   [ +  -  +  -  :          18 :                 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                 :          18 :                 return false;
     227                 :             :             }
     228   [ +  +  +  + ]:         400 :             if (last_block.m_height != static_cast<uint64_t>(active_tip->height) || tip_hash != active_tip->hash) {
     229   [ +  -  +  -  :           3 :                 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                 :           3 :                 return false;
     234                 :             :             }
     235                 :             :         }
     236         [ +  - ]:         592 :         LOCK(cs);
     237                 :         592 :         m_prev_mined_blocks = std::move(blocks);
     238                 :         592 :         m_mined_blocks_tip_hash = tip_hash;
     239         [ +  - ]:         592 :         m_cache.Clear();
     240         [ -  - ]:         613 :     } catch (const std::exception&) {
     241   [ -  -  -  - ]:           0 :         LogWarning("%s: Unable to read mined-block stats from stream (non-fatal)",
     242                 :             :                    FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY));
     243                 :           0 :         return false;
     244                 :           0 :     }
     245                 :         592 :     return true;
     246                 :             : }
     247                 :             : 
     248                 :        1149 : bool MemPoolFeeRateEstimator::Write(AutoFile& file) const
     249                 :             : {
     250                 :        1149 :     try {
     251         [ +  - ]:        1149 :         LOCK(cs);
     252         [ +  - ]:        1149 :         file << CURRENT_MEMPOOL_ESTIMATOR_VERSION;
     253         [ +  - ]:        1149 :         file << Using<VectorFormatter<MinedBlockStatsFormatter>>(m_prev_mined_blocks);
     254   [ +  -  +  - ]:        2298 :         file << m_mined_blocks_tip_hash;
     255         [ -  - ]:           0 :     } catch (const std::exception&) {
     256                 :           0 :         return false;
     257                 :           0 :     }
     258                 :        1149 :     return true;
     259                 :             : }
     260                 :             : 
     261                 :        1149 : void MemPoolFeeRateEstimator::FlushMinedBlockStats()
     262                 :             : {
     263         [ +  - ]:        2298 :     if (!m_mempool_estimator_file_path.parent_path().empty()) {
     264                 :        1149 :         std::error_code error;
     265         [ +  - ]:        1149 :         fs::create_directories(m_mempool_estimator_file_path.parent_path(), error);
     266         [ -  + ]:        1149 :         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                 :        2298 :     AutoFile file{fsbridge::fopen(m_mempool_estimator_file_path, "wb")};
     274         [ -  + ]:        1149 :     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   [ +  -  -  + ]:        1149 :     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   [ +  -  -  + ]:        2298 :     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   [ +  -  +  +  :        2295 :     LogDebug(BCLog::ESTIMATEFEE, "%s: mined-block stats flushed to %s.",
          -  +  +  -  +  
                      - ]
     291                 :             :              FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY),
     292                 :             :              fs::PathToString(m_mempool_estimator_file_path));
     293                 :        1149 : }
     294                 :             : 
     295                 :         607 : void MemPoolFeeRateEstimator::MempoolLoadFailed()
     296                 :             : {
     297                 :         607 :     LOCK(cs);
     298   [ +  +  +  - ]:         607 :     if (m_prev_mined_blocks.empty()) return;
     299   [ +  -  +  -  :          42 :     LogDebug(BCLog::ESTIMATEFEE, "%s: mempool did not load; clearing mined-block stats",
             +  -  +  - ]
     300                 :             :              FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY));
     301   [ +  -  +  - ]:          84 :     m_prev_mined_blocks.clear();
     302                 :         607 : }
     303                 :             : 
     304                 :      103894 : void MemPoolFeeRateEstimator::MempoolTxsRemovedForBlock(const std::shared_ptr<const CBlock>& block,
     305                 :             :                                                         const std::vector<RemovedMempoolTransactionInfo>& txs_removed_for_block,
     306                 :             :                                                         unsigned int block_height)
     307                 :             : {
     308                 :      103894 :     LOCK(cs);
     309         [ -  + ]:      103894 :     Assert(!block->vtx.empty());
     310                 :             :     // Accumulate total block weight and removed mempool tx weight, both excluding the coinbase.
     311                 :      503108 :     const auto get_tx_weight = [](const CTransactionRef& tx) {
     312                 :      798428 :         return static_cast<uint64_t>(GetTransactionWeight(*tx));
     313                 :             :     };
     314                 :             :     // Skip vtx[0], which is the coinbase.
     315                 :      103894 :     const uint64_t block_weight = std::accumulate(std::next(block->vtx.begin()), block->vtx.end(), uint64_t{0},
     316                 :      217415 :                                                   [&](uint64_t acc, const CTransactionRef& tx) {
     317                 :      217415 :                                                       return acc + get_tx_weight(tx);
     318                 :             :                                                   });
     319                 :      103894 :     const uint64_t removed_weight = std::accumulate(
     320                 :             :         txs_removed_for_block.begin(), txs_removed_for_block.end(), uint64_t{0},
     321                 :      181799 :         [&](uint64_t acc, const RemovedMempoolTransactionInfo& tx) {
     322                 :      181799 :             return acc + get_tx_weight(tx.info.m_tx);
     323                 :             :         });
     324         [ +  - ]:      103894 :     AddMinedBlockStats(m_prev_mined_blocks, {block_height, removed_weight, block_weight});
     325         [ +  - ]:      103894 :     m_mined_blocks_tip_hash = block->GetHash();
     326         [ +  - ]:      103894 :     m_cache.Clear();
     327                 :      103894 : }
     328                 :             : 
     329                 :             : // Require at least one block worth of activity across the window before using
     330                 :             : // the coverage ratio as a representative mempool health signal.
     331                 :             : static constexpr uint64_t MIN_REPRESENTATIVE_WINDOW_WEIGHT{DEFAULT_BLOCK_MAX_WEIGHT};
     332                 :             : 
     333                 :        3210 : MemPoolFeeRateEstimator::MempoolHealth MemPoolFeeRateEstimator::GetMempoolHealth() const
     334                 :             : {
     335                 :        3210 :     LOCK(cs);
     336         [ +  - ]:        3210 :     const auto estimator_name{FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY)};
     337   [ -  +  +  + ]:        3210 :     if (m_prev_mined_blocks.size() < MEMPOOL_HEALTH_WINDOW_BLOCKS) {
     338   [ +  -  +  -  :          29 :         LogDebug(BCLog::ESTIMATEFEE, "%s: mempool health check failed; tracked_blocks=%s required_blocks=%s",
             -  +  +  - ]
     339                 :             :                  estimator_name, m_prev_mined_blocks.size(), MEMPOOL_HEALTH_WINDOW_BLOCKS);
     340                 :          29 :         return MempoolHealth::INSUFFICIENT_DATA;
     341                 :             :     }
     342                 :        3181 :     uint64_t total_block_weight{0};
     343                 :        3181 :     uint64_t total_removed_weight{0};
     344                 :        3181 :     uint64_t expected_height{m_prev_mined_blocks.front().m_height};
     345         [ +  + ]:       22267 :     for (const auto& block : m_prev_mined_blocks) {
     346                 :       19086 :         Assume(block.m_height == expected_height);
     347                 :       19086 :         ++expected_height;
     348                 :       19086 :         total_block_weight += block.m_block_weight;
     349                 :       19086 :         total_removed_weight += block.m_removed_block_txs_weight;
     350                 :             :     }
     351                 :             :     // Too little block activity for the coverage ratio to be meaningful; skip it.
     352         [ +  + ]:        3181 :     if (total_block_weight < MIN_REPRESENTATIVE_WINDOW_WEIGHT) {
     353   [ +  -  +  -  :        3153 :         LogDebug(BCLog::ESTIMATEFEE, "%s: mempool health check passed; low activity, total_block_weight=%s minimum=%s",
                   +  - ]
     354                 :             :                  estimator_name, total_block_weight, MIN_REPRESENTATIVE_WINDOW_WEIGHT);
     355                 :        3153 :         return MempoolHealth::HEALTHY;
     356                 :             :     }
     357                 :          28 :     const double representation_ratio = static_cast<double>(total_removed_weight) / total_block_weight;
     358   [ +  -  +  -  :          34 :     LogDebug(BCLog::ESTIMATEFEE,
             +  +  +  - ]
     359                 :             :              "%s: mempool health check %s; removed_weight=%s total_block_weight=%s "
     360                 :             :              "coverage=%.2f required_coverage=%.2f",
     361                 :             :              estimator_name,
     362                 :             :              representation_ratio >= MEMPOOL_REPRESENTATION_THRESHOLD ? "passed" : "failed",
     363                 :             :              total_removed_weight,
     364                 :             :              total_block_weight,
     365                 :             :              representation_ratio,
     366                 :             :              MEMPOOL_REPRESENTATION_THRESHOLD);
     367         [ +  + ]:          28 :     return representation_ratio >= MEMPOOL_REPRESENTATION_THRESHOLD ? MempoolHealth::HEALTHY : MempoolHealth::LOW_COVERAGE;
     368                 :        3210 : }
     369                 :             : 
     370                 :        3181 : util::Expected<FeeRateEstimation, FeeRateEstimationError> MemPoolFeeRateEstimator::EstimateFeeRate(bool conservative) const
     371                 :             : {
     372                 :        3181 :     constexpr auto estimator_type{FeeRateEstimatorType::MEMPOOL_POLICY};
     373         [ +  + ]:        3181 :     if (!m_mempool.GetLoadTried()) {
     374                 :           1 :         return EstimationError(strprintf("%s: Mempool not loaded yet, no fee rate estimate available", FeeRateEstimatorTypeToString(estimator_type)));
     375                 :             :     }
     376         [ +  + ]:        3180 :     if (auto error{MempoolHealthError(GetMempoolHealth())}) {
     377                 :          14 :         return EstimationError(strprintf("%s: %s", FeeRateEstimatorTypeToString(estimator_type), *error));
     378                 :             :     }
     379                 :             :     // The estimator lock is not held while building a block template, so
     380                 :             :     // in a rare edge case concurrent callers may duplicate work.
     381                 :             :     //
     382                 :             :     // Cached fee rate estimates are tagged with the chain tip they were computed on
     383                 :             :     // and only served from the cache while that tip is current.
     384                 :             :     //
     385                 :             :     // The fee rate estimate returned directly below may still reflect a tip that went
     386                 :             :     // stale during the call; that is an accepted tradeoff of not holding
     387                 :             :     // locks across block assembly.
     388                 :        3166 :     {
     389   [ -  +  +  - ]:        9498 :         const uint256 tip_hash{WITH_LOCK(::cs_main, return Assume(m_chainman.CurrentChainstate().m_chain.Tip())->GetBlockHash())};
     390                 :        3166 :         LOCK(cs);
     391         [ +  - ]:        3166 :         const auto cached_estimate = m_cache.GetCachedEstimate(tip_hash);
     392         [ +  + ]:        3166 :         if (cached_estimate) {
     393                 :        2705 :             const auto cached_feerate{
     394         [ +  + ]:        2705 :                 conservative ? cached_estimate->m_conservative : cached_estimate->m_economical};
     395         [ +  - ]:        2705 :             return FeeRateEstimation{estimator_type, cached_feerate, MEMPOOL_FEE_ESTIMATOR_MAX_TARGET};
     396                 :             :         }
     397                 :        2705 :     }
     398                 :         461 :     node::BlockCreateOptions options;
     399                 :         461 :     options.test_block_validity = false;
     400   [ +  -  +  -  :        1844 :     const auto blocktemplate = WITH_LOCK(::cs_main, return (node::BlockAssembler{m_chainman.CurrentChainstate(), &m_mempool, options}).CreateNewBlock());
             +  -  +  - ]
     401   [ -  +  -  -  :         461 :     if (!blocktemplate) return EstimationError(strprintf("%s: Failed to create block template for fee rate estimation", FeeRateEstimatorTypeToString(estimator_type)));
                   -  - ]
     402                 :             :     // Sort again because the rounding up when converting from weight to vsize may cause slight misorder.
     403   [ -  -  -  -  :      449926 :     std::sort(blocktemplate->m_package_feerates.begin(), blocktemplate->m_package_feerates.end(), [](const auto& a, const auto& b) { return ByRatio{a} > ByRatio{b}; });
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  -  
             +  -  -  +  
                      + ]
     404   [ -  +  +  - ]:         461 :     const auto percentiles = CalculateMaxWeightPercentiles(blocktemplate->m_package_feerates);
     405                 :             :     // Fall back to a relayable floor (the higher of the min relay fee and the current
     406                 :             :     // mempool min fee) for any percentile the mempool was too sparse to fill.
     407   [ +  -  +  + ]:         461 :     const FeePerVSize floor{std::max(m_mempool.m_opts.min_relay_feerate, m_mempool.GetMinFee()).GetFeePerVSize()};
     408         [ +  + ]:         461 :     const FeePerVSize p50{percentiles.p50.IsEmpty() ? floor : percentiles.p50};
     409         [ +  + ]:         461 :     const FeePerVSize p75{percentiles.p75.IsEmpty() ? floor : percentiles.p75};
     410   [ +  -  +  - ]:        1383 :     WITH_LOCK(cs, m_cache.Update(p50, p75, blocktemplate->block.hashPrevBlock));
     411   [ +  -  +  -  :        1383 :     LogDebug(BCLog::ESTIMATEFEE, "%s: conservative/economical fee rate: %s/%s %s/kvB",
          +  -  +  -  +  
                -  +  - ]
     412                 :             :              FeeRateEstimatorTypeToString(estimator_type), CFeeRate(p50).GetFeePerK(),
     413                 :             :              CFeeRate(p75).GetFeePerK(), CURRENCY_ATOM);
     414         [ +  + ]:         918 :     return FeeRateEstimation{estimator_type, conservative ? p50 : p75, MEMPOOL_FEE_ESTIMATOR_MAX_TARGET};
     415                 :         461 : }
        

Generated by: LCOV version 2.0-1