Branch data Line data Source code
1 : : // Copyright (c) 2010 Satoshi Nakamoto
2 : : // Copyright (c) 2009-present 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 <rpc/register.h> // IWYU pragma: associated
7 : :
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/request.h>
17 : : #include <rpc/server.h>
18 : : #include <rpc/server_util.h>
19 : : #include <rpc/util.h>
20 : : #include <txmempool.h>
21 : : #include <univalue.h>
22 : : #include <util/check.h>
23 : : #include <util/expected.h>
24 : : #include <util/fees.h>
25 : : #include <validationinterface.h>
26 : :
27 : : #include <algorithm>
28 : : #include <array>
29 : : #include <cmath>
30 : : #include <map>
31 : : #include <string>
32 : : #include <string_view>
33 : : #include <utility>
34 : : #include <vector>
35 : :
36 : : using common::FeeModeFromString;
37 : : using common::FeeModesDetail;
38 : : using common::InvalidEstimateModeErrorMessage;
39 : : using node::NodeContext;
40 : :
41 : 81 : static RPCMethod estimatesmartfee()
42 : : {
43 : 81 : return RPCMethod{
44 : 81 : "estimatesmartfee",
45 [ + - ]: 162 : "Estimates the approximate fee per kilobyte needed for a transaction to begin\n"
46 : : "confirmation within conf_target blocks if possible and return the number of blocks\n"
47 : : "for which the estimate is valid. Uses virtual transaction size as defined\n"
48 : : "in BIP 141 (witness data is discounted).\n",
49 : : {
50 [ + - + - ]: 162 : {"conf_target", RPCArg::Type::NUM, RPCArg::Optional::NO, "Confirmation target in blocks (1 - 1008)"},
51 [ + - + - ]: 162 : {"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"economical"}, "The fee estimate mode.\n"
52 [ + - + - : 162 : + FeeModesDetail(std::string("default mode will be used"))},
+ - ]
53 [ + - + - ]: 162 : {"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
54 : : {
55 [ + - + - ]: 162 : {"fee_rate_estimator", RPCArg::Type::STR, RPCArg::Default{"none"},
56 [ + - ]: 162 : "Selects which fee rate estimator to use.\n"
57 : : "\"none\" returns the lower of the block policy and mempool estimates. If the mempool\n"
58 : : "estimate is unavailable, it returns that error instead of falling back to the block\n"
59 : : "policy estimate; use \"block_policy\" in that case to get the block policy estimate.\n"
60 : : "\"block_policy\" uses only the block policy fee rate estimator.\n"
61 : : "\"mempool_policy\" uses only the mempool fee rate estimator.\n"
62 : : "Unknown values are treated as \"none\"."},
63 [ + - + - ]: 162 : {"verbosity", RPCArg::Type::NUM, RPCArg::Default{1},
64 [ + - ]: 162 : "1 returns feerate or errors. 2 also returns \"mempool_health_statistics\"."},
65 : : },
66 : : },
67 : : },
68 [ + - ]: 162 : RPCResult{
69 [ + - + - ]: 162 : RPCResult::Type::OBJ, "", "",
70 : : {
71 [ + - + - ]: 162 : {RPCResult::Type::NUM, "feerate", /*optional=*/true, "estimate fee rate in " + CURRENCY_UNIT + "/kvB (only present if no errors were encountered)"},
72 [ + - + - ]: 162 : {RPCResult::Type::STR, "estimator", /*optional=*/true, "the fee estimator used to produce the result (only present for successful estimates when fee_rate_estimator is \"none\")"},
73 [ + - + - ]: 162 : {RPCResult::Type::ARR, "errors", /*optional=*/true, "Errors encountered during processing (if there are any)",
74 : : {
75 [ + - + - ]: 162 : {RPCResult::Type::STR, "", "error"},
76 : : }},
77 [ + - + - ]: 162 : {RPCResult::Type::NUM, "blocks", "the confirmation target in blocks for the returned fee rate estimate.\n"
78 : : "For the block policy fee rate estimator, this is the target the estimate was found at, clamped to at\n"
79 : : "least 2 and at most the estimator's maximum usable target. For the mempool fee rate\n"
80 : : "estimator, it is always 2."},
81 [ + - + - ]: 162 : {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)",
82 : : {
83 [ + - + - ]: 162 : {RPCResult::Type::OBJ, "", "",
84 : : {
85 [ + - + - ]: 162 : {RPCResult::Type::NUM, "block_height", "Block height"},
86 [ + - + - ]: 162 : {RPCResult::Type::NUM, "block_weight", "Total weight of non-coinbase transactions in the block"},
87 [ + - + - ]: 162 : {RPCResult::Type::NUM, "mempool_txs_weight", "Total weight of transactions removed from the mempool for this block"},
88 : : }},
89 : : }},
90 : 2025 : }},
[ + - + -
+ - + - +
- + + + +
+ + + + -
- - - - -
- - ]
91 : 81 : RPCExamples{
92 [ + - + - : 162 : HelpExampleCli("estimatesmartfee", "6") +
+ - ]
93 [ + - + - : 243 : HelpExampleRpc("estimatesmartfee", "6")
+ - + - ]
94 [ + - ]: 81 : },
95 : 81 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
96 : : {
97 : 1 : FeeRateEstimatorManager& fee_estimator_man = EnsureAnyFeeEstimatorMan(request.context);
98 : 0 : const NodeContext& node = EnsureAnyNodeContext(request.context);
99 : 0 : const CTxMemPool& mempool = EnsureMemPool(node);
100 : :
101 : 0 : CHECK_NONFATAL(mempool.m_opts.signals)->SyncWithValidationInterfaceQueue();
102 : 0 : unsigned int max_target = fee_estimator_man.MaximumTarget();
103 : 0 : unsigned int conf_target = ParseConfirmTarget(request.params[0], max_target);
104 : 0 : FeeEstimateMode fee_mode;
105 [ # # ]: 0 : if (!FeeModeFromString(self.Arg<std::string_view>("estimate_mode"), fee_mode)) {
106 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, InvalidEstimateModeErrorMessage());
107 : : }
108 [ # # ]: 0 : const UniValue options{request.params[2].isNull() ? UniValue::VOBJ : request.params[2]};
109 [ # # # # : 0 : RPCTypeCheckObj(options,
# # ]
110 : : {
111 [ # # ]: 0 : {"fee_rate_estimator", UniValueType(UniValue::VSTR)},
112 [ # # ]: 0 : {"verbosity", UniValueType(UniValue::VNUM)},
113 : : }, /*fAllowNull=*/true, /*fStrict=*/true);
114 [ # # ]: 0 : const auto fee_rate_estimator{FeeRateEstimatorTypeFromString(
115 : 0 : options["fee_rate_estimator"].isNull() ? "none" : options["fee_rate_estimator"].get_str())};
[ # # # #
# # # # #
# # # # #
# # # # #
# # # ]
116 : 0 : bool conservative{fee_mode == FeeEstimateMode::CONSERVATIVE};
117 [ # # # # : 0 : int verbosity{ParseVerbosity(options["verbosity"], /*default_verbosity=*/1, /*allow_bool=*/false)};
# # ]
118 : 0 : UniValue result(UniValue::VOBJ);
119 : 0 : UniValue errors(UniValue::VARR);
120 [ # # ]: 0 : const auto estimate{fee_estimator_man.GetFeeRateEstimate(fee_rate_estimator, conf_target, conservative)};
121 [ # # ]: 0 : if (estimate) {
122 [ # # ]: 0 : const CFeeRate min_mempool_feerate{mempool.GetMinFee()};
123 : 0 : const CFeeRate min_relay_feerate{mempool.m_opts.min_relay_feerate};
124 [ # # ]: 0 : const auto fee_rate{std::max({CFeeRate(estimate->feerate), min_mempool_feerate, min_relay_feerate})};
125 [ # # # # : 0 : result.pushKV("feerate", ValueFromAmount(fee_rate.GetFeePerK()));
# # ]
126 : : } else {
127 [ # # # # ]: 0 : errors.push_back(estimate.error().reason);
128 [ # # # # ]: 0 : result.pushKV("errors", std::move(errors));
129 : : }
130 [ # # # # ]: 0 : if (estimate && fee_rate_estimator == FeeRateEstimatorType::NONE) {
131 [ # # # # : 0 : result.pushKV("estimator", FeeRateEstimatorTypeToString(estimate->feerate_estimator));
# # # # ]
132 : : }
133 : 0 : const FeeRateEstimation& estimation{FeeRateEstimationRef(estimate)};
134 [ # # # # : 0 : result.pushKV("blocks", estimation.returned_target);
# # ]
135 [ # # ]: 0 : if (verbosity >= 2) {
136 : 0 : UniValue mempool_health_stats(UniValue::VARR);
137 [ # # ]: 0 : const auto blocks_data = fee_estimator_man.MempoolPolicyEstimatorBlocksStats();
138 [ # # ]: 0 : for (auto it = blocks_data.rbegin(); it != blocks_data.rend(); ++it) {
139 : 0 : UniValue entry(UniValue::VOBJ);
140 [ # # # # : 0 : entry.pushKV("block_height", it->m_height);
# # ]
141 [ # # # # : 0 : entry.pushKV("block_weight", it->m_block_weight);
# # ]
142 [ # # # # : 0 : entry.pushKV("mempool_txs_weight", it->m_removed_block_txs_weight);
# # ]
143 [ # # ]: 0 : mempool_health_stats.push_back(std::move(entry));
144 : 0 : }
145 [ # # # # ]: 0 : result.pushKV("mempool_health_statistics", std::move(mempool_health_stats));
146 : 0 : }
147 : 0 : return result;
148 [ # # # # : 0 : },
# # # # ]
149 : 891 : };
[ + - + -
+ - + + +
+ - - -
- ]
150 : 2430 : }
[ + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- - - - -
- - - - ]
151 : :
152 : 504 : static std::vector<RPCResult> FeeRateBucketDoc(bool elide = false)
153 : : {
154 : 504 : auto fields = std::vector<RPCResult>{
155 [ + - + - ]: 1008 : {RPCResult::Type::NUM, "startrange", "start of feerate range"},
156 [ + - + - ]: 1008 : {RPCResult::Type::NUM, "endrange", "end of feerate range"},
157 [ + - + - ]: 1008 : {RPCResult::Type::NUM, "withintarget", "number of txs over history horizon in the feerate range that were confirmed within target"},
158 [ + - + - ]: 1008 : {RPCResult::Type::NUM, "totalconfirmed", "number of txs over history horizon in the feerate range that were confirmed at any point"},
159 [ + - + - ]: 1008 : {RPCResult::Type::NUM, "inmempool", "current number of txs in mempool in the feerate range unconfirmed for at least target blocks"},
160 [ + - + - ]: 1008 : {RPCResult::Type::NUM, "leftmempool", "number of txs over history horizon in the feerate range that left mempool unconfirmed after target"},
161 [ + - + + : 7056 : };
- - ]
162 : 1260 : return elide ? ElideGroup(std::move(fields)) : fields;
[ + + + -
+ - + - +
+ - - ]
163 : 6552 : }
[ + - + -
+ - + - +
- + - -
- ]
164 : :
165 : 252 : static std::vector<RPCResult> FeeEstimateHorizonDoc(bool elide = false)
166 : : {
167 : 252 : auto fields = std::vector<RPCResult>{
168 [ + - + - ]: 504 : {RPCResult::Type::NUM, "feerate", /*optional=*/true, "estimate fee rate in " + CURRENCY_UNIT + "/kvB"},
169 [ + - + - ]: 504 : {RPCResult::Type::NUM, "decay", "exponential decay (per block) for historical moving average of confirmation data"},
170 [ + - + - ]: 504 : {RPCResult::Type::NUM, "scale", "The resolution of confirmation targets at this time horizon"},
171 [ + - + - : 504 : {RPCResult::Type::OBJ, "pass", /*optional=*/true, "information about the lowest range of feerates to succeed in meeting the threshold", FeeRateBucketDoc()},
+ - ]
172 [ + - + - : 504 : {RPCResult::Type::OBJ, "fail", /*optional=*/true, "information about the highest range of feerates to fail to meet the threshold", FeeRateBucketDoc(/*elide=*/true)},
+ - ]
173 [ + - + - ]: 504 : {RPCResult::Type::ARR, "errors", /*optional=*/true, "Errors encountered during processing (if there are any)",
174 : : {
175 [ + - + - ]: 504 : {RPCResult::Type::STR, "", "error"},
176 : : }},
177 : 3780 : };
[ + - + -
+ + + + -
- - - ]
178 : 672 : return elide ? ElideGroup(std::move(fields)) : fields;
[ + + + -
+ - + - +
+ - - ]
179 : 3780 : }
[ + - + -
+ - + - +
- + - + -
- - ]
180 : :
181 : 84 : static RPCMethod estimaterawfee()
182 : : {
183 : 84 : return RPCMethod{
184 : 84 : "estimaterawfee",
185 [ + - ]: 168 : "WARNING: This interface is unstable and may disappear or change!\n"
186 : : "\nWARNING: This is an advanced API call that is tightly coupled to the specific\n"
187 : : "implementation of fee estimation. The parameters it can be called with\n"
188 : : "and the results it returns will change if the internal implementation changes.\n"
189 : : "\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n"
190 : : "confirmation within conf_target blocks if possible. Uses virtual transaction size as\n"
191 : : "defined in BIP 141 (witness data is discounted).\n",
192 : : {
193 [ + - + - ]: 168 : {"conf_target", RPCArg::Type::NUM, RPCArg::Optional::NO, "Confirmation target in blocks (1 - 1008)"},
194 [ + - + - : 252 : {"threshold", RPCArg::Type::NUM, RPCArg::Default{0.95}, "The proportion of transactions in a given feerate range that must have been\n"
+ - ]
195 : : "confirmed within conf_target in order to consider those feerates as high enough and proceed to check\n"
196 : : "lower buckets."},
197 : : },
198 [ + - ]: 168 : RPCResult{
199 [ + - + - ]: 168 : RPCResult::Type::OBJ, "", "Results are returned for any horizon which tracks blocks up to the confirmation target",
200 : : {
201 [ + - + - ]: 168 : {RPCResult::Type::OBJ, "short", /*optional=*/true, "estimate for short time horizon",
202 [ + - ]: 168 : FeeEstimateHorizonDoc()},
203 [ + - + - ]: 168 : {RPCResult::Type::OBJ, "medium", /*optional=*/true, "estimate for medium time horizon",
204 [ + - ]: 168 : FeeEstimateHorizonDoc(/*elide=*/true)},
205 [ + - + - ]: 168 : {RPCResult::Type::OBJ, "long", /*optional=*/true, "estimate for long time horizon",
206 [ + - ]: 168 : FeeEstimateHorizonDoc(/*elide=*/true)},
207 [ + - + - : 420 : }},
+ + - - ]
208 : 84 : RPCExamples{
209 [ + - + - : 168 : HelpExampleCli("estimaterawfee", "6 0.9")
+ - ]
210 [ + - ]: 84 : },
211 : 84 : [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
212 : : {
213 : 1 : FeeRateEstimatorManager& fee_estimator_man = EnsureAnyFeeEstimatorMan(request.context);
214 : 0 : const NodeContext& node = EnsureAnyNodeContext(request.context);
215 : :
216 : 0 : CHECK_NONFATAL(node.validation_signals)->SyncWithValidationInterfaceQueue();
217 : 0 : unsigned int max_target = fee_estimator_man.MaximumTarget();
218 : 0 : unsigned int conf_target = ParseConfirmTarget(request.params[0], max_target);
219 : 0 : double threshold = 0.95;
220 [ # # ]: 0 : if (!request.params[1].isNull()) {
221 : 0 : threshold = request.params[1].get_real();
222 : : }
223 [ # # # # ]: 0 : if (threshold < 0 || threshold > 1) {
224 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid threshold");
225 : : }
226 : :
227 : 0 : UniValue result(UniValue::VOBJ);
228 : :
229 [ # # ]: 0 : for (const FeeEstimateHorizon horizon : ALL_FEE_ESTIMATE_HORIZONS) {
230 : 0 : CFeeRate feeRate;
231 : 0 : EstimationResult buckets;
232 : :
233 : : // Only output results for horizons which track the target
234 [ # # # # ]: 0 : if (conf_target > fee_estimator_man.BlockPolicyHighestTargetTracked(horizon)) continue;
235 : :
236 [ # # ]: 0 : feeRate = fee_estimator_man.BlockPolicyEstimateRawFee(conf_target, threshold, horizon, &buckets);
237 : 0 : UniValue horizon_result(UniValue::VOBJ);
238 : 0 : UniValue errors(UniValue::VARR);
239 : 0 : UniValue passbucket(UniValue::VOBJ);
240 [ # # # # : 0 : passbucket.pushKV("startrange", round(buckets.pass.start));
# # ]
241 [ # # # # : 0 : passbucket.pushKV("endrange", round(buckets.pass.end));
# # ]
242 [ # # # # : 0 : passbucket.pushKV("withintarget", round(buckets.pass.withinTarget * 100.0) / 100.0);
# # ]
243 [ # # # # : 0 : passbucket.pushKV("totalconfirmed", round(buckets.pass.totalConfirmed * 100.0) / 100.0);
# # ]
244 [ # # # # : 0 : passbucket.pushKV("inmempool", round(buckets.pass.inMempool * 100.0) / 100.0);
# # ]
245 [ # # # # : 0 : passbucket.pushKV("leftmempool", round(buckets.pass.leftMempool * 100.0) / 100.0);
# # ]
246 : 0 : UniValue failbucket(UniValue::VOBJ);
247 [ # # # # : 0 : failbucket.pushKV("startrange", round(buckets.fail.start));
# # ]
248 [ # # # # : 0 : failbucket.pushKV("endrange", round(buckets.fail.end));
# # ]
249 [ # # # # : 0 : failbucket.pushKV("withintarget", round(buckets.fail.withinTarget * 100.0) / 100.0);
# # ]
250 [ # # # # : 0 : failbucket.pushKV("totalconfirmed", round(buckets.fail.totalConfirmed * 100.0) / 100.0);
# # ]
251 [ # # # # : 0 : failbucket.pushKV("inmempool", round(buckets.fail.inMempool * 100.0) / 100.0);
# # ]
252 [ # # # # : 0 : failbucket.pushKV("leftmempool", round(buckets.fail.leftMempool * 100.0) / 100.0);
# # ]
253 : :
254 : : // CFeeRate(0) is used to indicate error as a return value from estimateRawFee
255 [ # # ]: 0 : if (feeRate != CFeeRate(0)) {
256 [ # # # # : 0 : horizon_result.pushKV("feerate", ValueFromAmount(feeRate.GetFeePerK()));
# # ]
257 [ # # # # : 0 : horizon_result.pushKV("decay", buckets.decay);
# # ]
258 [ # # # # : 0 : horizon_result.pushKV("scale", buckets.scale);
# # ]
259 [ # # # # ]: 0 : horizon_result.pushKV("pass", std::move(passbucket));
260 : : // buckets.fail.start == -1 indicates that all buckets passed, there is no fail bucket to output
261 [ # # # # : 0 : if (buckets.fail.start != -1) horizon_result.pushKV("fail", std::move(failbucket));
# # ]
262 : : } else {
263 : : // Output only information that is still meaningful in the event of error
264 [ # # # # : 0 : horizon_result.pushKV("decay", buckets.decay);
# # ]
265 [ # # # # : 0 : horizon_result.pushKV("scale", buckets.scale);
# # ]
266 [ # # # # ]: 0 : horizon_result.pushKV("fail", std::move(failbucket));
267 [ # # # # ]: 0 : errors.push_back("Insufficient data or no feerate found which meets threshold");
268 [ # # # # ]: 0 : horizon_result.pushKV("errors", std::move(errors));
269 : : }
270 [ # # # # ]: 0 : result.pushKV(StringForFeeEstimateHorizon(horizon), std::move(horizon_result));
271 : 0 : }
272 : 0 : return result;
273 : 0 : },
274 [ + - + - : 504 : };
+ + - - ]
275 : 840 : }
[ + - + -
+ - + - +
- - - -
- ]
276 : :
277 : 32 : void RegisterFeeRPCCommands(CRPCTable& t)
278 : : {
279 : 32 : static const CRPCCommand commands[]{
280 [ + - ]: 60 : {"util", &estimatesmartfee},
281 [ + - ]: 60 : {"hidden", &estimaterawfee},
282 [ + + + - : 92 : };
+ - + - -
- ]
283 [ + + ]: 96 : for (const auto& c : commands) {
284 : 64 : t.appendCommand(c.name, &c);
285 : : }
286 : 32 : }
|