Skip to content

Commit e55d36c

Browse files
committed
anchorspend: fix weight estimation.
Now we've fixed various underlying weight mis-estimation, we can do a few final tweaks and test that anchors work as intended. 1. We assumed a p2wpkh output, but modern change is p2tr. 2. We handed `2` instead of `1` to bitcoin_tx_core_weight (which doesn't matter, but was weird). 3. Make change calculation clearer. I'm not sure the previous one was wrong, but it was harder to understand. 4. Fix the test and make it clearly test that we are aiming for (and achieving) the right feerate. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> Changelog-Fixed: Protocol: anchors' fees are now much closer to the feerate targets.
1 parent 9ab6789 commit e55d36c

File tree

2 files changed

+48
-22
lines changed

2 files changed

+48
-22
lines changed

lightningd/anchorspend.c

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -232,13 +232,14 @@ struct anchor_details *create_anchor_details(const tal_t *ctx,
232232
return adet;
233233
}
234234

235-
/* total_weight includes the commitment tx we're trying to push! */
235+
/* total_weight includes the commitment tx we're trying to push, and the anchor fee output */
236236
static struct wally_psbt *anchor_psbt(const tal_t *ctx,
237237
struct channel *channel,
238238
const struct one_anchor *anch,
239239
struct utxo **utxos,
240240
u32 feerate_target,
241-
size_t total_weight)
241+
size_t total_weight,
242+
bool insufficient_funds)
242243
{
243244
struct lightningd *ld = channel->peer->ld;
244245
struct wally_psbt *psbt;
@@ -263,12 +264,23 @@ static struct wally_psbt *anchor_psbt(const tal_t *ctx,
263264
AMOUNT_SAT(330));
264265
psbt_input_add_pubkey(psbt, psbt->num_inputs - 1, &channel->local_funding_pubkey, false);
265266

266-
/* A zero-output tx is invalid: we must have change, even if not really economic */
267+
/* Calculate fee we need, given rate and weight */
268+
fee = amount_tx_fee(feerate_target, total_weight);
269+
/* Some fee already paid by commitment tx */
270+
if (!amount_sat_sub(&fee, fee, anch->info.commitment_fee))
271+
fee = AMOUNT_SAT(0);
272+
273+
/* How much do we have? */
267274
change = psbt_compute_fee(psbt);
268-
/* Assume we add a change output, what would the total fee be? */
269-
fee = amount_tx_fee(feerate_target, total_weight + change_weight());
275+
276+
/* We have to pay dust, at least! */
270277
if (!amount_sat_sub(&change, change, fee)
271278
|| amount_sat_less(change, chainparams->dust_limit)) {
279+
/* If we didn't run out of UTXOs, this implies our estimation was wrong! */
280+
if (!insufficient_funds)
281+
log_broken(channel->log, "anchor: could not afford fee %s from change %s, reducing fee",
282+
fmt_amount_sat(tmpctx, fee),
283+
fmt_amount_sat(tmpctx, psbt_compute_fee(psbt)));
272284
change = chainparams->dust_limit;
273285
}
274286

@@ -293,6 +305,7 @@ static struct wally_psbt *try_anchor_psbt(const tal_t *ctx,
293305
struct lightningd *ld = channel->peer->ld;
294306
struct wally_psbt *psbt;
295307
struct amount_sat fee;
308+
bool insufficient_funds;
296309

297310
/* Ask for some UTXOs which could meet this feerate, and since
298311
* we need one output, meet the minumum output requirement */
@@ -303,11 +316,11 @@ static struct wally_psbt *try_anchor_psbt(const tal_t *ctx,
303316
anch->info.commitment_fee,
304317
chainparams->dust_limit,
305318
feerate_target,
306-
total_weight, NULL);
319+
total_weight, &insufficient_funds);
307320

308321
/* Create a new candidate PSBT */
309322
psbt = anchor_psbt(ctx, channel, anch, *utxos, feerate_target,
310-
*total_weight);
323+
*total_weight, insufficient_funds);
311324
*fee_spent = psbt_compute_fee(psbt);
312325

313326
/* Add in base commitment fee to calculate *overall* package feerate */
@@ -335,11 +348,11 @@ static struct bitcoin_tx *spend_anchor(const tal_t *ctx,
335348
const u8 *msg;
336349

337350
/* Estimate weight of anchorspend tx plus commitment_tx (not including any UTXO we add) */
338-
base_weight = bitcoin_tx_core_weight(2, 1)
351+
base_weight = bitcoin_tx_core_weight(1, 1)
339352
+ bitcoin_tx_input_weight(false,
340353
bitcoin_tx_input_sig_weight()
341354
+ 1 + tal_bytelen(anch->adet->anchor_wscript))
342-
+ bitcoin_tx_output_weight(BITCOIN_SCRIPTPUBKEY_P2WPKH_LEN)
355+
+ change_weight()
343356
+ anch->info.commitment_weight;
344357

345358
total_value = AMOUNT_MSAT(0);
@@ -372,15 +385,17 @@ static struct bitcoin_tx *spend_anchor(const tal_t *ctx,
372385
&fee,
373386
&feerate,
374387
&utxos);
388+
log_debug(channel->log, "candidate_psbt total weight = %zu (commitment weight %u, anchor %zu)",
389+
weight, anch->info.commitment_weight, weight - anch->info.commitment_weight);
375390

376391
/* Is it even worth spending this fee to meet the deadline? */
377392
if (!amount_msat_greater_sat(total_value, fee)) {
378393
log_debug(channel->log,
379-
"Not worth fee %s for %s commit tx to get %s in %u blocks at feerate %uperkw",
394+
"Not worth fee %s for %s commit tx to get %s at block %u (%+i) at feerate %uperkw",
380395
fmt_amount_sat(tmpctx, fee),
381396
anch->commit_side == LOCAL ? "local" : "remote",
382397
fmt_amount_msat(tmpctx, val->msat),
383-
val->block, feerate_target);
398+
val->block, val->block - get_block_height(ld->topology), feerate_target);
384399
break;
385400
}
386401

