Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
251LOCK(cs);252file << CURRENT_MEMPOOL_ESTIMATOR_VERSION;253file << Using<VectorFormatter<MinedBlockStatsFormatter>>(m_prev_mined_blocks);254file << m_mined_blocks_tip_hash;255} catch (const std::exception&) {256return false;257}258return true;259}261void MemPoolFeeRateEstimator::FlushMinedBlockStats()262{263if (!m_mempool_estimator_file_path.parent_path().empty()) {264std::error_code error;265fs::create_directories(m_mempool_estimator_file_path.parent_path(), error);266if (error) {267LogWarning("%s: failed to create mempool policy estimator directory %s: %s. Continuing anyway",268FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY),269fs::PathToString(m_mempool_estimator_file_path.parent_path()), error.message());270return;271}272}273AutoFile file{fsbridge::fopen(m_mempool_estimator_file_path, "wb")};274if (file.IsNull()) {275LogWarning("%s: unable to open %s for writing. Continuing anyway",276FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY),277fs::PathToString(m_mempool_estimator_file_path));278return;279}280if (!Write(file)) {281LogWarning("%s: Unable to write mined-block stats to %s (non-fatal)",282FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY),283fs::PathToString(m_mempool_estimator_file_path));284}285if (file.fclose() != 0) {286LogWarning("Failed to close mempool policy estimator file %s: %s. Continuing anyway.",287fs::PathToString(m_mempool_estimator_file_path), SysErrorString(errno));288return;289}290LogDebug(BCLog::ESTIMATEFEE, "%s: mined-block stats flushed to %s.",291FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY),292fs::PathToString(m_mempool_estimator_file_path));293}295void MemPoolFeeRateEstimator::MempoolLoadFailed()296{297LOCK(cs);298if (m_prev_mined_blocks.empty()) return;299LogDebug(BCLog::ESTIMATEFEE, "%s: mempool did not load; clearing mined-block stats",300FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY));301m_prev_mined_blocks.clear();302}304void MemPoolFeeRateEstimator::MempoolTxsRemovedForBlock(const std::shared_ptr<const CBlock>& block,305const std::vector<RemovedMempoolTransactionInfo>& txs_removed_for_block,306unsigned int block_height)307{308LOCK(cs);309Assert(!block->vtx.empty());310// Accumulate total block weight and removed mempool tx weight, both excluding the coinbase.311const auto get_tx_weight = [](const CTransactionRef& tx) {312return static_cast<uint64_t>(GetTransactionWeight(*tx));313};314// Skip vtx[0], which is the coinbase.315const uint64_t block_weight = std::accumulate(std::next(block->vtx.begin()), block->vtx.end(), uint64_t{0},316[&](uint64_t acc, const CTransactionRef& tx) {317return acc + get_tx_weight(tx);318});319const uint64_t removed_weight = std::accumulate(320txs_removed_for_block.begin(), txs_removed_for_block.end(), uint64_t{0},321[&](uint64_t acc, const RemovedMempoolTransactionInfo& tx) {322return acc + get_tx_weight(tx.info.m_tx);323});324AddMinedBlockStats(m_prev_mined_blocks, {block_height, removed_weight, block_weight});325m_mined_blocks_tip_hash = block->GetHash();326m_cache.Clear();327}329// Require at least one block worth of activity across the window before using330// the coverage ratio as a representative mempool health signal.331static constexpr uint64_t MIN_REPRESENTATIVE_WINDOW_WEIGHT{DEFAULT_BLOCK_MAX_WEIGHT};333MemPoolFeeRateEstimator::MempoolHealth MemPoolFeeRateEstimator::GetMempoolHealth() const334{335LOCK(cs);336const auto estimator_name{FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY)};337if (m_prev_mined_blocks.size() < MEMPOOL_HEALTH_WINDOW_BLOCKS) {338LogDebug(BCLog::ESTIMATEFEE, "%s: mempool health check failed; tracked_blocks=%s required_blocks=%s",339estimator_name, m_prev_mined_blocks.size(), MEMPOOL_HEALTH_WINDOW_BLOCKS);340return MempoolHealth::INSUFFICIENT_DATA;341}342uint64_t total_block_weight{0};343uint64_t total_removed_weight{0};344uint64_t expected_height{m_prev_mined_blocks.front().m_height};345for (const auto& block : m_prev_mined_blocks) {346Assume(block.m_height == expected_height);347++expected_height;348total_block_weight += block.m_block_weight;349total_removed_weight += block.m_removed_block_txs_weight;350}351// Too little block activity for the coverage ratio to be meaningful; skip it.352if (total_block_weight < MIN_REPRESENTATIVE_WINDOW_WEIGHT) {353LogDebug(BCLog::ESTIMATEFEE, "%s: mempool health check passed; low activity, total_block_weight=%s minimum=%s",354estimator_name, total_block_weight, MIN_REPRESENTATIVE_WINDOW_WEIGHT);355return MempoolHealth::HEALTHY;356}357const double representation_ratio = static_cast<double>(total_removed_weight) / total_block_weight;358LogDebug(BCLog::ESTIMATEFEE,359"%s: mempool health check %s; removed_weight=%s total_block_weight=%s "360"coverage=%.2f required_coverage=%.2f",361estimator_name,362representation_ratio >= MEMPOOL_REPRESENTATION_THRESHOLD ? "passed" : "failed",363total_removed_weight,364total_block_weight,365representation_ratio,366MEMPOOL_REPRESENTATION_THRESHOLD);367return representation_ratio >= MEMPOOL_REPRESENTATION_THRESHOLD ? MempoolHealth::HEALTHY : MempoolHealth::LOW_COVERAGE;368}370util::Expected<FeeRateEstimation, FeeRateEstimationError> MemPoolFeeRateEstimator::EstimateFeeRate(bool conservative) const371{372constexpr auto estimator_type{FeeRateEstimatorType::MEMPOOL_POLICY};373if (!m_mempool.GetLoadTried()) {374return EstimationError(strprintf("%s: Mempool not loaded yet, no fee rate estimate available", FeeRateEstimatorTypeToString(estimator_type)));375}376if (auto error{MempoolHealthError(GetMempoolHealth())}) {377return EstimationError(strprintf("%s: %s", FeeRateEstimatorTypeToString(estimator_type), *error));378}379// The estimator lock is not held while building a block template, so380// 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 on383// 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 went386// stale during the call; that is an accepted tradeoff of not holding387// locks across block assembly.388{389const uint256 tip_hash{WITH_LOCK(::cs_main, return Assume(m_chainman.CurrentChainstate().m_chain.Tip())->GetBlockHash())};390LOCK(cs);391const auto cached_estimate = m_cache.GetCachedEstimate(tip_hash);392if (cached_estimate) {393const auto cached_feerate{394conservative ? cached_estimate->m_conservative : cached_estimate->m_economical};395return FeeRateEstimation{estimator_type, cached_feerate, MEMPOOL_FEE_ESTIMATOR_MAX_TARGET};396}397}398node::BlockCreateOptions options;399options.test_block_validity = false;400const auto blocktemplate = WITH_LOCK(::cs_main, return (node::BlockAssembler{m_chainman.CurrentChainstate(), &m_mempool, options}).CreateNewBlock());401if (!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.403std::sort(blocktemplate->m_package_feerates.begin(), blocktemplate->m_package_feerates.end(), [](const auto& a, const auto& b) { return ByRatio{a} > ByRatio{b}; });404const auto percentiles = CalculateMaxWeightPercentiles(blocktemplate->m_package_feerates);405// Fall back to a relayable floor (the higher of the min relay fee and the current406// mempool min fee) for any percentile the mempool was too sparse to fill.407const FeePerVSize floor{std::max(m_mempool.m_opts.min_relay_feerate, m_mempool.GetMinFee()).GetFeePerVSize()};408const FeePerVSize p50{percentiles.p50.IsEmpty() ? floor : percentiles.p50};409const FeePerVSize p75{percentiles.p75.IsEmpty() ? floor : percentiles.p75};410WITH_LOCK(cs, m_cache.Update(p50, p75, blocktemplate->block.hashPrevBlock));411LogDebug(BCLog::ESTIMATEFEE, "%s: conservative/economical fee rate: %s/%s %s/kvB",412FeeRateEstimatorTypeToString(estimator_type), CFeeRate(p50).GetFeePerK(),413CFeeRate(p75).GetFeePerK(), CURRENCY_ATOM);414return FeeRateEstimation{estimator_type, conservative ? p50 : p75, MEMPOOL_FEE_ESTIMATOR_MAX_TARGET};415}
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.