Skip to content

Commit

Permalink
bcli: adapt interface to the new fees estimation interface
Browse files Browse the repository at this point in the history
As did lightningd before, we use 2/CONSERVATIVE for urgent fees,
4/ECONOMICAL for normal fees, and 100/ECONOMICAL for slow fees.
  • Loading branch information
darosior committed Mar 6, 2020
1 parent 9ea3219 commit 256cffb
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 31 deletions.
141 changes: 112 additions & 29 deletions plugins/bcli.c
Original file line number Diff line number Diff line change
Expand Up @@ -449,17 +449,31 @@ static struct command_result *process_getblockchaininfo(struct bitcoin_cli *bcli
return command_finished(bcli->cmd, response);
}

static struct command_result *process_estimatefee(struct bitcoin_cli *bcli)

struct estimatefees_stash {
/* FIXME: We use u64 but lightningd will store them as u32. */
u64 urgent, normal, slow;
};

static struct command_result *
estimatefees_null_response(struct bitcoin_cli *bcli)
{
struct json_stream *response = jsonrpc_stream_success(bcli->cmd);

json_add_null(response, "urgent");
json_add_null(response, "normal");
json_add_null(response, "slow");

return command_finished(bcli->cmd, response);
}

static struct command_result *
estimatefees_parse_feerate(struct bitcoin_cli *bcli, u64 *feerate)
{
const jsmntok_t *tokens, *feeratetok = NULL;
struct json_stream *response;
bool valid;
u64 feerate;
char *err;

if (*bcli->exitstatus != 0)
goto end;

tokens = json_parse_input(bcli->output, bcli->output,
(int)bcli->output_bytes, &valid);
if (!tokens) {
Expand All @@ -478,23 +492,89 @@ static struct command_result *process_estimatefee(struct bitcoin_cli *bcli)

feeratetok = json_get_member(bcli->output, tokens, "feerate");
if (feeratetok &&
!json_to_bitcoin_amount(bcli->output, feeratetok, &feerate)) {
!json_to_bitcoin_amount(bcli->output, feeratetok, feerate)) {
err = tal_fmt(bcli->cmd, "%s: bad 'feerate' field (%.*s)",
bcli_args(bcli), (int)bcli->output_bytes,
bcli->output);
return command_done_err(bcli->cmd, BCLI_ERROR, err, NULL);
}
} else if (!feeratetok)
/* We return null if estimation failed, and bitcoin-cli will
* exit with 0 but no feerate field on failure. */
return estimatefees_null_response(bcli);

return NULL;
}

/* We got all the feerates, give them to lightningd. */
static struct command_result *estimatefees_final_step(struct bitcoin_cli *bcli)
{
struct command_result *err;
struct json_stream *response;
struct estimatefees_stash *stash = bcli->stash;

/* bitcoind could theoretically fail to estimate for a higher target. */
if (*bcli->exitstatus != 0)
return estimatefees_null_response(bcli);

err = estimatefees_parse_feerate(bcli, &stash->slow);
if (err)
return err;

end:
response = jsonrpc_stream_success(bcli->cmd);
if (feeratetok)
json_add_u64(response, "feerate", feerate);
else
json_add_null(response, "feerate");
json_add_u64(response, "urgent", stash->urgent);
json_add_u64(response, "normal", stash->normal);
json_add_u64(response, "slow", stash->slow);

return command_finished(bcli->cmd, response);
}

/* We got the response for the normal feerate, now treat the slow one. */
static struct command_result *estimatefees_third_step(struct bitcoin_cli *bcli)
{
struct command_result *err;
struct estimatefees_stash *stash = bcli->stash;
const char **params = tal_arr(bcli->cmd, const char *, 2);

/* bitcoind could theoretically fail to estimate for a higher target. */
if (*bcli->exitstatus != 0)
return estimatefees_null_response(bcli);

err = estimatefees_parse_feerate(bcli, &stash->normal);
if (err)
return err;

params[0] = "100";
params[1] = "ECONOMICAL";
start_bitcoin_cli(NULL, bcli->cmd, estimatefees_final_step, true,
BITCOIND_LOW_PRIO, "estimatesmartfee", params, stash);

return command_still_pending(bcli->cmd);
}

