Support p2tr deposit addresses

Changelog-Added: JSON-RPC: newaddr: p2tr option to create taproot addresses.
Changelog-Changed: Wallet: we now use taproot change addresses.
This commit is contained in:
Greg Sanders
2023-07-11 05:29:41 +09:30
committed by Rusty Russell
parent 55cad79d82
commit 4b70736d13
39 changed files with 514 additions and 321 deletions

View File

@@ -273,7 +273,8 @@
"NewaddrAddresstype": { "NewaddrAddresstype": {
"all": 2, "all": 2,
"bech32": 0, "bech32": 0,
"p2sh-segwit": 1 "p2sh-segwit": 1,
"p2tr": 3
}, },
"PayStatus": { "PayStatus": {
"complete": 0, "complete": 0,
@@ -1263,7 +1264,8 @@
}, },
"NewaddrResponse": { "NewaddrResponse": {
"NewAddr.bech32": 1, "NewAddr.bech32": 1,
"NewAddr.p2sh-segwit": 2 "NewAddr.p2sh-segwit": 2,
"NewAddr.p2tr": 3
}, },
"PayRequest": { "PayRequest": {
"Pay.amount_msat": 13, "Pay.amount_msat": 13,
@@ -4643,6 +4645,10 @@
"added": "pre-v0.10.1", "added": "pre-v0.10.1",
"deprecated": "v23.02" "deprecated": "v23.02"
}, },
"NewAddr.p2tr": {
"added": "v23.08",
"deprecated": false
},
"Pay": { "Pay": {
"added": "pre-v0.10.1", "added": "pre-v0.10.1",
"deprecated": null "deprecated": null

View File

@@ -972,7 +972,7 @@ struct amount_sat change_fee(u32 feerate_perkw, size_t total_weight)
struct amount_sat fee; struct amount_sat fee;
/* Must be able to pay for its own additional weight */ /* 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 */ /* Rounding can cause off by one errors, so we do this */
if (!amount_sat_sub(&fee, if (!amount_sat_sub(&fee,

View File

@@ -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); 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. * @excess: input amount we have above the tx fee and other outputs.
* @feerate_perkw: feerate. * @feerate_perkw: feerate.
* @total_weight: current weight of tx. * @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). * 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 * Otherwise returns the amount of the change output to add (@excess minus
* the change_fee()). * the change_fee()).

View File

@@ -349,9 +349,9 @@ struct bitcoin_tx **channel_txs(const tal_t *ctx,
/* Set the remote/local pubkeys on the commitment tx psbt */ /* Set the remote/local pubkeys on the commitment tx psbt */
psbt_input_add_pubkey(txs[0]->psbt, 0, 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, 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); add_htlcs(&txs, *htlcmap, channel, &keyset, side);

View File

@@ -81,7 +81,7 @@ penalty_tx_create(const tal_t *ctx,
bitcoin_tx_add_output(tx, final_scriptpubkey, NULL, to_them_sats); bitcoin_tx_add_output(tx, final_scriptpubkey, NULL, to_them_sats);
assert((final_index == NULL) == (final_ext_key == NULL)); assert((final_index == NULL) == (final_ext_key == NULL));
if (final_index) 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 */ /* Worst-case sig is 73 bytes */
weight = bitcoin_tx_weight(tx) + 1 + 3 + 73 + 0 + tal_count(wscript); weight = bitcoin_tx_weight(tx) + 1 + 3 + 73 + 0 + tal_count(wscript);

View File

@@ -916,12 +916,14 @@ message NewaddrRequest {
// NewAddr.addresstype // NewAddr.addresstype
enum NewaddrAddresstype { enum NewaddrAddresstype {
BECH32 = 0; BECH32 = 0;
P2TR = 3;
ALL = 2; ALL = 2;
} }
optional NewaddrAddresstype addresstype = 1; optional NewaddrAddresstype addresstype = 1;
} }
message NewaddrResponse { message NewaddrResponse {
optional string p2tr = 3;
optional string bech32 = 1; optional string bech32 = 1;
optional string p2sh_segwit = 2; optional string p2sh_segwit = 2;
} }

View File

@@ -789,6 +789,7 @@ impl From<responses::WaitsendpayResponse> for pb::WaitsendpayResponse {
impl From<responses::NewaddrResponse> for pb::NewaddrResponse { impl From<responses::NewaddrResponse> for pb::NewaddrResponse {
fn from(c: responses::NewaddrResponse) -> Self { fn from(c: responses::NewaddrResponse) -> Self {
Self { Self {
p2tr: c.p2tr, // Rule #2 for type string?
bech32: c.bech32, // Rule #2 for type string? bech32: c.bech32, // Rule #2 for type string?
#[allow(deprecated)] #[allow(deprecated)]
p2sh_segwit: c.p2sh_segwit, // Rule #2 for type string? p2sh_segwit: c.p2sh_segwit, // Rule #2 for type string?

8
cln-rpc/src/model.rs generated
View File

@@ -822,6 +822,8 @@ pub mod requests {
pub enum NewaddrAddresstype { pub enum NewaddrAddresstype {
#[serde(rename = "bech32")] #[serde(rename = "bech32")]
BECH32, BECH32,
#[serde(rename = "p2tr")]
P2TR,
#[serde(rename = "all")] #[serde(rename = "all")]
ALL, ALL,
} }
@@ -831,7 +833,8 @@ pub mod requests {
fn try_from(c: i32) -> Result<NewaddrAddresstype, anyhow::Error> { fn try_from(c: i32) -> Result<NewaddrAddresstype, anyhow::Error> {
match c { match c {
0 => Ok(NewaddrAddresstype::BECH32), 0 => Ok(NewaddrAddresstype::BECH32),
1 => Ok(NewaddrAddresstype::ALL), 1 => Ok(NewaddrAddresstype::P2TR),
2 => Ok(NewaddrAddresstype::ALL),
o => Err(anyhow::anyhow!("Unknown variant {} for enum NewaddrAddresstype", o)), o => Err(anyhow::anyhow!("Unknown variant {} for enum NewaddrAddresstype", o)),
} }
} }
@@ -841,6 +844,7 @@ pub mod requests {
fn to_string(&self) -> String { fn to_string(&self) -> String {
match self { match self {
NewaddrAddresstype::BECH32 => "BECH32", NewaddrAddresstype::BECH32 => "BECH32",
NewaddrAddresstype::P2TR => "P2TR",
NewaddrAddresstype::ALL => "ALL", NewaddrAddresstype::ALL => "ALL",
}.to_string() }.to_string()
} }
@@ -3285,6 +3289,8 @@ pub mod responses {
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize)]
pub struct NewaddrResponse { pub struct NewaddrResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub p2tr: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub bech32: Option<String>, pub bech32: Option<String>,
#[deprecated] #[deprecated]

View File

@@ -52,7 +52,7 @@ struct bitcoin_tx *create_close_tx(const tal_t *ctx,
assert((local_wallet_index == NULL) == (local_wallet_ext_key == NULL)); assert((local_wallet_index == NULL) == (local_wallet_ext_key == NULL));
if (local_wallet_index) if (local_wallet_index)
psbt_add_keypath_to_last_output( 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++; num_outputs++;
} }

View File

@@ -139,9 +139,9 @@ struct bitcoin_tx *initial_channel_tx(const tal_t *ctx,
if (init_tx) { if (init_tx) {
psbt_input_add_pubkey(init_tx->psbt, 0, 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, psbt_input_add_pubkey(init_tx->psbt, 0,
&channel->funding_pubkey[!side]); &channel->funding_pubkey[!side], false /* is_taproot */);
} }
return init_tx; return init_tx;

View File

@@ -3,9 +3,8 @@
#include <common/psbt_keypath.h> #include <common/psbt_keypath.h>
#include <common/utils.h> #include <common/utils.h>
#include <wally_bip32.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]; u8 fingerprint[BIP32_KEY_FINGERPRINT_LEN];
if (bip32_key_get_fingerprint( if (bip32_key_get_fingerprint(
(struct ext_key *) ext, fingerprint, sizeof(fingerprint)) != WALLY_OK) (struct ext_key *) ext, fingerprint, sizeof(fingerprint)) != WALLY_OK)
@@ -14,20 +13,30 @@ void psbt_set_keypath(u32 index, const struct ext_key *ext, struct wally_map *ma
u32 path[1]; u32 path[1];
path[0] = index; path[0] = index;
if (wally_map_keypath_add(map_in, 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), ext->pub_key, sizeof(ext->pub_key),
fingerprint, sizeof(fingerprint), fingerprint, sizeof(fingerprint),
path, 1) != WALLY_OK) path, 1) != WALLY_OK)
abort(); abort();
} }
}
void psbt_add_keypath_to_last_output(struct bitcoin_tx *tx, void psbt_add_keypath_to_last_output(struct bitcoin_tx *tx,
u32 key_index, u32 key_index,
const struct ext_key *ext) { const struct ext_key *ext,
bool is_taproot) {
size_t outndx = tx->psbt->num_outputs - 1; size_t outndx = tx->psbt->num_outputs - 1;
struct wally_map *map_in = &tx->psbt->outputs[outndx].keypaths;
tal_wally_start(); 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); tal_wally_end(tx->psbt);
} }

View File

@@ -3,20 +3,23 @@
#include "config.h" #include "config.h"
#include <ccan/short_types/short_types.h> #include <ccan/short_types/short_types.h>
#include <wally_psbt.h>
struct bitcoin_tx; struct bitcoin_tx;
struct ext_key; struct ext_key;
struct wally_map; 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 * @index - child index of the wallet key
* @ext - extended public key of the immediate parent 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, 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 /* psbt_add_keypath_to_last_output - augment the last output with the
* given wallet keypath * given wallet keypath
@@ -24,9 +27,11 @@ void psbt_set_keypath(u32 index,
* @tx - transaction to modify * @tx - transaction to modify
* @index - child index of the wallet key * @index - child index of the wallet key
* @ext - extended public key of the immediate parent 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, void psbt_add_keypath_to_last_output(struct bitcoin_tx *tx,
u32 index, u32 index,
const struct ext_key *ext); const struct ext_key *ext,
bool is_taproot);
#endif /* LIGHTNING_COMMON_PSBT_KEYPATH_H */ #endif /* LIGHTNING_COMMON_PSBT_KEYPATH_H */

View File

@@ -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_final_scriptsig(&psbt->inputs[0], NULL, 0);
wally_psbt_input_set_witness_script(&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_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].keypaths.num_items = 0;
psbt->inputs[0].signatures.num_items = 0; psbt->inputs[0].signatures.num_items = 0;
@@ -104,6 +107,8 @@ static const u8 *linearize_output(const tal_t *ctx,
/* We don't care if the keypaths change */ /* We don't care if the keypaths change */
psbt->outputs[0].keypaths.num_items = 0; 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 */ /* And you can add scripts, no problem */
wally_psbt_output_set_witness_script(&psbt->outputs[0], NULL, 0); wally_psbt_output_set_witness_script(&psbt->outputs[0], NULL, 0);
wally_psbt_output_set_redeem_script(&psbt->outputs[0], NULL, 0); wally_psbt_output_set_redeem_script(&psbt->outputs[0], NULL, 0);

File diff suppressed because one or more lines are too long

View File

@@ -613,6 +613,7 @@ def waitsendpay2py(m):
def newaddr2py(m): def newaddr2py(m):
return remove_default({ return remove_default({
"p2tr": m.p2tr, # PrimitiveField in generate_composite
"bech32": m.bech32, # PrimitiveField in generate_composite "bech32": m.bech32, # PrimitiveField in generate_composite
"p2sh_segwit": m.p2sh_segwit, # PrimitiveField in generate_composite "p2sh_segwit": m.p2sh_segwit, # PrimitiveField in generate_composite
}) })

View File

@@ -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* *addresstype* specifies the type of address wanted; currently *bech32*
(e.g. `tb1qu9j4lg5f9rgjyfhvfd905vw46eg39czmktxqgg` on bitcoin testnet (e.g. `tb1qu9j4lg5f9rgjyfhvfd905vw46eg39czmktxqgg` on bitcoin testnet
or `bc1qwqdg6squsna38e46795at95yu9atm8azzmyvckulcc7kytlcckxswvvzej` on or `bc1qwqdg6squsna38e46795at95yu9atm8azzmyvckulcc7kytlcckxswvvzej` on
bitcoin mainnet). The special value *all* generates all known address types bitcoin mainnet), or *p2tr* taproot addresses. The special value *all*
for the same underlying key. generates all known address types for the same underlying key.
If no *addresstype* is specified the address generated is a *bech32* address. If no *addresstype* is specified the address generated is a *bech32* address.
@@ -30,6 +30,7 @@ RETURN VALUE
[comment]: # (GENERATE-FROM-SCHEMA-START) [comment]: # (GENERATE-FROM-SCHEMA-START)
On success, an object is returned, containing: On success, an object is returned, containing:
- **p2tr** (string, optional): The taproot address *(added v23.08)*
- **bech32** (string, optional): The bech32 (native segwit) address - **bech32** (string, optional): The bech32 (native segwit) address
- **p2sh-segwit** (string, optional): The p2sh-wrapped address **deprecated, removal in v23.11** - **p2sh-segwit** (string, optional): The p2sh-wrapped address **deprecated, removal in v23.11**
@@ -56,4 +57,4 @@ RESOURCES
Main web site: <https://github.com/ElementsProject/lightning> Main web site: <https://github.com/ElementsProject/lightning>
[comment]: # ( SHA256STAMP:90d550bc2290dd2ab6ee67e377679fe45230a14ba6f4608fda8e51bb6670cc07) [comment]: # ( SHA256STAMP:f93771e450afe0fc20b2ff9763ba7654d4caf17c35cf45186f2cb9146a67503f)

View File

@@ -8,6 +8,7 @@
"type": "string", "type": "string",
"enum": [ "enum": [
"bech32", "bech32",
"p2tr",
"all" "all"
] ]
} }

View File

@@ -4,6 +4,11 @@
"additionalProperties": false, "additionalProperties": false,
"required": [], "required": [],
"properties": { "properties": {
"p2tr": {
"added": "v23.08",
"type": "string",
"description": "The taproot address"
},
"bech32": { "bech32": {
"type": "string", "type": "string",
"description": "The bech32 (native segwit) address" "description": "The bech32 (native segwit) address"

View File

@@ -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 * requires the HSM to find the pubkey, and we
* skip doing that until now as a bit of a reduction * skip doing that until now as a bit of a reduction
* of complexity in the calling code */ * 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. */ /* It's actually a P2WSH in this case. */
if (utxo->close_info && utxo->close_info->option_anchors) { if (utxo->close_info && utxo->close_info->option_anchors) {
@@ -507,6 +507,8 @@ static void sign_our_inputs(struct utxo **utxos, struct wally_psbt *psbt)
sizeof(privkey.secret.data), sizeof(privkey.secret.data),
EC_FLAG_GRIND_R) != WALLY_OK) { EC_FLAG_GRIND_R) != WALLY_OK) {
tal_wally_end(psbt); tal_wally_end(psbt);
/* Converting to v0 for log consumption */
psbt_set_version(psbt, 0);
hsmd_status_failed(STATUS_FAIL_INTERNAL_ERROR, hsmd_status_failed(STATUS_FAIL_INTERNAL_ERROR,
"Received wally_err attempting to " "Received wally_err attempting to "
"sign utxo with key %s. PSBT: %s", "sign utxo with key %s. PSBT: %s",

View File

@@ -290,7 +290,7 @@ static struct bitcoin_tx *spend_anchor(const tal_t *ctx,
psbt_input_set_wit_utxo(psbt, 1, psbt_input_set_wit_utxo(psbt, 1,
scriptpubkey_p2wsh(tmpctx, adet->anchor_wscript), scriptpubkey_p2wsh(tmpctx, adet->anchor_wscript),
AMOUNT_SAT(330)); 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)) if (!amount_sat_add(&change, utxos[0]->amount, AMOUNT_SAT(330))
|| !amount_sat_sub(&change, change, fee)) { || !amount_sat_sub(&change, change, fee)) {

View File

@@ -398,6 +398,10 @@ struct channel *new_channel(struct peer *peer, u64 dbid,
struct channel *channel = tal(peer->ld, struct channel); struct channel *channel = tal(peer->ld, struct channel);
struct amount_msat htlc_min, htlc_max; 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); assert(dbid != 0);
channel->peer = peer; channel->peer = peer;
channel->dbid = dbid; channel->dbid = dbid;
@@ -477,13 +481,18 @@ struct channel *new_channel(struct peer *peer, u64 dbid,
channel->shutdown_wrong_funding channel->shutdown_wrong_funding
= tal_steal(channel, shutdown_wrong_funding); = tal_steal(channel, shutdown_wrong_funding);
channel->closing_feerate_range = NULL; channel->closing_feerate_range = NULL;
if (local_shutdown_scriptpubkey) if (local_shutdown_scriptpubkey) {
channel->shutdown_scriptpubkey[LOCAL] channel->shutdown_scriptpubkey[LOCAL]
= tal_steal(channel, local_shutdown_scriptpubkey); = 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] channel->shutdown_scriptpubkey[LOCAL]
= p2wpkh_for_keyidx(channel, channel->peer->ld, = p2wpkh_for_keyidx(channel, channel->peer->ld,
channel->final_key_idx); channel->final_key_idx);
}
channel->last_was_revoke = last_was_revoke; channel->last_was_revoke = last_was_revoke;
channel->last_sent_commit = tal_steal(channel, last_sent_commit); channel->last_sent_commit = tal_steal(channel, last_sent_commit);
channel->first_blocknum = first_blocknum; channel->first_blocknum = first_blocknum;
@@ -534,9 +543,17 @@ struct channel *new_channel(struct peer *peer, u64 dbid,
channel->state_change_cause = reason; channel->state_change_cause = reason;
/* Make sure we see any spends using this key */ /* Make sure we see any spends using this key */
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, txfilter_add_scriptpubkey(peer->ld->owned_txfilter,
take(p2wpkh_for_keyidx(NULL, peer->ld, take(p2wpkh_for_keyidx(NULL, peer->ld,
channel->final_key_idx))); channel->final_key_idx)));
}
}
/* scid is NULL when opening a new channel so we don't /* scid is NULL when opening a new channel so we don't
* need to set error in that case as well */ * need to set error in that case as well */
if (is_stub_scid(scid)) if (is_stub_scid(scid))

View File

@@ -674,6 +674,11 @@ static struct command_result *json_close(struct command *cmd,
index_val = (u32) channel->final_key_idx; index_val = (u32) channel->final_key_idx;
final_index = &index_val; final_index = &index_val;
/* Don't send a scriptpubkey peer won't accept */
anysegwit = !chainparams->is_elements && feature_negotiated(cmd->ld->our_features,
channel->peer->their_features,
OPT_SHUTDOWN_ANYSEGWIT);
/* If we've set a local shutdown script for this peer, and it's not the /* If we've set a local shutdown script for this peer, and it's not the
* default upfront script, try to close to a different channel. * default upfront script, try to close to a different channel.
* Error is an operator error */ * Error is an operator error */
@@ -682,7 +687,12 @@ static struct command_result *json_close(struct command *cmd,
tal_count(close_to_script), tal_count(close_to_script),
channel->shutdown_scriptpubkey[LOCAL], channel->shutdown_scriptpubkey[LOCAL],
tal_count(channel->shutdown_scriptpubkey[LOCAL]))) { tal_count(channel->shutdown_scriptpubkey[LOCAL]))) {
u8 *default_close_to = p2wpkh_for_keyidx(tmpctx, cmd->ld, u8 *default_close_to = NULL;
if (anysegwit)
default_close_to = p2tr_for_keyidx(tmpctx, cmd->ld,
channel->final_key_idx);
else
default_close_to = p2wpkh_for_keyidx(tmpctx, cmd->ld,
channel->final_key_idx); channel->final_key_idx);
if (!memeq(default_close_to, tal_count(default_close_to), if (!memeq(default_close_to, tal_count(default_close_to),
channel->shutdown_scriptpubkey[LOCAL], channel->shutdown_scriptpubkey[LOCAL],
@@ -722,10 +732,6 @@ static struct command_result *json_close(struct command *cmd,
} else } else
close_script_set = false; close_script_set = false;
/* Don't send a scriptpubkey peer won't accept */
anysegwit = feature_negotiated(cmd->ld->our_features,
channel->peer->their_features,
OPT_SHUTDOWN_ANYSEGWIT);
if (!valid_shutdown_scriptpubkey(channel->shutdown_scriptpubkey[LOCAL], if (!valid_shutdown_scriptpubkey(channel->shutdown_scriptpubkey[LOCAL],
anysegwit, false)) { anysegwit, false)) {
/* Explicit check for future segwits. */ /* Explicit check for future segwits. */

View File

@@ -1287,7 +1287,7 @@ wallet_commit_channel(struct lightningd *ld,
= tal_steal(channel, our_upfront_shutdown_script); = tal_steal(channel, our_upfront_shutdown_script);
else else
channel->shutdown_scriptpubkey[LOCAL] channel->shutdown_scriptpubkey[LOCAL]
= p2wpkh_for_keyidx(channel, channel->peer->ld, = p2tr_for_keyidx(channel, channel->peer->ld,
channel->final_key_idx); channel->final_key_idx);
/* Can't have gotten their alias for this channel yet. */ /* Can't have gotten their alias for this channel yet. */

View File

@@ -691,9 +691,10 @@ static struct bitcoin_tx *onchaind_tx_unsigned(const tal_t *ctx,
bitcoin_tx_add_input(tx, &info->out, info->to_self_delay, bitcoin_tx_add_input(tx, &info->out, info->to_self_delay,
NULL, info->out_sats, NULL, info->wscript); NULL, info->out_sats, NULL, info->wscript);
/* FIXME should this be p2tr now? */
bitcoin_tx_add_output( bitcoin_tx_add_output(
tx, scriptpubkey_p2wpkh(tmpctx, &final_key), NULL, info->out_sats); 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 */ /* Worst-case sig is 73 bytes */
weight = bitcoin_tx_weight(tx) + 1 + 3 + 73 + 0 + tal_count(info->wscript); weight = bitcoin_tx_weight(tx) + 1 + 3 + 73 + 0 + tal_count(info->wscript);

View File

@@ -217,6 +217,15 @@ u8 *p2wpkh_for_keyidx(const tal_t *ctx, struct lightningd *ld, u64 keyidx)
return scriptpubkey_p2wpkh(ctx, &shutdownkey); 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, static void sign_last_tx(struct channel *channel,
struct bitcoin_tx *last_tx, struct bitcoin_tx *last_tx,
struct bitcoin_signature *last_sig) struct bitcoin_signature *last_sig)

View File

@@ -100,6 +100,7 @@ void channel_errmsg(struct channel *channel,
const u8 *err_for_them); const u8 *err_for_them);
u8 *p2wpkh_for_keyidx(const tal_t *ctx, struct lightningd *ld, u64 keyidx); u8 *p2wpkh_for_keyidx(const tal_t *ctx, struct lightningd *ld, u64 keyidx);
u8 *p2tr_for_keyidx(const tal_t *ctx, struct lightningd *ld, u64 keyidx);
/* We've loaded peers from database, set them going. */ /* We've loaded peers from database, set them going. */
void setup_peers(struct lightningd *ld); void setup_peers(struct lightningd *ld);

View File

@@ -969,8 +969,8 @@ static char *check_balances(const tal_t *ctx,
static bool is_segwit_output(struct wally_tx_output *output) static bool is_segwit_output(struct wally_tx_output *output)
{ {
const u8 *wit_prog = wally_tx_output_get_script(tmpctx, output); const u8 *script = wally_tx_output_get_script(tmpctx, output);
return is_p2wsh(wit_prog, NULL) || is_p2wpkh(wit_prog, NULL); return is_known_segwit_scripttype(script);
} }
static void set_remote_upfront_shutdown(struct state *state, static void set_remote_upfront_shutdown(struct state *state,

View File

@@ -1,6 +1,7 @@
#include "config.h" #include "config.h"
#include <bitcoin/chainparams.h> #include <bitcoin/chainparams.h>
#include <bitcoin/psbt.h> #include <bitcoin/psbt.h>
#include <bitcoin/script.h>
#include <ccan/array_size/array_size.h> #include <ccan/array_size/array_size.h>
#include <ccan/json_out/json_out.h> #include <ccan/json_out/json_out.h>
#include <ccan/tal/str/str.h> #include <ccan/tal/str/str.h>
@@ -511,7 +512,7 @@ mw_get_change_addr(struct multiwithdraw_command *mw)
req = jsonrpc_request_start(mw->cmd->plugin, mw->cmd, req = jsonrpc_request_start(mw->cmd->plugin, mw->cmd,
"newaddr", "newaddr",
&mw_after_newaddr, &mw_forward_error, mw); &mw_after_newaddr, &mw_forward_error, mw);
json_add_string(req->js, "addresstype", "bech32"); json_add_string(req->js, "addresstype", chainparams->is_elements ? "bech32" : "p2tr");
return send_outreq(mw->cmd->plugin, req); return send_outreq(mw->cmd->plugin, req);
} }
@@ -524,7 +525,7 @@ mw_after_newaddr(struct command *cmd,
const jsmntok_t *bech32tok; const jsmntok_t *bech32tok;
const u8 *script; const u8 *script;
bech32tok = json_get_member(buf, result, "bech32"); bech32tok = json_get_member(buf, result, chainparams->is_elements ? "bech32" : "p2tr");
if (!bech32tok if (!bech32tok
|| json_to_address_scriptpubkey(mw, chainparams, buf, bech32tok, || json_to_address_scriptpubkey(mw, chainparams, buf, bech32tok,
&script) != ADDRESS_PARSE_SUCCESS) &script) != ADDRESS_PARSE_SUCCESS)

View File

@@ -1,5 +1,6 @@
#include "config.h" #include "config.h"
#include <bitcoin/psbt.h> #include <bitcoin/psbt.h>
#include <bitcoin/script.h>
#include <ccan/array_size/array_size.h> #include <ccan/array_size/array_size.h>
#include <common/addr.h> #include <common/addr.h>
#include <common/json_param.h> #include <common/json_param.h>
@@ -573,7 +574,8 @@ static struct command_result *newaddr_sweep_done(struct command *cmd,
struct listfunds_info *info) struct listfunds_info *info)
{ {
struct out_req *req; struct out_req *req;
const jsmntok_t *addr = json_get_member(buf, result, "bech32"); const jsmntok_t *addr = json_get_member(buf, result, chainparams->is_elements ? "bech32" : "p2tr");
assert(addr);
info->txp = tal(info, struct txprepare); info->txp = tal(info, struct txprepare);
info->txp->is_upgrade = true; info->txp->is_upgrade = true;
@@ -627,6 +629,7 @@ static struct command_result *json_upgradewallet(struct command *cmd,
newaddr_sweep_done, newaddr_sweep_done,
forward_error, forward_error,
info); info);
json_add_string(req->js, "addresstype", "all");
return send_outreq(cmd->plugin, req); return send_outreq(cmd->plugin, req);
} }

View File

@@ -395,13 +395,13 @@ def test_bookkeeping_missed_chans_leases(node_factory, bitcoind):
# l1 events # l1 events
exp_events = [('channel_open', open_amt * 1000 + lease_fee, 0), exp_events = [('channel_open', open_amt * 1000 + lease_fee, 0),
('onchain_fee', 1224000, 0), ('onchain_fee', 1320000, 0),
('lease_fee', 0, lease_fee), ('lease_fee', 0, lease_fee),
('journal_entry', 0, invoice_msat)] ('journal_entry', 0, invoice_msat)]
_check_events(l1, channel_id, exp_events) _check_events(l1, channel_id, exp_events)
exp_events = [('channel_open', open_amt * 1000, 0), exp_events = [('channel_open', open_amt * 1000, 0),
('onchain_fee', 796000, 0), ('onchain_fee', 892000, 0),
('lease_fee', lease_fee, 0), ('lease_fee', lease_fee, 0),
('journal_entry', invoice_msat, 0)] ('journal_entry', invoice_msat, 0)]
_check_events(l2, channel_id, exp_events) _check_events(l2, channel_id, exp_events)
@@ -461,7 +461,7 @@ def test_bookkeeping_missed_chans_pushed(node_factory, bitcoind):
# l1 events # l1 events
exp_events = [('channel_open', open_amt * 1000, 0), exp_events = [('channel_open', open_amt * 1000, 0),
('onchain_fee', 4567000, 0), ('onchain_fee', 4927000, 0),
('pushed', 0, push_amt), ('pushed', 0, push_amt),
('journal_entry', 0, invoice_msat)] ('journal_entry', 0, invoice_msat)]
_check_events(l1, channel_id, exp_events) _check_events(l1, channel_id, exp_events)
@@ -534,7 +534,7 @@ def test_bookkeeping_missed_chans_pay_after(node_factory, bitcoind):
# l1 events # l1 events
exp_events = [('channel_open', open_amt * 1000, 0), exp_events = [('channel_open', open_amt * 1000, 0),
('onchain_fee', 4567000, 0), ('onchain_fee', 4927000, 0),
('invoice', 0, invoice_msat)] ('invoice', 0, invoice_msat)]
_check_events(l1, channel_id, exp_events) _check_events(l1, channel_id, exp_events)

View File

@@ -1338,7 +1338,7 @@ def test_penalty_htlc_tx_fulfill(node_factory, bitcoind, chainparams, anchors):
]} ]}
] + [ ] + [
{'blockheight': 108, 'accounts': [ {'blockheight': 108, 'accounts': [
{'balance_msat': '995433000msat', 'account_id': 'wallet'}, {'balance_msat': '995073000msat', 'account_id': 'wallet'},
{'balance_msat': '500000000msat', 'account_id': first_channel_id(l1, l2)}, {'balance_msat': '500000000msat', 'account_id': first_channel_id(l1, l2)},
{'balance_msat': '499994999msat', 'account_id': channel_id}]} {'balance_msat': '499994999msat', 'account_id': channel_id}]}
] * 2 # duplicated; we stop and restart l2 twice (both at block 108) ] * 2 # duplicated; we stop and restart l2 twice (both at block 108)
@@ -3203,7 +3203,7 @@ def test_shutdown(node_factory):
@pytest.mark.developer("needs to set upfront_shutdown_script") @pytest.mark.developer("needs to set upfront_shutdown_script")
def test_option_upfront_shutdown_script(node_factory, bitcoind, executor): def test_option_upfront_shutdown_script(node_factory, bitcoind, executor, chainparams):
l1 = node_factory.get_node(start=False, allow_warning=True) l1 = node_factory.get_node(start=False, allow_warning=True)
# Insist on upfront script we're not going to match. # Insist on upfront script we're not going to match.
# '0014' + l1.rpc.call('dev-listaddrs', [10])['addresses'][-1]['bech32_redeemscript'] # '0014' + l1.rpc.call('dev-listaddrs', [10])['addresses'][-1]['bech32_redeemscript']
@@ -3256,6 +3256,9 @@ def test_option_upfront_shutdown_script(node_factory, bitcoind, executor):
# Now, if we specify upfront and it's OK, all good. # Now, if we specify upfront and it's OK, all good.
l1.stop() l1.stop()
if not chainparams['elements']:
l1.daemon.env["DEV_OPENINGD_UPFRONT_SHUTDOWN_SCRIPT"] = bitcoind.rpc.getaddressinfo(addr['p2tr'])['scriptPubKey']
else:
# We need to prepend the segwit version (0) and push opcode (14). # We need to prepend the segwit version (0) and push opcode (14).
l1.daemon.env["DEV_OPENINGD_UPFRONT_SHUTDOWN_SCRIPT"] = '0014' + addr['bech32_redeemscript'] l1.daemon.env["DEV_OPENINGD_UPFRONT_SHUTDOWN_SCRIPT"] = '0014' + addr['bech32_redeemscript']
l1.start() l1.start()
@@ -3573,8 +3576,8 @@ def test_close_feerate_range(node_factory, bitcoind, chainparams):
l1.rpc.close(l2.info['id'], feerange=['253perkw', 'normal']) l1.rpc.close(l2.info['id'], feerange=['253perkw', 'normal'])
if not chainparams['elements']: if not chainparams['elements']:
l1_range = [139, 4140] l1_range = [151, 4500]
l2_range = [1035, 1000000] l2_range = [1125, 1000000]
else: else:
# That fee output is a little chunky. # That fee output is a little chunky.
l1_range = [221, 6577] l1_range = [221, 6577]

View File

@@ -636,7 +636,7 @@ def test_withdraw_misc(node_factory, bitcoind, chainparams):
{'type': 'chain_mvt', 'credit_msat': 2000000000, 'debit_msat': 0, 'tags': ['deposit']}, {'type': 'chain_mvt', 'credit_msat': 2000000000, 'debit_msat': 0, 'tags': ['deposit']},
{'type': 'chain_mvt', 'credit_msat': 2000000000, 'debit_msat': 0, 'tags': ['deposit']}, {'type': 'chain_mvt', 'credit_msat': 2000000000, 'debit_msat': 0, 'tags': ['deposit']},
{'type': 'chain_mvt', 'credit_msat': 2000000000, 'debit_msat': 0, 'tags': ['deposit']}, {'type': 'chain_mvt', 'credit_msat': 2000000000, 'debit_msat': 0, 'tags': ['deposit']},
{'type': 'chain_mvt', 'credit_msat': 11957603000, 'debit_msat': 0, 'tags': ['deposit']}, {'type': 'chain_mvt', 'credit_msat': 11956163000, 'debit_msat': 0, 'tags': ['deposit']},
] ]
check_coin_moves(l1, 'external', external_moves, chainparams) check_coin_moves(l1, 'external', external_moves, chainparams)

View File

@@ -316,7 +316,10 @@ def test_txprepare(node_factory, bitcoind, chainparams):
assert o['scriptPubKey']['type'] == 'witness_v0_keyhash' assert o['scriptPubKey']['type'] == 'witness_v0_keyhash'
assert scriptpubkey_addr(o['scriptPubKey']) == addr assert scriptpubkey_addr(o['scriptPubKey']) == addr
else: else:
assert o['scriptPubKey']['type'] in ['witness_v0_keyhash', 'fee'] if chainparams['elements']:
o['scriptPubKey']['type'] in ['witness_v0_keyhash', 'fee']
else:
assert o['scriptPubKey']['type'] in ['witness_v1_taproot', 'fee']
# Now prepare one with no change. # Now prepare one with no change.
prep2 = l1.rpc.txprepare([{addr: 'all'}]) prep2 = l1.rpc.txprepare([{addr: 'all'}])
@@ -438,7 +441,10 @@ def test_txprepare(node_factory, bitcoind, chainparams):
assert decode['vout'][outnum2]['scriptPubKey']['type'] == 'witness_v0_keyhash' assert decode['vout'][outnum2]['scriptPubKey']['type'] == 'witness_v0_keyhash'
assert scriptpubkey_addr(decode['vout'][outnum2]['scriptPubKey']) == addr assert scriptpubkey_addr(decode['vout'][outnum2]['scriptPubKey']) == addr
if chainparams['elements']:
assert decode['vout'][changenum]['scriptPubKey']['type'] == 'witness_v0_keyhash' assert decode['vout'][changenum]['scriptPubKey']['type'] == 'witness_v0_keyhash'
else:
assert decode['vout'][changenum]['scriptPubKey']['type'] == 'witness_v1_taproot'
def test_reserveinputs(node_factory, bitcoind, chainparams): def test_reserveinputs(node_factory, bitcoind, chainparams):
@@ -1237,45 +1243,39 @@ def test_hsmtool_secret_decryption(node_factory):
def test_hsmtool_dump_descriptors(node_factory, bitcoind): def test_hsmtool_dump_descriptors(node_factory, bitcoind):
l1 = node_factory.get_node() l1 = node_factory.get_node()
l1.fundwallet(10**6) l1.fundwallet(10**6)
# Get a tpub descriptor of lightningd's wallet # Get a tpub descriptor of lightningd's wallet
hsm_path = os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, "hsm_secret") hsm_path = os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, "hsm_secret")
cmd_line = ["tools/hsmtool", "dumponchaindescriptors", hsm_path, "testnet"] cmd_line = ["tools/hsmtool", "dumponchaindescriptors", hsm_path, "testnet"]
out = subprocess.check_output(cmd_line).decode("utf8").split("\n") descriptors = subprocess.check_output(cmd_line).decode("utf8").split("\n")
descriptor = [l for l in out if l.startswith("wpkh(tpub")][0]
# If we switch wallet, we can't generate address: do so now. # Deprecated or empty line
mine_to_addr = bitcoind.rpc.getnewaddress() descriptors = [desc for desc in descriptors if not (desc.startswith("sh(wpkh(") or desc == '')]
# Import the descriptor to bitcoind withdraw_addr = None
try: index_offset = 2 # index starts handing out addrs at 2
bitcoind.rpc.importmulti([{
"desc": descriptor,
# No need to rescan, we'll transact afterward
"timestamp": "now",
# The default
"range": [0, 99]
}])
except JSONRPCError:
# Oh look, a new API!
# Need watch-only wallet, since descriptor has no privkeys.
bitcoind.rpc.createwallet("lightningd-ro", True)
# FIXME: No way to access non-default wallet in python-bitcoinlib # Generate twenty addresses for all known descriptors
bitcoind.rpc.unloadwallet("lightningd-tests", True) cln_addrs = [l1.rpc.newaddr('all') for _ in range(20)]
bitcoind.rpc.importdescriptors([{ for descriptor in descriptors:
"desc": descriptor, for i, cln_addr in enumerate(cln_addrs):
# No need to rescan, we'll transact afterward computed_addr = bitcoind.rpc.deriveaddresses(descriptor, [i + index_offset, i + index_offset])[0]
"timestamp": "now", if descriptor.startswith("wpkh"):
# The default assert cln_addr["bech32"] == computed_addr
"range": [0, 99] withdraw_addr = cln_addr["bech32"]
}]) elif descriptor.startswith("tr"):
assert cln_addr["p2tr"] == computed_addr
withdraw_addr = cln_addr["p2tr"]
else:
raise Exception('Unexpected descriptor!')
# For last address per type:
# Funds sent to lightningd can be retrieved by bitcoind # Funds sent to lightningd can be retrieved by bitcoind
addr = l1.rpc.newaddr()["bech32"] txid = l1.rpc.withdraw(withdraw_addr, 10**3)["txid"]
txid = l1.rpc.withdraw(addr, 10**3)["txid"] bitcoind.generate_block(1, txid, bitcoind.rpc.getnewaddress())
bitcoind.generate_block(1, txid, mine_to_addr) l1.daemon.wait_for_log('Owning output .* txid {} CONFIRMED'.format(txid))
assert len(bitcoind.rpc.listunspent(1, 1, [addr])) == 1 actual_index = len(cln_addrs) - 1 + index_offset
res = bitcoind.rpc.scantxoutset("start", [{"desc": descriptor, "range": [actual_index, actual_index]}])
assert res["total_amount"] == Decimal('0.00001000')
def test_hsmtool_generatehsm(node_factory): def test_hsmtool_generatehsm(node_factory):
@@ -1545,6 +1545,44 @@ def test_withdraw_bech32m(node_factory, bitcoind):
assert set([output['scriptPubKey']['address'] for output in outputs]).issuperset([addr.lower() for addr in addrs]) assert set([output['scriptPubKey']['address'] for output in outputs]).issuperset([addr.lower() for addr in addrs])
@unittest.skipIf(TEST_NETWORK != 'regtest', "Elements-based schnorr is not yet supported")
def test_p2tr_deposit_withdrawal(node_factory, bitcoind):
# Don't get any funds from previous runs.
l1 = node_factory.get_node(random_hsm=True)
# Can fetch p2tr addresses through 'all' or specifically
deposit_addrs = [l1.rpc.newaddr('all')] * 3
withdrawal_addr = l1.rpc.newaddr('p2tr')
# Add some funds to withdraw
for addr_type in ['p2tr', 'bech32']:
for i in range(3):
l1.bitcoin.rpc.sendtoaddress(deposit_addrs[i][addr_type], 1)
bitcoind.generate_block(1)
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 6)
for i in range(3):
assert l1.rpc.listfunds()['outputs'][i]['address'] == deposit_addrs[i]['p2tr']
assert l1.rpc.listfunds()['outputs'][i + 3]['address'] == deposit_addrs[i]['bech32']
l1.rpc.withdraw(withdrawal_addr['p2tr'], 100000000 * 5)
wait_for(lambda: len(bitcoind.rpc.getrawmempool()) == 1)
raw_tx = bitcoind.rpc.getrawtransaction(bitcoind.rpc.getrawmempool()[0], 1)
assert len(raw_tx['vin']) == 6
assert len(raw_tx['vout']) == 2
# Change goes to p2tr
for output in raw_tx['vout']:
assert output["scriptPubKey"]["type"] == "witness_v1_taproot"
bitcoind.generate_block(1)
wait_for(lambda: len(l1.rpc.listtransactions()['transactions']) == 7)
# Only self-send + change is left
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 2)
# make sure tap derivation is embedded in PSBT output
@unittest.skipIf(TEST_NETWORK != 'regtest', "Address is network specific") @unittest.skipIf(TEST_NETWORK != 'regtest', "Address is network specific")
def test_upgradewallet(node_factory, bitcoind): def test_upgradewallet(node_factory, bitcoind):
# Make sure bitcoind doesn't think it's going backwards # Make sure bitcoind doesn't think it's going backwards

View File

@@ -98,7 +98,8 @@ def check_balance_snaps(n, expected_bals):
snaps = n.rpc.listsnapshots()['balance_snapshots'] snaps = n.rpc.listsnapshots()['balance_snapshots']
for snap, exp in zip(snaps, expected_bals): for snap, exp in zip(snaps, expected_bals):
assert snap['blockheight'] == exp['blockheight'] assert snap['blockheight'] == exp['blockheight']
assert _dictify(snap) == _dictify(exp) if _dictify(snap) != _dictify(exp):
raise Exception('Unexpected balance snap: {} vs {}'.format(_dictify(snap), _dictify(exp)))
def check_coin_moves(n, account_id, expected_moves, chainparams): def check_coin_moves(n, account_id, expected_moves, chainparams):
@@ -409,7 +410,8 @@ def basic_fee(feerate, anchor_expected):
def closing_fee(feerate, num_outputs): def closing_fee(feerate, num_outputs):
assert num_outputs == 1 or num_outputs == 2 assert num_outputs == 1 or num_outputs == 2
weight = 428 + 124 * num_outputs # Assumes p2tr outputs
weight = 428 + (8 + 1 + 1 + 1 + 32) * 4 * num_outputs
return (weight * feerate) // 1000 return (weight * feerate) // 1000

View File

@@ -558,7 +558,7 @@ static int dumponchaindescriptors(const char *hsm_secret_path, const char *old_p
if (bip32_key_to_base58(&master_extkey, BIP32_FLAG_KEY_PUBLIC, &enc_xpub) != WALLY_OK) if (bip32_key_to_base58(&master_extkey, BIP32_FLAG_KEY_PUBLIC, &enc_xpub) != WALLY_OK)
errx(ERROR_LIBWALLY, "Can't encode xpub"); errx(ERROR_LIBWALLY, "Can't encode xpub");
/* Now we format the descriptor strings (we only ever create P2WPKH and /* Now we format the descriptor strings (we only ever create P2TR, P2WPKH, and
* P2SH-P2WPKH outputs). */ * P2SH-P2WPKH outputs). */
descriptor = tal_fmt(NULL, "wpkh(%s/0/0/*)", enc_xpub); descriptor = tal_fmt(NULL, "wpkh(%s/0/0/*)", enc_xpub);
@@ -573,6 +573,12 @@ static int dumponchaindescriptors(const char *hsm_secret_path, const char *old_p
printf("%s#%s\n", descriptor, checksum.csum); printf("%s#%s\n", descriptor, checksum.csum);
tal_free(descriptor); tal_free(descriptor);
descriptor = tal_fmt(NULL, "tr(%s/0/0/*)", enc_xpub);
if (!descriptor_checksum(descriptor, strlen(descriptor), &checksum))
errx(ERROR_LIBWALLY, "Can't derive descriptor checksum for tr");
printf("%s#%s\n", descriptor, checksum.csum);
tal_free(descriptor);
wally_free_string(enc_xpub); wally_free_string(enc_xpub);
return 0; return 0;

View File

@@ -1365,9 +1365,9 @@ migrate_inflight_last_tx_to_psbt(struct lightningd *ld, struct db *db)
&remote_funding_pubkey, &last_sig)) &remote_funding_pubkey, &last_sig))
abort(); abort();
psbt_input_add_pubkey(last_tx->psbt, 0, psbt_input_add_pubkey(last_tx->psbt, 0,
&local_funding_pubkey); &local_funding_pubkey, false /* is_taproot */);
psbt_input_add_pubkey(last_tx->psbt, 0, psbt_input_add_pubkey(last_tx->psbt, 0,
&remote_funding_pubkey); &remote_funding_pubkey, false /* is_taproot */);
update_stmt = db_prepare_v2(db, update_stmt = db_prepare_v2(db,
SQL("UPDATE channel_funding_inflights" SQL("UPDATE channel_funding_inflights"
@@ -1461,9 +1461,9 @@ void migrate_last_tx_to_psbt(struct lightningd *ld, struct db *db)
&remote_funding_pubkey, &last_sig)) &remote_funding_pubkey, &last_sig))
abort(); abort();
psbt_input_add_pubkey(last_tx->psbt, 0, psbt_input_add_pubkey(last_tx->psbt, 0,
&local_funding_pubkey); &local_funding_pubkey, false /* is_taproot */);
psbt_input_add_pubkey(last_tx->psbt, 0, psbt_input_add_pubkey(last_tx->psbt, 0,
&remote_funding_pubkey); &remote_funding_pubkey, false /* is_taproot */);
update_stmt = db_prepare_v2(db, SQL("UPDATE channels" update_stmt = db_prepare_v2(db, SQL("UPDATE channels"
" SET last_tx = ?" " SET last_tx = ?"

View File

@@ -381,15 +381,24 @@ static struct command_result *finish_psbt(struct command *cmd,
"Failed to generate change address." "Failed to generate change address."
" Keys exhausted."); " Keys exhausted.");
if (chainparams->is_elements) {
bip32_pubkey(cmd->ld, &pubkey, keyidx); bip32_pubkey(cmd->ld, &pubkey, keyidx);
b32script = scriptpubkey_p2wpkh(tmpctx, &pubkey); b32script = scriptpubkey_p2wpkh(tmpctx, &pubkey);
} else {
b32script = p2tr_for_keyidx(tmpctx, cmd->ld, keyidx);
}
if (!b32script) {
return command_fail(cmd, LIGHTNINGD,
"Failed to generate change address."
" Keys generation failure");
}
txfilter_add_scriptpubkey(cmd->ld->owned_txfilter, b32script); txfilter_add_scriptpubkey(cmd->ld->owned_txfilter, b32script);
change_outnum = psbt->num_outputs; change_outnum = psbt->num_outputs;
psbt_append_output(psbt, b32script, change); psbt_append_output(psbt, b32script, change);
/* Add additional weight of output */ /* Add additional weight of output */
weight += bitcoin_tx_output_weight( weight += bitcoin_tx_output_weight(
BITCOIN_SCRIPTPUBKEY_P2WPKH_LEN); chainparams->is_elements ? BITCOIN_SCRIPTPUBKEY_P2WPKH_LEN : BITCOIN_SCRIPTPUBKEY_P2TR_LEN);
} else { } else {
change_outnum = -1; change_outnum = -1;
} }

View File

@@ -734,6 +734,8 @@ bool wallet_can_spend(struct wallet *w, const u8 *script,
*output_is_p2sh = true; *output_is_p2sh = true;
else if (is_p2wpkh(script, NULL)) else if (is_p2wpkh(script, NULL))
*output_is_p2sh = false; *output_is_p2sh = false;
else if (is_p2tr(script, NULL))
*output_is_p2sh = false;
else else
return false; return false;
@@ -761,6 +763,18 @@ bool wallet_can_spend(struct wallet *w, const u8 *script,
return true; return true;
} }
tal_free(s); tal_free(s);
/* Try taproot output now */
s = scriptpubkey_p2tr_derkey(w, ext.pub_key);
if (scripteq(s, script)) {
/* If we found a used key in the keyscan_gap we should
* remember that. */
if (i > bip32_max_index)
db_set_intvar(w->db, "bip32_max_index", i);
tal_free(s);
*index = i;
return true;
}
tal_free(s);
} }
return false; return false;
} }

View File

@@ -27,11 +27,19 @@
#include <wally_psbt.h> #include <wally_psbt.h>
#include <wire/wire_sync.h> #include <wire/wire_sync.h>
enum addrtype {
/* Deprecated! */
ADDR_P2SH_SEGWIT = 1,
ADDR_BECH32 = 2,
ADDR_P2TR = 4,
ADDR_ALL = (ADDR_P2SH_SEGWIT + ADDR_BECH32 + ADDR_P2TR)
};
/* May return NULL if encoding error occurs. */ /* May return NULL if encoding error occurs. */
static char * static char *
encode_pubkey_to_addr(const tal_t *ctx, encode_pubkey_to_addr(const tal_t *ctx,
const struct pubkey *pubkey, const struct pubkey *pubkey,
bool is_p2sh_p2wpkh, enum addrtype addrtype,
/* Output: redeemscript to use to redeem outputs /* Output: redeemscript to use to redeem outputs
* paying to the address. * paying to the address.
* May be NULL if redeemscript is do not care. */ * May be NULL if redeemscript is do not care. */
@@ -44,14 +52,16 @@ encode_pubkey_to_addr(const tal_t *ctx,
u8 *redeemscript; u8 *redeemscript;
bool ok; bool ok;
if (is_p2sh_p2wpkh) { assert(addrtype != ADDR_ALL);
if (addrtype == ADDR_P2SH_SEGWIT) {
redeemscript = bitcoin_redeem_p2sh_p2wpkh(ctx, pubkey); redeemscript = bitcoin_redeem_p2sh_p2wpkh(ctx, pubkey);
sha256(&h, redeemscript, tal_count(redeemscript)); sha256(&h, redeemscript, tal_count(redeemscript));
ripemd160(&h160, h.u.u8, sizeof(h)); ripemd160(&h160, h.u.u8, sizeof(h));
out = p2sh_to_base58(ctx, out = p2sh_to_base58(ctx,
chainparams, chainparams,
&h160); &h160);
} else { } else if (addrtype == ADDR_BECH32) {
hrp = chainparams->onchain_hrp; hrp = chainparams->onchain_hrp;
/* out buffer is 73 + strlen(human readable part), /* out buffer is 73 + strlen(human readable part),
@@ -68,6 +78,21 @@ encode_pubkey_to_addr(const tal_t *ctx,
ok = segwit_addr_encode(out, hrp, 0, h160.u.u8, sizeof(h160)); ok = segwit_addr_encode(out, hrp, 0, h160.u.u8, sizeof(h160));
if (!ok) if (!ok)
out = tal_free(out); out = tal_free(out);
} else {
assert(addrtype == ADDR_P2TR);
u8 *p2tr_spk = scriptpubkey_p2tr(ctx, pubkey);
u8 *x_key = p2tr_spk + 2;
hrp = chainparams->onchain_hrp;
redeemscript = NULL;
/* out buffer is 73 + strlen(human readable part),
* see common/bech32.h*/
out = tal_arr(ctx, char, 73 + strlen(hrp));
ok = segwit_addr_encode(out, hrp, /* witver */ 1, x_key, 32);
if (!ok)
out = tal_free(out);
} }
if (out_redeemscript) if (out_redeemscript)
@@ -78,14 +103,6 @@ encode_pubkey_to_addr(const tal_t *ctx,
return out; return out;
} }
enum addrtype {
/* Deprecated! */
ADDR_P2SH_SEGWIT = 1,
ADDR_BECH32 = 2,
ADDR_ALL = (ADDR_P2SH_SEGWIT + ADDR_BECH32)
};
/* Extract bool indicating "bech32" */
static struct command_result *param_newaddr(struct command *cmd, static struct command_result *param_newaddr(struct command *cmd,
const char *name, const char *name,
const char *buffer, const char *buffer,
@@ -98,11 +115,13 @@ static struct command_result *param_newaddr(struct command *cmd,
**addrtype = ADDR_P2SH_SEGWIT; **addrtype = ADDR_P2SH_SEGWIT;
else if (json_tok_streq(buffer, tok, "bech32")) else if (json_tok_streq(buffer, tok, "bech32"))
**addrtype = ADDR_BECH32; **addrtype = ADDR_BECH32;
else if (!chainparams->is_elements && json_tok_streq(buffer, tok, "p2tr"))
**addrtype = ADDR_P2TR;
else if (json_tok_streq(buffer, tok, "all")) else if (json_tok_streq(buffer, tok, "all"))
**addrtype = ADDR_ALL; **addrtype = ADDR_ALL;
else else
return command_fail(cmd, JSONRPC2_INVALID_PARAMS, return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"'%s' should be 'bech32', or 'all', not '%.*s'", "'%s' should be 'p2tr', 'bech32', or 'all', not '%.*s'",
name, tok->end - tok->start, buffer + tok->start); name, tok->end - tok->start, buffer + tok->start);
return NULL; return NULL;
} }
@@ -116,8 +135,9 @@ static struct command_result *json_newaddr(struct command *cmd,
struct pubkey pubkey; struct pubkey pubkey;
enum addrtype *addrtype; enum addrtype *addrtype;
s64 keyidx; s64 keyidx;
char *p2sh, *bech32; char *p2sh, *bech32, *p2tr;
u8 *b32script; u8 *b32script;
u8 *p2tr_script;
if (!param(cmd, buffer, params, if (!param(cmd, buffer, params,
p_opt_def("addresstype", param_newaddr, &addrtype, ADDR_BECH32), p_opt_def("addresstype", param_newaddr, &addrtype, ADDR_BECH32),
@@ -132,15 +152,19 @@ static struct command_result *json_newaddr(struct command *cmd,
bip32_pubkey(cmd->ld, &pubkey, keyidx); bip32_pubkey(cmd->ld, &pubkey, keyidx);
b32script = scriptpubkey_p2wpkh(tmpctx, &pubkey); b32script = scriptpubkey_p2wpkh(tmpctx, &pubkey);
p2tr_script = scriptpubkey_p2tr(tmpctx, &pubkey);
if (*addrtype & ADDR_BECH32) if (*addrtype & ADDR_BECH32)
txfilter_add_scriptpubkey(cmd->ld->owned_txfilter, b32script); txfilter_add_scriptpubkey(cmd->ld->owned_txfilter, b32script);
if (*addrtype & ADDR_P2TR)
txfilter_add_scriptpubkey(cmd->ld->owned_txfilter, p2tr_script);
if (cmd->ld->deprecated_apis && (*addrtype & ADDR_P2SH_SEGWIT)) if (cmd->ld->deprecated_apis && (*addrtype & ADDR_P2SH_SEGWIT))
txfilter_add_scriptpubkey(cmd->ld->owned_txfilter, txfilter_add_scriptpubkey(cmd->ld->owned_txfilter,
scriptpubkey_p2sh(tmpctx, b32script)); scriptpubkey_p2sh(tmpctx, b32script));
p2sh = encode_pubkey_to_addr(cmd, &pubkey, true, NULL); p2sh = encode_pubkey_to_addr(cmd, &pubkey, ADDR_P2SH_SEGWIT, NULL);
bech32 = encode_pubkey_to_addr(cmd, &pubkey, false, NULL); bech32 = encode_pubkey_to_addr(cmd, &pubkey, ADDR_BECH32, NULL);
if (!p2sh || !bech32) { p2tr = encode_pubkey_to_addr(cmd, &pubkey, ADDR_P2TR, NULL);
if (!p2sh || !bech32 || !p2tr) {
return command_fail(cmd, LIGHTNINGD, return command_fail(cmd, LIGHTNINGD,
"p2wpkh address encoding failure."); "p2wpkh address encoding failure.");
} }
@@ -148,6 +172,8 @@ static struct command_result *json_newaddr(struct command *cmd,
response = json_stream_success(cmd); response = json_stream_success(cmd);
if (*addrtype & ADDR_BECH32) if (*addrtype & ADDR_BECH32)
json_add_string(response, "bech32", bech32); json_add_string(response, "bech32", bech32);
if (*addrtype & ADDR_P2TR)
json_add_string(response, "p2tr", p2tr);
if (cmd->ld->deprecated_apis && (*addrtype & ADDR_P2SH_SEGWIT)) if (cmd->ld->deprecated_apis && (*addrtype & ADDR_P2SH_SEGWIT))
json_add_string(response, "p2sh-segwit", p2sh); json_add_string(response, "p2sh-segwit", p2sh);
return command_success(cmd, response); return command_success(cmd, response);
@@ -196,19 +222,28 @@ static struct command_result *json_listaddrs(struct command *cmd,
u8 *redeemscript_p2sh; u8 *redeemscript_p2sh;
char *out_p2sh = encode_pubkey_to_addr(cmd, char *out_p2sh = encode_pubkey_to_addr(cmd,
&pubkey, &pubkey,
true, ADDR_P2SH_SEGWIT,
&redeemscript_p2sh); &redeemscript_p2sh);
// bech32 : p2wpkh // bech32 : p2wpkh
u8 *redeemscript_p2wpkh; u8 *redeemscript_p2wpkh;
char *out_p2wpkh = encode_pubkey_to_addr(cmd, char *out_p2wpkh = encode_pubkey_to_addr(cmd,
&pubkey, &pubkey,
false, ADDR_BECH32,
&redeemscript_p2wpkh); &redeemscript_p2wpkh);
if (!out_p2wpkh) { if (!out_p2wpkh) {
abort(); abort();
} }
// p2tr
char *out_p2tr = encode_pubkey_to_addr(cmd,
&pubkey,
ADDR_P2TR,
/* out_redeemscript */ NULL);
if (!out_p2tr) {
abort();
}
// outputs // outputs
json_object_start(response, NULL); json_object_start(response, NULL);
json_add_u64(response, "keyidx", keyidx); json_add_u64(response, "keyidx", keyidx);
@@ -219,6 +254,7 @@ static struct command_result *json_listaddrs(struct command *cmd,
json_add_string(response, "bech32", out_p2wpkh); json_add_string(response, "bech32", out_p2wpkh);
json_add_hex_talarr(response, "bech32_redeemscript", json_add_hex_talarr(response, "bech32_redeemscript",
redeemscript_p2wpkh); redeemscript_p2wpkh);
json_add_string(response, "p2tr", out_p2tr);
json_object_end(response); json_object_end(response);
} }
json_array_end(response); json_array_end(response);
@@ -666,7 +702,8 @@ static void match_psbt_outputs_to_wallet(struct wally_psbt *psbt,
abort(); abort();
} }
psbt_set_keypath(index, &ext, &psbt->outputs[outndx].keypaths); psbt_output_set_keypath(index, &ext, is_p2tr(script, NULL),
&psbt->outputs[outndx]);
} }
tal_wally_end(psbt); tal_wally_end(psbt);
} }