Skip to content

Commit 0f5990b

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 1d5cb4b commit 0f5990b

File tree

2 files changed

+49
-23
lines changed

2 files changed

+49
-23
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 */
@@ -336,11 +349,11 @@ static struct bitcoin_tx *spend_anchor(const tal_t *ctx,
336349
const u8 *msg;
337350

338351
/* Estimate weight of anchorspend tx plus commitment_tx (not including any UTXO we add) */
339-
base_weight = bitcoin_tx_core_weight(2, 1)
352+
base_weight = bitcoin_tx_core_weight(1, 1)
340353
+ bitcoin_tx_input_weight(false,
341354
bitcoin_tx_input_sig_weight()
342355
+ 1 + tal_bytelen(anch->adet->anchor_wscript))
343-
+ bitcoin_tx_output_weight(BITCOIN_SCRIPTPUBKEY_P2WPKH_LEN)
356+
+ change_weight()
344357
+ anch->info.commitment_weight;
345358

346359
total_value = AMOUNT_MSAT(0);
@@ -387,15 +400,17 @@ static struct bitcoin_tx *spend_anchor(const tal_t *ctx,
387400
&fee,
388401
&feerate,
389402
&utxos);
403+
log_debug(channel->log, "candidate_psbt total weight = %zu (commitment weight %u, anchor %zu)",
404+
weight, anch->info.commitment_weight, weight - anch->info.commitment_weight);
390405

391406
/* Is it even worth spending this fee to meet the deadline? */
392407
if (!amount_msat_greater_sat(total_value, fee)) {
393408
log_debug(channel->log,
394-
"Not worth fee %s for %s commit tx to get %s in %u blocks at feerate %uperkw",
409+
"Not worth fee %s for %s commit tx to get %s at block %u (%+i) at feerate %uperkw",
395410
fmt_amount_sat(tmpctx, fee),
396411
anch->commit_side == LOCAL ? "local" : "remote",
397412
fmt_amount_msat(tmpctx, val->msat),
398-
val->block, feerate_target);
413+
val->block, val->block - get_block_height(ld->topology), feerate_target);
399414
break;
400415
}
401416

@@ -420,11 +435,11 @@ static struct bitcoin_tx *spend_anchor(const tal_t *ctx,
420435
break;
421436
}
422437

423-
log_debug(channel->log, "Worth fee %s for %s commit tx to get %s in %u blocks at feerate %uperkw",
438+
log_debug(channel->log, "Worth fee %s for %s commit tx to get %s at block %u (%+i) at feerate %uperkw",
424439
fmt_amount_sat(tmpctx, fee),
425440
anch->commit_side == LOCAL ? "local" : "remote",
426441
fmt_amount_msat(tmpctx, val->msat),
427-
val->block, feerate);
442+
val->block, val->block - get_block_height(ld->topology), feerate);
428443
psbt = candidate_psbt;
429444
psbt_fee = fee;
430445
psbt_weight = weight;
@@ -539,6 +554,7 @@ static struct bitcoin_tx *spend_anchor(const tal_t *ctx,
539554
tx->wtx = psbt_final_tx(tx, signed_psbt);
540555
assert(tx->wtx);
541556
tx->psbt = tal_steal(tx, signed_psbt);
557+
log_debug(channel->log, "anchor actual weight: %zu", bitcoin_tx_weight(tx));
542558

543559
return tx;
544560
}

tests/test_closing.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4000,26 +4000,36 @@ def test_peer_anchor_push(node_factory, bitcoind, executor, chainparams):
40004000
# We put l3's tx in the mempool, but won't mine it.
40014001
bitcoind.rpc.sendrawtransaction(closetx)
40024002

4003-
# We aim for feerate ~3750, so this won't mine it.
4004-
# HTLC's going to time out at block 119
4005-
for block in range(108, 119):
4003+
# We aim for feerate ~3750, so this won't mine l3's unilateral close.
4004+
# HTLC's going to time out at block 120 (we give one block grace)
4005+
for block in range(110, 120):
40064006
bitcoind.generate_block(1, needfeerate=5000)
4007+
assert bitcoind.rpc.getblockcount() == block
40074008
sync_blockheight(bitcoind, [l2])
4009+
assert only_one(l2.rpc.listpeerchannels(l3.info['id'])['channels'])['state'] == 'CHANNELD_NORMAL'
40084010

40094011
# Drops to chain
4012+
bitcoind.generate_block(1, needfeerate=5000)
40104013
wait_for(lambda: only_one(l2.rpc.listpeerchannels(l3.info['id'])['channels'])['state'] == 'AWAITING_UNILATERAL')
40114014

4012-
# But, l3's tx already there, and identical feerate will not RBF
4015+
# But, l3's tx already there, and identical feerate will not RBF.
40134016
l2.daemon.wait_for_log("rejecting replacement")
4014-
assert bitcoind.rpc.getrawmempool() != []
4017+
wait_for(lambda: len(bitcoind.rpc.getrawmempool()) == 2)
40154018

40164019
# As blocks pass, we will use anchor to boost l3's tx.
4017-
for block in range(119, 124):
4018-
bitcoind.generate_block(1, needfeerate=5000)
4020+
for block, feerate in zip(range(120, 124), (12000, 13000, 14000, 15000)):
4021+
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")
4022+
bitcoind.generate_block(1, needfeerate=16000)
40194023
sync_blockheight(bitcoind, [l2])
4024+
assert len(bitcoind.rpc.getrawmempool()) == 2
40204025

4021-
# mempool should be empty, but our 'needfeerate' logic is bogus and leaves
4022-
# the anchor spend tx! So just check that l2 did see the commitment tx
4026+
# Feerate tops out at +1, so this is the same. This time we mine it!
4027+
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")
4028+
l2.daemon.wait_for_log("sendrawtx exit 0")
4029+
4030+
bitcoind.generate_block(1, needfeerate=15000)
4031+
4032+
assert bitcoind.rpc.getrawmempool() == []
40234033
wait_for(lambda: only_one(l2.rpc.listpeerchannels(l3.info['id'])['channels'])['state'] == 'ONCHAIN')
40244034

40254035

@@ -4274,7 +4284,7 @@ def censoring_sendrawtx(r):
42744284
height = bitcoind.rpc.getblockchaininfo()['blocks']
42754285
l1.daemon.wait_for_log(r"Low-priority anchorspend aiming for block {} \(feerate 7458\)".format(height + 13))
42764286
# Can be out-by-one (short sig)!
4277-
l1.daemon.wait_for_log(r"Anchorspend for local commit tx fee 12037 \(w=674\), commit_tx fee 1735sat \(w=768\): package feerate 9550 perkw")
4287+
l1.daemon.wait_for_log(r"Anchorspend for local commit tx fee 9377sat \(w=722\), commit_tx fee 1735sat \(w=768\): package feerate 7457 perkw")
42784288
assert not l1.daemon.is_in_log("Low-priority anchorspend aiming for block {}".format(height + 12))
42794289

42804290
bitcoind.generate_block(1)

0 commit comments

Comments
 (0)