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.
751LOCK(m_cs_fee_estimator);752// Return failure if trying to analyze a target we're not tracking753if (confTarget <= 0 || (unsigned int)confTarget > stats->GetMaxConfirms())754return CFeeRate(0);755if (successThreshold > 1)756return CFeeRate(0);758double median = stats->EstimateMedianVal(confTarget, sufficientTxs, successThreshold, nBestSeenHeight, result);760if (median < 0)761return CFeeRate(0);763return CFeeRate(llround(median));764}766unsigned int CBlockPolicyEstimator::HighestTargetTracked(FeeEstimateHorizon horizon) const767{768LOCK(m_cs_fee_estimator);769switch (horizon) {770case FeeEstimateHorizon::SHORT_HALFLIFE: {771return shortStats->GetMaxConfirms();772}773case FeeEstimateHorizon::MED_HALFLIFE: {774return feeStats->GetMaxConfirms();775}776case FeeEstimateHorizon::LONG_HALFLIFE: {777return longStats->GetMaxConfirms();778}779} // no default case, so the compiler can warn about missing cases780assert(false);781}783unsigned int CBlockPolicyEstimator::BlockSpan() const784{785if (firstRecordedHeight == 0) return 0;786assert(nBestSeenHeight >= firstRecordedHeight);788return nBestSeenHeight - firstRecordedHeight;789}791unsigned int CBlockPolicyEstimator::HistoricalBlockSpan() const792{793if (historicalFirst == 0) return 0;794assert(historicalBest >= historicalFirst);796if (nBestSeenHeight - historicalBest > OLDEST_ESTIMATE_HISTORY) return 0;798return historicalBest - historicalFirst;799}801unsigned int CBlockPolicyEstimator::MaxUsableEstimate() const802{803// Block spans are divided by 2 to make sure there are enough potential failing data points for the estimate804return std::min(longStats->GetMaxConfirms(), std::max(BlockSpan(), HistoricalBlockSpan()) / 2);805}807/** Return a fee estimate at the required successThreshold from the shortest808* time horizon which tracks confirmations up to the desired target. If809* checkShorterHorizon is requested, also allow short time horizon estimates810* for a lower target to reduce the given answer */811double CBlockPolicyEstimator::estimateCombinedFee(unsigned int confTarget, double successThreshold, bool checkShorterHorizon, EstimationResult *result) const812{813double estimate = -1;814if (confTarget >= 1 && confTarget <= longStats->GetMaxConfirms()) {815// Find estimate from shortest time horizon possible816if (confTarget <= shortStats->GetMaxConfirms()) { // short horizon817estimate = shortStats->EstimateMedianVal(confTarget, SUFFICIENT_TXS_SHORT, successThreshold, nBestSeenHeight, result);818}819else if (confTarget <= feeStats->GetMaxConfirms()) { // medium horizon820estimate = feeStats->EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, successThreshold, nBestSeenHeight, result);821}822else { // long horizon823estimate = longStats->EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, successThreshold, nBestSeenHeight, result);824}825if (checkShorterHorizon) {826EstimationResult tempResult;827// If a lower confTarget from a more recent horizon returns a lower answer use it.828if (confTarget > feeStats->GetMaxConfirms()) {829double medMax = feeStats->EstimateMedianVal(feeStats->GetMaxConfirms(), SUFFICIENT_FEETXS, successThreshold, nBestSeenHeight, &tempResult);830if (medMax > 0 && (estimate == -1 || medMax < estimate)) {831estimate = medMax;832if (result) *result = tempResult;833}834}835if (confTarget > shortStats->GetMaxConfirms()) {836double shortMax = shortStats->EstimateMedianVal(shortStats->GetMaxConfirms(), SUFFICIENT_TXS_SHORT, successThreshold, nBestSeenHeight, &tempResult);837if (shortMax > 0 && (estimate == -1 || shortMax < estimate)) {838estimate = shortMax;839if (result) *result = tempResult;840}841}842}843}844return estimate;845}847/** Ensure that for a conservative estimate, the DOUBLE_SUCCESS_PCT is also met848* at 2 * target for any longer time horizons.849*/850double CBlockPolicyEstimator::estimateConservativeFee(unsigned int doubleTarget, EstimationResult *result) const851{852double estimate = -1;853EstimationResult tempResult;854if (doubleTarget <= shortStats->GetMaxConfirms()) {855estimate = feeStats->EstimateMedianVal(doubleTarget, SUFFICIENT_FEETXS, DOUBLE_SUCCESS_PCT, nBestSeenHeight, result);856}857if (doubleTarget <= feeStats->GetMaxConfirms()) {858double longEstimate = longStats->EstimateMedianVal(doubleTarget, SUFFICIENT_FEETXS, DOUBLE_SUCCESS_PCT, nBestSeenHeight, &tempResult);859if (longEstimate > estimate) {860estimate = longEstimate;861if (result) *result = tempResult;862}863}864return estimate;865}867/** estimateSmartFee returns the max of the feerates calculated with a 60%868* threshold required at target / 2, an 85% threshold required at target and a869* 95% threshold required at 2 * target. Each calculation is performed at the870* shortest time horizon which tracks the required target. Conservative871* estimates, however, required the 95% threshold at 2 * target be met for any872* longer time horizons also.873*/874CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, FeeCalculation *feeCalc, bool conservative) const875{876LOCK(m_cs_fee_estimator);878FeeCalculation temp_fee_calc;879if (!feeCalc) feeCalc = &temp_fee_calc;881feeCalc->desiredTarget = confTarget;882feeCalc->returnedTarget = confTarget;883feeCalc->best_height = nBestSeenHeight;885double median = -1;886EstimationResult tempResult;888// Return failure if trying to analyze a target we're not tracking889if (confTarget <= 0 || (unsigned int)confTarget > longStats->GetMaxConfirms()) {890return CFeeRate(0); // error condition891}893// It's not possible to get reasonable estimates for confTarget of 1894if (confTarget == 1) confTarget = 2;896unsigned int maxUsableEstimate = MaxUsableEstimate();897if ((unsigned int)confTarget > maxUsableEstimate) {898confTarget = maxUsableEstimate;899}900feeCalc->returnedTarget = confTarget;902if (confTarget <= 1) return CFeeRate(0); // error condition904assert(confTarget > 0); //estimateCombinedFee and estimateConservativeFee take unsigned ints905/** true is passed to estimateCombined fee for target/2 and target so906* that we check the max confirms for shorter time horizons as well.907* This is necessary to preserve monotonically increasing estimates.908* For non-conservative estimates we do the same thing for 2*target, but909* for conservative estimates we want to skip these shorter horizons910* checks for 2*target because we are taking the max over all time911* horizons so we already have monotonically increasing estimates and912* the purpose of conservative estimates is not to let short term913* fluctuations lower our estimates by too much.914*915* Note: In certain rare edge cases, monotonically increasing estimates may916* not be guaranteed. Specifically, given two targets N and M, where M > N,917* if a sub-estimate for target N fails to return a valid fee rate, while918* target M has valid fee rate for that sub-estimate, target M may result919* in a higher fee rate estimate than target N.920*921* See: https://github.com/bitcoin/bitcoin/issues/11800#issuecomment-349697807922*/923double halfEst = estimateCombinedFee(confTarget/2, HALF_SUCCESS_PCT, true, &tempResult);924feeCalc->est = tempResult;925feeCalc->reason = BlockPolicyEstimateReason::HALF_ESTIMATE;926median = halfEst;927double actualEst = estimateCombinedFee(confTarget, SUCCESS_PCT, true, &tempResult);928if (actualEst > median) {929median = actualEst;930feeCalc->est = tempResult;931feeCalc->reason = BlockPolicyEstimateReason::FULL_ESTIMATE;932}933double doubleEst = estimateCombinedFee(2 * confTarget, DOUBLE_SUCCESS_PCT, !conservative, &tempResult);934if (doubleEst > median) {935median = doubleEst;936feeCalc->est = tempResult;937feeCalc->reason = BlockPolicyEstimateReason::DOUBLE_ESTIMATE;938}940if (conservative || median == -1) {941double consEst = estimateConservativeFee(2 * confTarget, &tempResult);942if (consEst > median) {943median = consEst;944feeCalc->est = tempResult;945feeCalc->reason = BlockPolicyEstimateReason::CONSERVATIVE;946}947}949if (median < 0) return CFeeRate(0); // error condition951LogDebug(BCLog::ESTIMATEFEE, "estimateSmartFee Selected feerate: %g Tgt: %d (requested %d) Reason: \"%s\" Decay %.5f: Estimation: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)",952median, feeCalc->returnedTarget, feeCalc->desiredTarget, StringForBlockPolicyEstimateReason(feeCalc->reason), feeCalc->est.decay,953feeCalc->est.pass.start, feeCalc->est.pass.end,954(feeCalc->est.pass.totalConfirmed + feeCalc->est.pass.inMempool + feeCalc->est.pass.leftMempool) > 0.0 ? 100 * feeCalc->est.pass.withinTarget / (feeCalc->est.pass.totalConfirmed + feeCalc->est.pass.inMempool + feeCalc->est.pass.leftMempool) : 0.0,955feeCalc->est.pass.withinTarget, feeCalc->est.pass.totalConfirmed, feeCalc->est.pass.inMempool, feeCalc->est.pass.leftMempool,956feeCalc->est.fail.start, feeCalc->est.fail.end,957(feeCalc->est.fail.totalConfirmed + feeCalc->est.fail.inMempool + feeCalc->est.fail.leftMempool) > 0.0 ? 100 * feeCalc->est.fail.withinTarget / (feeCalc->est.fail.totalConfirmed + feeCalc->est.fail.inMempool + feeCalc->est.fail.leftMempool) : 0.0,958feeCalc->est.fail.withinTarget, feeCalc->est.fail.totalConfirmed, feeCalc->est.fail.inMempool, feeCalc->est.fail.leftMempool);960return CFeeRate(llround(median));961}963util::Expected<FeeRateEstimation, FeeRateEstimationError> CBlockPolicyEstimator::EstimateFeeRate(int target, bool conservative) const964{965FeeCalculation fee_calc;966CFeeRate feerate{estimateSmartFee(target, &fee_calc, conservative)};967if (feerate == CFeeRate(0)) {968return EstimationError(FeeRateEstimatorType::BLOCK_POLICY, fee_calc.returnedTarget, "Insufficient data or no feerate found");969}970return FeeRateEstimation{FeeRateEstimatorType::BLOCK_POLICY, feerate.GetFeePerVSize(), fee_calc.returnedTarget};971}973unsigned int CBlockPolicyEstimator::MaximumTarget() const974{975return HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);976}978void CBlockPolicyEstimator::Flush() {979FlushUnconfirmed();980FlushFeeEstimates();981}983void CBlockPolicyEstimator::FlushFeeEstimates()984{985if (!m_estimation_filepath.parent_path().empty()) {986std::error_code error;987fs::create_directories(m_estimation_filepath.parent_path(), error);988if (error) {989LogWarning("Failed to create fee estimates directory %s: %s. Continue anyway.", fs::PathToString(m_estimation_filepath.parent_path()), error.message());990return;991}992}994AutoFile est_file{fsbridge::fopen(m_estimation_filepath, "wb")};995if (est_file.IsNull() || !Write(est_file)) {996LogWarning("Failed to write fee estimates to %s. Continue anyway.", fs::PathToString(m_estimation_filepath));997(void)est_file.fclose();998return;999}1000if (est_file.fclose() != 0) {
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.