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