LCOV - code coverage report
Current view: top level - src/rpc - fees.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 137 140 97.9 %
Date: 2026-06-25 07:23:43 Functions: 6 6 100.0 %

          Line data    Source code
       1             : // Copyright (c) 2010 Satoshi Nakamoto
       2             : // Copyright (c) 2009-2021 The Bitcoin Core developers
       3             : // Distributed under the MIT software license, see the accompanying
       4             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       5             : 
       6             : #include <core_io.h>
       7             : #include <node/context.h>
       8             : #include <policy/feerate.h>
       9             : #include <policy/fees.h>
      10             : #include <policy/policy.h>
      11             : #include <policy/settings.h>
      12             : #include <rpc/protocol.h>
      13             : #include <rpc/request.h>
      14             : #include <rpc/server.h>
      15             : #include <rpc/server_util.h>
      16             : #include <rpc/util.h>
      17             : #include <txmempool.h>
      18             : #include <univalue.h>
      19             : #include <util/fees.h>
      20             : #include <util/system.h>
      21             : 
      22             : #include <algorithm>
      23             : #include <array>
      24             : #include <cmath>
      25             : #include <list>
      26             : #include <string>
      27             : #include <vector>
      28             : 
      29             : using node::NodeContext;
      30             : 
      31        6478 : static RPCHelpMan estimatesmartfee()
      32             : {
      33       12956 :     return RPCHelpMan{"estimatesmartfee",
      34        6478 :         "\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n"
      35             :         "confirmation within conf_target blocks if possible and return the number of blocks\n"
      36             :         "for which the estimate is valid.\n",
      37       19434 :         {
      38        6478 :             {"conf_target", RPCArg::Type::NUM, RPCArg::Optional::NO, "Confirmation target in blocks (1 - 1008)"},
      39       12956 :             {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"conservative"}, "The fee estimate mode.\n"
      40             :             "Whether to return a more conservative estimate which also satisfies\n"
      41             :             "a longer history. A conservative estimate potentially returns a\n"
      42             :             "higher feerate and is more likely to be sufficient for the desired\n"
      43             :             "target, but is not as responsive to short term drops in the\n"
      44             :             "prevailing fee market. Must be one of (case insensitive):\n"
      45        6478 :              "\"" + FeeModes("\"\n\"") + "\""},
      46             :         },
      47        6478 :         RPCResult{
      48        6478 :             RPCResult::Type::OBJ, "", "",
      49       25912 :             {
      50        6478 :                 {RPCResult::Type::NUM, "feerate", /*optional=*/true, "estimate fee rate in " + CURRENCY_UNIT + "/kB (only present if no errors were encountered)"},
      51       12956 :                 {RPCResult::Type::ARR, "errors", /*optional=*/true, "Errors encountered during processing (if there are any)",
      52       12956 :                     {
      53        6478 :                         {RPCResult::Type::STR, "", "error"},
      54             :                     }},
      55        6478 :                 {RPCResult::Type::NUM, "blocks", "block number where estimate was found\n"
      56             :                 "The request target will be clamped between 2 and the highest target\n"
      57             :                 "fee estimation is able to return based on how long it has been running.\n"
      58             :                 "An error is returned if not enough transactions and blocks\n"
      59             :                 "have been observed to make an estimate for any number of blocks."},
      60             :         }},
      61        6478 :         RPCExamples{
      62       12956 :             HelpExampleCli("estimatesmartfee", "6") +
      63        6478 :             HelpExampleRpc("estimatesmartfee", "6")
      64             :         },
      65        6794 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
      66             :         {
      67         318 :             RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VSTR});
      68             : 
      69         312 :             CBlockPolicyEstimator& fee_estimator = EnsureAnyFeeEstimator(request.context);
      70         312 :             const NodeContext& node = EnsureAnyNodeContext(request.context);
      71         312 :             const CTxMemPool& mempool = EnsureMemPool(node);
      72             : 
      73         312 :             unsigned int max_target = fee_estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
      74         312 :             unsigned int conf_target = ParseConfirmTarget(request.params[0], max_target);
      75         312 :             bool conservative = true;
      76         312 :             if (!request.params[1].isNull()) {
      77             :                 FeeEstimateMode fee_mode;
      78           8 :                 if (!FeeModeFromString(request.params[1].get_str(), fee_mode)) {
      79           2 :                     throw JSONRPCError(RPC_INVALID_PARAMETER, InvalidEstimateModeErrorMessage());
      80             :                 }
      81           6 :                 if (fee_mode == FeeEstimateMode::ECONOMICAL) conservative = false;
      82           6 :             }
      83             : 
      84         310 :             UniValue result(UniValue::VOBJ);
      85         310 :             UniValue errors(UniValue::VARR);
      86         310 :             FeeCalculation feeCalc;
      87         310 :             CFeeRate feeRate{fee_estimator.estimateSmartFee(conf_target, &feeCalc, conservative)};
      88         310 :             if (feeRate != CFeeRate(0)) {
      89         302 :                 CFeeRate min_mempool_feerate{mempool.GetMinFee(gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000)};
      90         302 :                 CFeeRate min_relay_feerate{::minRelayTxFee};
      91         302 :                 feeRate = std::max({feeRate, min_mempool_feerate, min_relay_feerate});
      92         302 :                 result.pushKV("feerate", ValueFromAmount(feeRate.GetFeePerK()));
      93         302 :             } else {
      94           8 :                 errors.push_back("Insufficient data or no feerate found");
      95           8 :                 result.pushKV("errors", errors);
      96             :             }
      97         310 :             result.pushKV("blocks", feeCalc.returnedTarget);
      98         310 :             return result;
      99         316 :         },
     100             :     };
     101           0 : }
     102             : 
     103        6456 : static RPCHelpMan estimaterawfee()
     104             : {
     105       12912 :     return RPCHelpMan{"estimaterawfee",
     106        6456 :         "\nWARNING: This interface is unstable and may disappear or change!\n"
     107             :         "\nWARNING: This is an advanced API call that is tightly coupled to the specific\n"
     108             :         "implementation of fee estimation. The parameters it can be called with\n"
     109             :         "and the results it returns will change if the internal implementation changes.\n"
     110             :         "\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n"
     111             :         "confirmation within conf_target blocks if possible.\n",
     112       19368 :         {
     113        6456 :             {"conf_target", RPCArg::Type::NUM, RPCArg::Optional::NO, "Confirmation target in blocks (1 - 1008)"},
     114        6456 :             {"threshold", RPCArg::Type::NUM, RPCArg::Default{0.95}, "The proportion of transactions in a given feerate range that must have been\n"
     115             :             "confirmed within conf_target in order to consider those feerates as high enough and proceed to check\n"
     116             :             "lower buckets."},
     117             :         },
     118        6456 :         RPCResult{
     119        6456 :             RPCResult::Type::OBJ, "", "Results are returned for any horizon which tracks blocks up to the confirmation target",
     120       25824 :             {
     121       12912 :                 {RPCResult::Type::OBJ, "short", /*optional=*/true, "estimate for short time horizon",
     122       45192 :                     {
     123        6456 :                         {RPCResult::Type::NUM, "feerate", /*optional=*/true, "estimate fee rate in " + CURRENCY_UNIT + "/kB"},
     124        6456 :                         {RPCResult::Type::NUM, "decay", "exponential decay (per block) for historical moving average of confirmation data"},
     125        6456 :                         {RPCResult::Type::NUM, "scale", "The resolution of confirmation targets at this time horizon"},
     126       12912 :                         {RPCResult::Type::OBJ, "pass", /*optional=*/true, "information about the lowest range of feerates to succeed in meeting the threshold",
     127       45192 :                         {
     128        6456 :                                 {RPCResult::Type::NUM, "startrange", "start of feerate range"},
     129        6456 :                                 {RPCResult::Type::NUM, "endrange", "end of feerate range"},
     130        6456 :                                 {RPCResult::Type::NUM, "withintarget", "number of txs over history horizon in the feerate range that were confirmed within target"},
     131        6456 :                                 {RPCResult::Type::NUM, "totalconfirmed", "number of txs over history horizon in the feerate range that were confirmed at any point"},
     132        6456 :                                 {RPCResult::Type::NUM, "inmempool", "current number of txs in mempool in the feerate range unconfirmed for at least target blocks"},
     133        6456 :                                 {RPCResult::Type::NUM, "leftmempool", "number of txs over history horizon in the feerate range that left mempool unconfirmed after target"},
     134             :                         }},
     135       12912 :                         {RPCResult::Type::OBJ, "fail", /*optional=*/true, "information about the highest range of feerates to fail to meet the threshold",
     136       12912 :                         {
     137        6456 :                             {RPCResult::Type::ELISION, "", ""},
     138             :                         }},
     139       12912 :                         {RPCResult::Type::ARR, "errors", /*optional=*/true, "Errors encountered during processing (if there are any)",
     140       12912 :                         {
     141        6456 :                             {RPCResult::Type::STR, "error", ""},
     142             :                         }},
     143             :                 }},
     144       12912 :                 {RPCResult::Type::OBJ, "medium", /*optional=*/true, "estimate for medium time horizon",
     145       12912 :                 {
     146        6456 :                     {RPCResult::Type::ELISION, "", ""},
     147             :                 }},
     148       12912 :                 {RPCResult::Type::OBJ, "long", /*optional=*/true, "estimate for long time horizon",
     149       12912 :                 {
     150        6456 :                     {RPCResult::Type::ELISION, "", ""},
     151             :                 }},
     152             :             }},
     153        6456 :         RPCExamples{
     154        6456 :             HelpExampleCli("estimaterawfee", "6 0.9")
     155             :         },
     156        6768 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     157             :         {
     158         312 :             RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VNUM}, true);
     159             : 
     160         308 :             CBlockPolicyEstimator& fee_estimator = EnsureAnyFeeEstimator(request.context);
     161             : 
     162         308 :             unsigned int max_target = fee_estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
     163         308 :             unsigned int conf_target = ParseConfirmTarget(request.params[0], max_target);
     164         308 :             double threshold = 0.95;
     165         308 :             if (!request.params[1].isNull()) {
     166           2 :                 threshold = request.params[1].get_real();
     167           2 :             }
     168         308 :             if (threshold < 0 || threshold > 1) {
     169           0 :                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid threshold");
     170             :             }
     171             : 
     172         308 :             UniValue result(UniValue::VOBJ);
     173             : 
     174        1226 :             for (const FeeEstimateHorizon horizon : ALL_FEE_ESTIMATE_HORIZONS) {
     175         918 :                 CFeeRate feeRate;
     176         918 :                 EstimationResult buckets;
     177             : 
     178             :                 // Only output results for horizons which track the target
     179         918 :                 if (conf_target > fee_estimator.HighestTargetTracked(horizon)) continue;
     180             : 
     181         762 :                 feeRate = fee_estimator.estimateRawFee(conf_target, threshold, horizon, &buckets);
     182         762 :                 UniValue horizon_result(UniValue::VOBJ);
     183         762 :                 UniValue errors(UniValue::VARR);
     184         762 :                 UniValue passbucket(UniValue::VOBJ);
     185         762 :                 passbucket.pushKV("startrange", round(buckets.pass.start));
     186         762 :                 passbucket.pushKV("endrange", round(buckets.pass.end));
     187         762 :                 passbucket.pushKV("withintarget", round(buckets.pass.withinTarget * 100.0) / 100.0);
     188         762 :                 passbucket.pushKV("totalconfirmed", round(buckets.pass.totalConfirmed * 100.0) / 100.0);
     189         762 :                 passbucket.pushKV("inmempool", round(buckets.pass.inMempool * 100.0) / 100.0);
     190         762 :                 passbucket.pushKV("leftmempool", round(buckets.pass.leftMempool * 100.0) / 100.0);
     191         762 :                 UniValue failbucket(UniValue::VOBJ);
     192         762 :                 failbucket.pushKV("startrange", round(buckets.fail.start));
     193         762 :                 failbucket.pushKV("endrange", round(buckets.fail.end));
     194         762 :                 failbucket.pushKV("withintarget", round(buckets.fail.withinTarget * 100.0) / 100.0);
     195         762 :                 failbucket.pushKV("totalconfirmed", round(buckets.fail.totalConfirmed * 100.0) / 100.0);
     196         762 :                 failbucket.pushKV("inmempool", round(buckets.fail.inMempool * 100.0) / 100.0);
     197         762 :                 failbucket.pushKV("leftmempool", round(buckets.fail.leftMempool * 100.0) / 100.0);
     198             : 
     199             :                 // CFeeRate(0) is used to indicate error as a return value from estimateRawFee
     200         762 :                 if (feeRate != CFeeRate(0)) {
     201         744 :                     horizon_result.pushKV("feerate", ValueFromAmount(feeRate.GetFeePerK()));
     202         744 :                     horizon_result.pushKV("decay", buckets.decay);
     203         744 :                     horizon_result.pushKV("scale", (int)buckets.scale);
     204         744 :                     horizon_result.pushKV("pass", passbucket);
     205             :                     // buckets.fail.start == -1 indicates that all buckets passed, there is no fail bucket to output
     206         744 :                     if (buckets.fail.start != -1) horizon_result.pushKV("fail", failbucket);
     207         744 :                 } else {
     208             :                     // Output only information that is still meaningful in the event of error
     209          18 :                     horizon_result.pushKV("decay", buckets.decay);
     210          18 :                     horizon_result.pushKV("scale", (int)buckets.scale);
     211          18 :                     horizon_result.pushKV("fail", failbucket);
     212          18 :                     errors.push_back("Insufficient data or no feerate found which meets threshold");
     213          18 :                     horizon_result.pushKV("errors", errors);
     214             :                 }
     215         762 :                 result.pushKV(StringForFeeEstimateHorizon(horizon), horizon_result);
     216         762 :             }
     217         308 :             return result;
     218         312 :         },
     219             :     };
     220           0 : }
     221             : 
     222        3201 : void RegisterFeeRPCCommands(CRPCTable& t)
     223             : {
     224        9339 :     static const CRPCCommand commands[]{
     225        3069 :         {"util", &estimatesmartfee},
     226        3069 :         {"hidden", &estimaterawfee},
     227             :     };
     228        9603 :     for (const auto& c : commands) {
     229        6402 :         t.appendCommand(c.name, &c);
     230             :     }
     231        3201 : }

Generated by: LCOV version 1.16