Problem
Mempool transactions and mempool-health statistics are persisted separately:
mempool.datstores the transactions.fees/mempool_policy_estimator.datstores the six-block health history.
With -persistmempool=0, LoadMempool() does not restore mempool.dat and returns false. However, that result is not used to record whether restoration succeeded; SetLoadTried() is still called afterward. The flag therefore means that the loading stage finished, not that the previous mempool was restored.
The estimator only checks GetLoadTried(), so it can continue with a new, empty mempool. Meanwhile, mempool_policy_estimator.dat may contain six valid records ending at the unchanged chain tip. Those records describe the previous process’s mempool, but they are used to consider the current empty mempool healthy.
Building a template from the empty mempool produces no transaction-package fee rates. The estimator cannot calculate its 50th and 75th percentiles and substitutes the relay floor:
max(minrelaytxfee, mempoolminfee)
This is the minimum fee the node currently accepts for relay or mempool admission. It does not necessarily represent the fee required for prompt confirmation.
Combined mode then selects:
min(block-policy estimate, mempool-policy estimate)
Could this cause combined mode to recommend the relay floor immediately after restart, even though the block-policy estimator indicates that a higher fee is required?
Test scenario
Given:
Six healthy records ending at the current chain tip. A block-policy estimate above the relay floor. Disconnected peers, preventing automatic mempool repopulation.
Scenario:
- Restart with
-persistmempool=0so the following shutdown will not save mempool.dat. - Populate the in-memory mempool with transactions representing more than 75% of a block’s available weight.
- Restart again with
-persistmempool=0. - Confirm that the transactions were not restored and the current mempool is empty.
- Confirm that
getmempoolinfo()["loaded"]is nevertheless true. - Confirm that the separate health file still provides six valid records and that the block-policy estimate remains above the relay floor.
- Request a combined estimate.
The current code accepts the persisted health records, builds a template from the empty mempool, substitutes the relay floor for the missing percentiles, and returns that low floor as a successful mempool_policy estimate. Combined mode chooses it because it is lower than the block-policy estimate. This is the behavior that causes the test to fail.
Duration of the problem I think in real life scenerio, the misleading result does not necessarily last long:
- If transaction-filled blocks are connected while their transactions were absent from the local mempool, the coverage ratio falls below 75% and the estimator becomes unhealthy. Depending on block weights, this can happen before all six records are replaced.
- If the mempool refills sufficiently, template-based estimation becomes representative again.
Therefore, the issue is likely temporary on an active chain with transaction-filled blocks,
Questions
- Should
GetLoadTried()distinguish between “loading was attempted” and “the mempool was successfully restored”? - Should persisted health statistics be cleared or ignored when
LoadMempool()fails or persistence is disabled? - Should
mempool_policy_estimator.datbe trusted solely because its last block matches the current chain tip, even whenmempool.datwas not restored?