Skip to content

Commit

Permalink
Support p2tr deposit addresses
Browse files Browse the repository at this point in the history
  • Loading branch information
instagibbs committed May 22, 2023
1 parent 09fa087 commit 58c55f1
Show file tree
Hide file tree
Showing 37 changed files with 293 additions and 124 deletions.
11 changes: 9 additions & 2 deletions .msggen.json
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,8 @@
"NewaddrAddresstype": {
"all": 2,
"bech32": 0,
"p2sh-segwit": 1
"p2sh-segwit": 1,
"p2tr": 3
},
"PayStatus": {
"complete": 0,
Expand Down Expand Up @@ -1257,7 +1258,9 @@
},
"NewaddrResponse": {
"NewAddr.bech32": 1,
"NewAddr.p2sh-segwit": 2
"NewAddr.p2sh-segwit": 2,
"NewAddr.p2tr": 3,
"NewAddr.script": 4
},
"PayRequest": {
"Pay.amount_msat": 13,
Expand Down Expand Up @@ -4608,6 +4611,10 @@
"added": "pre-v0.10.1",
"deprecated": "v23.02"
},
"NewAddr.p2tr": {
"added": "v23.05",
"deprecated": false
},
"Pay": {
"added": "pre-v0.10.1",
"deprecated": null
Expand Down
2 changes: 1 addition & 1 deletion bitcoin/tx.c
Original file line number Diff line number Diff line change
Expand Up @@ -972,7 +972,7 @@ struct amount_sat change_amount(struct amount_sat excess, u32 feerate_perkw,
struct amount_sat change_fee;

/* Must be able to pay for its own additional weight */
outweight = bitcoin_tx_output_weight(BITCOIN_SCRIPTPUBKEY_P2WPKH_LEN);
outweight = bitcoin_tx_output_weight(chainparams->is_elements ? BITCOIN_SCRIPTPUBKEY_P2WPKH_LEN : BITCOIN_SCRIPTPUBKEY_P2TR_LEN);

/* Rounding can cause off by one errors, so we do this */
if (!amount_sat_sub(&change_fee,
Expand Down
4 changes: 3 additions & 1 deletion bitcoin/tx.h
Original file line number Diff line number Diff line change
Expand Up @@ -320,10 +320,12 @@ size_t bitcoin_tx_simple_input_weight(bool p2sh);
size_t bitcoin_tx_2of2_input_witness_weight(void);

/**
* change_amount - Is it worth making a P2WPKH change output at this feerate?
* change_amount - Is it worth making a change output at this feerate?
* @excess: input amount we have above the tx fee and other outputs.
* @feerate_perkw: feerate.
*
* Change script is P2TR for Bitcoin, P2WPKH for Elements
*
* If it's not worth (or possible) to make change, returns AMOUNT_SAT(0).
* Otherwise returns the amount of the change output to add (@excess minus
* the additional fee for the change output itself).
Expand Down
4 changes: 2 additions & 2 deletions channeld/full_channel.c
Original file line number Diff line number Diff line change
Expand Up @@ -338,9 +338,9 @@ struct bitcoin_tx **channel_txs(const tal_t *ctx,

/* Set the remote/local pubkeys on the commitment tx psbt */
psbt_input_add_pubkey(txs[0]->psbt, 0,
&channel->funding_pubkey[side]);
&channel->funding_pubkey[side], false /* is_taproot */);
psbt_input_add_pubkey(txs[0]->psbt, 0,
&channel->funding_pubkey[!side]);
&channel->funding_pubkey[!side], false /* is_taproot */);

add_htlcs(&txs, *htlcmap, channel, &keyset, side);

Expand Down
2 changes: 1 addition & 1 deletion channeld/watchtower.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ penalty_tx_create(const tal_t *ctx,
bitcoin_tx_add_output(tx, final_scriptpubkey, NULL, to_them_sats);
assert((final_index == NULL) == (final_ext_key == NULL));
if (final_index)
psbt_add_keypath_to_last_output(tx, *final_index, final_ext_key);
psbt_add_keypath_to_last_output(tx, *final_index, final_ext_key, is_p2tr(final_scriptpubkey, NULL));

/* Worst-case sig is 73 bytes */
weight = bitcoin_tx_weight(tx) + 1 + 3 + 73 + 0 + tal_count(wscript);
Expand Down
2 changes: 2 additions & 0 deletions cln-grpc/proto/node.proto

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions cln-grpc/src/convert.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion cln-rpc/src/model.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion common/close_tx.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ struct bitcoin_tx *create_close_tx(const tal_t *ctx,
assert((local_wallet_index == NULL) == (local_wallet_ext_key == NULL));
if (local_wallet_index)
psbt_add_keypath_to_last_output(
tx, *local_wallet_index, local_wallet_ext_key);
tx, *local_wallet_index, local_wallet_ext_key, is_p2tr(script, NULL));
num_outputs++;
}

Expand Down
4 changes: 2 additions & 2 deletions common/initial_channel.c
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,9 @@ struct bitcoin_tx *initial_channel_tx(const tal_t *ctx,

if (init_tx) {
psbt_input_add_pubkey(init_tx->psbt, 0,
&channel->funding_pubkey[side]);
&channel->funding_pubkey[side], false /* is_taproot */);
psbt_input_add_pubkey(init_tx->psbt, 0,
&channel->funding_pubkey[!side]);
&channel->funding_pubkey[!side], false /* is_taproot */);
}

return init_tx;
Expand Down
29 changes: 19 additions & 10 deletions common/psbt_keypath.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@
#include <common/psbt_keypath.h>
#include <common/utils.h>
#include <wally_bip32.h>
#include <wally_psbt.h>

void psbt_set_keypath(u32 index, const struct ext_key *ext, struct wally_map *map_in) {
void psbt_output_set_keypath(u32 index, const struct ext_key *ext, bool is_taproot, struct wally_psbt_output *output) {
u8 fingerprint[BIP32_KEY_FINGERPRINT_LEN];
if (bip32_key_get_fingerprint(
(struct ext_key *) ext, fingerprint, sizeof(fingerprint)) != WALLY_OK)
Expand All @@ -14,20 +13,30 @@ void psbt_set_keypath(u32 index, const struct ext_key *ext, struct wally_map *ma
u32 path[1];
path[0] = index;

if (wally_map_keypath_add(map_in,
ext->pub_key, sizeof(ext->pub_key),
fingerprint, sizeof(fingerprint),
path, 1) != WALLY_OK)
abort();
if (is_taproot) {
if (wally_psbt_output_taproot_keypath_add(output,
ext->pub_key + 1, sizeof(ext->pub_key) - 1,
NULL, 0,
fingerprint, sizeof(fingerprint),
path, 1) != WALLY_OK)
abort();
} else {
if (wally_psbt_output_keypath_add(output,
ext->pub_key, sizeof(ext->pub_key),
fingerprint, sizeof(fingerprint),
path, 1) != WALLY_OK)
abort();
}

}

void psbt_add_keypath_to_last_output(struct bitcoin_tx *tx,
u32 key_index,
const struct ext_key *ext) {
const struct ext_key *ext,
bool is_taproot) {
size_t outndx = tx->psbt->num_outputs - 1;
struct wally_map *map_in = &tx->psbt->outputs[outndx].keypaths;

tal_wally_start();
psbt_set_keypath(key_index, ext, map_in);
psbt_output_set_keypath(key_index, ext, is_taproot, &tx->psbt->outputs[outndx]);
tal_wally_end(tx->psbt);
}
15 changes: 10 additions & 5 deletions common/psbt_keypath.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,35 @@

#include "config.h"
#include <ccan/short_types/short_types.h>
#include <wally_psbt.h>

struct bitcoin_tx;
struct ext_key;
struct wally_map;

/* psbt_set_keypath - Set the keypath of a PSBT output.
/* psbt_output_set_keypath- Set the keypath of a PSBT output.
*
* @index - child index of the wallet key
* @ext - extended public key of the immediate parent of the wallet key
* @map_in - wally keypaths map
* @is_taproot - PSBT output has taproot script
* @output - PSBT output to set
*/
void psbt_set_keypath(u32 index,
void psbt_output_set_keypath(u32 index,
const struct ext_key *ext,
struct wally_map *map_in);
bool is_taproot,
struct wally_psbt_output *output);

/* psbt_add_keypath_to_last_output - augment the last output with the
* given wallet keypath
*
* @tx - transaction to modify
* @index - child index of the wallet key
* @ext - extended public key of the immediate parent of the wallet key
* @is_taproot - if the output is taproot
*/
void psbt_add_keypath_to_last_output(struct bitcoin_tx *tx,
u32 index,
const struct ext_key *ext);
const struct ext_key *ext,
bool is_taproot);

#endif /* LIGHTNING_COMMON_PSBT_KEYPATH_H */
5 changes: 5 additions & 0 deletions common/psbt_open.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ static const u8 *linearize_input(const tal_t *ctx,
wally_psbt_input_set_final_scriptsig(&psbt->inputs[0], NULL, 0);
wally_psbt_input_set_witness_script(&psbt->inputs[0], NULL, 0);
wally_psbt_input_set_redeem_script(&psbt->inputs[0], NULL, 0);
wally_psbt_input_set_taproot_signature(&psbt->inputs[0], NULL, 0);
psbt->inputs[0].taproot_leaf_hashes.num_items = 0;
psbt->inputs[0].taproot_leaf_paths.num_items = 0;
psbt->inputs[0].keypaths.num_items = 0;
psbt->inputs[0].signatures.num_items = 0;

Expand Down Expand Up @@ -104,6 +107,8 @@ static const u8 *linearize_output(const tal_t *ctx,

/* We don't care if the keypaths change */
psbt->outputs[0].keypaths.num_items = 0;
psbt->outputs[0].taproot_leaf_hashes.num_items = 0;
psbt->outputs[0].taproot_leaf_paths.num_items = 0;
/* And you can add scripts, no problem */
wally_psbt_output_set_witness_script(&psbt->outputs[0], NULL, 0);
wally_psbt_output_set_redeem_script(&psbt->outputs[0], NULL, 0);
Expand Down
1 change: 1 addition & 0 deletions contrib/pyln-testing/pyln/testing/grpc2py.py
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,7 @@ def waitsendpay2py(m):

def newaddr2py(m):
return remove_default({
"p2tr": m.p2tr, # PrimitiveField in generate_composite
"bech32": m.bech32, # PrimitiveField in generate_composite
"p2sh_segwit": m.p2sh_segwit, # PrimitiveField in generate_composite
})
Expand Down
3 changes: 2 additions & 1 deletion doc/lightning-newaddr.7.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ RETURN VALUE
[comment]: # (GENERATE-FROM-SCHEMA-START)
On success, an object is returned, containing:

- **p2tr** (string, optional): The taproot address *(added v23.05)*
- **bech32** (string, optional): The bech32 (native segwit) address
- **p2sh-segwit** (string, optional): The p2sh-wrapped address **deprecated, removal in v23.11**

Expand All @@ -56,4 +57,4 @@ RESOURCES

Main web site: <https://github.com/ElementsProject/lightning>

[comment]: # ( SHA256STAMP:90d550bc2290dd2ab6ee67e377679fe45230a14ba6f4608fda8e51bb6670cc07)
[comment]: # ( SHA256STAMP:409fe64b6c30c0c61b8d0e6b326985a8115cd5a655c5e4ab0ad23d88f9c79ba0)
1 change: 1 addition & 0 deletions doc/schemas/newaddr.request.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"type": "string",
"enum": [
"bech32",
"p2tr",
"all"
]
}
Expand Down
5 changes: 5 additions & 0 deletions doc/schemas/newaddr.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
"additionalProperties": false,
"required": [],
"properties": {
"p2tr": {
"added": "v23.05",
"type": "string",
"description": "The taproot address"
},
"bech32": {
"type": "string",
"description": "The bech32 (native segwit) address"
Expand Down
4 changes: 3 additions & 1 deletion hsmd/libhsmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ static void sign_our_inputs(struct utxo **utxos, struct wally_psbt *psbt)
* requires the HSM to find the pubkey, and we
* skip doing that until now as a bit of a reduction
* of complexity in the calling code */
psbt_input_add_pubkey(psbt, j, &pubkey);
psbt_input_add_pubkey(psbt, j, &pubkey, utxo->scriptPubkey && is_p2tr(utxo->scriptPubkey, NULL));

/* It's actually a P2WSH in this case. */
if (utxo->close_info && utxo->close_info->option_anchor_outputs) {
Expand All @@ -503,6 +503,8 @@ static void sign_our_inputs(struct utxo **utxos, struct wally_psbt *psbt)
sizeof(privkey.secret.data),
EC_FLAG_GRIND_R) != WALLY_OK) {
tal_wally_end(psbt);
/* Converting to v0 for log consumption */
psbt_set_version(psbt, 0);
hsmd_status_failed(STATUS_FAIL_INTERNAL_ERROR,
"Received wally_err attempting to "
"sign utxo with key %s. PSBT: %s",
Expand Down
25 changes: 19 additions & 6 deletions lightningd/channel.c
Original file line number Diff line number Diff line change
Expand Up @@ -466,13 +466,18 @@ struct channel *new_channel(struct peer *peer, u64 dbid,
channel->shutdown_wrong_funding
= tal_steal(channel, shutdown_wrong_funding);
channel->closing_feerate_range = NULL;
if (local_shutdown_scriptpubkey)
if (local_shutdown_scriptpubkey) {
channel->shutdown_scriptpubkey[LOCAL]
= tal_steal(channel, local_shutdown_scriptpubkey);
else
} else if (!chainparams->is_elements && channel_type_has(type, OPT_SHUTDOWN_ANYSEGWIT)) {
channel->shutdown_scriptpubkey[LOCAL]
= p2tr_for_keyidx(channel, channel->peer->ld,
channel->final_key_idx);
} else {
channel->shutdown_scriptpubkey[LOCAL]
= p2wpkh_for_keyidx(channel, channel->peer->ld,
channel->final_key_idx);
channel->final_key_idx);
}
channel->last_was_revoke = last_was_revoke;
channel->last_sent_commit = tal_steal(channel, last_sent_commit);
channel->first_blocknum = first_blocknum;
Expand Down Expand Up @@ -523,9 +528,17 @@ struct channel *new_channel(struct peer *peer, u64 dbid,
channel->state_change_cause = reason;

/* Make sure we see any spends using this key */
txfilter_add_scriptpubkey(peer->ld->owned_txfilter,
take(p2wpkh_for_keyidx(NULL, peer->ld,
channel->final_key_idx)));
if (!local_shutdown_scriptpubkey) {
if (!chainparams->is_elements && channel_type_has(type, OPT_SHUTDOWN_ANYSEGWIT)) {
txfilter_add_scriptpubkey(peer->ld->owned_txfilter,
take(p2tr_for_keyidx(NULL, peer->ld,
channel->final_key_idx)));
} else {
txfilter_add_scriptpubkey(peer->ld->owned_txfilter,
take(p2wpkh_for_keyidx(NULL, peer->ld,
channel->final_key_idx)));
}
}
/* scid is NULL when opening a new channel so we don't
* need to set error in that case as well */
if (is_stub_scid(scid))
Expand Down
2 changes: 1 addition & 1 deletion lightningd/dual_open_control.c
Original file line number Diff line number Diff line change
Expand Up @@ -1277,7 +1277,7 @@ wallet_commit_channel(struct lightningd *ld,
= tal_steal(channel, our_upfront_shutdown_script);
else
channel->shutdown_scriptpubkey[LOCAL]
= p2wpkh_for_keyidx(channel, channel->peer->ld,
= p2tr_for_keyidx(channel, channel->peer->ld,
channel->final_key_idx);

/* Can't have gotten their alias for this channel yet. */
Expand Down
3 changes: 2 additions & 1 deletion lightningd/onchain_control.c
Original file line number Diff line number Diff line change
Expand Up @@ -710,9 +710,10 @@ static struct bitcoin_tx *onchaind_tx_unsigned(const tal_t *ctx,
bitcoin_tx_add_input(tx, &info->out, info->to_self_delay,
NULL, info->out_sats, NULL, info->wscript);

/* FIXME should this be p2tr now? */
bitcoin_tx_add_output(
tx, scriptpubkey_p2wpkh(tmpctx, &final_key), NULL, info->out_sats);
psbt_add_keypath_to_last_output(tx, channel->final_key_idx, &final_wallet_ext_key);
psbt_add_keypath_to_last_output(tx, channel->final_key_idx, &final_wallet_ext_key, false /* is_taproot */);

/* Worst-case sig is 73 bytes */
weight = bitcoin_tx_weight(tx) + 1 + 3 + 73 + 0 + tal_count(info->wscript);
Expand Down
9 changes: 9 additions & 0 deletions lightningd/peer_control.c
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,15 @@ u8 *p2wpkh_for_keyidx(const tal_t *ctx, struct lightningd *ld, u64 keyidx)
return scriptpubkey_p2wpkh(ctx, &shutdownkey);
}

u8 *p2tr_for_keyidx(const tal_t *ctx, struct lightningd *ld, u64 keyidx)
{
struct pubkey shutdownkey;

bip32_pubkey(ld, &shutdownkey, keyidx);

return scriptpubkey_p2tr(ctx, &shutdownkey);
}

static void sign_last_tx(struct channel *channel,
struct bitcoin_tx *last_tx,
struct bitcoin_signature *last_sig)
Expand Down
Loading

0 comments on commit 58c55f1

Please sign in to comment.