Skip to content

Commit 7a276bb

Browse files
committed
common/utxo: use a real type for the UTXO, not a boolean is_p2sh.
To actually evaluate spend cost, we need to know whether it's taproot or not. Using an enum (rather than making callers examine the script) means we can ensure all cases are handled. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
1 parent 2154289 commit 7a276bb

File tree

9 files changed

+90
-25
lines changed

9 files changed

+90
-25
lines changed

common/utxo.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ size_t utxo_spend_weight(const struct utxo *utxo, size_t min_witness_weight)
88
/* If the min is less than what we'd use for a 'normal' tx,
99
* we return the value with the greater added/calculated */
1010
if (wit_weight < min_witness_weight)
11-
return bitcoin_tx_input_weight(utxo->is_p2sh,
11+
return bitcoin_tx_input_weight(utxo->utxotype == UTXO_P2SH_P2WPKH,
1212
min_witness_weight);
1313

14-
return bitcoin_tx_input_weight(utxo->is_p2sh, wit_weight);
14+
return bitcoin_tx_input_weight(utxo->utxotype == UTXO_P2SH_P2WPKH, wit_weight);
1515
}
1616

1717
u32 utxo_is_immature(const struct utxo *utxo, u32 blockheight)
@@ -31,3 +31,18 @@ u32 utxo_is_immature(const struct utxo *utxo, u32 blockheight)
3131
return 0;
3232
}
3333
}
34+
35+
const char *utxotype_to_str(enum utxotype utxotype)
36+
{
37+
switch (utxotype) {
38+
case UTXO_P2SH_P2WPKH:
39+
return "p2sh_p2wpkh";
40+
case UTXO_P2WPKH:
41+
return "p2wpkh";
42+
case UTXO_P2WSH_FROM_CLOSE:
43+
return "p2wsh_from_close";
44+
case UTXO_P2TR:
45+
return "p2tr";
46+
}
47+
abort();
48+
}

common/utxo.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,24 @@ enum output_status {
3131
OUTPUT_STATE_ANY = 255
3232
};
3333

