Browse Source

feerates: new command to inject/query fee estimates.

This is useful mainly in the case where bitcoind is not giving estimates,
but can also be used to bias results if you want.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ppa-0.6.1
Rusty Russell 7 years ago
parent
commit
c7c5affa3f
  1. 5
      CHANGELOG.md
  2. 12
      contrib/pylightning/lightning/lightning.py
  3. 79
      lightningd/chaintopology.c
  4. 40
      tests/test_misc.py

5
CHANGELOG.md

@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Sending lightningd a SIGHUP will make it reopen its `log-file`, if any. - Sending lightningd a SIGHUP will make it reopen its `log-file`, if any.
- Protocol: `option_data_loss_protect` now supported to protect peers - Protocol: `option_data_loss_protect` now supported to protect peers
against being out-of-date. against being out-of-date.
- JSON API: `feerates` command to inject fee estimates manually.
### Changed ### Changed
@ -42,6 +43,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- We now try to connect to all known addresses for a peer, not just - We now try to connect to all known addresses for a peer, not just
the one given or the first one announced. the one given or the first one announced.
- Crash logs are now placed one-per file like `crash.log.20180822233752` - Crash logs are now placed one-per file like `crash.log.20180822233752`
- We will no longer allow withdrawing funds or funding channels if we
do not have a fee estimate (eg. bitcoind not synced).
### Deprecated ### Deprecated
@ -56,7 +59,7 @@ changes.
used to exist and set to `GOSSIPING` before we opened a channel). used to exist and set to `GOSSIPING` before we opened a channel).
`connected` will indicate if we're connected, and the `channels` `connected` will indicate if we're connected, and the `channels`
array indicates individual channel states (if any). array indicates individual channel states (if any).
- Options: `default-fee-rate` is no longer available. - Options: `default-fee-rate` is no longer available; use `feerates`.
- Removed all Deprecated options from 0.6. - Removed all Deprecated options from 0.6.
### Fixed ### Fixed

12
contrib/pylightning/lightning/lightning.py

@ -448,3 +448,15 @@ class LightningRpc(UnixDomainSocketRpc):
"id": peer_id, "id": peer_id,
} }
return self.call("disconnect", payload) return self.call("disconnect", payload)
def feerates(self, style, urgent=None, normal=None, slow=None):
"""
Supply feerate estimates manually.
"""
payload = {
"style": style,
"urgent": urgent,
"normal": normal,
"slow": slow
}
return self.call("feerates", payload)

79
lightningd/chaintopology.c

