Join GitHub today
GitHub is home to over 20 million developers working together to host and review code, manage projects, and build software together.
Change API to estimaterawfee #10543
Conversation
fanquake
added the
RPC/REST/ZMQ
label
Jun 6, 2017
fanquake
added this to the
0.15.0
milestone
Jun 6, 2017
|
utACK I prefer this API. I'll compile it and play with it a bit later today. Thanks for doing this |
|
My reason for asking this is that it would allow someone to gather information using this RPC, without needing implementation-specific information. Sure, they'll need to understand the details to process the output anyway, but not the just collect. |
|
ACK. Seems to work well, and easier to log/use
|
|
Concept ACK |
|
concept ACK as well. |
|
Concept ACK; this will reduce the number of RPC calls we need to make to get a full picture of the fee market. |
|
rebased due to adjacent line change in src/rpc/client.cpp |
|
I've been running this patch for the last week on https://www.estimatefee.com to come up with estimates for how long a specific transaction will take to confirm. Haven't had any problems, and it works well and offers a nicer API. So ACK'ing again |
While we're changing the interface anyway, would it make sense to change the "information absent" response to something else than |
ACK |
|
utACK 7780de8 irrespective of if the "information absent" response is changed. |
|
OK Updated to change error reporting and information absent
When an answer can't be returned due to no fee rate meeting the threshold at the target (pass bucket is omitted but fail bucket still contains useful information):
When the target is not supported at a given horizon, the result is just omitted. Also if all feerates pass, the fail bucket is omitted:
When there is insufficient data to even calculate whether the target is met (pass bucket is omitted but fail bucket still contains useful information): *hacked to require many more data points for med and long horizons
|
laanwj
added
to Blockers in High-priority for review
Jun 29, 2017
| @@ -876,7 +876,7 @@ UniValue estimatesmartfee(const JSONRPCRequest& request) | ||
| UniValue estimaterawfee(const JSONRPCRequest& request) | ||
| { | ||
| - if (request.fHelp || request.params.size() < 1|| request.params.size() > 3) | ||
| + if (request.fHelp || request.params.size() < 1|| request.params.size() > 2) |
| + for (FeeEstimateHorizon horizon : {FeeEstimateHorizon::SHORT_HALFLIFE, FeeEstimateHorizon::MED_HALFLIFE, FeeEstimateHorizon::LONG_HALFLIFE}) { | ||
| + CFeeRate feeRate; | ||
| + EstimationResult buckets; | ||
| + if ((unsigned int)nBlocks <= ::feeEstimator.HighestTargetTracked(horizon)) { |
promag
Jul 6, 2017
Contributor
What about early continue; to avoid large indentation:
if ((unsigned int)nBlocks > ::feeEstimator.HighestTargetTracked(horizon)) continue;| + failbucket.push_back(Pair("totalconfirmed", round(buckets.fail.totalConfirmed * 100.0) / 100.0)); | ||
| + failbucket.push_back(Pair("inmempool", round(buckets.fail.inMempool * 100.0) / 100.0)); | ||
| + failbucket.push_back(Pair("leftmempool", round(buckets.fail.leftMempool * 100.0) / 100.0)); | ||
| + if (!(feeRate == CFeeRate(0))) { |
morcos
Jul 7, 2017
Contributor
I like having the success condition be the first thing you read, but I'll add a comment
|
Addressed nits and squashed |
| @@ -876,7 +876,7 @@ UniValue estimatesmartfee(const JSONRPCRequest& request) | ||
| UniValue estimaterawfee(const JSONRPCRequest& request) | ||
| { | ||
| - if (request.fHelp || request.params.size() < 1|| request.params.size() > 3) | ||
| + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) | ||
| throw std::runtime_error( | ||
| "estimaterawfee nblocks (threshold horizon)\n" |
| + " \"totalconfirmed\" : x.x, (numeric) number of txs over history horizon in the feerate range that were confirmed at any point\n" | ||
| + " \"inmempool\" : x.x, (numeric) current number of txs in mempool in the feerate range unconfirmed for at least target blocks\n" | ||
| + " \"leftmempool\" : x.x, (numeric) number of txs over history horizon in the feerate range that left mempool unconfirmed after target\n" | ||
| + " }\n" |
| - failbucket.push_back(Pair("inmempool", round(buckets.fail.inMempool * 100.0) / 100.0)); | ||
| - failbucket.push_back(Pair("leftmempool", round(buckets.fail.leftMempool * 100.0) / 100.0)); | ||
| - result.push_back(Pair("fail", failbucket)); | ||
| + const char* horizonNames[] = {"short", "medium", "long"}; |
| + if ((unsigned int)nBlocks > ::feeEstimator.HighestTargetTracked(horizon)) continue; | ||
| + | ||
| + feeRate = ::feeEstimator.estimateRawFee(nBlocks, threshold, horizon, &buckets); | ||
| + UniValue horizonresult(UniValue::VOBJ); |
| + UniValue passbucket(UniValue::VOBJ); | ||
| + passbucket.push_back(Pair("startrange", round(buckets.pass.start))); | ||
| + passbucket.push_back(Pair("endrange", round(buckets.pass.end))); | ||
| + passbucket.push_back(Pair("withintarget", round(buckets.pass.withinTarget * 100.0) / 100.0)); |
promag
Jul 7, 2017
Contributor
Should we make these round with 2 decimal places as strings? See https://stackoverflow.com/a/1343925.
morcos
Jul 7, 2017
Contributor
This RPC is kind of a low level debugging function anyway, so I might just save that for some later PR if people want it.
| + failbucket.push_back(Pair("leftmempool", round(buckets.fail.leftMempool * 100.0) / 100.0)); | ||
| + | ||
| + // CFeeRate(0) is used to indicate error as a return value from estimateRawFee | ||
| + if (!(feeRate == CFeeRate(0))) { |
promag
Jul 7, 2017
Contributor
Maybe for another PR, but IMO operator!= would be more expressive, no?
morcos
Jul 7, 2017
Contributor
Yeah I should have just added that operator before, since this has annoyed me a couple of times.
|
Addressed nits and squashed Thanks for review @promag ! |
| @@ -40,6 +40,7 @@ class CFeeRate | ||
| friend bool operator==(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK == b.nSatoshisPerK; } | ||
| friend bool operator<=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK <= b.nSatoshisPerK; } | ||
| friend bool operator>=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK >= b.nSatoshisPerK; } | ||
| + friend bool operator!=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK != b.nSatoshisPerK; } |
| @@ -190,6 +190,9 @@ class CBlockPolicyEstimator | ||
| /** Empty mempool transactions on shutdown to record failure to confirm for txs still in mempool */ | ||
| void FlushUnconfirmed(CTxMemPool& pool); | ||
| + /** Calculation of highest target that estimates are tracked for */ | ||
| + unsigned int HighestTargetTracked(FeeEstimateHorizon horizon) const; |
promag
Jul 7, 2017
Contributor
Since nBlocks and also CBlockIndex::nHeight are int, why not return int here too?
morcos
Jul 7, 2017
Contributor
Yeah I thought about that, but I figure it should really be an unsigned int, and so why not start moving things slowly in right direction.
promag
Jul 7, 2017
Contributor
Best practice? Not network code but still. See https://stackoverflow.com/a/21621533.
| + EstimationResult buckets; | ||
| + | ||
| + // Only output results for horizons which track the target | ||
| + if ((unsigned int)nBlocks > ::feeEstimator.HighestTargetTracked(horizon)) continue; |
promag
Jul 7, 2017
Contributor
This omits the given horizon key from the output. Is this considered best practice for an API? An alternative is to return an array:
[{ "horizon": "short", "feerate": 0.00068495, ... }]
morcos
Jul 7, 2017
Contributor
Yes it's omitted because it is not meaningful to return an answer for a target higher than the horizon tracks.
promag
Jul 7, 2017
Contributor
Hence the suggestion to return the array, a client iterates each element (if any) and uses the horizon value.
morcos
Jul 7, 2017
Contributor
Oh sorry, I misunderstood at first. I suppose I'm not familiar enough with JSON practices to know what would be preferred.
| + feeRate = ::feeEstimator.estimateRawFee(nBlocks, threshold, horizon, &buckets); | ||
| + UniValue horizon_result(UniValue::VOBJ); | ||
| + UniValue errors(UniValue::VARR); | ||
| + UniValue passbucket(UniValue::VOBJ); |
morcos
Jul 7, 2017
Contributor
yeah i just fixed variables introduced in this PR, instead of changing preexisting variables
morcos
Jul 7, 2017
Contributor
Maybe in this case it could have been since I touched a lot of the code, but I think in general it's easier not to make more changes than necessary in order to facilitate the review.
| + horizon_result.push_back(Pair("scale", (int)buckets.scale)); | ||
| + horizon_result.push_back(Pair("fail", failbucket)); | ||
| + errors.push_back("Insufficient data or no feerate found which meets threshold"); | ||
| + horizon_result.push_back(Pair("errors",errors)); |
morcos
Jul 7, 2017
Contributor
will fix if there are any more changes, but leave for now to avoid churn.
|
tACK 6dc1410 |
|
re-utACK 6dc1410 |
ryanofsky
reviewed
Jul 10, 2017
utACK 6dc1410. Left comments but please ignore any suggestions you think are not worth implementing here.
| + failbucket.push_back(Pair("leftmempool", round(buckets.fail.leftMempool * 100.0) / 100.0)); | ||
| + horizon_result.push_back(Pair("fail", failbucket)); | ||
| + } | ||
| + result.push_back(Pair(horizon_names[horizon], horizon_result)); |
ryanofsky
Jul 10, 2017
Contributor
In commit "Change API to estimaterawfee"
This is pretty fragile. Consider replacing horizon_names array with FeeEstimateHorizon -> std::string function, or at adding a comment to FeeEstimateHorizon saying the enum values are used as array indices and need to be stable.
| + return longStats->GetMaxConfirms(); | ||
| + } | ||
| + default: { | ||
| + return 0; |
ryanofsky
Jul 10, 2017
Contributor
In commit "Add function to report highest estimate target tracked per horizon"
Seems like it would be safer to throw an error than to rely on code handling this to do something with this 0.
|
Addressed comments (rawpi.ver4) -> (rawapi.ver4.squash) -> 5e3b7b5 (rawapi.ver4.rebase) |
laanwj
merged commit 5e3b7b5
into
bitcoin:master
Jul 11, 2017
1 check passed
laanwj
added a commit
that referenced
this pull request
Jul 11, 2017
|
|
laanwj |
b27b004
|
ryanofsky
reviewed
Jul 11, 2017
utACK 5e3b7b5. Same as previous review with the two suggested changes.
laanwj
removed
from Blockers in High-priority for review
Jul 11, 2017
sipa
added a commit
that referenced
this pull request
Jul 17, 2017
|
|
sipa |
75b5643
|
morcos commentedJun 6, 2017
Report results for all 3 possible time horizons instead of specifying time horizon as an argument.
@sipa requested this. I'm indifferent, but we should merge this for 0.15 if it's considered a better way to present the information, before the old api is released.
@instagibbs @RHavar both found the old interface a bit unintuitive
@jlopp any thoughts?