Skip to content

Commit 88616c7

Browse files
committed
closingd: use a more accurate fee for closing fee negotiation.
We were actually using the last commit tx's size, since we were setting it in lightningd. Instead, hand the min and desired feerates to closingd, and (as it knows the weight of the closing tx), and have it start negotiation from there. This can be significantly less when anchor outputs are enabled: for example in test_closing.py, the commit tx weight is 1124 Sipa, the close is 672 Sipa! Signed-off-by: Rusty Russell <[email protected]> Changelog-Changed: Protocol: Use a more accurate fee for mutual close negotiation.
1 parent 2be82a5 commit 88616c7

File tree

8 files changed

+134
-53
lines changed

8 files changed

+134
-53
lines changed

closingd/closingd.c

+92-6
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,84 @@ static void closing_dev_memleak(const tal_t *ctx,
491491
}
492492
#endif /* DEVELOPER */
493493

494+
/* Figure out what weight we actually expect for this closing tx (using zero fees
495+
* gives the largest possible tx: larger values might omit outputs). */
496+
static size_t closing_tx_weight_estimate(u8 *scriptpubkey[NUM_SIDES],
497+
const u8 *funding_wscript,
498+
const struct amount_sat *out,
499+
struct amount_sat funding,
500+
struct amount_sat dust_limit)
501+
{
502+
/* We create a dummy close */
503+
struct bitcoin_tx *tx;
504+
struct bitcoin_txid dummy_txid;
505+
struct bitcoin_signature dummy_sig;
506+
struct privkey dummy_privkey;
507+
struct pubkey dummy_pubkey;
508+
u8 **witness;
509+
510+
memset(&dummy_txid, 0, sizeof(dummy_txid));
511+
tx = create_close_tx(tmpctx, chainparams,
512+
scriptpubkey[LOCAL], scriptpubkey[REMOTE],
513+
funding_wscript,
514+
&dummy_txid, 0,
515+
funding,
516+
out[LOCAL],
517+
out[REMOTE],
518+
dust_limit);
519+
520+
/* Create a signature, any signature, so we can weigh fully "signed"
521+
* tx. */
522+
dummy_sig.sighash_type = SIGHASH_ALL;
523+
memset(&dummy_privkey, 1, sizeof(dummy_privkey));
524+
sign_hash(&dummy_privkey, &dummy_txid.shad, &dummy_sig.s);
525+
pubkey_from_privkey(&dummy_privkey, &dummy_pubkey);
526+
witness = bitcoin_witness_2of2(NULL, &dummy_sig, &dummy_sig,
527+
&dummy_pubkey, &dummy_pubkey);
528+
bitcoin_tx_input_set_witness(tx, 0, take(witness));
529+
530+
return bitcoin_tx_weight(tx);
531+
}
532+
533+
/* Get the minimum and desired fees */
534+
static void calc_fee_bounds(size_t expected_weight,
535+
u32 min_feerate,
536+
u32 desired_feerate,
537+
struct amount_sat maxfee,
538+
struct amount_sat *minfee,
539+
struct amount_sat *desiredfee)
540+
{
541+
*minfee = amount_tx_fee(min_feerate, expected_weight);
542+
*desiredfee = amount_tx_fee(desired_feerate, expected_weight);
543+
544+
/* Can't exceed maxfee. */
545+
if (amount_sat_greater(*minfee, maxfee))
546+
*minfee = maxfee;
547+
548+
if (amount_sat_less(*desiredfee, *minfee)) {
549+
status_unusual("Our ideal fee is %s (%u sats/perkw),"
550+
" but our minimum is %s: using that",
551+
type_to_string(tmpctx, struct amount_sat, desiredfee),
552+
desired_feerate,
553+
type_to_string(tmpctx, struct amount_sat, minfee));
554+
*desiredfee = *minfee;
555+
}
556+
if (amount_sat_greater(*desiredfee, maxfee)) {
557+
status_unusual("Our ideal fee is %s (%u sats/perkw),"
558+
" but our maximum is %s: using that",
559+
type_to_string(tmpctx, struct amount_sat, desiredfee),
560+
desired_feerate,
561+
type_to_string(tmpctx, struct amount_sat, &maxfee));
562+
*desiredfee = maxfee;
563+
}
564+
565+
status_debug("Expected closing weight = %zu, fee %s (min %s, max %s)",
566+
expected_weight,
567+
type_to_string(tmpctx, struct amount_sat, desiredfee),
568+
type_to_string(tmpctx, struct amount_sat, minfee),
569+
type_to_string(tmpctx, struct amount_sat, &maxfee));
570+
}
571+
494572
int main(int argc, char *argv[])
495573
{
496574
setup_locale();
@@ -504,6 +582,7 @@ int main(int argc, char *argv[])
504582
struct amount_sat funding, out[NUM_SIDES];
505583
struct amount_sat our_dust_limit;
506584
struct amount_sat min_fee_to_accept, commitment_fee, offer[NUM_SIDES];
585+
u32 min_feerate, initial_feerate;
507586
struct feerange feerange;
508587
enum side opener;
509588
u8 *scriptpubkey[NUM_SIDES], *funding_wscript;
@@ -531,8 +610,8 @@ int main(int argc, char *argv[])
531610
&out[LOCAL],
532611
&out[REMOTE],
533612
&our_dust_limit,
534-
&min_fee_to_accept, &commitment_fee,
535-
&offer[LOCAL],
613+
&min_feerate, &initial_feerate,
614+
&commitment_fee,
536615
&scriptpubkey[LOCAL],
537616
&scriptpubkey[REMOTE],
538617
&fee_negotiation_step,
@@ -544,6 +623,17 @@ int main(int argc, char *argv[])
544623
/* stdin == requests, 3 == peer, 4 = gossip, 5 = gossip_store, 6 = hsmd */
545624
per_peer_state_set_fds(notleak(pps), 3, 4, 5);
546625

626+
funding_wscript = bitcoin_redeem_2of2(ctx,
627+
&funding_pubkey[LOCAL],
628+
&funding_pubkey[REMOTE]);
629+
630+
/* Start at what we consider a reasonable feerate for this tx. */
631+
calc_fee_bounds(closing_tx_weight_estimate(scriptpubkey,
632+
funding_wscript,
633+
out, funding, our_dust_limit),
634+
min_feerate, initial_feerate, commitment_fee,
635+
&min_fee_to_accept, &offer[LOCAL]);
636+
547637
snprintf(fee_negotiation_step_str, sizeof(fee_negotiation_step_str),
548638
"%" PRIu64 "%s", fee_negotiation_step,
549639
fee_negotiation_step_unit ==
@@ -565,10 +655,6 @@ int main(int argc, char *argv[])
565655
&wrong_funding->txid),
566656
wrong_funding->n);
567657