34+
enum utxotype {
35+
/* Obsolete: we used to have P2SH-wrapped outputs (removed in 24.02, though can still have old UTXOs) */
36+
UTXO_P2SH_P2WPKH = 1,
37+
/* "bech32" addresses */
38+
UTXO_P2WPKH = 2,
39+
/* Used for closing addresses: implies ->close_info is non-NULL */
40+
UTXO_P2WSH_FROM_CLOSE = 3,
41+
/* "p2tr" addresses. */
42+
UTXO_P2TR = 4,
43+
};
44+
45+
const char *utxotype_to_str(enum utxotype utxotype);
46+
3447
struct utxo {
3548
struct bitcoin_outpoint outpoint;
3649
struct amount_sat amount;
3750
u32 keyindex;
38-
bool is_p2sh;
51+
enum utxotype utxotype;
3952
enum output_status status;
4053

4154
/* Optional unilateral close information, NULL if this is just

devtools/mkfunding.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ static struct bitcoin_tx *tx_spending_utxo(const tal_t *ctx,
4242
struct bitcoin_tx *tx = bitcoin_tx(ctx, chainparams, 1, num_output,
4343
nlocktime);
4444

45-
assert(!utxo->is_p2sh);
45+
assert(utxo->utxotype != UTXO_P2SH_P2WPKH);
4646
bitcoin_tx_add_input(tx, &utxo->outpoint,
4747
nsequence, NULL, utxo->amount,
4848
utxo->scriptPubkey, NULL);
@@ -95,7 +95,7 @@ int main(int argc, char *argv[])
9595
if (argc != 1 + 7)
9696
errx(1, "Usage: mkfunding <input-txid> <input-txout> <input-amount> <feerate-per-kw> <input-privkey> <local-funding-privkey> <remote-funding-privkey>");
9797

98-
input.is_p2sh = false;
98+
input.utxotype = UTXO_P2WPKH;
9999
input.close_info = NULL;
100100

101101
argnum = 1;

tests/test_closing.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,9 @@ def test_closing_simple(node_factory, bitcoind, chainparams):
7676
]
7777
bitcoind.generate_block(1)
7878

79-
l1.daemon.wait_for_log(r'Owning output.* \(SEGWIT\).* txid %s.* CONFIRMED' % closetxid)
80-
l2.daemon.wait_for_log(r'Owning output.* \(SEGWIT\).* txid %s.* CONFIRMED' % closetxid)
79+
outtype = 'p2tr' if not chainparams['elements'] else 'p2wpkh'
80+
l1.daemon.wait_for_log(rf'Owning output.* \({outtype}\).* txid %s.* CONFIRMED' % closetxid)
81+
l2.daemon.wait_for_log(rf'Owning output.* \({outtype}\).* txid %s.* CONFIRMED' % closetxid)
8182

8283
# Make sure both nodes have grabbed their close tx funds
8384
assert closetxid in set([o['txid'] for o in l1.rpc.listfunds()['outputs']])
@@ -333,13 +334,15 @@ def test_closing_specified_destination(node_factory, bitcoind, chainparams):
333334
bitcoind.generate_block(1)
334335
sync_blockheight(bitcoind, [l1, l2, l3, l4])
335336

337+
outtype = 'p2tr' if not chainparams['elements'] else 'p2wpkh'
338+
336339
# l1 can't spent the output to addr.
337340
for txid in closetxs.values():
338-
assert not l1.daemon.is_in_log(r'Owning output.* \(SEGWIT\).* txid {}.* CONFIRMED'.format(txid))
341+
assert not l1.daemon.is_in_log(rf'Owning output.* \({outtype}\).* txid {txid}.* CONFIRMED')
339342

340343
# Check the txid has at least 1 confirmation
341344
for n, txid in closetxs.items():
342-
n.daemon.wait_for_log(r'Owning output.* \(SEGWIT\).* txid {}.* CONFIRMED'.format(txid))
345+
n.daemon.wait_for_log(rf'Owning output.* \({outtype}\).* txid {txid}.* CONFIRMED')
343346

344347
for n in [l2, l3, l4]:
345348
# Make sure both nodes have grabbed their close tx funds

tests/test_misc.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ def ping_tests(l1, l2):
359359
ping_tests(l1, l2)
360360

361361

362-
def test_htlc_sig_persistence(node_factory, bitcoind, executor):
362+
def test_htlc_sig_persistence(node_factory, bitcoind, executor, chainparams):
363363
"""Interrupt a payment between two peers, then fail and recover funds using the HTLC sig.
364364
"""
365365
# Feerates identical so we don't get gratuitous commit to update them
@@ -399,8 +399,9 @@ def test_htlc_sig_persistence(node_factory, bitcoind, executor):
399399

400400
bitcoind.generate_block(5)
401401
bitcoind.generate_block(1, wait_for_mempool=txid)
402+
outtype = 'p2tr' if not chainparams['elements'] else 'p2wpkh'
402403
l1.daemon.wait_for_logs([
403-
r'Owning output . (\d+)sat .SEGWIT. txid',
404+
rf'Owning output . (\d+)sat \({outtype}\) txid {txid} CONFIRMED',
404405
])
405406

406407
# We should now have 1) the unilateral to us, and b) the HTLC respend to us

wallet/reservation.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ struct wally_psbt *psbt_using_utxos(const tal_t *ctx,
277277
u32 this_nsequence;
278278
struct bitcoin_tx *tx;
279279

280-
if (utxos[i]->is_p2sh) {
280+
if (utxos[i]->utxotype == UTXO_P2SH_P2WPKH) {
281281
bip32_pubkey(wallet->ld, &key, utxos[i]->keyindex);
282282
scriptSig = bitcoin_scriptsig_p2sh_p2wpkh(tmpctx, &key);
283283
redeemscript = bitcoin_redeem_p2sh_p2wpkh(tmpctx, &key);

wallet/test/run-wallet.c

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1403,9 +1403,11 @@ static bool test_wallet_outputs(struct lightningd *ld, const tal_t *ctx)
14031403
u.close_info->peer_id = id;
14041404
u.close_info->commitment_point = &pk;
14051405
u.close_info->option_anchors = false;
1406-
/* Arbitrarily set scriptpubkey len to 20 */
1407-
u.scriptPubkey = tal_arr(w, u8, 20);
1408-
memset(u.scriptPubkey, 1, 20);
1406+
/* P2WSH */
1407+
u.scriptPubkey = tal_arr(w, u8, BITCOIN_SCRIPTPUBKEY_P2WSH_LEN);
1408+
u.scriptPubkey[0] = OP_0;
1409+
u.scriptPubkey[1] = sizeof(struct sha256);
1410+
memset(u.scriptPubkey + 2, 1, sizeof(struct sha256));
14091411
CHECK_MSG(wallet_add_utxo(w, &u, our_change),
14101412
"wallet_add_utxo with close_info");
14111413

@@ -1473,8 +1475,10 @@ static bool test_wallet_outputs(struct lightningd *ld, const tal_t *ctx)
14731475
CHECK_MSG(!wallet_err, wallet_err);
14741476

14751477
u.blockheight = blockheight;
1476-
u.scriptPubkey = tal_arr(w, u8, 20);
1477-
memset(u.scriptPubkey, 1, 20);
1478+
u.scriptPubkey = tal_arr(w, u8, BITCOIN_SCRIPTPUBKEY_P2WPKH_LEN);
1479+
u.scriptPubkey[0] = OP_0;
1480+
u.scriptPubkey[1] = sizeof(struct ripemd160);
1481+
memset(u.scriptPubkey + 2, 1, sizeof(struct ripemd160));
14781482
CHECK_MSG(wallet_add_utxo(w, &u, p2sh_wpkh),
14791483
"wallet_add_utxo with close_info no commitment_point");
14801484
CHECK_MSG(!wallet_err, wallet_err);

wallet/wallet.c

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,6 @@ static struct utxo *wallet_stmt2output(const tal_t *ctx, struct db_stmt *stmt)
338338
db_col_txid(stmt, "prev_out_tx", &utxo->outpoint.txid);
339339
utxo->outpoint.n = db_col_int(stmt, "prev_out_index");
340340
utxo->amount = db_col_amount_sat(stmt, "value");
341-
utxo->is_p2sh = db_col_int(stmt, "type") == p2sh_wpkh;
342341
utxo->status = db_col_int(stmt, "status");
343342
utxo->keyindex = db_col_int(stmt, "keyindex");
344343

@@ -364,6 +363,19 @@ static struct utxo *wallet_stmt2output(const tal_t *ctx, struct db_stmt *stmt)
364363
}
365364

