Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
3a6aca0
pytest: test for signing a signed PSBT.
rustyrussell Nov 18, 2025
6be04fc
common: don't abort() if wally_psbt_output_taproot_keypath_add() fails.
rustyrussell Nov 18, 2025
ccd626d
lightningd: allow another gossip state transition.
rustyrussell Nov 18, 2025
172dbeb
pytest: test case where we crash before bitcoind gets the opening tx.
rustyrussell Nov 18, 2025
1a85a39
lightningd: save funding_psbt in channel, database.
rustyrussell Nov 18, 2025
e321ade
sendpsbt: update channel psbts if this is a channel PSBT.
rustyrussell Nov 18, 2025
85cc51a
lightningd: re-xmit funding txs on startup.
rustyrussell Nov 18, 2025
83ea880
pytest: test failure if we crash after fundchannel_complete but befor…
rustyrussell Nov 18, 2025
73f7527
lightningd: expose funding PSBT (if we have it) in JSON API.
rustyrussell Nov 18, 2025
555eb5c
spender: look for unsigned PSBT on awaiting channels on startup, and …
rustyrussell Nov 18, 2025
c2cff56
lightningd: save funding PSBT to database if we're to withhold it.
rustyrussell Nov 18, 2025
a5fd2bb
lightningd: add withheld flag to listpeerchannels and listclosedchann…
rustyrussell Nov 18, 2025
f3f3176
lightningd: immediately close without broadcast whenever we close a w…
rustyrussell Nov 18, 2025
2b85078
lightningd: don't rebroadcast withheld channels' funding_psbt on rest…
rustyrussell Nov 18, 2025
381602a
lightningd: add withhold option to fundchannel_complete.
rustyrussell Nov 18, 2025
4d2a35f
lightningd: remove withheld flag when we see sendpsbt.
rustyrussell Nov 18, 2025
8453500
pytest: test withhold parameter to fundchannel_complete.
rustyrussell Nov 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions .msggen.json
Original file line number Diff line number Diff line change
Expand Up @@ -1698,7 +1698,8 @@
},
"FundchannelCompleteRequest": {
"FundChannel_Complete.id": 1,
"FundChannel_Complete.psbt": 2
"FundChannel_Complete.psbt": 2,
"FundChannel_Complete.withhold": 3
},
"FundchannelCompleteResponse": {
"FundChannel_Complete.channel_id": 1,
Expand Down Expand Up @@ -2075,8 +2076,10 @@
"ListClosedChannels.closedchannels[].funding_fee_paid_msat": 15,
"ListClosedChannels.closedchannels[].funding_fee_rcvd_msat": 16,
"ListClosedChannels.closedchannels[].funding_outnum": 13,
"ListClosedChannels.closedchannels[].funding_psbt": 26,
"ListClosedChannels.closedchannels[].funding_pushed_msat": 17,
"ListClosedChannels.closedchannels[].funding_txid": 12,
"ListClosedChannels.closedchannels[].funding_withheld": 27,
"ListClosedChannels.closedchannels[].last_commitment_fee_msat": 23,
"ListClosedChannels.closedchannels[].last_commitment_txid": 22,
"ListClosedChannels.closedchannels[].last_stable_connection": 25,
Expand Down Expand Up @@ -2844,8 +2847,10 @@
"ListPeerChannels.channels[].funding.fee_paid_msat": 4,
"ListPeerChannels.channels[].funding.fee_rcvd_msat": 5,
"ListPeerChannels.channels[].funding.local_funds_msat": 2,
"ListPeerChannels.channels[].funding.psbt": 6,
"ListPeerChannels.channels[].funding.pushed_msat": 1,
"ListPeerChannels.channels[].funding.remote_funds_msat": 3
"ListPeerChannels.channels[].funding.remote_funds_msat": 3,
"ListPeerChannels.channels[].funding.withheld": 7
},
"ListpeerchannelsChannelsHtlcs": {
"ListPeerChannels.channels[].htlcs[].amount_msat": 3,
Expand Down Expand Up @@ -7318,6 +7323,10 @@
"added": "pre-v0.10.1",
"deprecated": null
},
"FundChannel_Complete.withhold": {
"added": "v25.12",
"deprecated": null
},
"FundChannel_Start": {
"added": "pre-v0.10.1",
"deprecated": null
Expand Down Expand Up @@ -8350,6 +8359,10 @@
"added": "pre-v0.10.1",
"deprecated": null
},
"ListClosedChannels.closedchannels[].funding_psbt": {
"added": "v25.12",
"deprecated": null
},
"ListClosedChannels.closedchannels[].funding_pushed_msat": {
"added": "pre-v0.10.1",
"deprecated": null
Expand All @@ -8358,6 +8371,10 @@
"added": "pre-v0.10.1",
"deprecated": null
},
"ListClosedChannels.closedchannels[].funding_withheld": {
"added": "v25.12",
"deprecated": null
},
"ListClosedChannels.closedchannels[].last_commitment_fee_msat": {
"added": "pre-v0.10.1",
"deprecated": null
Expand Down Expand Up @@ -10342,6 +10359,10 @@
"added": "v23.02",
"deprecated": null
},
"ListPeerChannels.channels[].funding.psbt": {
"added": "v25.12",
"deprecated": null
},
"ListPeerChannels.channels[].funding.pushed_msat": {
"added": "v23.02",
"deprecated": null
Expand All @@ -10350,6 +10371,10 @@
"added": "v23.02",
"deprecated": null
},
"ListPeerChannels.channels[].funding.withheld": {
"added": "v25.12",
"deprecated": null
},
"ListPeerChannels.channels[].funding_outnum": {
"added": "v23.02",
"deprecated": null
Expand Down
7 changes: 5 additions & 2 deletions channeld/watchtower.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,11 @@ penalty_tx_create(const tal_t *ctx,
if (final_index) {
size_t script_len = tal_bytelen(final_scriptpubkey);
bool is_tr = is_p2tr(final_scriptpubkey, script_len, NULL);
psbt_add_keypath_to_last_output(tx, *final_index,
final_ext_key, is_tr);
if (!psbt_add_keypath_to_last_output(tx, *final_index,
final_ext_key, is_tr))
status_failed(STATUS_FAIL_INTERNAL_ERROR,
"Cannot add final keypath?");

}

/* Worst-case sig is 73 bytes */
Expand Down
5 changes: 5 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.

6 changes: 6 additions & 0 deletions cln-grpc/src/convert.rs

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

10 changes: 10 additions & 0 deletions cln-rpc/src/model.rs

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

8 changes: 5 additions & 3 deletions common/close_tx.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,11 @@ struct bitcoin_tx *create_close_tx(const tal_t *ctx,
assert((local_wallet_index == NULL) == (local_wallet_ext_key == NULL));
if (local_wallet_index) {
size_t script_len = tal_bytelen(script);
psbt_add_keypath_to_last_output(
tx, *local_wallet_index, local_wallet_ext_key,
is_p2tr(script, script_len, NULL));
/* Should not happen! */
if (!psbt_add_keypath_to_last_output(
tx, *local_wallet_index, local_wallet_ext_key,
is_p2tr(script, script_len, NULL)))
return tal_free(tx);
}
num_outputs++;
}
Expand Down
23 changes: 16 additions & 7 deletions common/psbt_keypath.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
#include <common/psbt_keypath.h>
#include <common/utils.h>

void psbt_output_set_keypath(u32 index, const struct ext_key *ext, bool is_taproot, struct wally_psbt_output *output) {
bool 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 @@ -18,24 +22,29 @@ void psbt_output_set_keypath(u32 index, const struct ext_key *ext, bool is_tapro
NULL, 0,
fingerprint, sizeof(fingerprint),
path, 1) != WALLY_OK)
abort();
return false;
} else {
if (wally_psbt_output_keypath_add(output,
ext->pub_key, sizeof(ext->pub_key),
fingerprint, sizeof(fingerprint),
path, 1) != WALLY_OK)
abort();
return false;
}

return true;
}

