Improve error reporting for estimaterawfee

This commit is contained in:
Alex Morcos 2017-06-28 10:50:32 -04:00
parent 1fafd704da
commit 5e3b7b5686

View file

@ -849,17 +849,17 @@ UniValue estimaterawfee(const JSONRPCRequest& request)
"confirmation within nblocks blocks if possible. Uses virtual transaction size as defined\n" "confirmation within nblocks blocks if possible. Uses virtual transaction size as defined\n"
"in BIP 141 (witness data is discounted).\n" "in BIP 141 (witness data is discounted).\n"
"\nArguments:\n" "\nArguments:\n"
"1. nblocks (numeric)\n" "1. nblocks (numeric) Confirmation target in blocks (1 - 1008)\n"
"2. threshold (numeric, optional) The proportion of transactions in a given feerate range that must have been\n" "2. threshold (numeric, optional) The proportion of transactions in a given feerate range that must have been\n"
" confirmed within nblocks in order to consider those feerates as high enough and proceed to check\n" " confirmed within nblocks in order to consider those feerates as high enough and proceed to check\n"
" lower buckets. Default: 0.95\n" " lower buckets. Default: 0.95\n"
"\nResult:\n" "\nResult:\n"
"{\n" "{\n"
" \"short\" : { (json object) estimate for short time horizon\n" " \"short\" : { (json object, optional) estimate for short time horizon\n"
" \"feerate\" : x.x, (numeric) estimate fee-per-kilobyte (in BTC)\n" " \"feerate\" : x.x, (numeric, optional) estimate fee-per-kilobyte (in BTC)\n"
" \"decay\" : x.x, (numeric) exponential decay (per block) for historical moving average of confirmation data\n" " \"decay\" : x.x, (numeric) exponential decay (per block) for historical moving average of confirmation data\n"
" \"scale\" : x, (numeric) The resolution of confirmation targets at this time horizon\n" " \"scale\" : x, (numeric) The resolution of confirmation targets at this time horizon\n"
" \"pass\" : { (json object) information about the lowest range of feerates to succeed in meeting the threshold\n" " \"pass\" : { (json object, optional) information about the lowest range of feerates to succeed in meeting the threshold\n"
" \"startrange\" : x.x, (numeric) start of feerate range\n" " \"startrange\" : x.x, (numeric) start of feerate range\n"
" \"endrange\" : x.x, (numeric) end of feerate range\n" " \"endrange\" : x.x, (numeric) end of feerate range\n"
" \"withintarget\" : x.x, (numeric) number of txs over history horizon in the feerate range that were confirmed within target\n" " \"withintarget\" : x.x, (numeric) number of txs over history horizon in the feerate range that were confirmed within target\n"
@ -867,13 +867,14 @@ UniValue estimaterawfee(const JSONRPCRequest& request)
" \"inmempool\" : x.x, (numeric) current number of txs in mempool in the feerate range unconfirmed for at least target blocks\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" " \"leftmempool\" : x.x, (numeric) number of txs over history horizon in the feerate range that left mempool unconfirmed after target\n"
" },\n" " },\n"
" \"fail\" : { ... }, (json object) information about the highest range of feerates to fail to meet the threshold\n" " \"fail\" : { ... }, (json object, optional) information about the highest range of feerates to fail to meet the threshold\n"
" \"errors\": [ str... ] (json array of strings, optional) Errors encountered during processing\n"
" },\n" " },\n"
" \"medium\" : { ... }, (json object) estimate for medium time horizon\n" " \"medium\" : { ... }, (json object, optional) estimate for medium time horizon\n"
" \"long\" : { ... } (json object) estimate for long time horizon\n" " \"long\" : { ... } (json object) estimate for long time horizon\n"
"}\n" "}\n"
"\n" "\n"
"A negative feerate is returned if no answer can be given.\n" "Results are returned for any horizon which tracks blocks up to the confirmation target.\n"
"\nExample:\n" "\nExample:\n"
+ HelpExampleCli("estimaterawfee", "6 0.9") + HelpExampleCli("estimaterawfee", "6 0.9")
); );
@ -881,39 +882,59 @@ UniValue estimaterawfee(const JSONRPCRequest& request)
RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VNUM, UniValue::VNUM}, true); RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VNUM, UniValue::VNUM}, true);
RPCTypeCheckArgument(request.params[0], UniValue::VNUM); RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
int nBlocks = request.params[0].get_int(); int nBlocks = request.params[0].get_int();
if (nBlocks < 1 || (unsigned int)nBlocks > ::feeEstimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid nblocks");
}
double threshold = 0.95; double threshold = 0.95;
if (!request.params[1].isNull()) { if (!request.params[1].isNull()) {
threshold = request.params[1].get_real(); threshold = request.params[1].get_real();
} }
if (threshold < 0 || threshold > 1) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid threshold");
}
UniValue result(UniValue::VOBJ); UniValue result(UniValue::VOBJ);
for (FeeEstimateHorizon horizon : {FeeEstimateHorizon::SHORT_HALFLIFE, FeeEstimateHorizon::MED_HALFLIFE, FeeEstimateHorizon::LONG_HALFLIFE}) { for (FeeEstimateHorizon horizon : {FeeEstimateHorizon::SHORT_HALFLIFE, FeeEstimateHorizon::MED_HALFLIFE, FeeEstimateHorizon::LONG_HALFLIFE}) {
CFeeRate feeRate; CFeeRate feeRate;
EstimationResult buckets; EstimationResult buckets;
feeRate = ::feeEstimator.estimateRawFee(nBlocks, threshold, horizon, &buckets);
// Only output results for horizons which track the target
if ((unsigned int)nBlocks > ::feeEstimator.HighestTargetTracked(horizon)) continue;
feeRate = ::feeEstimator.estimateRawFee(nBlocks, threshold, horizon, &buckets);
UniValue horizon_result(UniValue::VOBJ); UniValue horizon_result(UniValue::VOBJ);
horizon_result.push_back(Pair("feerate", feeRate == CFeeRate(0) ? -1.0 : ValueFromAmount(feeRate.GetFeePerK()))); UniValue errors(UniValue::VARR);
if (!(feeRate == CFeeRate(0))) { 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));
passbucket.push_back(Pair("totalconfirmed", round(buckets.pass.totalConfirmed * 100.0) / 100.0));
passbucket.push_back(Pair("inmempool", round(buckets.pass.inMempool * 100.0) / 100.0));
passbucket.push_back(Pair("leftmempool", round(buckets.pass.leftMempool * 100.0) / 100.0));
UniValue failbucket(UniValue::VOBJ);
failbucket.push_back(Pair("startrange", round(buckets.fail.start)));
failbucket.push_back(Pair("endrange", round(buckets.fail.end)));
failbucket.push_back(Pair("withintarget", round(buckets.fail.withinTarget * 100.0) / 100.0));
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));
// CFeeRate(0) is used to indicate error as a return value from estimateRawFee
if (feeRate != CFeeRate(0)) {
horizon_result.push_back(Pair("feerate", ValueFromAmount(feeRate.GetFeePerK())));
horizon_result.push_back(Pair("decay", buckets.decay)); horizon_result.push_back(Pair("decay", buckets.decay));
horizon_result.push_back(Pair("scale", (int)buckets.scale)); horizon_result.push_back(Pair("scale", (int)buckets.scale));
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));
passbucket.push_back(Pair("totalconfirmed", round(buckets.pass.totalConfirmed * 100.0) / 100.0));
passbucket.push_back(Pair("inmempool", round(buckets.pass.inMempool * 100.0) / 100.0));
passbucket.push_back(Pair("leftmempool", round(buckets.pass.leftMempool * 100.0) / 100.0));
horizon_result.push_back(Pair("pass", passbucket)); horizon_result.push_back(Pair("pass", passbucket));
UniValue failbucket(UniValue::VOBJ); // buckets.fail.start == -1 indicates that all buckets passed, there is no fail bucket to output
failbucket.push_back(Pair("startrange", round(buckets.fail.start))); if (buckets.fail.start != -1) horizon_result.push_back(Pair("fail", failbucket));
failbucket.push_back(Pair("endrange", round(buckets.fail.end))); } else {
failbucket.push_back(Pair("withintarget", round(buckets.fail.withinTarget * 100.0) / 100.0)); // Output only information that is still meaningful in the event of error
failbucket.push_back(Pair("totalconfirmed", round(buckets.fail.totalConfirmed * 100.0) / 100.0)); horizon_result.push_back(Pair("decay", buckets.decay));
failbucket.push_back(Pair("inmempool", round(buckets.fail.inMempool * 100.0) / 100.0)); horizon_result.push_back(Pair("scale", (int)buckets.scale));
failbucket.push_back(Pair("leftmempool", round(buckets.fail.leftMempool * 100.0) / 100.0));
horizon_result.push_back(Pair("fail", failbucket)); 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));
} }
result.push_back(Pair(StringForFeeEstimateHorizon(horizon), horizon_result)); result.push_back(Pair(StringForFeeEstimateHorizon(horizon), horizon_result));
} }