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.
1// Copyright (c) 2010 Satoshi Nakamoto2// Copyright (c) 2009-present The Bitcoin Core developers3// Distributed under the MIT software license, see the accompanying4// file COPYING or http://www.opensource.org/licenses/mit-license.php.6#include <util/fees.h>8#include <common/messages.h>9#include <core_io.h>10#include <node/context.h>11#include <policy/feerate.h>12#include <policy/fees/block_policy_estimator.h>13#include <policy/fees/estimator_man.h>14#include <policy/fees/mempool_estimator.h>15#include <rpc/protocol.h>16#include <rpc/register.h> // IWYU pragma: associated17#include <rpc/request.h>18#include <rpc/server.h>19#include <rpc/server_util.h>20#include <rpc/util.h>21#include <txmempool.h>22#include <univalue.h>23#include <util/check.h>24#include <util/expected.h>25#include <validationinterface.h>27#include <algorithm>28#include <array>29#include <cmath>30#include <map>31#include <optional>32#include <string>33#include <string_view>34#include <utility>35#include <vector>37using common::FeeModeFromString;38using common::FeeModesDetail;39using common::InvalidEstimateModeErrorMessage;40using node::NodeContext;42static RPCMethod estimatesmartfee()43{44return RPCMethod{45"estimatesmartfee",46"Estimates the approximate fee per kilobyte needed for a transaction to begin\n"47"confirmation within conf_target blocks if possible and return the number of blocks\n"48"for which the estimate is valid. Uses virtual transaction size as defined\n"49"in BIP 141 (witness data is discounted).\n",50{51{"conf_target", RPCArg::Type::NUM, RPCArg::Optional::NO, "Confirmation target in blocks (1 - 1008)"},52{"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"economical"}, "The fee estimate mode.\n"53+ FeeModesDetail(std::string("default mode will be used"))},54{"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",55{56{"fee_rate_estimator", RPCArg::Type::STR, RPCArg::Default{"auto"},57"Selects which fee rate estimator to use.\n"58"\"auto\" (the default) combines both estimators, returning the lower of the block policy and\n"59"mempool fee rate estimates. If the mempool fee rate estimator cannot produce an estimate, the\n"60"block policy fee rate estimate is returned instead. An error is returned only if the block\n"61"policy fee rate estimate is unavailable.\n"62"\"block_policy\" uses only the block policy fee rate estimator.\n"63"\"mempool_policy\" uses only the mempool fee rate estimator."},64{"verbosity", RPCArg::Type::NUM, RPCArg::Default{1},65"1 returns feerate or errors. 2 also returns \"mempool_health_statistics\"."},66},67},68},69RPCResult{70RPCResult::Type::OBJ, "", "",71{72{RPCResult::Type::NUM, "feerate", /*optional=*/true, "estimate fee rate in " + CURRENCY_UNIT + "/kvB (only present if no errors were encountered)"},73{RPCResult::Type::STR, "estimator", /*optional=*/true, "the fee estimator used to produce the result (only present for successful estimates when fee_rate_estimator is \"auto\")"},74{RPCResult::Type::ARR, "errors", /*optional=*/true, "Errors encountered during processing (if there are any)",75{76{RPCResult::Type::STR, "", "error"},77}},78{RPCResult::Type::NUM, "blocks", "the confirmation target in blocks for the returned fee rate estimate.\n"79"For the block policy fee rate estimator, this is the target the estimate was found at, clamped to at\n"80"least 2 and at most the estimator's maximum usable target. For the mempool fee rate\n"81"estimator, it is always 2."},82{RPCResult::Type::ARR, "mempool_health_statistics", /*optional=*/true, "Health statistics for the most recently mined blocks tracked by the mempool fee rate estimator (only present when verbosity >= 2)",83{84{RPCResult::Type::OBJ, "", "",85{86{RPCResult::Type::NUM, "block_height", "Block height"},87{RPCResult::Type::NUM, "block_weight", "Total weight of non-coinbase transactions in the block"},88{RPCResult::Type::NUM, "mempool_txs_weight", "Total weight of transactions removed from the mempool for this block"},89}},90}},91}},92RPCExamples{93HelpExampleCli("estimatesmartfee", "6") +94HelpExampleRpc("estimatesmartfee", "6")95},96[](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue97{98FeeRateEstimatorManager& fee_estimator_man = EnsureAnyFeeEstimatorMan(request.context);99const NodeContext& node = EnsureAnyNodeContext(request.context);100const CTxMemPool& mempool = EnsureMemPool(node);102CHECK_NONFATAL(mempool.m_opts.signals)->SyncWithValidationInterfaceQueue();103unsigned int max_target = fee_estimator_man.MaximumTarget();104unsigned int conf_target = ParseConfirmTarget(request.params[0], max_target);105FeeEstimateMode fee_mode;106if (!FeeModeFromString(self.Arg<std::string_view>("estimate_mode"), fee_mode)) {107throw JSONRPCError(RPC_INVALID_PARAMETER, InvalidEstimateModeErrorMessage());108}109const UniValue options{request.params[2].isNull() ? UniValue::VOBJ : request.params[2]};110RPCTypeCheckObj(options,111{112{"fee_rate_estimator", UniValueType(UniValue::VSTR)},113{"verbosity", UniValueType(UniValue::VNUM)},114}, /*fAllowNull=*/true, /*fStrict=*/true);115const auto parsed_fee_rate_estimator{FeeRateEstimatorTypeFromString(116options["fee_rate_estimator"].isNull() ? "auto" : options["fee_rate_estimator"].get_str())};117if (!parsed_fee_rate_estimator) {118throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid fee_rate_estimator parameter, must be one of: \"auto\", \"block_policy\", \"mempool_policy\"");119}120const FeeRateEstimatorType fee_rate_estimator{*parsed_fee_rate_estimator};121bool conservative{fee_mode == FeeEstimateMode::CONSERVATIVE};122int verbosity{ParseVerbosity(options["verbosity"], /*default_verbosity=*/1, /*allow_bool=*/false)};123UniValue result(UniValue::VOBJ);124UniValue errors(UniValue::VARR);125const auto estimate{fee_estimator_man.GetFeeRateEstimate(fee_rate_estimator, conf_target, conservative)};126if (estimate) {127const CFeeRate min_mempool_feerate{mempool.GetMinFee()};128const CFeeRate min_relay_feerate{mempool.m_opts.min_relay_feerate};129const auto fee_rate{std::max({CFeeRate(estimate->feerate), min_mempool_feerate, min_relay_feerate})};130result.pushKV("feerate", ValueFromAmount(fee_rate.GetFeePerK()));131} else {132errors.push_back(estimate.error().reason);133result.pushKV("errors", std::move(errors));134}135if (estimate && fee_rate_estimator == FeeRateEstimatorType::AUTO) {136result.pushKV("estimator", FeeRateEstimatorTypeToString(estimate->feerate_estimator));137}138const FeeRateEstimation& estimation{FeeRateEstimationRef(estimate)};139result.pushKV("blocks", estimation.returned_target);140if (verbosity >= 2) {141UniValue mempool_health_stats(UniValue::VARR);142const auto blocks_data = fee_estimator_man.MempoolPolicyEstimatorBlocksStats();143for (auto it = blocks_data.rbegin(); it != blocks_data.rend(); ++it) {144UniValue entry(UniValue::VOBJ);145entry.pushKV("block_height", it->m_height);146entry.pushKV("block_weight", it->m_block_weight);147entry.pushKV("mempool_txs_weight", it->m_removed_block_txs_weight);148mempool_health_stats.push_back(std::move(entry));149}150result.pushKV("mempool_health_statistics", std::move(mempool_health_stats));151}152return result;153},154};155}157static std::vector<RPCResult> FeeRateBucketDoc(bool elide = false)158{159auto fields = std::vector<RPCResult>{160{RPCResult::Type::NUM, "startrange", "start of feerate range"},161{RPCResult::Type::NUM, "endrange", "end of feerate range"},162{RPCResult::Type::NUM, "withintarget", "number of txs over history horizon in the feerate range that were confirmed within target"},163{RPCResult::Type::NUM, "totalconfirmed", "number of txs over history horizon in the feerate range that were confirmed at any point"},164{RPCResult::Type::NUM, "inmempool", "current number of txs in mempool in the feerate range unconfirmed for at least target blocks"},165{RPCResult::Type::NUM, "leftmempool", "number of txs over history horizon in the feerate range that left mempool unconfirmed after target"},166};167return elide ? ElideGroup(std::move(fields)) : fields;168}170static std::vector<RPCResult> FeeEstimateHorizonDoc(bool elide = false)171{172auto fields = std::vector<RPCResult>{173{RPCResult::Type::NUM, "feerate", /*optional=*/true, "estimate fee rate in " + CURRENCY_UNIT + "/kvB"},174{RPCResult::Type::NUM, "decay", "exponential decay (per block) for historical moving average of confirmation data"},175{RPCResult::Type::NUM, "scale", "The resolution of confirmation targets at this time horizon"},176{RPCResult::Type::OBJ, "pass", /*optional=*/true, "information about the lowest range of feerates to succeed in meeting the threshold", FeeRateBucketDoc()},177{RPCResult::Type::OBJ, "fail", /*optional=*/true, "information about the highest range of feerates to fail to meet the threshold", FeeRateBucketDoc(/*elide=*/true)},178{RPCResult::Type::ARR, "errors", /*optional=*/true, "Errors encountered during processing (if there are any)",179{180{RPCResult::Type::STR, "", "error"},181}},182};183return elide ? ElideGroup(std::move(fields)) : fields;184}186static RPCMethod estimaterawfee()187{188return RPCMethod{189"estimaterawfee",190"WARNING: This interface is unstable and may disappear or change!\n"191"\nWARNING: This is an advanced API call that is tightly coupled to the specific\n"192"implementation of fee estimation. The parameters it can be called with\n"193"and the results it returns will change if the internal implementation changes.\n"194"\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n"195"confirmation within conf_target blocks if possible. Uses virtual transaction size as\n"196"defined in BIP 141 (witness data is discounted).\n",197{198{"conf_target", RPCArg::Type::NUM, RPCArg::Optional::NO, "Confirmation target in blocks (1 - 1008)"},199{"threshold", RPCArg::Type::NUM, RPCArg::Default{0.95}, "The proportion of transactions in a given feerate range that must have been\n"200"confirmed within conf_target in order to consider those feerates as high enough and proceed to check\n"201"lower buckets."},202},203RPCResult{204RPCResult::Type::OBJ, "", "Results are returned for any horizon which tracks blocks up to the confirmation target",205{206{RPCResult::Type::OBJ, "short", /*optional=*/true, "estimate for short time horizon",207FeeEstimateHorizonDoc()},208{RPCResult::Type::OBJ, "medium", /*optional=*/true, "estimate for medium time horizon",209FeeEstimateHorizonDoc(/*elide=*/true)},210{RPCResult::Type::OBJ, "long", /*optional=*/true, "estimate for long time horizon",211FeeEstimateHorizonDoc(/*elide=*/true)},212}},213RPCExamples{214HelpExampleCli("estimaterawfee", "6 0.9")215},216[](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue217{218FeeRateEstimatorManager& fee_estimator_man = EnsureAnyFeeEstimatorMan(request.context);219const NodeContext& node = EnsureAnyNodeContext(request.context);221CHECK_NONFATAL(node.validation_signals)->SyncWithValidationInterfaceQueue();222unsigned int max_target = fee_estimator_man.MaximumTarget();223unsigned int conf_target = ParseConfirmTarget(request.params[0], max_target);224double threshold = 0.95;225if (!request.params[1].isNull()) {226threshold = request.params[1].get_real();227}228if (threshold < 0 || threshold > 1) {229throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid threshold");230}232UniValue result(UniValue::VOBJ);234for (const FeeEstimateHorizon horizon : ALL_FEE_ESTIMATE_HORIZONS) {235CFeeRate feeRate;236EstimationResult buckets;238// Only output results for horizons which track the target239if (conf_target > fee_estimator_man.BlockPolicyHighestTargetTracked(horizon)) continue;241feeRate = fee_estimator_man.BlockPolicyEstimateRawFee(conf_target, threshold, horizon, &buckets);242UniValue horizon_result(UniValue::VOBJ);243UniValue errors(UniValue::VARR);244UniValue passbucket(UniValue::VOBJ);245passbucket.pushKV("startrange", round(buckets.pass.start));246passbucket.pushKV("endrange", round(buckets.pass.end));247passbucket.pushKV("withintarget", round(buckets.pass.withinTarget * 100.0) / 100.0);248passbucket.pushKV("totalconfirmed", round(buckets.pass.totalConfirmed * 100.0) / 100.0);249passbucket.pushKV("inmempool", round(buckets.pass.inMempool * 100.0) / 100.0);250passbucket.pushKV("leftmempool", round(buckets.pass.leftMempool * 100.0) / 100.0);