@ -20,6 +20,7 @@
#include <lightningd/channel_control.h> #include <lightningd/channel_control.h>
#include <lightningd/gossip_control.h> #include <lightningd/gossip_control.h>
#include <lightningd/json.h> #include <lightningd/json.h>
#include <lightningd/jsonrpc_errors.h>
#include <lightningd/param.h> #include <lightningd/param.h>
/* Mutual recursion via timer. */ /* Mutual recursion via timer. */
@ -388,6 +389,84 @@ static void start_fee_estimate(struct chain_topology *topo)
update_feerates, topo); update_feerates, topo);
} }
static void json_feerates(struct command *cmd,
const char *buffer, const jsmntok_t *params)
{
struct chain_topology *topo = cmd->ld->topology;
struct json_result *response;
u32 *urgent, *normal, *slow, feerates[NUM_FEERATES];
bool missing;
const jsmntok_t *style;
bool bitcoind_style;
if (!param(cmd, buffer, params,
p_req("style", json_tok_tok, &style),
p_opt("urgent", json_tok_number, &urgent),
p_opt("normal", json_tok_number, &normal),
p_opt("slow", json_tok_number, &slow),
NULL))
return;
/* update_feerates uses 0 as "don't know" */
feerates[FEERATE_URGENT] = urgent ? *urgent : 0;
feerates[FEERATE_NORMAL] = normal ? *normal : 0;
feerates[FEERATE_SLOW] = slow ? *slow : 0;
if (json_tok_streq(buffer, style, "sipa"))
bitcoind_style = false;
else if (json_tok_streq(buffer, style, "bitcoind")) {
/* Everyone uses satoshi per kbyte, but we use satoshi per ksipa
* (don't round down to zero though)! */
for (size_t i = 0; i < ARRAY_SIZE(feerates); i++)
feerates[i] = (feerates[i] + 3) / 4;
bitcoind_style = true;
} else {
command_fail(cmd, JSONRPC2_INVALID_PARAMS, "invalid style");
return;
}
log_info(topo->log,
"feerates: inserting feerates in sipa/kb %u/%u/%u",
feerates[FEERATE_URGENT],
feerates[FEERATE_NORMAL],
feerates[FEERATE_SLOW]);
update_feerates(topo->bitcoind, feerates, topo);
missing = false;
for (size_t i = 0; i < ARRAY_SIZE(feerates); i++) {
feerates[i] = try_get_feerate(topo, i);
if (!feerates[i])
missing = true;
if (bitcoind_style)
feerates[i] *= 4;
}
response = new_json_result(cmd);
json_object_start(response, NULL);
json_object_start(response, bitcoind_style ? "bitcoind" : "sipa");
for (size_t i = 0; i < ARRAY_SIZE(feerates); i++) {
if (!feerates[i])
continue;
json_add_num(response, feerate_name(i), feerates[i]);
}
json_object_end(response);
if (missing)
json_add_string(response, "warning",
"Some fee estimates unavailable: bitcoind startup?");
json_object_end(response);
command_success(cmd, response);
}
static const struct json_command feerates_command = {
"feerates",
json_feerates,
"Add/query feerate estimates, either satoshi-per-kw ({style} sipa) or satoshi-per-kb ({style} bitcoind) for {urgent}, {normal} and {slow}."
};
AUTODATA(json_command, &feerates_command);
static void next_updatefee_timer(struct chain_topology *topo) static void next_updatefee_timer(struct chain_topology *topo)
{ {
/* This takes care of its own lifetime. */ /* This takes care of its own lifetime. */

40
tests/test_misc.py

@ -858,6 +858,46 @@ def test_ipv4_and_ipv6(node_factory):
assert int(bind[0]['port']) == port assert int(bind[0]['port']) == port
def test_feerates(node_factory):
l1 = node_factory.get_node(options={'log-level': 'io'}, start=False)
l1.bitcoind_cmd_override(cmd='estimatesmartfee',
failscript="""echo '{ "errors": [ "Insufficient data or no feerate found" ], "blocks": 0 }'; exit 0""")
l1.start()
# Query feerates (shouldn't give any!)
feerates = l1.rpc.feerates('sipa')
assert len(feerates['sipa']) == 0
assert feerates['warning'] == 'Some fee estimates unavailable: bitcoind startup?'
assert 'bitcoind' not in feerates
feerates = l1.rpc.feerates('bitcoind')
assert len(feerates['bitcoind']) == 0
assert feerates['warning'] == 'Some fee estimates unavailable: bitcoind startup?'
assert 'sipa' not in feerates
# Now try setting them, one at a time.
feerates = l1.rpc.feerates('sipa', 15000)
assert len(feerates['sipa']) == 1
assert feerates['sipa']['urgent'] == 15000
assert feerates['warning'] == 'Some fee estimates unavailable: bitcoind startup?'
assert 'bitcoind' not in feerates
feerates = l1.rpc.feerates('bitcoind', normal=25000)
assert len(feerates['bitcoind']) == 2
assert feerates['bitcoind']['urgent'] == 15000 * 4
assert feerates['bitcoind']['normal'] == 25000
assert feerates['warning'] == 'Some fee estimates unavailable: bitcoind startup?'
assert 'sipa' not in feerates
feerates = l1.rpc.feerates('sipa', None, None, 5000)
assert len(feerates['sipa']) == 3
assert feerates['sipa']['urgent'] == 15000
assert feerates['sipa']['normal'] == 25000 // 4
assert feerates['sipa']['slow'] == 5000
assert 'warning' not in feerates
assert 'bitcoind' not in feerates
def test_logging(node_factory): def test_logging(node_factory):
# Since we redirect, node.start() will fail: do manually. # Since we redirect, node.start() will fail: do manually.
l1 = node_factory.get_node(options={'log-file': 'logfile'}, may_fail=True, start=False) l1 = node_factory.get_node(options={'log-file': 'logfile'}, may_fail=True, start=False)

Loading…
Cancel
Save