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