LCOV - code coverage report
Current view: top level - src/policy/fees - mempool_estimator.cpp (source / functions) Coverage Total Hit
Test: test_bitcoin_coverage.info Lines: 70.5 % 207 146
Test Date: 2026-08-25 06:16:57 Functions: 81.8 % 22 18
Branches: 42.8 % 332 142

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

Generated by: LCOV version 2.0-1