fees: return `block_policy` fee rate estimate when `mempool_policy` is not ready #36182

pull ismaelsadeeq wants to merge 3 commits into bitcoin:master from ismaelsadeeq:09-2026-fee-estimator-not-ready-fallback changing 9 files +156 −58
  1. ismaelsadeeq commented at 2:16 PM on September 7, 2026: member

    The mempool fee rate estimator can't always give an estimate: i) right after first startup it hasn't tracked enough blocks yet, and ii) after a cold restart (persistence off, or mempool.dat missing/corrupt).

    i) Can make a user wait potentially ~1 hour after updating their node before estimatesmartfee (default fee_rate_estimator=none) returns a fee rate estimate. That's too restrictive; see context: #34075 (review)

    ii) Currently returns the minimum relay tx fee when the last saved mempool is healthy; this may be an underestimation because we may have had much higher fee rate transactions in the now missing mempool

    Hence this PR falls back to the block policy estimate while the mempool estimator isn't ready, i.e. during its data-gathering phase.

    It also updates init to notify the mempool fee rate estimator of the mempool reload outcome. When the reload fails (e.g. -persistmempool=0, data corruption), we drop the 3 oldest mined-block statistics so the estimator waits for the mempool to warm up again before serving. In this case, we also return a block policy fee rate estimate

    This is a much simpler alternative to #36095.

    We could instead make the mempool load a synchronous validation-interface notification event, but I wanted a minimal fix first and to open this up for discussion, since this PR targets the v32 release.

  2. DrahtBot added the label TX fees and policy on Sep 7, 2026
  3. DrahtBot commented at 2:16 PM on September 7, 2026: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    <!--006a51241073e994b41acfe9ec718e94-->

    Code Coverage & Benchmarks

    For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/36182.

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline and AI policy for information on the review process.

    Type Reviewers
    Concept ACK sedited, polespinasa, achow101, jeanpablojp

    If your review is incorrectly listed, please copy-paste <code>&lt;!--meta-tag:bot-skip--&gt;</code> into the comment that the bot should ignore.

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #36167 ([RFC] Enable -Wunused by fanquake)
    • #33854 (fix assumevalid is ignored during reindex by Eunovo)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

    <!--5faf32d7da4f0f540f40219e4f7537a3-->

  4. sedited commented at 2:40 PM on September 7, 2026: contributor

    Concept ACK

  5. sedited requested review from willcl-ark on Sep 7, 2026
  6. sedited requested review from achow101 on Sep 7, 2026
  7. sedited requested review from polespinasa on Sep 7, 2026
  8. polespinasa commented at 2:48 PM on September 7, 2026: member

    Concept ACK

    Are we too late to get this for v32? Would be nice to fix this, specially case 1 if #34075 is getting into v32.

  9. sedited commented at 3:03 PM on September 7, 2026: contributor

    Are we too late to get this for v32?

    Not too late. Fixes can also still be made after branch off, if it not ends up making it in by the 10th.

  10. sedited added this to the milestone 32.0 on Sep 7, 2026
  11. in src/policy/fees/mempool_estimator.h:139 in 0d05cd4e00
     144 | -        INSUFFICIENT_DATA,
     145 | -        //! Recent blocks include too few mempool transactions to estimate a fee rate.
     146 | -        LOW_COVERAGE,
     147 | -    };
     148 | -    MempoolHealth GetMempoolHealth() const EXCLUSIVE_LOCKS_REQUIRED(!cs);
     149 | +    //! Check whether recent mined blocks look healthy: INSUFFICIENT_DATA or LOW_COVERAGE, else nullopt.
    


    polespinasa commented at 3:33 PM on September 7, 2026:

    in 0d05cd4e00fa73894e38bca32004f1c53b96822e fees: enable the mempool estimator to return a typed failure enum

    nit: the doc feels weird, can be written as INSUFFICIENT_DATA or LOW_COVERAGE means healthy. Consider something similar to

        //! Return INSUFFICIENT_DATA or LOW_COVERAGE if recent mined blocks fail the health check; otherwise, return nullopt.
    

    ismaelsadeeq commented at 8:58 PM on September 7, 2026:

    Fixed

  12. in src/policy/fees/mempool_estimator.cpp:160 in 74b764de51
     156 | @@ -157,6 +157,20 @@ std::string_view MempoolEstimationFailureToString(MempoolEstimationFailure failu
     157 |      assert(false);
     158 |  }
     159 |  
     160 | +bool IsNotReady(MempoolEstimationFailure failure)
    


    polespinasa commented at 3:48 PM on September 7, 2026:

    in 74b764de510abb3538fb365a749bff4fe2b2c989 fees: fall back to block policy while the mempool estimator is not ready

    nit: Probably cleaner IsReady(...) and then in extimator_man.cpp use if (!IsReady()). Feels a bit weird to have a function expecting a negative result.

    Also probably worth making this function a member of MemPoolFeeRateEstimator so we can check here and in other parts of the code if necessary in the future if the mempool is ready and skip the other checks.

    <details> <summary> diff </summary>

    $ git diff
    diff --git a/src/policy/fees/estimator_man.cpp b/src/policy/fees/estimator_man.cpp
    index e1ae7e8b03..5b207d4cd8 100644
    --- a/src/policy/fees/estimator_man.cpp
    +++ b/src/policy/fees/estimator_man.cpp
    @@ -29,10 +29,11 @@ util::Expected<FeeRateEstimation, FeeRateEstimationError> FeeRateEstimatorManage
             LogDebug(BCLog::ESTIMATEFEE, "%s", block_policy_estimate.error().reason);
             return block_policy_estimate;
         }
    +    // If mempool fee rate estimator is not ready, fallback to block
    +    // policy extimator.
    +    if (!m_mempool_estimator->IsReady()) return block_policy_estimate;
         auto mempool_estimate = m_mempool_estimator->EstimateFeeRate(conservative);
         if (!mempool_estimate) {
    -        if (IsNotReady(mempool_estimate.error())) return block_policy_estimate;
    -        // When mempol fee rate estimator is ready, return the error.
             // Callers can still request block policy explicitly.
             auto mempool_error = EstimationError(mempool_estimate.error());
             LogDebug(BCLog::ESTIMATEFEE, "%s", mempool_error.error().reason);
    diff --git a/src/policy/fees/mempool_estimator.cpp b/src/policy/fees/mempool_estimator.cpp
    index 35ccfcc5f7..42c53c0262 100644
    --- a/src/policy/fees/mempool_estimator.cpp
    +++ b/src/policy/fees/mempool_estimator.cpp
    @@ -157,20 +157,6 @@ std::string_view MempoolEstimationFailureToString(MempoolEstimationFailure failu
         assert(false);
     }
     
    -bool IsNotReady(MempoolEstimationFailure failure)
    -{
    -    switch (failure) {
    -    case MempoolEstimationFailure::INSUFFICIENT_DATA:
    -        return true;
    -    case MempoolEstimationFailure::MEMPOOL_NOT_LOADED:
    -    case MempoolEstimationFailure::LOW_COVERAGE:
    -    case MempoolEstimationFailure::BLOCK_TEMPLATE_FAILED:
    -        return false;
    -    }
    -    // no default case, so the compiler can warn about missing cases
    -    assert(false);
    -}
    -
     util::Unexpected<FeeRateEstimationError> EstimationError(MempoolEstimationFailure failure)
     {
         constexpr auto estimator_type{FeeRateEstimatorType::MEMPOOL_POLICY};
    @@ -351,7 +337,7 @@ std::optional<MempoolEstimationFailure> MemPoolFeeRateEstimator::GetMempoolHealt
     {
         LOCK(cs);
         const auto estimator_name{FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY)};
    -    if (m_prev_mined_blocks.size() < MEMPOOL_HEALTH_WINDOW_BLOCKS) {
    +    if (!IsReady()) {
             LogDebug(BCLog::ESTIMATEFEE, "%s: mempool health check failed; tracked_blocks=%s required_blocks=%s",
                      estimator_name, m_prev_mined_blocks.size(), MEMPOOL_HEALTH_WINDOW_BLOCKS);
             return MempoolEstimationFailure::INSUFFICIENT_DATA;
    diff --git a/src/policy/fees/mempool_estimator.h b/src/policy/fees/mempool_estimator.h
    index 5475b37563..e4856c92fc 100644
    --- a/src/policy/fees/mempool_estimator.h
    +++ b/src/policy/fees/mempool_estimator.h
    @@ -152,6 +152,8 @@ public:
         //! Serialize mined-block stats without taking ownership of file.
         //! Callers must explicitly close file and check for errors after writing.
         bool Write(AutoFile& file) const EXCLUSIVE_LOCKS_REQUIRED(!cs);
    +    //! Checks if the estimator has enought block data making it ready.
    +    bool IsReady() const { return m_prev_mined_blocks.size() >= MEMPOOL_HEALTH_WINDOW_BLOCKS; };
     
     private:
         void ReadFromDisk() EXCLUSIVE_LOCKS_REQUIRED(!cs);
    diff --git a/src/test/mempool_fee_estimator_tests.cpp b/src/test/mempool_fee_estimator_tests.cpp
    index eda6a69154..3e25d03866 100644
    --- a/src/test/mempool_fee_estimator_tests.cpp
    +++ b/src/test/mempool_fee_estimator_tests.cpp
    @@ -337,14 +337,6 @@ BOOST_AUTO_TEST_CASE(MempoolFeeRateEstimator)
         }
     }
     
    -BOOST_AUTO_TEST_CASE(is_not_ready)
    -{
    -    BOOST_CHECK(IsNotReady(MempoolEstimationFailure::INSUFFICIENT_DATA));
    -    BOOST_CHECK(!IsNotReady(MempoolEstimationFailure::MEMPOOL_NOT_LOADED));
    -    BOOST_CHECK(!IsNotReady(MempoolEstimationFailure::LOW_COVERAGE));
    -    BOOST_CHECK(!IsNotReady(MempoolEstimationFailure::BLOCK_TEMPLATE_FAILED));
    -}
    -
     BOOST_AUTO_TEST_CASE(mempool_reload_drops_stale_stats)
     {
         MemPoolFeeRateEstimator estimator{MempoolPolicyEstimatorPath(*m_node.args), *m_node.mempool, *m_node.chainman};
    

    </details>

    Or just remove the function at all and minimize the diff, it is only used once.

    <details> <summary> diff </summary>

    $ git diff
    diff --git a/src/policy/fees/estimator_man.cpp b/src/policy/fees/estimator_man.cpp
    index e1ae7e8b03..bce8ef2368 100644
    --- a/src/policy/fees/estimator_man.cpp
    +++ b/src/policy/fees/estimator_man.cpp
    @@ -31,7 +31,7 @@ util::Expected<FeeRateEstimation, FeeRateEstimationError> FeeRateEstimatorManage
         }
         auto mempool_estimate = m_mempool_estimator->EstimateFeeRate(conservative);
         if (!mempool_estimate) {
    -        if (IsNotReady(mempool_estimate.error())) return block_policy_estimate;
    +        if (MempoolEstimationFailure::INSUFFICIENT_DATA == mempool_estimate.error()) return block_policy_estimate;
             // When mempol fee rate estimator is ready, return the error.
             // Callers can still request block policy explicitly.
             auto mempool_error = EstimationError(mempool_estimate.error());
    diff --git a/src/policy/fees/mempool_estimator.cpp b/src/policy/fees/mempool_estimator.cpp
    index 35ccfcc5f7..b36ad696ec 100644
    --- a/src/policy/fees/mempool_estimator.cpp
    +++ b/src/policy/fees/mempool_estimator.cpp
    @@ -157,20 +157,6 @@ std::string_view MempoolEstimationFailureToString(MempoolEstimationFailure failu
         assert(false);
     }
     
    -bool IsNotReady(MempoolEstimationFailure failure)
    -{
    -    switch (failure) {
    -    case MempoolEstimationFailure::INSUFFICIENT_DATA:
    -        return true;
    -    case MempoolEstimationFailure::MEMPOOL_NOT_LOADED:
    -    case MempoolEstimationFailure::LOW_COVERAGE:
    -    case MempoolEstimationFailure::BLOCK_TEMPLATE_FAILED:
    -        return false;
    -    }
    -    // no default case, so the compiler can warn about missing cases
    -    assert(false);
    -}
    -
     util::Unexpected<FeeRateEstimationError> EstimationError(MempoolEstimationFailure failure)
     {
         constexpr auto estimator_type{FeeRateEstimatorType::MEMPOOL_POLICY};
    diff --git a/src/policy/fees/mempool_estimator.h b/src/policy/fees/mempool_estimator.h
    index 5475b37563..2fd740763e 100644
    --- a/src/policy/fees/mempool_estimator.h
    +++ b/src/policy/fees/mempool_estimator.h
    @@ -49,9 +49,6 @@ enum class MempoolEstimationFailure {
     
     std::string_view MempoolEstimationFailureToString(MempoolEstimationFailure failure);
     
    -//! Whether a caller should fall back to another estimator for this failure.
    -bool IsNotReady(MempoolEstimationFailure failure);
    -
     //! Flatten a fee rate estimation failure into a fee rate estimation error.
     util::Unexpected<FeeRateEstimationError> EstimationError(MempoolEstimationFailure failure);
     
    diff --git a/src/test/mempool_fee_estimator_tests.cpp b/src/test/mempool_fee_estimator_tests.cpp
    index eda6a69154..3e25d03866 100644
    --- a/src/test/mempool_fee_estimator_tests.cpp
    +++ b/src/test/mempool_fee_estimator_tests.cpp
    @@ -337,14 +337,6 @@ BOOST_AUTO_TEST_CASE(MempoolFeeRateEstimator)
         }
     }
     
    -BOOST_AUTO_TEST_CASE(is_not_ready)
    -{
    -    BOOST_CHECK(IsNotReady(MempoolEstimationFailure::INSUFFICIENT_DATA));
    -    BOOST_CHECK(!IsNotReady(MempoolEstimationFailure::MEMPOOL_NOT_LOADED));
    -    BOOST_CHECK(!IsNotReady(MempoolEstimationFailure::LOW_COVERAGE));
    -    BOOST_CHECK(!IsNotReady(MempoolEstimationFailure::BLOCK_TEMPLATE_FAILED));
    -}
    -
     BOOST_AUTO_TEST_CASE(mempool_reload_drops_stale_stats)
     {
         MemPoolFeeRateEstimator estimator{MempoolPolicyEstimatorPath(*m_node.args), *m_node.mempool, *m_node.chainman};
    

    </details>


    ismaelsadeeq commented at 8:58 PM on September 7, 2026:

    Good idea, taken, thanks.

  13. in src/policy/fees/estimator_man.cpp:35 in 74b764de51
      30 | @@ -31,8 +31,9 @@ util::Expected<FeeRateEstimation, FeeRateEstimationError> FeeRateEstimatorManage
      31 |      }
      32 |      auto mempool_estimate = m_mempool_estimator->EstimateFeeRate(conservative);
      33 |      if (!mempool_estimate) {
      34 | -        // A failed mempool estimate is surfaced as a warning rather than silently returning the
      35 | -        // block policy estimate, which callers can still request explicitly.
      36 | +        if (IsNotReady(mempool_estimate.error())) return block_policy_estimate;
      37 | +        // When mempol fee rate estimator is ready, return the error.
    


    polespinasa commented at 3:48 PM on September 7, 2026:

    in 74b764de510abb3538fb365a749bff4fe2b2c989 fees: fall back to block policy while the mempool estimator is not ready

    nit: mempol -> mempool


    ismaelsadeeq commented at 8:58 PM on September 7, 2026:

    Fixed

  14. in src/rpc/fees.cpp:51 in 74b764de51
      45 | @@ -46,9 +46,9 @@ static RPCMethod estimatesmartfee()
      46 |                  {
      47 |                      {"fee_rate_estimator", RPCArg::Type::STR, RPCArg::Default{"none"},
      48 |                       "Selects which fee rate estimator to use.\n"
      49 | -                     "\"none\" returns the lower of the block policy and mempool estimates. If the mempool\n"
      50 | -                     "estimate is unavailable, it returns that error instead of falling back to the block\n"
      51 | -                     "policy estimate; use \"block_policy\" in that case to get the block policy estimate.\n"
      52 | +                     "\"none\" returns the lower of the block policy and mempool estimates. When the mempool\n"
      53 | +                     "fee rate estimator is not ready to return a fee rate estimate, block policy fee rate \n"
      54 | +                     "estimate is returned. Else we return mempool fee rate estimator errors, when we encounter them."
    


    polespinasa commented at 3:50 PM on September 7, 2026:

    in 74b764d fees: fall back to block policy while the mempool estimator is not ready

                         "estimate is returned. Otherwise, an error from the mempool fee rate estimator is returned."
    

    polespinasa commented at 5:32 PM on September 7, 2026:

    in 0d05cd4 fees: enable the mempool estimator to return a typed failure enum

    Missing \n at the end.


    ismaelsadeeq commented at 8:59 PM on September 7, 2026:

    Fixed


    ismaelsadeeq commented at 9:00 PM on September 7, 2026:

    Fixed

  15. in src/policy/fees/mempool_estimator.cpp:165 in 74b764de51
     156 | @@ -157,6 +157,20 @@ std::string_view MempoolEstimationFailureToString(MempoolEstimationFailure failu
     157 |      assert(false);
     158 |  }
     159 |  
     160 | +bool IsNotReady(MempoolEstimationFailure failure)
     161 | +{
     162 | +    switch (failure) {
     163 | +    case MempoolEstimationFailure::INSUFFICIENT_DATA:
     164 | +        return true;
     165 | +    case MempoolEstimationFailure::MEMPOOL_NOT_LOADED:
    


    polespinasa commented at 5:31 PM on September 7, 2026:

    in 74b764d fees: fall back to block policy while the mempool estimator is not ready

    If I understood correctly, MEMPOOL_NOT_LOADED can only happend during node init while the thread is attempting to load the mempool. That state is short and unlikely to be hit, but I think that is the definition of not being ready. Probably MEMPOOL_NOT_LOADED should return true too.


    ismaelsadeeq commented at 9:00 PM on September 7, 2026:

    I think it is okay to wait within this short interval than return the block policy fee rate estimate.

  16. in test/functional/feature_fee_estimation.py:337 in 74b764de51
     333 | @@ -334,6 +334,13 @@ def test_old_fee_estimate_file(self):
     334 |          self.restart_node(0)
     335 |          assert_equal(self.nodes[0].estimatesmartfee(1, "economical", {"fee_rate_estimator": "block_policy"})["feerate"], fee_rate)
     336 |  
     337 | +        self.stop_node(0)
    


    polespinasa commented at 5:34 PM on September 7, 2026:

    in 0d05cd4 fees: enable the mempool estimator to return a typed failure enum

    I think this test does not belong in this function. It is not testing an old fee estimate file, but a fallback in case estimator is not ready.


    ismaelsadeeq commented at 9:00 PM on September 7, 2026:

    Yes, fixed.

  17. in src/policy/fees/mempool_estimator.h:40 in b5c1242137
      36 | @@ -37,6 +37,7 @@ constexpr std::chrono::seconds CACHE_LIFE{7};
      37 |  // Constants for mempool sanity checks.
      38 |  constexpr size_t MEMPOOL_HEALTH_WINDOW_BLOCKS = 6;
      39 |  constexpr double MEMPOOL_REPRESENTATION_THRESHOLD = 0.75;
      40 | +constexpr size_t MEMPOOL_COLD_RESTART_STATS_TO_DROP{3};
    


    polespinasa commented at 5:36 PM on September 7, 2026:

    in b5c12421379d14cbe1ab6346c987ff948dc2ecb4 fees: warm up the mempool estimator after a cold restart

    A brief comment on why the value of this constant would be nice.


    ismaelsadeeq commented at 9:01 PM on September 7, 2026:

    Added

  18. in src/policy/fees/mempool_estimator.cpp:318 in b5c1242137
     309 | @@ -310,6 +310,13 @@ void MemPoolFeeRateEstimator::FlushMinedBlockStats()
     310 |               fs::PathToString(m_mempool_estimator_file_path));
     311 |  }
     312 |  
     313 | +void MemPoolFeeRateEstimator::MempoolReloadCompleted(bool reloaded)
     314 | +{
     315 | +    if (reloaded) return;
     316 | +    LOCK(cs);
     317 | +    const size_t to_drop{std::min(MEMPOOL_COLD_RESTART_STATS_TO_DROP, m_prev_mined_blocks.size())};
     318 | +    m_prev_mined_blocks.erase(m_prev_mined_blocks.begin(), m_prev_mined_blocks.begin() + to_drop);
    


    polespinasa commented at 5:37 PM on September 7, 2026:

    in 0d05cd4 fees: enable the mempool estimator to return a typed failure enum

    nit: probably could add a LogDebug or LogInfo here. Could be useful.


    ismaelsadeeq commented at 9:02 PM on September 7, 2026:

    Done, and made an update to only drop when stats are>3, in a way that we always have 3.

  19. polespinasa commented at 5:37 PM on September 7, 2026: member

    reviewed b5c12421379d14cbe1ab6346c987ff948dc2ecb4

    Overall the patch looks pretty good to me :)

  20. fees: enable the mempool estimator to return a typed failure enum
    EstimateFeeRate() returns a typed MempoolEstimationFailure rather
    than the shared FeeRateEstimationError, so the estimator owns its
    closed set of failure reasons.
    
    GetMempoolHealth() becomes GetMempoolHealthCheck(), returning that
    failure or nullopt, dropping the separate MempoolHealth enum.
    
    The manager flattens the failure back into FeeRateEstimationError, so its
    public API and the RPC callers are unchanged.
    7f46e2749b
  21. ismaelsadeeq force-pushed on Sep 7, 2026
  22. achow101 commented at 9:25 PM on September 7, 2026: member

    Concept ACK

    I'm not sure how useful it is to fallback to the block policy estimator for fresh starts as it also requires seeing a number of blocks before it works. However, I think it makes sense to fallback to block policy if it is available and mempool policy is not.

  23. jeanpablojp commented at 10:16 PM on September 7, 2026: contributor

    Concept ACK

    A question on the warm-up. The window keeps three of the six pre-restart blocks, and GetMempoolHealthCheck sums across the whole window, so those three still carry the ratio. Sweeping the three fresh blocks that follow, the check turns healthy once they reach 50% coverage, with the retained blocks fully covered and of the same weight, against 75% for a window of six fresh blocks.

    So how far the bar drops is set by the blocks on their way out, not by the fresh ones being measured against it. Is that intended?

    doc/release-notes-34075.md and one test comment still describe the old behaviour. Worth updating them here?

    <details><summary>the two places</summary>

    The release note still says the combined estimate returns an error when too few recent blocks have been observed, and the comment above the restart in test_estimatesmartfee_return_mempool_estimates says the same.

    </details>

  24. fees: fall back to block_policy when mempool_policy has insufficient data
    When the mempool policy estimator has insufficient data to give an estimate,
    GetFeeRateEstimate() returns the block policy estimate instead of an error.
    An INSUFFICIENT_DATA failure signals that case; the other failures
    (a still-loading or unreliable mempool, or failed block assembly) are
    returned as errors.
    de74e217c3
  25. fees: drop stale mined-block stats when the mempool does not reload
    When the mempool does not reload (persistence off, or mempool.dat missing or
    corrupt) the persisted mined-block window describes a mempool that is gone.
    MempoolReloadCompleted() drops the oldest stats so the window falls below the
    health threshold, deferring to block policy until fresh blocks refill it.
    abd298270b
  26. in src/test/mempool_fee_estimator_tests.cpp:356 in a089204991 outdated
     351 | +    estimator.MempoolReloadCompleted(/*reloaded=*/true);
     352 | +    BOOST_CHECK(estimator.IsMempoolHealthy());
     353 | +
     354 | +    // A cold reload drops the oldest stats, falling below the health window.
     355 | +    estimator.MempoolReloadCompleted(/*reloaded=*/false);
     356 | +    BOOST_CHECK(estimator.GetMempoolHealthCheck() == MempoolEstimationFailure::INSUFFICIENT_DATA);
    


    jeanpablojp commented at 10:16 PM on September 7, 2026:

    This one holds for any window shorter than six, so it does not pin the three that are kept.

        BOOST_CHECK(estimator.GetMempoolHealthCheck() == MempoolEstimationFailure::INSUFFICIENT_DATA);
        BOOST_CHECK_EQUAL(estimator.GetPrevBlockData().size(), MEMPOOL_COLD_RESTART_STATS_TO_KEEP);
    

    ismaelsadeeq commented at 12:24 PM on September 8, 2026:

    Taken, thanks

  27. in test/functional/feature_fee_estimation.py:399 in a089204991
     394 | +        self.start_node(0)
     395 | +        self.assert_cold_restart_falls_back()
     396 | +
     397 | +        # persistence disabled
     398 | +        self.restart_node(0, extra_args=["-persistmempool=0"])
     399 | +        self.assert_cold_restart_falls_back()
    


    jeanpablojp commented at 10:16 PM on September 7, 2026:

    test_fallback_when_mempool_estimator_not_ready deletes fees/mempool_policy_estimator.dat just above and nothing mines after it, so all three cases here start from an empty window, where MempoolReloadCompleted returns early and does nothing. Removing the drop altogether leaves this file passing.

    Refilling the window before each case brings the coverage back, and does fail if the drop goes away.

        def test_cold_restart_falls_back_to_block_policy(self):
            mempool_dat = self.nodes[0].chain_path / "mempool.dat"
            # Each case needs a full mined-block window before the restart. A window
            # that is already short passes these assertions whether or not the cold
            # restart drops anything.
    
            # mempool.dat missing
            self.generate(self.nodes[0], 6, sync_fun=lambda: None)
            self.stop_node(0)
            mempool_dat.unlink(missing_ok=True)
            self.start_node(0)
            self.assert_cold_restart_falls_back()
    
            # mempool.dat corrupted
            self.generate(self.nodes[0], 6, sync_fun=lambda: None)
            self.stop_node(0)
            mempool_dat.write_bytes(b"not a valid mempool.dat")
            self.start_node(0)
            self.assert_cold_restart_falls_back()
    
            # persistence disabled
            self.generate(self.nodes[0], 6, sync_fun=lambda: None)
            self.restart_node(0, extra_args=["-persistmempool=0"])
            self.assert_cold_restart_falls_back()
    

    ismaelsadeeq commented at 12:25 PM on September 8, 2026:

    Indeed, fixed with some modifications.

  28. ismaelsadeeq force-pushed on Sep 8, 2026
  29. ismaelsadeeq commented at 1:08 PM on September 8, 2026: member

    re: #36182#pullrequestreview-5135508585

    AFAICT, it's the same moving mempool representation ratio, now used as a proxy for mempool inflow. The same assumption holds even before a restart: if the first 3 blocks have 100% coverage, we only need 50% from the next 3 to stay healthy.

    In the case you mentioned, 100% coverage before and then 50% after is a good sign your peers are relaying with sane policy rules, and the drop in coverage is most likely just your own lost mempool so using it for fee estimation won't be wildly off.

    I fixed the docs as you suggested. The CI failure seems unrelated.

    Thanks for the review.

  30. DrahtBot added the label CI failed on Sep 8, 2026

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-09-09 07:56 UTC