@@ -405,11 +420,11 @@ static struct bitcoin_tx *spend_anchor(const tal_t *ctx,
405420
break;
406421
}
407422

408-
log_debug(channel->log, "Worth fee %s for %s commit tx to get %s in %u blocks at feerate %uperkw",
423+
log_debug(channel->log, "Worth fee %s for %s commit tx to get %s at block %u (%+i) at feerate %uperkw",
409424
fmt_amount_sat(tmpctx, fee),
410425
anch->commit_side == LOCAL ? "local" : "remote",
411426
fmt_amount_msat(tmpctx, val->msat),
412-
val->block, feerate);
427+
val->block, val->block - get_block_height(ld->topology), feerate);
413428
psbt = candidate_psbt;
414429
psbt_fee = fee;
415430
psbt_weight = weight;
@@ -513,6 +528,7 @@ static struct bitcoin_tx *spend_anchor(const tal_t *ctx,
513528
tx->wtx = psbt_final_tx(tx, signed_psbt);
514529
assert(tx->wtx);
515530
tx->psbt = tal_steal(tx, signed_psbt);
531+
log_debug(channel->log, "anchor actual weight: %zu", bitcoin_tx_weight(tx));
516532

517533
return tx;
518534
}

tests/test_closing.py

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3992,26 +3992,36 @@ def test_peer_anchor_push(node_factory, bitcoind, executor, chainparams):
39923992
# We put l3's tx in the mempool, but won't mine it.
39933993
bitcoind.rpc.sendrawtransaction(closetx)
39943994

3995-
# We aim for feerate ~3750, so this won't mine it.
3996-
# HTLC's going to time out at block 119
3997-
for block in range(108, 119):
3995+
# We aim for feerate ~3750, so this won't mine l3's unilateral close.
3996+
# HTLC's going to time out at block 120 (we give one block grace)
3997+
for block in range(110, 120):
39983998
bitcoind.generate_block(1, needfeerate=5000)
3999+
assert bitcoind.rpc.getblockcount() == block
39994000
sync_blockheight(bitcoind, [l2])
4001+
assert only_one(l2.rpc.listpeerchannels(l3.info['id'])['channels'])['state'] == 'CHANNELD_NORMAL'
40004002

40014003
# Drops to chain
4004+
bitcoind.generate_block(1, needfeerate=5000)
40024005
wait_for(lambda: only_one(l2.rpc.listpeerchannels(l3.info['id'])['channels'])['state'] == 'AWAITING_UNILATERAL')
40034006

4004-
# But, l3's tx already there, and identical feerate will not RBF
4007+
# But, l3's tx already there, and identical feerate will not RBF.
40054008
l2.daemon.wait_for_log("rejecting replacement")
4006-
assert bitcoind.rpc.getrawmempool() != []
4009+
wait_for(lambda: len(bitcoind.rpc.getrawmempool()) == 2)
40074010

40084011
# As blocks pass, we will use anchor to boost l3's tx.
4009-
for block in range(119, 124):
4010-
bitcoind.generate_block(1, needfeerate=5000)
4012+
for block, feerate in zip(range(120, 124), (12000, 13000, 14000, 15000)):
4013+
l2.daemon.wait_for_log(fr"Worth fee [0-9]*sat for remote commit tx to get 100000000msat at block 125 \(\+{125 - block}\) at feerate {feerate}perkw")
4014+
bitcoind.generate_block(1, needfeerate=16000)
40114015
sync_blockheight(bitcoind, [l2])
4016+
assert len(bitcoind.rpc.getrawmempool()) == 2
40124017

4013-
# mempool should be empty, but our 'needfeerate' logic is bogus and leaves
4014-
# the anchor spend tx! So just check that l2 did see the commitment tx
4018+
# Feerate tops out at +1, so this is the same. This time we mine it!
4019+
l2.daemon.wait_for_log(fr"Worth fee [0-9]*sat for remote commit tx to get 100000000msat at block 125 \(\+1\) at feerate 15000perkw")
4020+
l2.daemon.wait_for_log("sendrawtx exit 0")
4021+
4022+
bitcoind.generate_block(1, needfeerate=15000)
4023+
4024+
assert bitcoind.rpc.getrawmempool() == []
40154025
wait_for(lambda: only_one(l2.rpc.listpeerchannels(l3.info['id'])['channels'])['state'] == 'ONCHAIN')
40164026

40174027

0 commit comments

Comments
 (0)