Skip to content

Commit 29acfbd

Browse files
committed
closing: add option to set closing range.
This affects the range we offer even without quick-close, but it's more critical for quick-close. Signed-off-by: Rusty Russell <[email protected]> Changelog-Added: JSONRPC: `close` now takes a `feerange` parameter to set min/max fee rates for mutual close.
1 parent f6e410e commit 29acfbd

14 files changed

+136
-14
lines changed

closingd/closingd.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,7 @@ static void calc_fee_bounds(size_t expected_weight,
622622
/* option_anchor_outputs sets commitment_fee to max, so this
623623
* doesn't do anything */
624624
if (amount_sat_greater(*maxfee, commitment_fee)) {
625+
/* FIXME: would be nice to notify close cmd here! */
625626
status_unusual("Maximum feerate %u would give fee %s:"
626627
" we must limit it to the final commitment fee %s",
627628
*max_feerate,

contrib/pyln-client/pyln/client/lightning.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,7 @@ def check(self, command_to_check, **kwargs):
516516
return self.call("check", payload)
517517

518518
def close(self, peer_id, unilateraltimeout=None, destination=None,
519-
fee_negotiation_step=None, force_lease_closed=None):
519+
fee_negotiation_step=None, force_lease_closed=None, feerange=None):
520520
"""
521521
Close the channel with peer {id}, forcing a unilateral
522522
close after {unilateraltimeout} seconds if non-zero, and
@@ -533,6 +533,7 @@ def close(self, peer_id, unilateraltimeout=None, destination=None,
533533
"destination": destination,
534534
"fee_negotiation_step": fee_negotiation_step,
535535
"force_lease_closed": force_lease_closed,
536+
"feerange": feerange,
536537
}
537538
return self.call("close", payload)
538539

doc/lightning-close.7

Lines changed: 28 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

doc/lightning-close.7.md

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ lightning-close -- Command for closing channels with direct peers
44
SYNOPSIS
55
--------
66

7-
**close** *id* \[*unilateraltimeout*\] \[*destination*\] \[*fee_negotiation_step*\] \[*wrong_funding*\] \[*force_lease_closed*\]
7+
**close** *id* \[*unilateraltimeout*\] \[*destination*\] \[*fee_negotiation_step*\] \[*wrong_funding*\] \[*force_lease_closed*\] [\*feerange\*]
88

99
DESCRIPTION
1010
-----------
@@ -33,7 +33,11 @@ friends to upgrade!
3333

3434
The *fee_negotiation_step* parameter controls how closing fee
3535
negotiation is performed assuming the peer proposes a fee that is
36-
different than our estimate. On every negotiation step we must give up
36+
different than our estimate. (Note that using this option
37+
prevents **experimental-quick-close**, as the quick-close protocol
38+
does not allow negotiation).
39+
40+
On every negotiation step we must give up
3741
some amount from our proposal towards the peer's proposal. This parameter
3842
can be an integer in which case it is interpreted as number of satoshis
3943
to step at a time. Or it can be an integer followed by "%" to designate
@@ -62,6 +66,23 @@ can rescue openings which have been manually miscreated.
6266
unless this flag is passed in. Defaults to false.
6367

6468

69+
*feerange* is an optional array [ *min*, *max* ], indicating the
70+
minimum and maximum feerates to offer. *slow* and *unilateral_close*
71+
are the defaults.
72+
73+
Rates are one of the strings *urgent* (aim for next block), *normal*
74+
(next 4 blocks or so) or *slow* (next 100 blocks or so) to use
75+
lightningd’s internal estimates, or one of the names from
76+
lightning-feerates(7). Otherwise, they can be numbers with
77+
an optional suffix: *perkw* means the number is interpreted as
78+
satoshi-per-kilosipa (weight), and *perkb* means it is interpreted
79+
bitcoind-style as satoshi-per-kilobyte. Omitting the suffix is
80+
equivalent to *perkb*.
81+
82+
Note that the maximum fee will be capped at the final commitment
83+
transaction fee (unless the experimental anchor-outputs option is
84+
negotiated).
85+
6586
The peer needs to be live and connected in order to negotiate a mutual
6687
close. The default of unilaterally closing after 48 hours is usually a
6788
reasonable indication that you can no longer contact the peer.

lightningd/channel.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ struct channel *new_unsaved_channel(struct peer *peer,
263263
channel->closing_fee_negotiation_step_unit
264264
= CLOSING_FEE_NEGOTIATION_STEP_UNIT_PERCENTAGE;
265265
channel->shutdown_wrong_funding = NULL;
266+
channel->closing_feerate_range = NULL;
266267

267268
/* Channel is connected! */
268269
channel->connected = true;
@@ -429,6 +430,7 @@ struct channel *new_channel(struct peer *peer, u64 dbid,
429430
= CLOSING_FEE_NEGOTIATION_STEP_UNIT_PERCENTAGE;
430431
channel->shutdown_wrong_funding
431432
= tal_steal(channel, shutdown_wrong_funding);
433+
channel->closing_feerate_range = NULL;
432434
if (local_shutdown_scriptpubkey)
433435
channel->shutdown_scriptpubkey[LOCAL]
434436
= tal_steal(channel, local_shutdown_scriptpubkey);

lightningd/channel.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,9 @@ struct channel {
174174
/* optional wrong_funding for mutual close */
175175
const struct bitcoin_outpoint *shutdown_wrong_funding;
176176

177+
/* optional feerate min/max for mutual close */
178+
u32 *closing_feerate_range;
179+
177180
/* Reestablishment stuff: last sent commit and revocation details. */
178181
bool last_was_revoke;
179182
struct changed_htlc *last_sent_commit;

lightningd/closing_control.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ void peer_start_closingd(struct channel *channel,
193193
struct per_peer_state *pps)
194194
{
195195
u8 *initmsg;
196-
u32 feerate, *max_feerate;
196+
u32 min_feerate, feerate, *max_feerate;
197197
struct amount_msat their_msat;
198198
struct amount_sat feelimit;
199199
int hsmfd;
@@ -266,6 +266,14 @@ void peer_start_closingd(struct channel *channel,
266266
} else
267267
max_feerate = NULL;
268268

269+
min_feerate = feerate_min(ld, NULL);
270+
271+
/* If they specified feerates in `close`, they apply now! */
272+
if (channel->closing_feerate_range) {
273+
min_feerate = channel->closing_feerate_range[0];
274+
max_feerate = &channel->closing_feerate_range[1];
275+
}
276+
269277
/* BOLT #3:
270278
*
271279
* Each node offering a signature:
@@ -298,8 +306,7 @@ void peer_start_closingd(struct channel *channel,
298306
amount_msat_to_sat_round_down(channel->our_msat),
299307
amount_msat_to_sat_round_down(their_msat),
300308
channel->our_config.dust_limit,
301-
feerate_min(ld, NULL), feerate,
302-
max_feerate,
309+
min_feerate, feerate, max_feerate,
303310
feelimit,
304311
channel->shutdown_scriptpubkey[LOCAL],
305312
channel->shutdown_scriptpubkey[REMOTE],

lightningd/peer_control.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1661,6 +1661,31 @@ static struct command_result *param_outpoint(struct command *cmd,
16611661
"should be a txid:outnum");
16621662
}
16631663

1664+
static struct command_result *param_feerate_range(struct command *cmd,
1665+
const char *name,
1666+
const char *buffer,
1667+
const jsmntok_t *tok,
1668+
u32 **feerate_range)
1669+
{
1670+
struct command_result *ret;
1671+
u32 *rate;
1672+
1673+
*feerate_range = tal_arr(cmd, u32, 2);
1674+
if (tok->type != JSMN_ARRAY || tok->size != 2)
1675+
return command_fail_badparam(cmd, name, buffer, tok,
1676+
"should be an array of 2 entries");
1677+
1678+
ret = param_feerate(cmd, name, buffer, tok+1, &rate);
1679+
if (ret)
1680+
return ret;
1681+
(*feerate_range)[0] = *rate;
1682+
ret = param_feerate(cmd, name, buffer, tok+2, &rate);
1683+
if (ret)
1684+
return ret;
1685+
(*feerate_range)[1] = *rate;
1686+
return NULL;
1687+
}
1688+
16641689
static struct command_result *json_close(struct command *cmd,
16651690
const char *buffer,
16661691
const jsmntok_t *obj UNNEEDED,
@@ -1674,6 +1699,7 @@ static struct command_result *json_close(struct command *cmd,
16741699
bool close_script_set, wrong_funding_changed, *force_lease_close;
16751700
const char *fee_negotiation_step_str;
16761701
struct bitcoin_outpoint *wrong_funding;
1702+
u32 *feerate_range;
16771703
char* end;
16781704
bool anysegwit;
16791705

@@ -1687,6 +1713,7 @@ static struct command_result *json_close(struct command *cmd,
16871713
p_opt("wrong_funding", param_outpoint, &wrong_funding),
16881714
p_opt_def("force_lease_closed", param_bool,
16891715
&force_lease_close, false),
1716+
p_opt("feerange", param_feerate_range, &feerate_range),
16901717
NULL))
16911718
return command_param_failed();
16921719

@@ -1845,6 +1872,9 @@ static struct command_result *json_close(struct command *cmd,
18451872
wrong_funding_changed = false;
18461873
}
18471874

1875+
/* Works fine if feerate_range is NULL */
1876+
channel->closing_feerate_range = tal_steal(channel, feerate_range);
1877+
18481878
/* Normal case.
18491879
* We allow states shutting down and sigexchange; a previous
18501880
* close command may have timed out, and this current command

lightningd/test/run-invoice-select-inchan.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,11 @@ struct command_result *param_escaped_string(struct command *cmd UNNEEDED,
534534
const jsmntok_t *tok UNNEEDED,
535535
const char **str UNNEEDED)
536536
{ fprintf(stderr, "param_escaped_string called!\n"); abort(); }
537+
/* Generated stub for param_feerate */
538+
struct command_result *param_feerate(struct command *cmd UNNEEDED, const char *name UNNEEDED,
539+
const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
540+
u32 **feerate UNNEEDED)
541+
{ fprintf(stderr, "param_feerate called!\n"); abort(); }
537542
/* Generated stub for param_label */
538543
struct command_result *param_label(struct command *cmd UNNEEDED, const char *name UNNEEDED,
539544
const char * buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,

tests/test_closing.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3327,3 +3327,25 @@ def test_anysegwit_close_needs_feature(node_factory, bitcoind):
33273327
l1.rpc.close(l2.info['id'], destination='bcrt1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k0ylj56')
33283328
wait_for(lambda: only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['state'] == 'CLOSINGD_COMPLETE')
33293329
bitcoind.generate_block(1, wait_for_mempool=1)
3330+
3331+
3332+
def test_close_feerate_range(node_factory, bitcoind, chainparams):
3333+
"""Test the quick-close fee range negotiation"""
3334+
l1, l2 = node_factory.line_graph(2, opts={'experimental-quick-close': None})
3335+
3336+
# Lowball the range here.
3337+
l1.rpc.close(l2.info['id'], feerange=['253perkw', 'normal'])
3338+
3339+
if not chainparams['elements']:
3340+
l1_range = [138, 4110]
3341+
l2_range = [1027, 1000000]
3342+
else:
3343+
# That fee output is a little chunky.
3344+
l1_range = [175, 5212]
3345+
l2_range = [1303, 1000000]
3346+
3347+
l1.daemon.wait_for_log('Negotiating closing fee between {}sat and {}sat satoshi'.format(l1_range[0], l1_range[1]))
3348+
l2.daemon.wait_for_log('Negotiating closing fee between {}sat and {}sat satoshi'.format(l2_range[0], l2_range[1]))
3349+
3350+
overlap = [max(l1_range[0], l2_range[0]), min(l1_range[1], l2_range[1])]
3351+
l1.daemon.wait_for_log('performing quickclose in range {}sat-{}sat'.format(overlap[0], overlap[1]))

wallet/db_postgres_sqlgen.c

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

wallet/db_sqlite3_sqlgen.c

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

wallet/statements_gettextgen.po

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

wallet/test/run-wallet.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,11 @@ struct command_result *param_channel_id(struct command *cmd UNNEEDED,
575575
const jsmntok_t *tok UNNEEDED,
576576
struct channel_id **cid UNNEEDED)
577577
{ fprintf(stderr, "param_channel_id called!\n"); abort(); }
578+
/* Generated stub for param_feerate */
579+
struct command_result *param_feerate(struct command *cmd UNNEEDED, const char *name UNNEEDED,
580+
const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
581+
u32 **feerate UNNEEDED)
582+
{ fprintf(stderr, "param_feerate called!\n"); abort(); }
578583
/* Generated stub for param_loglevel */
579584
struct command_result *param_loglevel(struct command *cmd UNNEEDED,
580585
const char *name UNNEEDED,

0 commit comments

Comments
 (0)