void psbt_add_keypath_to_last_output(struct bitcoin_tx *tx,
bool psbt_add_keypath_to_last_output(struct bitcoin_tx *tx,
u32 key_index,
const struct ext_key *ext,
bool is_taproot) {
const struct ext_key *ext,
bool is_taproot)
{
size_t outndx = tx->psbt->num_outputs - 1;
bool ok;

tal_wally_start();
psbt_output_set_keypath(key_index, ext, is_taproot, &tx->psbt->outputs[outndx]);
ok = psbt_output_set_keypath(key_index, ext, is_taproot, &tx->psbt->outputs[outndx]);
tal_wally_end(tx->psbt);

return ok;
}
17 changes: 11 additions & 6 deletions common/psbt_keypath.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define LIGHTNING_COMMON_PSBT_KEYPATH_H

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

Expand All @@ -15,11 +16,14 @@ struct wally_map;
* @ext - extended public key of the immediate parent of the wallet key
* @is_taproot - PSBT output has taproot script
* @output - PSBT output to set
*
* This can fail, if it's adding the same thing twice (taproot only)
*/
void psbt_output_set_keypath(u32 index,
const struct ext_key *ext,
bool is_taproot,
struct wally_psbt_output *output);
WARN_UNUSED_RESULT
bool psbt_output_set_keypath(u32 index,
const struct ext_key *ext,
bool is_taproot,
struct wally_psbt_output *output);

/* psbt_add_keypath_to_last_output - augment the last output with the
* given wallet keypath
Expand All @@ -29,9 +33,10 @@ void psbt_output_set_keypath(u32 index,
* @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,
WARN_UNUSED_RESULT
bool psbt_add_keypath_to_last_output(struct bitcoin_tx *tx,
u32 index,
const struct ext_key *ext,
bool is_taproot);
bool is_taproot);

#endif /* LIGHTNING_COMMON_PSBT_KEYPATH_H */
38 changes: 37 additions & 1 deletion contrib/msggen/msggen/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -13594,6 +13594,13 @@
"description": [
"Transaction to use for funding (does not need to be signed but must be otherwise complete)."
]
},
"withhold": {
"type": "boolean",
"added": "v25.12",
"description": [
"Mark this channel 'withheld' so we know we haven't broadcast the funding transaction. If the channel is closed before we call `sendpsbt` on this psbt, it will simply be closed immediately."
]
}
}
},
Expand Down Expand Up @@ -18439,6 +18446,7 @@
"total_htlcs_sent",
"funding_txid",
"funding_outnum",
"funding_withheld",
"leased",
"final_to_us_msat",
"min_to_us_msat",
Expand Down Expand Up @@ -18584,6 +18592,20 @@
"The 0-based output number of the funding transaction which opens the channel."
]
},
"funding_psbt": {
"type": "string",
"added": "v25.12",
"description": [
"The PSBT (may be non-final or unsigned) we should use to open the channel, if any"
]
},
"funding_withheld": {
"type": "boolean",
"added": "v25.12",
"description": [
"True if we have not broadcast the funding transaction (see fundchannel_complete)."
]
},
"leased": {
"type": "boolean",
"description": [
Expand Down Expand Up @@ -24327,6 +24349,20 @@
"description": [
"Amount we were paid by peer at open."
]
},
"psbt": {
"type": "string",
"added": "v25.12",
"description": [
"The PSBT (may be non-final or unsigned) we should use to open the channel, if any. This is initially from `fundchannel_complete`, but will be updated with if `sendpsbt` is called with an updated PSBT."
]
},
"withheld": {
"type": "boolean",
"added": "v25.12",
"description": [
"True if `fundchannel_complete` told us it will not broadcast the funding transaction (so we know not to bother with any other onchain transactions in the case of this channel). This is set to false if `sendpsbt` is send on the above PSBT."
]
}
}
},
Expand Down Expand Up @@ -32300,7 +32336,7 @@
"rpc": "sendpsbt",
"title": "Command to finalize, extract and send a partially signed bitcoin transaction (PSBT).",
"description": [
"The **sendpsbt** is a low-level RPC command which sends a fully-signed PSBT."
"The **sendpsbt** is a low-level RPC command which sends a fully-signed PSBT. If the PSBT is the same one promised by a channel (via fundchannel_complete) it will also be associated with that channel and re-transmitted if necessary on restart."
],
"request": {
"required": [
Expand Down
3 changes: 2 additions & 1 deletion contrib/pyln-client/pyln/client/lightning.py
Original file line number Diff line number Diff line change
Expand Up @@ -847,13 +847,14 @@ def fundchannel_cancel(self, node_id):
}
return self.call("fundchannel_cancel", payload)

def fundchannel_complete(self, node_id, psbt):
def fundchannel_complete(self, node_id, psbt, withhold=True):
"""
Complete channel establishment with {id}, using {psbt}.
"""
payload = {
"id": node_id,
"psbt": psbt,
"withhold": withhold,
}
return self.call("fundchannel_complete", payload)

Expand Down
1,580 changes: 790 additions & 790 deletions contrib/pyln-grpc-proto/pyln/grpc/node_pb2.py

Large diffs are not rendered by default.

Loading
Loading