Change API to estimaterawfee

Report results for all 3 possible time horizons instead of specifying time horizon as an argument.
This commit is contained in:
Alex Morcos 2017-06-06 13:08:27 -04:00
parent 9edda0c5f5
commit 9c85b91dc1
5 changed files with 68 additions and 51 deletions

View file

@ -40,6 +40,7 @@ public:
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; }
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; }
CFeeRate& operator+=(const CFeeRate& a) { nSatoshisPerK += a.nSatoshisPerK; return *this; } CFeeRate& operator+=(const CFeeRate& a) { nSatoshisPerK += a.nSatoshisPerK; return *this; }
std::string ToString() const; std::string ToString() const;

View file

@ -16,6 +16,19 @@
static constexpr double INF_FEERATE = 1e99; static constexpr double INF_FEERATE = 1e99;
std::string StringForFeeEstimateHorizon(FeeEstimateHorizon horizon) {
static const std::map<FeeEstimateHorizon, std::string> horizon_strings = {
{FeeEstimateHorizon::SHORT_HALFLIFE, "short"},
{FeeEstimateHorizon::MED_HALFLIFE, "medium"},
{FeeEstimateHorizon::LONG_HALFLIFE, "long"},
};
auto horizon_string = horizon_strings.find(horizon);
if (horizon_string == horizon_strings.end()) return "unknown";
return horizon_string->second;
}
std::string StringForFeeReason(FeeReason reason) { std::string StringForFeeReason(FeeReason reason) {
static const std::map<FeeReason, std::string> fee_reason_strings = { static const std::map<FeeReason, std::string> fee_reason_strings = {
{FeeReason::NONE, "None"}, {FeeReason::NONE, "None"},

View file

@ -74,6 +74,8 @@ enum FeeEstimateHorizon {
LONG_HALFLIFE = 2 LONG_HALFLIFE = 2
}; };
std::string StringForFeeEstimateHorizon(FeeEstimateHorizon horizon);
/* Enumeration of reason for returned fee estimate */ /* Enumeration of reason for returned fee estimate */
enum class FeeReason { enum class FeeReason {
NONE, NONE,

View file

@ -113,7 +113,6 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "estimatesmartfee", 1, "conservative" }, { "estimatesmartfee", 1, "conservative" },
{ "estimaterawfee", 0, "nblocks" }, { "estimaterawfee", 0, "nblocks" },
{ "estimaterawfee", 1, "threshold" }, { "estimaterawfee", 1, "threshold" },
{ "estimaterawfee", 2, "horizon" },
{ "prioritisetransaction", 1, "dummy" }, { "prioritisetransaction", 1, "dummy" },
{ "prioritisetransaction", 2, "fee_delta" }, { "prioritisetransaction", 2, "fee_delta" },
{ "setban", 2, "bantime" }, { "setban", 2, "bantime" },

View file

@ -838,9 +838,9 @@ UniValue estimatesmartfee(const JSONRPCRequest& request)
UniValue estimaterawfee(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( throw std::runtime_error(
"estimaterawfee nblocks (threshold horizon)\n" "estimaterawfee nblocks (threshold)\n"
"\nWARNING: This interface is unstable and may disappear or change!\n" "\nWARNING: This interface is unstable and may disappear or change!\n"
"\nWARNING: This is an advanced API call that is tightly coupled to the specific\n" "\nWARNING: This is an advanced API call that is tightly coupled to the specific\n"
" implementation of fee estimation. The parameters it can be called with\n" " implementation of fee estimation. The parameters it can be called with\n"
@ -853,68 +853,70 @@ UniValue estimaterawfee(const JSONRPCRequest& request)
"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"
"3. horizon (numeric, optional) How long a history of estimates to consider. 0=short, 1=medium, 2=long.\n"
" Default: 1\n"
"\nResult:\n" "\nResult:\n"
"{\n" "{\n"
" \"feerate\" : x.x, (numeric) estimate fee-per-kilobyte (in BTC)\n" " \"short\" : { (json object) estimate for short time horizon\n"
" \"decay\" : x.x, (numeric) exponential decay (per block) for historical moving average of confirmation data\n" " \"feerate\" : x.x, (numeric) estimate fee-per-kilobyte (in BTC)\n"
" \"scale\" : x, (numeric) The resolution of confirmation targets at this time horizon\n" " \"decay\" : x.x, (numeric) exponential decay (per block) for historical moving average of confirmation data\n"
" \"pass\" : { (json object) information about the lowest range of feerates to succeed in meeting the threshold\n" " \"scale\" : x, (numeric) The resolution of confirmation targets at this time horizon\n"
" \"startrange\" : x.x, (numeric) start of feerate range\n" " \"pass\" : { (json object) information about the lowest range of feerates to succeed in meeting the threshold\n"
" \"endrange\" : x.x, (numeric) end of feerate range\n" " \"startrange\" : x.x, (numeric) start of feerate range\n"
" \"withintarget\" : x.x, (numeric) number of txs over history horizon in the feerate range that were confirmed within target\n" " \"endrange\" : x.x, (numeric) end of feerate range\n"
" \"totalconfirmed\" : x.x, (numeric) number of txs over history horizon in the feerate range that were confirmed at any point\n" " \"withintarget\" : x.x, (numeric) number of txs over history horizon in the feerate range that were confirmed within target\n"
" \"inmempool\" : x.x, (numeric) current number of txs in mempool in the feerate range unconfirmed for at least target blocks\n" " \"totalconfirmed\" : x.x, (numeric) number of txs over history horizon in the feerate range that were confirmed at any point\n"
" \"leftmempool\" : x.x, (numeric) number of txs over history horizon in the feerate range that left mempool unconfirmed after target\n" " \"inmempool\" : x.x, (numeric) current number of txs in mempool in the feerate range unconfirmed for at least target blocks\n"
" }\n" " \"leftmempool\" : x.x, (numeric) number of txs over history horizon in the feerate range that left mempool unconfirmed after target\n"
" \"fail\" : { ... } (json object) information about the highest range of feerates to fail to meet the threshold\n" " },\n"
" \"fail\" : { ... }, (json object) information about the highest range of feerates to fail to meet the threshold\n"
" },\n"
" \"medium\" : { ... }, (json object) estimate for medium 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" "A negative feerate is returned if no answer can be given.\n"
"\nExample:\n" "\nExample:\n"
+ HelpExampleCli("estimaterawfee", "6 0.9 1") + HelpExampleCli("estimaterawfee", "6 0.9")
); );
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();
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();
FeeEstimateHorizon horizon = FeeEstimateHorizon::MED_HALFLIFE;
if (!request.params[2].isNull()) {
int horizonInt = request.params[2].get_int();
if (horizonInt < 0 || horizonInt > 2) {
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid horizon for fee estimates");
} else {
horizon = (FeeEstimateHorizon)horizonInt;
}
} }
UniValue result(UniValue::VOBJ);
CFeeRate feeRate;
EstimationResult buckets;
feeRate = ::feeEstimator.estimateRawFee(nBlocks, threshold, horizon, &buckets);
result.push_back(Pair("feerate", feeRate == CFeeRate(0) ? -1.0 : ValueFromAmount(feeRate.GetFeePerK()))); UniValue result(UniValue::VOBJ);
result.push_back(Pair("decay", buckets.decay));
result.push_back(Pair("scale", (int)buckets.scale)); for (FeeEstimateHorizon horizon : {FeeEstimateHorizon::SHORT_HALFLIFE, FeeEstimateHorizon::MED_HALFLIFE, FeeEstimateHorizon::LONG_HALFLIFE}) {
UniValue passbucket(UniValue::VOBJ); CFeeRate feeRate;
passbucket.push_back(Pair("startrange", round(buckets.pass.start))); EstimationResult buckets;
passbucket.push_back(Pair("endrange", round(buckets.pass.end))); feeRate = ::feeEstimator.estimateRawFee(nBlocks, threshold, horizon, &buckets);
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)); UniValue horizon_result(UniValue::VOBJ);
passbucket.push_back(Pair("inmempool", round(buckets.pass.inMempool * 100.0) / 100.0)); horizon_result.push_back(Pair("feerate", feeRate == CFeeRate(0) ? -1.0 : ValueFromAmount(feeRate.GetFeePerK())));
passbucket.push_back(Pair("leftmempool", round(buckets.pass.leftMempool * 100.0) / 100.0)); if (!(feeRate == CFeeRate(0))) {
result.push_back(Pair("pass", passbucket)); horizon_result.push_back(Pair("decay", buckets.decay));
UniValue failbucket(UniValue::VOBJ); horizon_result.push_back(Pair("scale", (int)buckets.scale));
failbucket.push_back(Pair("startrange", round(buckets.fail.start))); UniValue passbucket(UniValue::VOBJ);
failbucket.push_back(Pair("endrange", round(buckets.fail.end))); passbucket.push_back(Pair("startrange", round(buckets.pass.start)));
failbucket.push_back(Pair("withintarget", round(buckets.fail.withinTarget * 100.0) / 100.0)); passbucket.push_back(Pair("endrange", round(buckets.pass.end)));
failbucket.push_back(Pair("totalconfirmed", round(buckets.fail.totalConfirmed * 100.0) / 100.0)); passbucket.push_back(Pair("withintarget", round(buckets.pass.withinTarget * 100.0) / 100.0));
failbucket.push_back(Pair("inmempool", round(buckets.fail.inMempool * 100.0) / 100.0)); passbucket.push_back(Pair("totalconfirmed", round(buckets.pass.totalConfirmed * 100.0) / 100.0));
failbucket.push_back(Pair("leftmempool", round(buckets.fail.leftMempool * 100.0) / 100.0)); passbucket.push_back(Pair("inmempool", round(buckets.pass.inMempool * 100.0) / 100.0));
result.push_back(Pair("fail", failbucket)); passbucket.push_back(Pair("leftmempool", round(buckets.pass.leftMempool * 100.0) / 100.0));
horizon_result.push_back(Pair("pass", passbucket));
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));
horizon_result.push_back(Pair("fail", failbucket));
}
result.push_back(Pair(StringForFeeEstimateHorizon(horizon), horizon_result));
}
return result; return result;
} }
@ -932,7 +934,7 @@ static const CRPCCommand commands[] =
{ "util", "estimatefee", &estimatefee, true, {"nblocks"} }, { "util", "estimatefee", &estimatefee, true, {"nblocks"} },
{ "util", "estimatesmartfee", &estimatesmartfee, true, {"nblocks", "conservative"} }, { "util", "estimatesmartfee", &estimatesmartfee, true, {"nblocks", "conservative"} },
{ "hidden", "estimaterawfee", &estimaterawfee, true, {"nblocks", "threshold", "horizon"} }, { "hidden", "estimaterawfee", &estimaterawfee, true, {"nblocks", "threshold"} },
}; };
void RegisterMiningRPCCommands(CRPCTable &t) void RegisterMiningRPCCommands(CRPCTable &t)