LCOV - code coverage report
Current view: top level - src/rpc - fees.cpp (source / functions) Coverage Total Hit
Test: test_bitcoin_coverage.info Lines: 28.0 % 107 30
Test Date: 2024-11-04 04:45:35 Functions: 60.0 % 5 3
Branches: 31.9 % 542 173

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

Generated by: LCOV version 2.0-1