366365
utxo->scriptPubkey = db_col_arr(utxo, stmt, "scriptpubkey", u8);
366+
/* FIXME: add p2tr to type? */
367+
if (db_col_int(stmt, "type") == p2sh_wpkh)
368+
utxo->utxotype = UTXO_P2SH_P2WPKH;
369+
else if (is_p2wpkh(utxo->scriptPubkey, tal_bytelen(utxo->scriptPubkey), NULL))
370+
utxo->utxotype = UTXO_P2WPKH;
371+
else if (is_p2tr(utxo->scriptPubkey, tal_bytelen(utxo->scriptPubkey), NULL))
372+
utxo->utxotype = UTXO_P2TR;
373+
else if (is_p2wsh(utxo->scriptPubkey, tal_bytelen(utxo->scriptPubkey), NULL)) {
374+
if (!utxo->close_info)
375+
fatal("Unspendable scriptPubkey without close_info %s", tal_hex(tmpctx, utxo->scriptPubkey));
376+
utxo->utxotype = UTXO_P2WSH_FROM_CLOSE;
377+
} else
378+
fatal("Unknown utxo type %s", tal_hex(tmpctx, utxo->scriptPubkey));
367379

368380
utxo->blockheight = NULL;
369381
utxo->spendheight = NULL;
@@ -776,7 +788,7 @@ struct utxo *wallet_find_utxo(const tal_t *ctx, struct wallet *w,
776788
while (!utxo && db_step(stmt)) {
777789
utxo = wallet_stmt2output(ctx, stmt);
778790
if (excluded(excludes, utxo)
779-
|| (nonwrapped && utxo->is_p2sh)
791+
|| (nonwrapped && utxo->utxotype == UTXO_P2SH_P2WPKH)
780792
|| !deep_enough(maxheight, utxo, current_blockheight))
781793
utxo = tal_free(utxo);
782794

@@ -3028,7 +3040,24 @@ static void got_utxo(struct wallet *w,
30283040
struct amount_asset asset = wally_tx_output_get_amount(txout);
30293041

30303042
utxo->keyindex = keyindex;
3031-
utxo->is_p2sh = (addrtype == ADDR_P2SH_SEGWIT);
3043+
/* This switch() pattern catches anyone adding new cases, plus
3044+
* runtime errors */
3045+
switch (addrtype) {
3046+
case ADDR_P2SH_SEGWIT:
3047+
utxo->utxotype = UTXO_P2SH_P2WPKH;
3048+
goto type_ok;
3049+
case ADDR_BECH32:
3050+
utxo->utxotype = UTXO_P2WPKH;
3051+
goto type_ok;
3052+
case ADDR_P2TR:
3053+
utxo->utxotype = UTXO_P2TR;
3054+
goto type_ok;
3055+
case ADDR_ALL:
3056+
break;
3057+
}
3058+
abort();
3059+
3060+
type_ok:
30323061
utxo->amount = amount_asset_to_sat(&asset);
30333062
utxo->status = OUTPUT_STATE_AVAILABLE;
30343063
wally_txid(wtx, &utxo->outpoint.txid);
@@ -3042,7 +3071,7 @@ static void got_utxo(struct wallet *w,
30423071
log_debug(w->log, "Owning output %zu %s (%s) txid %s%s%s",
30433072
outnum,
30443073
fmt_amount_sat(tmpctx, utxo->amount),
3045-
utxo->is_p2sh ? "P2SH" : "SEGWIT",
3074+
utxotype_to_str(utxo->utxotype),
30463075
fmt_bitcoin_txid(tmpctx, &utxo->outpoint.txid),
30473076
blockheight ? " CONFIRMED" : "",
30483077
is_coinbase ? " COINBASE" : "");
@@ -3058,7 +3087,7 @@ static void got_utxo(struct wallet *w,
30583087
notify_chain_mvt(w->ld, mvt);
30593088
}
30603089

3061-
if (!wallet_add_utxo(w, utxo, utxo->is_p2sh ? p2sh_wpkh : our_change)) {
3090+
if (!wallet_add_utxo(w, utxo, utxo->utxotype == UTXO_P2SH_P2WPKH ? p2sh_wpkh : our_change)) {
30623091
/* In case we already know the output, make
30633092
* sure we actually track its
30643093
* blockheight. This can happen when we grab
@@ -3070,7 +3099,7 @@ static void got_utxo(struct wallet *w,
30703099
}
30713100

30723101
/* This is an unconfirmed change output, we should track it */
3073-
if (!utxo->is_p2sh && !blockheight)
3102+
if (utxo->utxotype != UTXO_P2SH_P2WPKH && !blockheight)
30743103
txfilter_add_scriptpubkey(w->ld->owned_txfilter, txout->script);
30753104

30763105
outpointfilter_add(w->owned_outpoints, &utxo->outpoint);

wallet/walletrpc.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ static void json_add_utxo(struct json_stream *response,
349349
json_add_num(response, "output", utxo->outpoint.n);
350350
json_add_amount_sat_msat(response, "amount_msat", utxo->amount);
351351

352-
if (utxo->is_p2sh) {
352+
if (utxo->utxotype == UTXO_P2SH_P2WPKH) {
353353
struct pubkey key;
354354
bip32_pubkey(wallet->ld, &key, utxo->keyindex);
355355

@@ -693,7 +693,7 @@ static struct command_result *match_psbt_inputs_to_utxos(struct command *cmd,
693693
if (!psbt->inputs[i].utxo && !psbt->inputs[i].witness_utxo) {
694694
u8 *scriptPubKey;
695695

696-
if (utxo->is_p2sh) {
696+
if (utxo->utxotype == UTXO_P2SH_P2WPKH) {
697697
struct pubkey key;
698698
u8 *redeemscript;
699699
int wally_err;

0 commit comments

Comments
 (0)