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.
251assert(confAvg.size() == failAvg.size());252for (unsigned int j = 0; j < buckets.size(); j++) {253for (unsigned int i = 0; i < confAvg.size(); i++) {254confAvg[i][j] *= decay;255failAvg[i][j] *= decay;256}257m_feerate_avg[j] *= decay;258txCtAvg[j] *= decay;259}260}262// returns -1 on error conditions263double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,264double successBreakPoint, unsigned int nBlockHeight,265EstimationResult *result) const266{267// Counters for a bucket (or range of buckets)268double nConf = 0; // Number of tx's confirmed within the confTarget269double totalNum = 0; // Total number of tx's that were ever confirmed270int extraNum = 0; // Number of tx's still in mempool for confTarget or longer271double failNum = 0; // Number of tx's that were never confirmed but removed from the mempool after confTarget272const int periodTarget = (confTarget + scale - 1) / scale;273const int maxbucketindex = buckets.size() - 1;275// We'll combine buckets until we have enough samples.276// The near and far variables will define the range we've combined277// The best variables are the last range we saw which still had a high278// enough confirmation rate to count as success.279// The cur variables are the current range we're counting.280unsigned int curNearBucket = maxbucketindex;281unsigned int bestNearBucket = maxbucketindex;282unsigned int curFarBucket = maxbucketindex;283unsigned int bestFarBucket = maxbucketindex;285// We'll always group buckets into sets that meet sufficientTxVal --286// this ensures that we're using consistent groups between different287// confirmation targets.288double partialNum = 0;290bool foundAnswer = false;291unsigned int bins = unconfTxs.size();292bool newBucketRange = true;293bool passing = true;294EstimatorBucket passBucket;295EstimatorBucket failBucket;297// Start counting from highest feerate transactions298for (int bucket = maxbucketindex; bucket >= 0; --bucket) {299if (newBucketRange) {300curNearBucket = bucket;301newBucketRange = false;302}303curFarBucket = bucket;304nConf += confAvg[periodTarget - 1][bucket];305partialNum += txCtAvg[bucket];306totalNum += txCtAvg[bucket];307failNum += failAvg[periodTarget - 1][bucket];308for (unsigned int confct = confTarget; confct < GetMaxConfirms(); confct++)309extraNum += unconfTxs[(nBlockHeight - confct) % bins][bucket];310extraNum += oldUnconfTxs[bucket];311// If we have enough transaction data points in this range of buckets,312// we can test for success313// (Only count the confirmed data points, so that each confirmation count314// will be looking at the same amount of data and same bucket breaks)316if (partialNum < sufficientTxVal / (1 - decay)) {317// the buckets we've added in this round aren't sufficient318// so keep adding319continue;320} else {321partialNum = 0; // reset for the next range we'll add323double curPct = nConf / (totalNum + failNum + extraNum);325// Check to see if we are no longer getting confirmed at the success rate326if (curPct < successBreakPoint) {327if (passing == true) {328// First time we hit a failure record the failed bucket329unsigned int failMinBucket = std::min(curNearBucket, curFarBucket);330unsigned int failMaxBucket = std::max(curNearBucket, curFarBucket);331failBucket.start = failMinBucket ? buckets[failMinBucket - 1] : 0;332failBucket.end = buckets[failMaxBucket];333failBucket.withinTarget = nConf;334failBucket.totalConfirmed = totalNum;335failBucket.inMempool = extraNum;336failBucket.leftMempool = failNum;337passing = false;338}339continue;340}341// Otherwise update the cumulative stats, and the bucket variables342// and reset the counters343else {344failBucket = EstimatorBucket(); // Reset any failed bucket, currently passing345foundAnswer = true;346passing = true;347passBucket.withinTarget = nConf;348nConf = 0;349passBucket.totalConfirmed = totalNum;350totalNum = 0;351passBucket.inMempool = extraNum;352passBucket.leftMempool = failNum;353failNum = 0;354extraNum = 0;355bestNearBucket = curNearBucket;356bestFarBucket = curFarBucket;357newBucketRange = true;358}359}360}362double median = -1;363double txSum = 0;365// Calculate the "average" feerate of the best bucket range that met success conditions366// Find the bucket with the median transaction and then report the average feerate from that bucket367// This is a compromise between finding the median which we can't since we don't save all tx's368// and reporting the average which is less accurate369unsigned int minBucket = std::min(bestNearBucket, bestFarBucket);370unsigned int maxBucket = std::max(bestNearBucket, bestFarBucket);371for (unsigned int j = minBucket; j <= maxBucket; j++) {372txSum += txCtAvg[j];373}374if (foundAnswer && txSum != 0) {375txSum = txSum / 2;376for (unsigned int j = minBucket; j <= maxBucket; j++) {377if (txCtAvg[j] < txSum)378txSum -= txCtAvg[j];379else { // we're in the right bucket380median = m_feerate_avg[j] / txCtAvg[j];381break;382}383}385passBucket.start = minBucket ? buckets[minBucket-1] : 0;386passBucket.end = buckets[maxBucket];387}389// If we were passing until we reached last few buckets with insufficient data, then report those as failed390if (passing && !newBucketRange) {391unsigned int failMinBucket = std::min(curNearBucket, curFarBucket);392unsigned int failMaxBucket = std::max(curNearBucket, curFarBucket);393failBucket.start = failMinBucket ? buckets[failMinBucket - 1] : 0;394failBucket.end = buckets[failMaxBucket];395failBucket.withinTarget = nConf;396failBucket.totalConfirmed = totalNum;397failBucket.inMempool = extraNum;398failBucket.leftMempool = failNum;399}401float passed_within_target_perc = 0.0;402float failed_within_target_perc = 0.0;403if ((passBucket.totalConfirmed + passBucket.inMempool + passBucket.leftMempool)) {404passed_within_target_perc = 100 * passBucket.withinTarget / (passBucket.totalConfirmed + passBucket.inMempool + passBucket.leftMempool);405}406if ((failBucket.totalConfirmed + failBucket.inMempool + failBucket.leftMempool)) {407failed_within_target_perc = 100 * failBucket.withinTarget / (failBucket.totalConfirmed + failBucket.inMempool + failBucket.leftMempool);408}410LogDebug(BCLog::ESTIMATEFEE, "FeeEst: %d > %.0f%% decay %.5f: feerate: %g from (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n",411confTarget, 100.0 * successBreakPoint, decay,412median, passBucket.start, passBucket.end,413passed_within_target_perc,414passBucket.withinTarget, passBucket.totalConfirmed, passBucket.inMempool, passBucket.leftMempool,415failBucket.start, failBucket.end,416failed_within_target_perc,417failBucket.withinTarget, failBucket.totalConfirmed, failBucket.inMempool, failBucket.leftMempool);420if (result) {421result->pass = passBucket;422result->fail = failBucket;423result->decay = decay;424result->scale = scale;425}426return median;427}429void TxConfirmStats::Write(AutoFile& fileout) const430{431fileout << Using<EncodedDoubleFormatter>(decay);432fileout << scale;433fileout << Using<VectorFormatter<EncodedDoubleFormatter>>(m_feerate_avg);434fileout << Using<VectorFormatter<EncodedDoubleFormatter>>(txCtAvg);435fileout << Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(confAvg);436fileout << Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(failAvg);437}439void TxConfirmStats::Read(AutoFile& filein, size_t numBuckets)440{441// Read data file and do some very basic sanity checking442// buckets and bucketMap are not updated yet, so don't access them443// If there is a read failure, we'll just discard this entire object anyway444uint64_t maxConfirms, maxPeriods;446// The current version will store the decay with each individual TxConfirmStats and also keep a scale factor447filein >> Using<EncodedDoubleFormatter>(decay);448if (decay <= 0 || decay >= 1) {449throw std::runtime_error("Corrupt estimates file. Decay must be between 0 and 1 (non-inclusive)");450}451filein >> scale;452if (scale == 0) {453throw std::runtime_error("Corrupt estimates file. Scale must be non-zero");454}456filein >> Using<VectorFormatter<EncodedDoubleFormatter>>(m_feerate_avg);457if (m_feerate_avg.size() != numBuckets) {458throw std::runtime_error("Corrupt estimates file. Mismatch in feerate average bucket count");459}460filein >> Using<VectorFormatter<EncodedDoubleFormatter>>(txCtAvg);461if (txCtAvg.size() != numBuckets) {462throw std::runtime_error("Corrupt estimates file. Mismatch in tx count bucket count");463}464filein >> Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(confAvg);465maxPeriods = confAvg.size();466maxConfirms = scale * maxPeriods;468if (maxConfirms <= 0 || maxConfirms > 6 * 24 * 7) { // one week469throw std::runtime_error("Corrupt estimates file. Must maintain estimates for between 1 and 1008 (one week) confirms");470}471for (unsigned int i = 0; i < maxPeriods; i++) {472if (confAvg[i].size() != numBuckets) {473throw std::runtime_error("Corrupt estimates file. Mismatch in feerate conf average bucket count");474}475}477filein >> Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(failAvg);478if (maxPeriods != failAvg.size()) {479throw std::runtime_error("Corrupt estimates file. Mismatch in confirms tracked for failures");480}481for (unsigned int i = 0; i < maxPeriods; i++) {482if (failAvg[i].size() != numBuckets) {483throw std::runtime_error("Corrupt estimates file. Mismatch in one of failure average bucket counts");484}485}487// Resize the current block variables which aren't stored in the data file488// to match the number of confirms and buckets489resizeInMemoryCounters(numBuckets);491LogDebug(BCLog::ESTIMATEFEE, "Reading estimates: %u buckets counting confirms up to %u blocks\n",492numBuckets, maxConfirms);493}495unsigned int TxConfirmStats::NewTx(unsigned int nBlockHeight, double val)496{497unsigned int bucketindex = bucketMap.lower_bound(val)->second;498unsigned int blockIndex = nBlockHeight % unconfTxs.size();499unconfTxs[blockIndex][bucketindex]++;500return bucketindex;
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.