Skip to content

Commit ee41768

Browse files
committed
lightningd: rewrite anchor spend to use multiple UTXOs if needed.
Closes: #6747 Changelog-EXPERIMENTAL: Fixed anchor spending to be able to use more than one UTXO. Signed-off-by: Rusty Russell <[email protected]>
1 parent f67c948 commit ee41768

File tree

5 files changed

+113
-94
lines changed

5 files changed

+113
-94
lines changed

bitcoin/tx.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -966,17 +966,19 @@ size_t bitcoin_tx_2of2_input_witness_weight(void)
966966
);
967967
}
968968

969+
size_t change_weight(void)
970+
{
971+
return bitcoin_tx_output_weight(chainparams->is_elements ? BITCOIN_SCRIPTPUBKEY_P2WPKH_LEN : BITCOIN_SCRIPTPUBKEY_P2TR_LEN);
972+
}
973+
969974
struct amount_sat change_fee(u32 feerate_perkw, size_t total_weight)
970975
{
971-
size_t outweight;
972976
struct amount_sat fee;
973977

974978
/* Must be able to pay for its own additional weight */
975-
outweight = bitcoin_tx_output_weight(chainparams->is_elements ? BITCOIN_SCRIPTPUBKEY_P2WPKH_LEN : BITCOIN_SCRIPTPUBKEY_P2TR_LEN);
976-
977979
/* Rounding can cause off by one errors, so we do this */
978980
if (!amount_sat_sub(&fee,
979-
amount_tx_fee(feerate_perkw, outweight + total_weight),
981+
amount_tx_fee(feerate_perkw, change_weight() + total_weight),
980982
amount_tx_fee(feerate_perkw, total_weight)))
981983
abort();
982984
return fee;

bitcoin/tx.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,11 @@ size_t bitcoin_tx_simple_input_weight(bool p2sh);
319319
/* The witness for our 2of2 input (closing or commitment tx). */
320320
size_t bitcoin_tx_2of2_input_witness_weight(void);
321321

322+
/**
323+
* change_weight - what's the weight of a change output?
324+
*/
325+
size_t change_weight(void);
326+
322327
/**
323328
* change_fee - what's the cost to add a change output to this tx?
324329
* @feerate_perkw: feerate.

hsmd/libhsmd.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -511,8 +511,8 @@ static void sign_our_inputs(struct utxo **utxos, struct wally_psbt *psbt)
511511
psbt_set_version(psbt, 0);
512512
hsmd_status_failed(STATUS_FAIL_INTERNAL_ERROR,
513513
"Received wally_err attempting to "
514-
"sign utxo with key %s. PSBT: %s",
515-
type_to_string(tmpctx, struct pubkey,
514+
"sign input %zu with key %s. PSBT: %s",
515+
j, type_to_string(tmpctx, struct pubkey,
516516
&pubkey),
517517
type_to_string(tmpctx, struct wally_psbt,
518518
psbt));

lightningd/anchorspend.c

Lines changed: 100 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <lightningd/lightningd.h>
2020
#include <lightningd/log.h>
2121
#include <lightningd/peer_control.h>
22+
#include <wally_psbt.h>
2223

2324
/* This is attached to each anchor tx retransmission */
2425
struct one_anchor {
@@ -209,15 +210,51 @@ struct anchor_details *create_anchor_details(const tal_t *ctx,
209210
return adet;
210211
}
211212

212-
static u32 commit_feerate(const struct local_anchor_info *info)
213+
/* total_weight includes the commitment tx we're trying to push! */
214+
static struct wally_psbt *anchor_psbt(const tal_t *ctx,
215+
struct channel *channel,
216+
struct one_anchor *anch,
217+
struct utxo **utxos,
218+
u32 feerate_target,
219+
size_t total_weight)
213220
{
214-
u32 feerate;
221+
struct lightningd *ld = channel->peer->ld;
222+
struct wally_psbt *psbt;
223+
struct amount_sat change, fee;
224+
struct pubkey final_key;
215225

216-
/* Fee should not overflow! */
217-
if (!amount_feerate(&feerate, info->commitment_fee, info->commitment_weight))
218-
abort();
226+
/* PSBT knows how to spend utxos. */
227+
psbt = psbt_using_utxos(ctx, ld->wallet, utxos,
228+
default_locktime(ld->topology),
229+
BITCOIN_TX_RBF_SEQUENCE, NULL);
219230

220-
return feerate;
231+
/* BOLT #3:
232+
* #### `to_local_anchor` and `to_remote_anchor` Output (option_anchors)
233+
*...
234+
* The amount of the output is fixed at 330 sats, the default
235+
* dust limit for P2WSH.
236+
*/
237+
psbt_append_input(psbt, &anch->info.anchor_point, BITCOIN_TX_RBF_SEQUENCE,
238+
NULL, anch->adet->anchor_wscript, NULL);
239+
psbt_input_set_wit_utxo(psbt, psbt->num_inputs - 1,
240+
scriptpubkey_p2wsh(tmpctx, anch->adet->anchor_wscript),
241+
AMOUNT_SAT(330));
242+
psbt_input_add_pubkey(psbt, psbt->num_inputs - 1, &channel->local_funding_pubkey, false);
243+
244+
/* A zero-output tx is invalid: we must have change, even if not really economic */
245+
change = psbt_compute_fee(psbt);
246+
/* Assume we add a change output, what would the total fee be? */
247+
fee = amount_tx_fee(feerate_target, total_weight + change_weight());
248+
if (!amount_sat_sub(&change, change, fee)
249+
|| amount_sat_less(change, chainparams->dust_limit)) {
250+
change = chainparams->dust_limit;
251+
}
252+
253+
bip32_pubkey(ld, &final_key, channel->final_key_idx);
254+
psbt_append_output(psbt,
255+
scriptpubkey_p2wpkh(tmpctx, &final_key),
256+
change);
257+
return psbt;
221258
}
222259

223260
/* If it's possible and worth it, return signed tx. Otherwise NULL. */
@@ -227,59 +264,93 @@ static struct bitcoin_tx *spend_anchor(const tal_t *ctx,
227264
{
228265
struct lightningd *ld = channel->peer->ld;
229266
struct utxo **utxos;
230-
size_t weight;
231-
struct amount_sat fee, diff, change;
267+
size_t base_weight, weight;
268+
struct amount_sat fee, diff;
232269
struct bitcoin_tx *tx;
233-
bool worthwhile;
234270
struct wally_psbt *psbt;
235271
struct amount_msat total_value;
236-
struct pubkey final_key;
237272
const u8 *msg;
238273

239-
/* Estimate weight of spend tx plus commitment_tx */
240-
weight = bitcoin_tx_core_weight(2, 1)
241-
+ bitcoin_tx_input_weight(false, bitcoin_tx_simple_input_witness_weight())
274+
/* Estimate weight of spend tx plus commitment_tx (not including any UTXO we add) */
275+
base_weight = bitcoin_tx_core_weight(2, 1)
242276
+ bitcoin_tx_input_weight(false,
243277
bitcoin_tx_input_sig_weight()
244278
+ 1 + tal_bytelen(anch->adet->anchor_wscript))
245279
+ bitcoin_tx_output_weight(BITCOIN_SCRIPTPUBKEY_P2WPKH_LEN)
246280
+ anch->info.commitment_weight;
247281

248-
worthwhile = false;
249282
total_value = AMOUNT_MSAT(0);
250-
for (int i = tal_count(anch->adet->vals) - 1; i >= 0 && !worthwhile; --i) {
283+
psbt = NULL;
284+
for (int i = tal_count(anch->adet->vals) - 1; i >= 0; --i) {
251285
const struct deadline_value *val = &anch->adet->vals[i];
252-
u32 feerate;
286+
u32 feerate, feerate_target;
287+
struct wally_psbt *candidate_psbt;
253288

254289
/* Calculate the total value for the current deadline
255290
* and all the following */
256291
if (!amount_msat_add(&total_value, total_value, val->msat))
257292
return NULL;
258293

259-
feerate = feerate_for_target(ld->topology, val->block);
260-
/* Would the commit tx make that feerate by itself? */
261-
if (commit_feerate(&anch->info) >= feerate)
294+
feerate_target = feerate_for_target(ld->topology, val->block);
295+
296+
/* Ask for some UTXOs which could meet this feerate */
297+
weight = base_weight;
298+
utxos = wallet_utxo_boost(tmpctx,
299+
ld->wallet,
300+
get_block_height(ld->topology),
301+
anch->info.commitment_fee,
302+
feerate_target,
303+
&weight);
304+
305+
/* Create a new candidate PSBT */
306+
candidate_psbt = anchor_psbt(tmpctx, channel, anch, utxos, feerate_target, weight);
307+
if (!candidate_psbt)
262308
continue;
263309

264-
/* Get the fee required to meet the current block */
265-
fee = amount_tx_fee(feerate, weight);
310+
fee = psbt_compute_fee(candidate_psbt);
311+
312+
/* Is it even worth spending this fee to meet the deadline? */
313+
if (!amount_msat_greater_sat(total_value, fee)) {
314+
log_debug(channel->log,
315+
"Not worth fee %s for %s commit tx to get %s in %u blocks at feerate %uperkw",
316+
fmt_amount_sat(tmpctx, fee),
317+
anch->commit_side == LOCAL ? "local" : "remote",
318+
fmt_amount_msat(tmpctx, val->msat),
319+
val->block, feerate);
320+
break;
321+
}
266322

267-
/* We already have part of the fee in commitment_tx. */
268-
if (amount_sat_sub(&fee, fee, anch->info.commitment_fee)
269-
&& amount_msat_greater_sat(total_value, fee)) {
270-
worthwhile = true;
323+
if (!amount_feerate(&feerate, fee, weight))
324+
abort();
325+
326+
if (feerate < feerate_target) {
327+
/* We might have had lower feerates which worked: only complain if
328+
* we have *nothing* */
329+
if (tal_count(utxos) == 0 && !psbt) {
330+
log_unusual(channel->log,
331+
"No utxos to bump commit_tx to feerate %uperkw!",
332+
feerate_target);
333+
break;
334+
}
335+
336+
log_unusual(channel->log,
337+
"We want to bump commit_tx to feerate %uperkw, but can only bump to %uperkw!",
338+
feerate_target, feerate);
339+
psbt = candidate_psbt;
340+
/* We don't expect to do any better at higher feerates */
341+
break;
271342
}
272343

273-
log_debug(channel->log, "%s fee %s for %s commit tx to get %s in %u blocks at feerate %uperkw",
274-
worthwhile ? "Worth" : "Not worth",
344+
log_debug(channel->log, "Worth fee %s for %s commit tx to get %s in %u blocks at feerate %uperkw",
275345
fmt_amount_sat(tmpctx, fee),
276346
anch->commit_side == LOCAL ? "local" : "remote",
277347
fmt_amount_msat(tmpctx, val->msat),
278348
val->block, feerate);
349+
psbt = candidate_psbt;
279350
}
280351

281-
/* Not worth it? */
282-
if (!worthwhile)
352+
/* No psbt was worth it? */
353+
if (!psbt)
283354
return NULL;
284355

285356
/* Higher enough than previous to be valid RBF?
@@ -299,63 +370,6 @@ static struct bitcoin_tx *spend_anchor(const tal_t *ctx,
299370
(fee.satoshis + anch->info.commitment_fee.satoshis) /* Raw: debug log */
300371
* 1000 / weight);
301372

302-
/* FIXME: Use more than one utxo! */
303-
utxos = tal_arr(tmpctx, struct utxo *, 1);
304-
utxos[0] = wallet_find_utxo(utxos, ld->wallet,
305-
get_block_height(ld->topology),
306-
NULL,
307-
0, /* FIXME: unused! */
308-
0, false, NULL);
309-
if (!utxos[0]) {
310-
log_unusual(channel->log,
311-
"We want to bump commit_tx fee, but no funds!");
312-
return NULL;
313-
}
314-
315-
/* FIXME: we get a random UTXO. We should really allow
316-
* multiple UTXOs here */
317-
if (amount_sat_less(utxos[0]->amount, fee)) {
318-
log_unusual(channel->log,
319-
"We want to bump commit_tx with fee %s, but utxo %s is only %s!",
320-
fmt_amount_sat(tmpctx, fee),
321-
type_to_string(tmpctx, struct bitcoin_outpoint,
322-
&utxos[0]->outpoint),
323-
fmt_amount_sat(tmpctx, utxos[0]->amount));
324-
return NULL;
325-
}
326-
327-
/* PSBT knows how to spend utxos. */
328-
psbt = psbt_using_utxos(tmpctx, ld->wallet, utxos,
329-
default_locktime(ld->topology),
330-
BITCOIN_TX_RBF_SEQUENCE, NULL);
331-
332-
/* BOLT #3:
333-
* #### `to_local_anchor` and `to_remote_anchor` Output (option_anchors)
334-
*...
335-
* The amount of the output is fixed at 330 sats, the default
336-
* dust limit for P2WSH.
337-
*/
338-
psbt_append_input(psbt, &anch->info.anchor_point, BITCOIN_TX_RBF_SEQUENCE,
339-
NULL, anch->adet->anchor_wscript, NULL);
340-
psbt_input_set_wit_utxo(psbt, 1,
341-
scriptpubkey_p2wsh(tmpctx, anch->adet->anchor_wscript),
342-
AMOUNT_SAT(330));
343-
psbt_input_add_pubkey(psbt, 1, &channel->local_funding_pubkey, false);
344-
345-
if (!amount_sat_add(&change, utxos[0]->amount, AMOUNT_SAT(330))
346-
|| !amount_sat_sub(&change, change, fee)) {
347-
log_broken(channel->log,
348-
"Error calculating anchorspend change: utxo %s fee %s",
349-
fmt_amount_sat(tmpctx, utxos[0]->amount),
350-
fmt_amount_sat(tmpctx, fee));
351-
return NULL;
352-
}
353-
354-
bip32_pubkey(ld, &final_key, channel->final_key_idx);
355-
psbt_append_output(psbt,
356-
scriptpubkey_p2wpkh(tmpctx, &final_key),
357-
change);
358-
359373
/* OK, HSM, sign it! */
360374
msg = towire_hsmd_sign_anchorspend(NULL,
361375
&channel->peer->id,

tests/test_closing.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3703,7 +3703,6 @@ def ignore_sendrawtx(r):
37033703
wait_for(lambda: len(bitcoind.rpc.getrawmempool()) == 1)
37043704

37053705

3706-
@pytest.mark.xfail(strict=True)
37073706
@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd anchors unsupported')
37083707
def test_closing_anchorspend_htlc_tx_rbf(node_factory, bitcoind):
37093708
# We want an outstanding HTLC for l1, so it uses anchor to push.
@@ -3898,7 +3897,6 @@ def test_closing_minfee(node_factory, bitcoind):
38983897
bitcoind.generate_block(1, wait_for_mempool=txid)
38993898

39003899

3901-
@pytest.mark.xfail(strict=True)
39023900
@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd anchors not supportd')
39033901
def test_peer_anchor_push(node_factory, bitcoind, executor, chainparams):
39043902
"""Test that we use anchor on peer's commit to CPFP tx"""

0 commit comments

Comments
 (0)