Skip to content

Commit

Permalink
Support p2tr deposit addresses
Browse files Browse the repository at this point in the history
Changelog-Added: JSON-RPC: newaddr: p2tr option to create taproot addresses.
Changelog-Changed: Wallet: we now use taproot change addresses.
  • Loading branch information
instagibbs authored and rustyrussell committed Jul 11, 2023
1 parent 55cad79 commit 4b70736
Show file tree
Hide file tree
Showing 39 changed files with 518 additions and 325 deletions.
10 changes: 8 additions & 2 deletions .msggen.json
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,8 @@
"NewaddrAddresstype": {
"all": 2,
"bech32": 0,
"p2sh-segwit": 1
"p2sh-segwit": 1,
"p2tr": 3
},
"PayStatus": {
"complete": 0,
Expand Down Expand Up @@ -1263,7 +1264,8 @@
},
"NewaddrResponse": {
"NewAddr.bech32": 1,
"NewAddr.p2sh-segwit": 2
"NewAddr.p2sh-segwit": 2,
"NewAddr.p2tr": 3
},
"PayRequest": {
"Pay.amount_msat": 13,
Expand Down Expand Up @@ -4643,6 +4645,10 @@
"added": "pre-v0.10.1",
"deprecated": "v23.02"
},
"NewAddr.p2tr": {
"added": "v23.08",
"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_fee(u32 feerate_perkw, size_t total_weight)
struct amount_sat 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(&fee,
Expand Down
4 changes: 3 additions & 1 deletion bitcoin/tx.h
Original file line number Diff line number Diff line change
Expand Up @@ -330,11 +330,13 @@ size_t bitcoin_tx_2of2_input_witness_weight(void);
struct amount_sat change_fee(u32 feerate_perkw, size_t total_weight);

/**
* 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.
* @total_weight: current weight of tx.
*
* 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 change_fee()).
Expand Down
4 changes: 2 additions & 2 deletions channeld/full_channel.c
Original file line number Diff line number Diff line change
Expand Up @@ -349,9 +349,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.

8 changes: 7 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 @@ -139,9 +139,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
394 changes: 197 additions & 197 deletions contrib/pyln-grpc-proto/pyln/grpc/node_pb2.py

Large diffs are not rendered by default.

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 @@ -613,6 +613,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
7 changes: 4 additions & 3 deletions doc/lightning-newaddr.7.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ The funding transaction needs to be confirmed before funds can be used.
*addresstype* specifies the type of address wanted; currently *bech32*
(e.g. `tb1qu9j4lg5f9rgjyfhvfd905vw46eg39czmktxqgg` on bitcoin testnet
or `bc1qwqdg6squsna38e46795at95yu9atm8azzmyvckulcc7kytlcckxswvvzej` on
bitcoin mainnet). The special value *all* generates all known address types
for the same underlying key.
bitcoin mainnet), or *p2tr* taproot addresses. The special value *all*
generates all known address types for the same underlying key.

If no *addresstype* is specified the address generated is a *bech32* address.

Expand All @@ -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.08)*
- **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:f93771e450afe0fc20b2ff9763ba7654d4caf17c35cf45186f2cb9146a67503f)
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.08",
"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 @@ -489,7 +489,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_anchors) {
Expand All @@ -507,6 +507,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
2 changes: 1 addition & 1 deletion lightningd/anchorspend.c
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ static struct bitcoin_tx *spend_anchor(const tal_t *ctx,
psbt_input_set_wit_utxo(psbt, 1,
scriptpubkey_p2wsh(tmpctx, adet->anchor_wscript),
AMOUNT_SAT(330));
psbt_input_add_pubkey(psbt, 1, &channel->local_funding_pubkey);
psbt_input_add_pubkey(psbt, 1, &channel->local_funding_pubkey, false);

if (!amount_sat_add(&change, utxos[0]->amount, AMOUNT_SAT(330))
|| !amount_sat_sub(&change, change, fee)) {
Expand Down
29 changes: 23 additions & 6 deletions lightningd/channel.c
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,10 @@ struct channel *new_channel(struct peer *peer, u64 dbid,
struct channel *channel = tal(peer->ld, struct channel);
struct amount_msat htlc_min, htlc_max;

bool anysegwit = !chainparams->is_elements && feature_negotiated(peer->ld->our_features,
peer->their_features,
OPT_SHUTDOWN_ANYSEGWIT);

assert(dbid != 0);
channel->peer = peer;
channel->dbid = dbid;
Expand Down Expand Up @@ -477,13 +481,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 (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 @@ -534,9 +543,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 (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
Loading

0 comments on commit 4b70736

Please sign in to comment.