lightningd: add force-feerates option.

Useful for regtest and testnet.  Sure, you shouldn't use this on mainnet,
but I haven't restricted it because our users are usually pretty clever.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Fixes: #1806
Changelog-Added: config: `force_feerates` option to allow overriding feerate estimates (mainly for regtest).
This commit is contained in:
Rusty Russell
2021-07-08 12:17:03 +09:30
parent 131e79ab91
commit adab9eb301
11 changed files with 150 additions and 3 deletions

View File

@@ -148,6 +148,8 @@ On success, an object is returned, containing:
.IP \[bu] .IP \[bu]
\fBlog-timestamps\fR (boolean, optional): \fBlog-timestamps\fR field from config or cmdline, or default \fBlog-timestamps\fR (boolean, optional): \fBlog-timestamps\fR field from config or cmdline, or default
.IP \[bu] .IP \[bu]
\fBforce-feerates\fR (string, optional): \fBforce-feerates\fR field from config or cmdline, if any
.IP \[bu]
\fBsubdaemon\fR (string, optional): \fBsubdaemon\fR fields from config or cmdline if any (can be more than one) \fBsubdaemon\fR (string, optional): \fBsubdaemon\fR fields from config or cmdline if any (can be more than one)
.IP \[bu] .IP \[bu]
\fBtor-service-password\fR (string, optional): \fBtor-service-password\fR field from config or cmdline, if any \fBtor-service-password\fR (string, optional): \fBtor-service-password\fR field from config or cmdline, if any
@@ -268,4 +270,4 @@ Vincenzo Palazzo \fI<vincenzo.palazzo@protonmail.com\fR> wrote the initial versi
Main web site: \fIhttps://github.com/ElementsProject/lightning\fR Main web site: \fIhttps://github.com/ElementsProject/lightning\fR
\" SHA256STAMP:f813a777c6e074907980797dafd798a48633469a59e1ac526e2581e2b1a14c83 \" SHA256STAMP:4591f6c754162b2dcdf82a36d584a48795752d39a986bc1d39c49e0cdbea440f

View File

@@ -84,6 +84,7 @@ On success, an object is returned, containing:
- **log-prefix** (string, optional): `log-prefix` field from config or cmdline, or default - **log-prefix** (string, optional): `log-prefix` field from config or cmdline, or default
- **log-file** (string, optional): `log-file` field from config or cmdline, or default - **log-file** (string, optional): `log-file` field from config or cmdline, or default
- **log-timestamps** (boolean, optional): `log-timestamps` field from config or cmdline, or default - **log-timestamps** (boolean, optional): `log-timestamps` field from config or cmdline, or default
- **force-feerates** (string, optional): `force-feerates` field from config or cmdline, if any
- **subdaemon** (string, optional): `subdaemon` fields from config or cmdline if any (can be more than one) - **subdaemon** (string, optional): `subdaemon` fields from config or cmdline if any (can be more than one)
- **tor-service-password** (string, optional): `tor-service-password` field from config or cmdline, if any - **tor-service-password** (string, optional): `tor-service-password` field from config or cmdline, if any
[comment]: # (GENERATE-FROM-SCHEMA-END) [comment]: # (GENERATE-FROM-SCHEMA-END)
@@ -203,4 +204,4 @@ RESOURCES
--------- ---------
Main web site: <https://github.com/ElementsProject/lightning> Main web site: <https://github.com/ElementsProject/lightning>
[comment]: # ( SHA256STAMP:3c3f2cd354ef5b33ad34febd29b04b1861c62d545c6a5b9181eb2b2b3880258f) [comment]: # ( SHA256STAMP:ad98179a7b6254a936d4fde179918b6a975e186adcbc396917a0c2ed2888519e)

View File

@@ -316,6 +316,21 @@ How long to wait before sending commitment messages to the peer: in
theory increasing this would reduce load, but your node would have to be theory increasing this would reduce load, but your node would have to be
extremely busy node for you to even notice\. extremely busy node for you to even notice\.
\fBforce-feerates\fR==\fIVALUES\fR
Networks like regtest and testnet have unreliable fee estimates: we
usually treat them as the minumum (253 sats/kw) if we can't get them\.
This allows override of one or more of our standard feerates (see
\fBlightning-feerates\fR(7))\. Up to 5 values, separated by '/' can be
provided: if fewer are provided, then the final value is used for the
remainder\. The values are in per-kw (roughly 1/4 of bitcoind's per-kb
values), an in order are "opening", "mutual_close",
"unilateral_close", "delayed_to_us", "htlc_resolution", and "penalty"\.
You would usually put this option in the per-chain config file, to avoid
setting it on Bitcoin mainnet! e\.g\. \fB~rusty/.lightning/regtest/config\fR\.
.SH Lightning channel and HTLC options .SH Lightning channel and HTLC options
\fBlarge-channels\fR \fBlarge-channels\fR
@@ -635,4 +650,4 @@ Main web site: \fIhttps://github.com/ElementsProject/lightning\fR
Note: the modules in the ccan/ directory have their own licenses, but Note: the modules in the ccan/ directory have their own licenses, but
the rest of the code is covered by the BSD-style MIT license\. the rest of the code is covered by the BSD-style MIT license\.
\" SHA256STAMP:40c9f5e9e4ee5257e25a1fc196d2c85c3bc5b21d3f390a4e7fafa031c4e7ad5e \" SHA256STAMP:d456e1acd004f9528d8772231afdecff1aaa01d80161c833483f6f078f4c7d70