568-
funding_wscript = bitcoin_redeem_2of2(ctx,
569-
&funding_pubkey[LOCAL],
570-
&funding_pubkey[REMOTE]);
571-
572658
peer_billboard(
573659
true,
574660
"Negotiating closing fee between %s and %s satoshi (ideal %s) "

closingd/closingd_wire.csv

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ msgdata,closingd_init,opener,enum side,
1717
msgdata,closingd_init,local_sat,amount_sat,
1818
msgdata,closingd_init,remote_sat,amount_sat,
1919
msgdata,closingd_init,our_dust_limit,amount_sat,
20-
msgdata,closingd_init,min_fee_satoshi,amount_sat,
20+
msgdata,closingd_init,min_feerate_perksipa,u32,
21+
msgdata,closingd_init,preferred_feerate_perksipa,u32,
2122
msgdata,closingd_init,fee_limit_satoshi,amount_sat,
22-
msgdata,closingd_init,initial_fee_satoshi,amount_sat,
2323
msgdata,closingd_init,local_scriptpubkey_len,u16,
2424
msgdata,closingd_init,local_scriptpubkey,u8,local_scriptpubkey_len
2525
msgdata,closingd_init,remote_scriptpubkey_len,u16,

closingd/closingd_wiregen.c

+7-7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

closingd/closingd_wiregen.h

+3-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lightningd/closing_control.c

+2-13
Original file line numberDiff line numberDiff line change
@@ -197,8 +197,8 @@ void peer_start_closingd(struct channel *channel,
197197
{
198198
u8 *initmsg;
199199
u32 feerate;
200-
struct amount_sat minfee, startfee, feelimit;
201200
struct amount_msat their_msat;
201+
struct amount_sat feelimit;
202202
int hsmfd;
203203
struct lightningd *ld = channel->peer->ld;
204204
u32 final_commit_feerate;
@@ -247,24 +247,13 @@ void peer_start_closingd(struct channel *channel,
247247
feelimit = commit_tx_base_fee(final_commit_feerate, 0,
248248
channel->option_anchor_outputs);
249249

250-
/* Pick some value above slow feerate (or min possible if unknown) */
251-
minfee = commit_tx_base_fee(feerate_min(ld, NULL), 0,
252-
channel->option_anchor_outputs);
253-
254250
/* If we can't determine feerate, start at half unilateral feerate. */
255251
feerate = mutual_close_feerate(ld->topology);
256252
if (!feerate) {
257253
feerate = final_commit_feerate / 2;
258254
if (feerate < feerate_floor())
259255
feerate = feerate_floor();
260256
}
261-
startfee = commit_tx_base_fee(feerate, 0,
262-
channel->option_anchor_outputs);
263-
264-
if (amount_sat_greater(startfee, feelimit))
265-
startfee = feelimit;
266-
if (amount_sat_greater(minfee, feelimit))
267-
minfee = feelimit;
268257

269258
/* BOLT #3:
270259
*
@@ -298,7 +287,7 @@ void peer_start_closingd(struct channel *channel,
298287
amount_msat_to_sat_round_down(channel->our_msat),
299288
amount_msat_to_sat_round_down(their_msat),
300289
channel->our_config.dust_limit,
301-
minfee, feelimit, startfee,
290+
feerate_min(ld, NULL), feerate, feelimit,
302291
channel->shutdown_scriptpubkey[LOCAL],
303292
channel->shutdown_scriptpubkey[REMOTE],
304293
channel->closing_fee_negotiation_step,

tests/test_closing.py

+13-13
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from pyln.testing.utils import SLOW_MACHINE
66
from utils import (
77
only_one, sync_blockheight, wait_for, TIMEOUT,
8-
account_balance, first_channel_id, basic_fee, TEST_NETWORK,
8+
account_balance, first_channel_id, closing_fee, TEST_NETWORK,
99
scriptpubkey_addr
1010
)
1111

@@ -22,7 +22,7 @@
2222
def test_closing(node_factory, bitcoind, chainparams):
2323
l1, l2 = node_factory.line_graph(2)
2424
chan = l1.get_channel_scid(l2)
25-
fee = basic_fee(3750) if not chainparams['elements'] else 4477
25+
fee = closing_fee(3750, 2) if not chainparams['elements'] else 3603
2626

2727
l1.pay(l2, 200000000)
2828

@@ -377,7 +377,7 @@ def feerate_for(target, minimum=0, maximum=10000000):
377377
"""Binary search to find feerate"""
378378
assert minimum != maximum
379379
mid = (minimum + maximum) // 2
380-
mid_fee = basic_fee(mid)
380+
mid_fee = closing_fee(mid, 1)
381381
if mid_fee > target:
382382
return feerate_for(target, minimum, mid)
383383
elif mid_fee < target:
@@ -452,11 +452,11 @@ def test_closing_negotiation_step_30pct(node_factory, bitcoind, chainparams):
452452
opts['fee_negotiation_step'] = '30%'
453453

454454
opts['close_initiated_by'] = 'opener'
455-
opts['expected_close_fee'] = 20537 if not chainparams['elements'] else 33870
455+
opts['expected_close_fee'] = 20537 if not chainparams['elements'] else 26046
456456
closing_negotiation_step(node_factory, bitcoind, chainparams, opts)
457457

458458
opts['close_initiated_by'] = 'peer'
459-
opts['expected_close_fee'] = 20233 if not chainparams['elements'] else 33366
459+
opts['expected_close_fee'] = 20233 if not chainparams['elements'] else 25657
460460
closing_negotiation_step(node_factory, bitcoind, chainparams, opts)
461461

462462

@@ -466,11 +466,11 @@ def test_closing_negotiation_step_50pct(node_factory, bitcoind, chainparams):
466466
opts['fee_negotiation_step'] = '50%'
467467

468468
opts['close_initiated_by'] = 'opener'
469-
opts['expected_close_fee'] = 20334 if not chainparams['elements'] else 33533
469+
opts['expected_close_fee'] = 20334 if not chainparams['elements'] else 25789
470470
closing_negotiation_step(node_factory, bitcoind, chainparams, opts)
471471

472472
opts['close_initiated_by'] = 'peer'
473-
opts['expected_close_fee'] = 20334 if not chainparams['elements'] else 33533
473+
opts['expected_close_fee'] = 20334 if not chainparams['elements'] else 25789
474474
closing_negotiation_step(node_factory, bitcoind, chainparams, opts)
475475

476476

@@ -480,7 +480,7 @@ def test_closing_negotiation_step_100pct(node_factory, bitcoind, chainparams):
480480
opts['fee_negotiation_step'] = '100%'
481481

482482
opts['close_initiated_by'] = 'opener'
483-
opts['expected_close_fee'] = 20001 if not chainparams['elements'] else 32985
483+
opts['expected_close_fee'] = 20001 if not chainparams['elements'] else 25366
484484
closing_negotiation_step(node_factory, bitcoind, chainparams, opts)
485485

486486
# The close fee of 20499 looks strange in this case - one would expect
@@ -489,7 +489,7 @@ def test_closing_negotiation_step_100pct(node_factory, bitcoind, chainparams):
489489
# * the opener is always first to propose, he uses 50% step, so he proposes 20500
490490
# * the range is narrowed to [20001, 20499] and the peer proposes 20499
491491
opts['close_initiated_by'] = 'peer'
492-
opts['expected_close_fee'] = 20499 if not chainparams['elements'] else 33808
492+
opts['expected_close_fee'] = 20499 if not chainparams['elements'] else 25998
493493
closing_negotiation_step(node_factory, bitcoind, chainparams, opts)
494494

495495

@@ -499,11 +499,11 @@ def test_closing_negotiation_step_1sat(node_factory, bitcoind, chainparams):
499499
opts['fee_negotiation_step'] = '1'
500500

501501
opts['close_initiated_by'] = 'opener'
502-
opts['expected_close_fee'] = 20989 if not chainparams['elements'] else 34621
502+
opts['expected_close_fee'] = 20989 if not chainparams['elements'] else 26624
503503
closing_negotiation_step(node_factory, bitcoind, chainparams, opts)
504504

505505
opts['close_initiated_by'] = 'peer'
506-
opts['expected_close_fee'] = 20010 if not chainparams['elements'] else 32995
506+
opts['expected_close_fee'] = 20010 if not chainparams['elements'] else 25373
507507
closing_negotiation_step(node_factory, bitcoind, chainparams, opts)
508508

509509

@@ -513,11 +513,11 @@ def test_closing_negotiation_step_700sat(node_factory, bitcoind, chainparams):
513513
opts['fee_negotiation_step'] = '700'
514514

515515
opts['close_initiated_by'] = 'opener'
516-
opts['expected_close_fee'] = 20151 if not chainparams['elements'] else 33459
516+
opts['expected_close_fee'] = 20151 if not chainparams['elements'] else 25650
517517
closing_negotiation_step(node_factory, bitcoind, chainparams, opts)
518518

519519
opts['close_initiated_by'] = 'peer'
520-
opts['expected_close_fee'] = 20499 if not chainparams['elements'] else 33746
520+
opts['expected_close_fee'] = 20499 if not chainparams['elements'] else 25998
521521
closing_negotiation_step(node_factory, bitcoind, chainparams, opts)
522522

523523

0 commit comments

Comments
 (0)