/* We got the response for the urgent feerate, now treat the normal one. */
static struct command_result *estimatefees_second_step(struct bitcoin_cli *bcli)
{
struct command_result *err;
struct estimatefees_stash *stash = bcli->stash;
const char **params = tal_arr(bcli->cmd, const char *, 2);

/* If we cannot estimatefees, no need to continue bothering bitcoind. */
if (*bcli->exitstatus != 0)
return estimatefees_null_response(bcli);

err = estimatefees_parse_feerate(bcli, &stash->urgent);
if (err)
return err;

params[0] = "4";
params[1] = "ECONOMICAL";
start_bitcoin_cli(NULL, bcli->cmd, estimatefees_third_step, true,
BITCOIND_LOW_PRIO, "estimatesmartfee", params, stash);

return command_still_pending(bcli->cmd);
}


static struct command_result *process_sendrawtransaction(struct bitcoin_cli *bcli)
{
struct json_stream *response;
Expand Down Expand Up @@ -623,25 +703,27 @@ static struct command_result *getchaininfo(struct command *cmd,
return command_still_pending(cmd);
}

/* Get current feerate.
* Calls `estimatesmartfee` and returns the feerate as btc/k*VBYTE*.
/* Get the current urgent, normal, and slow feerates.
*
* Calls `estimatesmartfee` with targets 2/CONSERVATIVE (urgent),
* 4/ECONOMICAL (normal), and 100/ECONOMICAL (slow) then returns the
* feerates as btc/k*VBYTE*.
*/
static struct command_result *getfeerate(struct command *cmd,
const char *buf UNUSED,
const jsmntok_t *toks UNUSED)
static struct command_result *estimatefees(struct command *cmd,
const char *buf UNUSED,
const jsmntok_t *toks UNUSED)
{
u32 *blocks;
struct estimatefees_stash *stash = tal(cmd, struct estimatefees_stash);
const char **params = tal_arr(cmd, const char *, 2);

if (!param(cmd, buf, toks,
p_req("blocks", param_number, &blocks),
p_req("mode", param_string, &params[1]),
NULL))
if (!param(cmd, buf, toks, NULL))
return command_param_failed();

params[0] = tal_fmt(params, "%u", *blocks);
start_bitcoin_cli(NULL, cmd, process_estimatefee, true,
BITCOIND_LOW_PRIO, "estimatesmartfee", params, NULL);
/* First call to estimatesmartfee, for urgent estimation. */
params[0] = "2";
params[1] = "CONSERVATIVE";
start_bitcoin_cli(NULL, cmd, estimatefees_second_step, true,
BITCOIND_LOW_PRIO, "estimatesmartfee", params, stash);

return command_still_pending(cmd);
}
Expand Down Expand Up @@ -775,11 +857,12 @@ static const struct plugin_command commands[] = {
getchaininfo
},
{
"getfeerate",
"estimatefees",
"bitcoin",
"Get the Bitcoin feerate in btc/kilo-vbyte.",
"Get the urgent, normal and slow Bitcoin feerates in"
" btc/kilo-vbyte.",
"",
getfeerate
estimatefees
},
{
"sendrawtransaction",
Expand Down
3 changes: 1 addition & 2 deletions tests/test_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -983,8 +983,7 @@ def test_bcli(node_factory, bitcoind, chainparams):
l1.rpc.plugin_stop("bcli")

# Failure case of feerate is tested in test_misc.py
assert "feerate" in l1.rpc.call("getfeerate", {"blocks": 3,
"mode": "CONSERVATIVE"})
assert "urgent" in l1.rpc.call("estimatefees")

resp = l1.rpc.call("getchaininfo")
assert resp["chain"] == chainparams['name']
Expand Down

0 comments on commit 256cffb

Please sign in to comment.