View File

@@ -257,6 +257,19 @@ How long to wait before sending commitment messages to the peer: in
theory increasing this would reduce load, but your node would have to be theory increasing this would reduce load, but your node would have to be
extremely busy node for you to even notice. extremely busy node for you to even notice.
**force-feerates**==*VALUES*
Networks like regtest and testnet have unreliable fee estimates: we
usually treat them as the minumum (253 sats/kw) if we can't get them.
This allows override of one or more of our standard feerates (see
lightning-feerates(7)). Up to 5 values, separated by '/' can be
provided: if fewer are provided, then the final value is used for the
remainder. The values are in per-kw (roughly 1/4 of bitcoind's per-kb
values), an in order are "opening", "mutual_close",
"unilateral_close", "delayed_to_us", "htlc_resolution", and "penalty".
You would usually put this option in the per-chain config file, to avoid
setting it on Bitcoin mainnet! e.g. `~rusty/.lightning/regtest/config`.
### Lightning channel and HTLC options ### Lightning channel and HTLC options
**large-channels** **large-channels**

View File

@@ -235,6 +235,10 @@
"type": "boolean", "type": "boolean",
"description": "`log-timestamps` field from config or cmdline, or default" "description": "`log-timestamps` field from config or cmdline, or default"
}, },
"force-feerates": {
"type": "string",
"description": "force-feerate configuration setting, if any"
},
"subdaemon": { "subdaemon": {
"type": "string", "type": "string",
"description": "`subdaemon` fields from config or cmdline if any (can be more than one)" "description": "`subdaemon` fields from config or cmdline if any (can be more than one)"

View File

@@ -187,6 +187,12 @@ static void estimatefees_callback(const char *buf, const jsmntok_t *toks,
bitcoin_plugin_error(call->bitcoind, buf, toks, bitcoin_plugin_error(call->bitcoind, buf, toks,
"estimatefees", "estimatefees",
"missing '%s' field", feerate_name(f)); "missing '%s' field", feerate_name(f));
/* We still use the bcli plugin for min and max, even with
* force_feerates */
if (f < tal_count(call->bitcoind->ld->force_feerates)) {
feerates[f] = call->bitcoind->ld->force_feerates[f];
continue;
}
/* FIXME: We could trawl recent blocks for median fee... */ /* FIXME: We could trawl recent blocks for median fee... */
if (!json_to_u32(buf, feeratetok, &feerates[f])) { if (!json_to_u32(buf, feeratetok, &feerates[f])) {

View File

@@ -22,6 +22,7 @@ struct txwatch;
/* FIXME: move all feerate stuff out to new lightningd/feerate.[ch] files */ /* FIXME: move all feerate stuff out to new lightningd/feerate.[ch] files */
enum feerate { enum feerate {
/* DO NOT REORDER: force-feerates uses this order! */
FEERATE_OPENING, FEERATE_OPENING,
FEERATE_MUTUAL_CLOSE, FEERATE_MUTUAL_CLOSE,
FEERATE_UNILATERAL_CLOSE, FEERATE_UNILATERAL_CLOSE,

View File

@@ -290,6 +290,11 @@ static struct lightningd *new_lightningd(const tal_t *ctx)
* each invoice we generate has a different set of channels. */ * each invoice we generate has a different set of channels. */
ld->rr_counter = 0; ld->rr_counter = 0;
/*~ Because fee estimates on testnet and regtest are unreliable,
* we allow overriding them with --force-feerates, in which
* case this is a pointer to an enum feerate-indexed array of values */
ld->force_feerates = NULL;
return ld; return ld;
} }

View File

@@ -199,6 +199,10 @@ struct lightningd {
/* RPC response to send once we've shut down. */ /* RPC response to send once we've shut down. */
const char *stop_response; const char *stop_response;
/* Used these feerates instead of whatever bcli returns (up to
* FEERATE_PENALTY). */
u32 *force_feerates;
#if DEVELOPER #if DEVELOPER
/* If we want to debug a subdaemon/plugin. */ /* If we want to debug a subdaemon/plugin. */
const char *dev_debug_subprocess; const char *dev_debug_subprocess;

View File

@@ -131,6 +131,51 @@ static char *opt_set_mode(const char *arg, mode_t *m)
return NULL; return NULL;
} }
static char *opt_force_feerates(const char *arg, struct lightningd *ld)
{
char **vals = tal_strsplit(tmpctx, arg, "/", STR_EMPTY_OK);
size_t n;
/* vals has NULL at end, enum feerate is 0 based */
if (tal_count(vals) - 1 > FEERATE_PENALTY + 1)
return "Too many values";
if (!ld->force_feerates)
ld->force_feerates = tal_arr(ld, u32, FEERATE_PENALTY + 1);
n = 0;
for (size_t i = 0; i < tal_count(ld->force_feerates); i++) {
char *err = opt_set_u32(vals[n], &ld->force_feerates[i]);
if (err)
return err;
fprintf(stderr, "Set feerate %zu based on val %zu\n", i, n);
if (vals[n+1])
n++;
}
return NULL;
}
static char *fmt_force_feerates(const tal_t *ctx, const u32 *force_feerates)
{
char *ret;
size_t last;
if (!force_feerates)
return NULL;
ret = tal_fmt(ctx, "%i", force_feerates[0]);
last = 0;
for (size_t i = 1; i < tal_count(force_feerates); i++) {
if (force_feerates[i] == force_feerates[i-1])
continue;
/* Different? Catchup! */
for (size_t j = last + 1; j <= i; j++)
tal_append_fmt(&ret, "/%i", force_feerates[j]);
last = i;
}
return ret;
}
#if EXPERIMENTAL_FEATURES #if EXPERIMENTAL_FEATURES
static char *opt_set_accept_extra_tlv_types(const char *arg, static char *opt_set_accept_extra_tlv_types(const char *arg,
struct lightningd *ld) struct lightningd *ld)
@@ -1003,6 +1048,10 @@ static void register_opts(struct lightningd *ld)
"Set the file mode (permissions) for the " "Set the file mode (permissions) for the "
"JSON-RPC socket"); "JSON-RPC socket");
opt_register_arg("--force-feerates",
opt_force_feerates, NULL, ld,
"Set testnet/regtest feerates in sats perkw, opening/mutual_close/unlateral_close/delayed_to_us/htlc_resolution/penalty: if fewer specified, last number applies to remainder");
opt_register_arg("--subdaemon", opt_subdaemon, NULL, opt_register_arg("--subdaemon", opt_subdaemon, NULL,
ld, "Arg specified as SUBDAEMON:PATH. " ld, "Arg specified as SUBDAEMON:PATH. "
"Specifies an alternate subdaemon binary. " "Specifies an alternate subdaemon binary. "
@@ -1423,6 +1472,8 @@ static void add_config(struct lightningd *ld,
json_add_opt_log_levels(response, ld->log); json_add_opt_log_levels(response, ld->log);
} else if (opt->cb_arg == (void *)opt_disable_plugin) { } else if (opt->cb_arg == (void *)opt_disable_plugin) {
json_add_opt_disable_plugins(response, ld->plugins); json_add_opt_disable_plugins(response, ld->plugins);
} else if (opt->cb_arg == (void *)opt_force_feerates) {
answer = fmt_force_feerates(name0, ld->force_feerates);
} else if (opt->cb_arg == (void *)opt_important_plugin) { } else if (opt->cb_arg == (void *)opt_important_plugin) {
/* Do nothing, this is already handled by /* Do nothing, this is already handled by
* opt_add_plugin. */ * opt_add_plugin. */

View File

@@ -2561,3 +2561,48 @@ def test_getlog(node_factory):
# This should not # This should not
logs = l1.rpc.getlog(level='io')['log'] logs = l1.rpc.getlog(level='io')['log']
assert [l for l in logs if l['type'] == 'SKIPPED'] == [] assert [l for l in logs if l['type'] == 'SKIPPED'] == []
def test_force_feerates(node_factory):
l1 = node_factory.get_node(options={'force-feerates': 1111})
assert l1.rpc.listconfigs()['force-feerates'] == '1111'
assert l1.rpc.feerates('perkw')['perkw'] == {
"opening": 1111,
"mutual_close": 1111,
"unilateral_close": 1111,
"delayed_to_us": 1111,
"htlc_resolution": 1111,
"penalty": 1111,
"min_acceptable": 1875,
"max_acceptable": 150000}
l1.stop()
l1.daemon.opts['force-feerates'] = '1111/2222'
l1.start()
assert l1.rpc.listconfigs()['force-feerates'] == '1111/2222'
assert l1.rpc.feerates('perkw')['perkw'] == {
"opening": 1111,
"mutual_close": 2222,
"unilateral_close": 2222,
"delayed_to_us": 2222,
"htlc_resolution": 2222,
"penalty": 2222,
"min_acceptable": 1875,
"max_acceptable": 150000}
l1.stop()
l1.daemon.opts['force-feerates'] = '1111/2222/3333/4444/5555/6666'
l1.start()
assert l1.rpc.listconfigs()['force-feerates'] == '1111/2222/3333/4444/5555/6666'
assert l1.rpc.feerates('perkw')['perkw'] == {
"opening": 1111,
"mutual_close": 2222,
"unilateral_close": 3333,
"delayed_to_us": 4444,
"htlc_resolution": 5555,
"penalty": 6666,
"min_acceptable": 1875,
"max_acceptable": 150000}