Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Regtest fee control #4629

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 19 additions & 10 deletions channeld/channeld.c
Original file line number Diff line number Diff line change
Expand Up @@ -1106,7 +1106,7 @@ static struct bitcoin_signature *unraw_sigs(const tal_t *ctx,
/* Do we want to update fees? */
static bool want_fee_update(const struct peer *peer, u32 *target)
{
u32 max, val;
u32 current, val;

if (peer->channel->opener != LOCAL)
return false;
Expand All @@ -1116,19 +1116,28 @@ static bool want_fee_update(const struct peer *peer, u32 *target)
if (peer->stfu)
return false;
#endif

max = approx_max_feerate(peer->channel);
val = peer->desired_feerate;

/* FIXME: We should avoid adding HTLCs until we can meet this
* feerate! */
if (val > max)
val = max;
current = channel_feerate(peer->channel, REMOTE);

/* max is *approximate*: only take it into account if we're
* trying to increase feerate. */
if (peer->desired_feerate > current) {
/* FIXME: We should avoid adding HTLCs until we can meet this
* feerate! */
u32 max = approx_max_feerate(peer->channel);

val = peer->desired_feerate;
/* Respect max, but don't let us *decrease* us */
if (val > max)
val = max;
if (val < current)
val = current;
} else
val = peer->desired_feerate;

if (target)
*target = val;

return val != channel_feerate(peer->channel, REMOTE);
return val != current;
}

static void send_commit(struct peer *peer)
Expand Down
4 changes: 3 additions & 1 deletion doc/lightning-listconfigs.7

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion doc/lightning-listconfigs.7.md
Original file line number Diff line number Diff line change
Expand Up @@ -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-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
- **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)
- **tor-service-password** (string, optional): `tor-service-password` field from config or cmdline, if any
[comment]: # (GENERATE-FROM-SCHEMA-END)
Expand Down Expand Up @@ -203,4 +204,4 @@ RESOURCES
---------

Main web site: <https://github.com/ElementsProject/lightning>
[comment]: # ( SHA256STAMP:3c3f2cd354ef5b33ad34febd29b04b1861c62d545c6a5b9181eb2b2b3880258f)
[comment]: # ( SHA256STAMP:ad98179a7b6254a936d4fde179918b6a975e186adcbc396917a0c2ed2888519e)
17 changes: 16 additions & 1 deletion doc/lightningd-config.5
Original file line number Diff line number Diff line change
Expand Up @@ -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
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

\fBlarge-channels\fR
Expand Down Expand Up @@ -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
the rest of the code is covered by the BSD-style MIT license\.

\" SHA256STAMP:40c9f5e9e4ee5257e25a1fc196d2c85c3bc5b21d3f390a4e7fafa031c4e7ad5e
\" SHA256STAMP:d456e1acd004f9528d8772231afdecff1aaa01d80161c833483f6f078f4c7d70
13 changes: 13 additions & 0 deletions doc/lightningd-config.5.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
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

**large-channels**
Expand Down
4 changes: 4 additions & 0 deletions doc/schemas/listconfigs.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,10 @@
"type": "boolean",
"description": "`log-timestamps` field from config or cmdline, or default"
},
"force-feerates": {
"type": "string",
"description": "force-feerate configuration setting, if any"
},
"subdaemon": {
"type": "string",
"description": "`subdaemon` fields from config or cmdline if any (can be more than one)"
Expand Down
17 changes: 14 additions & 3 deletions lightningd/bitcoind.c
Original file line number Diff line number Diff line change
Expand Up @@ -187,12 +187,23 @@ static void estimatefees_callback(const char *buf, const jsmntok_t *toks,
bitcoin_plugin_error(call->bitcoind, buf, toks,
"estimatefees",
"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... */
if (!json_to_u32(buf, feeratetok, &feerates[f])) {
log_unusual(call->bitcoind->log,
"Unable to estimate %s fees",
feerate_name(f));
if (chainparams->testnet)
log_debug(call->bitcoind->log,
"Unable to estimate %s fees",
feerate_name(f));
else
log_unusual(call->bitcoind->log,
"Unable to estimate %s fees",
feerate_name(f));

#if DEVELOPER
/* This is needed to test for failed feerate estimates
Expand Down
1 change: 1 addition & 0 deletions lightningd/chaintopology.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ struct txwatch;

/* FIXME: move all feerate stuff out to new lightningd/feerate.[ch] files */
enum feerate {
/* DO NOT REORDER: force-feerates uses this order! */
FEERATE_OPENING,
FEERATE_MUTUAL_CLOSE,
FEERATE_UNILATERAL_CLOSE,
Expand Down
5 changes: 5 additions & 0 deletions lightningd/lightningd.c
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,11 @@ static struct lightningd *new_lightningd(const tal_t *ctx)
* each invoice we generate has a different set of channels. */
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;
}

Expand Down
4 changes: 4 additions & 0 deletions lightningd/lightningd.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,10 @@ struct lightningd {
/* RPC response to send once we've shut down. */
const char *stop_response;

/* Used these feerates instead of whatever bcli returns (up to
* FEERATE_PENALTY). */
u32 *force_feerates;

#if DEVELOPER
/* If we want to debug a subdaemon/plugin. */
const char *dev_debug_subprocess;
Expand Down
51 changes: 51 additions & 0 deletions lightningd/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,51 @@ static char *opt_set_mode(const char *arg, mode_t *m)
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
static char *opt_set_accept_extra_tlv_types(const char *arg,
struct lightningd *ld)
Expand Down Expand Up @@ -1003,6 +1048,10 @@ static void register_opts(struct lightningd *ld)
"Set the file mode (permissions) for the "
"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,
ld, "Arg specified as SUBDAEMON:PATH. "
"Specifies an alternate subdaemon binary. "
Expand Down Expand Up @@ -1423,6 +1472,8 @@ static void add_config(struct lightningd *ld,
json_add_opt_log_levels(response, ld->log);
} else if (opt->cb_arg == (void *)opt_disable_plugin) {
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) {
/* Do nothing, this is already handled by
* opt_add_plugin. */
Expand Down
45 changes: 45 additions & 0 deletions tests/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2561,3 +2561,48 @@ def test_getlog(node_factory):
# This should not
logs = l1.rpc.getlog(level='io')['log']
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}