mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-19 15:14:23 +01:00
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>
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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. */
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user