Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
9ac4c5d
common: option_independent_secrets, a channel type add-on (266/267)
davelund110 Oct 6, 2026
97bab93
lightningd: count the peer's revocations, and find its last secret, i…
davelund110 Oct 6, 2026
c06b000
wallet, lightningd: store a peer's secrets once they outgrow the shac…
davelund110 Oct 6, 2026
3c668af
onchaind: take a revoked commitment's secret from lightningd
davelund110 Oct 6, 2026
4a6429c
doc/schemas, cln-rpc, cln-grpc: name independent_secrets/even in chan…
davelund110 Oct 6, 2026
fb6483e
lightningd: --independent-secrets, to offer option_independent_secrets
davelund110 Oct 6, 2026
a5c73cd
hsmd, lightningd: --dev-independent-secrets-sender
davelund110 Oct 6, 2026
7a2bc6d
pytest: test option_independent_secrets
davelund110 Oct 6, 2026
1b45dd0
doc: describe option_independent_secrets
davelund110 Oct 6, 2026
b251c64
lightningd: report a channel's stored secrets in listpeerchannels
davelund110 Oct 17, 2026
d0373fd
lightningd: close a channel once it stores max-independent-secrets
davelund110 Oct 17, 2026
cc970d9
lightningd: apply the cap's backstop during a lease, and time out its…
davelund110 Oct 18, 2026
ea4b4c6
lightningd: keep the cap's backstop and close timeout across a restart
davelund110 Oct 18, 2026
3b69700
connectd: refuse a peer that sets option_independent_secrets' even bi…
davelund110 Oct 21, 2026
57c11b9
lightningd: hold the cap's mutual close until a pending splice locks
davelund110 Oct 21, 2026
0f0b2ca
pytest: punish a breach on a splice's funding with stored secrets
davelund110 Oct 21, 2026
cfbdb72
lightningd: start the cap's close when a pending splice is aborted
davelund110 Oct 22, 2026
46ac29e
lightningd: warn about a max-independent-secrets below 1,000,000
davelund110 Oct 22, 2026
9cfa165
lightningd: keep the dev sender to channels with the add-on
davelund110 Oct 22, 2026
64319e8
pytest: check the spliced penalty's funding output and amount
davelund110 Oct 22, 2026
b0eec30
doc: the cap's backstop and splices, 266 in init, and the next release
davelund110 Oct 22, 2026
4585d53
lightningd: check the cap last, since its backstop can free the channel
davelund110 Oct 22, 2026
ed0d136
lightningd: give the cap's close time after a restart, and keep a low…
davelund110 Oct 22, 2026
70fba63
lightningd: refuse the dev sender's plain channel before `check` returns
davelund110 Oct 23, 2026
d67c652
pytest: the cap's close after an aborted splice or RBF attempt
davelund110 Oct 23, 2026
d4d1391
doc: added in v26.06.9-blake2b.10, and an aborted RBF attempt
davelund110 Oct 23, 2026
d3e988f
lightningd, channeld: refuse a splice once the cap's close is coming
davelund110 Oct 24, 2026
cbc4c55
wallet, doc: where the backstop counts from, and the timeout with no …
davelund110 Oct 24, 2026
f72e90c
lightningd, channeld: start the cap's close before channeld goes on a…
davelund110 Oct 26, 2026
4260774
pytest: the cap's close refuses a splice at a splice lock, checked by…
davelund110 Oct 26, 2026
231c6a6
channeld: handle lightningd's requests between messages sent before r…
davelund110 Oct 26, 2026
de45089
pytest: punish a breach on a channel we opened
davelund110 Oct 26, 2026
44c4d93
channeld, lightningd: don't start comments with a BOLT number
davelund110 Oct 26, 2026
03c4d78
wallet/test: mock channel_start_mutual_close for run-wallet
davelund110 Oct 26, 2026
d5c9746
lightningd: refuse option_independent_secrets bits in plugin featurebits
davelund110 Oct 26, 2026
8d067aa
lightningd, openingd: a channel_type with option_independent_secrets …
davelund110 Oct 26, 2026
dbef153
channeld, doc: say why a splice is refused with a reconnect while clo…
davelund110 Oct 26, 2026
2a73f66
doc: the cap's close timeout survives a restart, unlike close's
davelund110 Oct 26, 2026
bceeab4
pytest: test_sql expects independent_secrets_stored in peerchannels
davelund110 Oct 26, 2026
98f277a
pytest: find a cap close timeout logged while restarting
davelund110 Oct 26, 2026
a8cb2c0
pytest: skip the splice-lock stall test without sqlite3
davelund110 Oct 26, 2026
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
39 changes: 39 additions & 0 deletions .msggen.json
Original file line number Diff line number Diff line change
Expand Up @@ -2550,6 +2550,7 @@
"ListConfigs.configs.htlc-minimum-msat": 43,
"ListConfigs.configs.ignore-fee-limits": 32,
"ListConfigs.configs.important-plugin": 9,
"ListConfigs.configs.independent-secrets": 76,
"ListConfigs.configs.large-channels": 20,
"ListConfigs.configs.lightning-dir": 12,
"ListConfigs.configs.log-file": 60,
Expand All @@ -2559,6 +2560,7 @@
"ListConfigs.configs.mainnet": 5,
"ListConfigs.configs.max-concurrent-htlcs": 42,
"ListConfigs.configs.max-dust-htlc-exposure-msat": 45,
"ListConfigs.configs.max-independent-secrets": 77,
"ListConfigs.configs.max-locktime-blocks": 34,
"ListConfigs.configs.min-capacity-sat": 46,
"ListConfigs.configs.network": 13,
Expand Down Expand Up @@ -2770,6 +2772,10 @@
"ListConfigs.configs.important-plugin.sources[]": 2,
"ListConfigs.configs.important-plugin.values_str[]": 1
},
"ListconfigsConfigsIndependent-secrets": {
"ListConfigs.configs.independent-secrets.set": 1,
"ListConfigs.configs.independent-secrets.source": 2
},
"ListconfigsConfigsLarge-channels": {
"ListConfigs.configs.large-channels.set": 1,
"ListConfigs.configs.large-channels.source": 2
Expand Down Expand Up @@ -2806,6 +2812,10 @@
"ListConfigs.configs.max-dust-htlc-exposure-msat.source": 2,
"ListConfigs.configs.max-dust-htlc-exposure-msat.value_msat": 1
},
"ListconfigsConfigsMax-independent-secrets": {
"ListConfigs.configs.max-independent-secrets.source": 1,
"ListConfigs.configs.max-independent-secrets.value_int": 2
},
"ListconfigsConfigsMax-locktime-blocks": {
"ListConfigs.configs.max-locktime-blocks.source": 2,
"ListConfigs.configs.max-locktime-blocks.value_int": 1
Expand Down Expand Up @@ -3242,6 +3252,7 @@
"ListPeerChannels.channels[].in_offered_msat": 45,
"ListPeerChannels.channels[].in_payments_fulfilled": 46,
"ListPeerChannels.channels[].in_payments_offered": 44,
"ListPeerChannels.channels[].independent_secrets_stored": 64,
"ListPeerChannels.channels[].inflight[]": 16,
"ListPeerChannels.channels[].initial_feerate": 12,
"ListPeerChannels.channels[].last_feerate": 13,
Expand Down Expand Up @@ -10287,6 +10298,18 @@
"added": "v23.08",
"deprecated": null
},
"ListConfigs.configs.independent-secrets": {
"added": "v26.06.9-blake2b.10",
"deprecated": null
},
"ListConfigs.configs.independent-secrets.set": {
"added": "v26.06.9-blake2b.10",
"deprecated": null
},
"ListConfigs.configs.independent-secrets.source": {
"added": "v26.06.9-blake2b.10",
"deprecated": null
},
"ListConfigs.configs.large-channels": {
"added": "v23.08",
"deprecated": null
Expand Down Expand Up @@ -10395,6 +10418,18 @@
"added": "v23.08",
"deprecated": null
},
"ListConfigs.configs.max-independent-secrets": {
"added": "v26.06.9-blake2b.10",
"deprecated": null
},
"ListConfigs.configs.max-independent-secrets.source": {
"added": "v26.06.9-blake2b.10",
"deprecated": null
},
"ListConfigs.configs.max-independent-secrets.value_int": {
"added": "v26.06.9-blake2b.10",
"deprecated": null
},
"ListConfigs.configs.max-locktime-blocks": {
"added": "pre-v0.10.1",
"deprecated": "v24.05"
Expand Down Expand Up @@ -11771,6 +11806,10 @@
"added": "v23.02",
"deprecated": null
},
"ListPeerChannels.channels[].independent_secrets_stored": {
"added": "v26.06.9-blake2b.10",
"deprecated": null
},
"ListPeerChannels.channels[].inflight[]": {
"added": "v23.02",
"deprecated": null
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ This is an unofficial version of [Core Lightning](https://github.com/ElementsPro
- **Unified signatures.** Wallet transactions and new channels are signed with the opt-in `SIGHASH_UNIFIED` digest, so a channel funded past activation from coins received past activation is signed in a way a node that has not upgraded rejects, and cannot be replayed on the SHA256d fork. Built on [connorslab's](https://github.com/connorslab/lightning) unified-sigs work.
- **A required peer feature bit.** The node advertises `option_blake2b` as compulsory, so it will not connect to a Lightning node that has not upgraded.
- **Downgrades are refused.** A build without unified signing computes a different signature hash and could not close the channels this one opens, so `lightning-downgrade` stops before touching the database.
- **Independent per-commitment secrets.** With `--independent-secrets` (off by default), on a new channel with a peer which also offers `option_independent_secrets`, the node accepts per-commitment secrets that don't come from a shachain and keeps each one it needs to punish a breach, so the peer can generate them k-of-n and no single signer can give away the secret of its current state. Secrets from a peer which uses a shachain still go in one, so nothing changes with such a peer. See [doc/independent-secrets.md](doc/independent-secrets.md).

## Before opening channels

The required feature bit means you **cannot cooperatively close a channel opened before activation** with a counterparty that has not upgraded.

The feature bits are the ones proposed for BOLT 9: 512 for `option_blake2b` and 514 for `option_unified_sigs`. A stored channel type using the earlier bit 70 is moved to 514 on upgrade.
The feature bits are the ones proposed for BOLT 9: 512 for `option_blake2b` and 514 for `option_unified_sigs`. `option_independent_secrets` uses 266, from [lightning-blake2b/bolts#17](https://github.com/lightning-blake2b/bolts/pull/17). A stored channel type using the earlier bit 70 is moved to 514 on upgrade.

Fund channels only from coins received past activation. A channel funded from a coin received before activation has a funding transaction a node that has not upgraded also accepts, which reopens the exposure unified signing exists to close.

Expand Down
68 changes: 65 additions & 3 deletions channeld/channeld.c
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,11 @@ static void check_mutual_channel_ready(const struct peer *peer)
lock_signer_outpoint(&peer->channel->funding);
}

static u8 *master_wait_sync_reply(const tal_t *ctx,
struct peer *peer,
const u8 *msg,
int replytype);

/* Call this method when splice_locked status are changed. If both sides have
* splice_locked'ed than this function consumes the `splice_locked_ready` values
* and considers the channel funding to be switched to the splice tx. */
Expand Down Expand Up @@ -547,7 +552,13 @@ static void check_mutual_splice_locked(struct peer *peer)
msg = towire_channeld_got_splice_locked(NULL, inflight->amnt,
inflight->splice_amnt,
&inflight->outpoint.txid);
wire_sync_write(MASTER_FD, take(msg));
/* If max-independent-secrets was reached while the splice was
* pending, lightningd starts the close it held back before it
* replies. So we keep its shutdown request, and handle it before
* anything else the peer sends, such as a new splice, which we then
* refuse. */
master_wait_sync_reply(tmpctx, peer, take(msg),
WIRE_CHANNELD_GOT_SPLICE_LOCKED_REPLY);

billboard_update(peer);

Expand Down Expand Up @@ -1000,6 +1011,18 @@ static void maybe_send_shutdown(struct peer *peer)
billboard_update(peer);
}

/* Under BOLT 2, neither `splice_init` after `shutdown`, nor `shutdown` while
* a splice isn't locked, is allowed. So a channel stops splicing once either side has
* sent `shutdown`, or once lightningd has asked us to send one (which it can
* do by itself, say at max-independent-secrets): otherwise that shutdown
* would have to wait for a splice that may never lock. */
static bool channel_closing(const struct peer *peer)
{
return peer->send_shutdown
|| peer->shutdown_sent[LOCAL]
|| peer->shutdown_sent[REMOTE];
}

static void send_shutdown_complete(struct peer *peer)
{
/* Now we can tell master shutdown is complete. */
Expand Down Expand Up @@ -3256,6 +3279,7 @@ static const u8 *peer_expect_msg_four(const tal_t *ctx,
* before resuming splice negotiation, so we need a way to process it in this
* order. */
static void peer_in(struct peer *peer, const u8 *msg);
static void req_in(struct peer *peer, const u8 *msg);

/* The question of "who signs splice commitments first" is the same order as the
* splice `tx_signature`s are. This function handles sending & receiving the
Expand Down Expand Up @@ -4529,6 +4553,17 @@ static void splice_accepter(struct peer *peer, const u8 *inmsg)
" a splice while we have one pending. Did you"
" mean to send SPLICE_ACK?");

/* For the splice_init receiver, BOLT 2 says to warn and close the
* connection if it has received `shutdown`, and to refuse otherwise
* with `tx_abort`. We warn and reconnect too when only we are
* closing (we have sent `shutdown`, or are about to). After a
* tx_abort, channeld restarts on the same connection, and the new
* one can't tell whether our `shutdown` went out, so it could send
* it twice or never; reestablish retransmits it, as BOLT 2 says. */
if (channel_closing(peer))
peer_failed_warn(peer->pps, &peer->channel_id,
"Splice refused: the channel is closing");

peer->splicing = splicing_new(peer);

ictx = new_interactivetx_context(tmpctx, our_role,
Expand Down Expand Up @@ -5313,6 +5348,12 @@ static void handle_splice_stfu_success(struct peer *peer)
{
u8 *msg;
struct tlv_tx_init_rbf_tlvs *init_rbf_tlvs;

/* We were asked to close while we waited for quiescence. We can't
* leave it without the peer, so we reestablish, and close then. */
if (channel_closing(peer))
peer_failed_warn(peer->pps, &peer->channel_id,
"Splice cancelled: the channel is closing");
if (!last_inflight(peer)) {
msg = towire_splice(tmpctx,
&peer->channel_id,
Expand Down Expand Up @@ -5362,6 +5403,14 @@ static void handle_splice_init(struct peer *peer, const u8 *inmsg)
return;
}

if (channel_closing(peer)) {
msg = towire_channeld_splice_state_error(NULL, "Can't splice"
" a channel that is"
" closing.");
wire_sync_write(MASTER_FD, take(msg));
return;
}

peer->splicing = splicing_new(peer);

if (!fromwire_channeld_splice_init(peer->splicing, inmsg,
Expand Down Expand Up @@ -6648,9 +6697,21 @@ static void peer_reconnect(struct peer *peer,
* - MUST wait to receive the other node's `channel_reestablish`
* message before sending any other messages for that channel.
*/
/* LND doesn't wait. */
for (size_t i = 0; i < tal_count(premature_msgs); i++)
/* LND doesn't wait. Between those messages, handle what lightningd
* has asked meanwhile first, as the main loop does: a revocation that
* reaches max-independent-secrets has it ask for a shutdown, which
* must come before a splice the peer sent after that revocation. */
for (size_t i = 0; i < tal_count(premature_msgs); i++) {
const u8 *req;

while ((req = msg_dequeue(peer->from_master)) != NULL) {
status_debug("Now dealing with deferred %s",
channeld_wire_name(fromwire_peektype(req)));
req_in(peer, req);
tal_free(req);
}
peer_in(peer, premature_msgs[i]);
}
tal_free(premature_msgs);
}

Expand Down Expand Up @@ -7237,6 +7298,7 @@ static void req_in(struct peer *peer, const u8 *msg)
case WIRE_CHANNELD_GOT_REVOKE_REPLY:
case WIRE_CHANNELD_GOT_CHANNEL_READY:
case WIRE_CHANNELD_GOT_SPLICE_LOCKED:
case WIRE_CHANNELD_GOT_SPLICE_LOCKED_REPLY:
case WIRE_CHANNELD_GOT_ANNOUNCEMENT:
case WIRE_CHANNELD_GOT_SHUTDOWN:
case WIRE_CHANNELD_SHUTDOWN_COMPLETE:
Expand Down
3 changes: 3 additions & 0 deletions channeld/channeld_wire.csv
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ msgdata,channeld_got_splice_locked,funding_sats,amount_sat,
msgdata,channeld_got_splice_locked,splice_amnt,s64,
msgdata,channeld_got_splice_locked,locked_txid,bitcoin_txid,

# lightningd has handled the lock, including starting a close it held back.
msgtype,channeld_got_splice_locked_reply,1219

#include <common/penalty_base.h>

subtype,local_anchor_info
Expand Down
13 changes: 13 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/proto/primitives.proto
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ enum ChannelTypeName {
zeroconf_even = 4;
anchors_even = 5;
unified_sigs_even = 6;
independent_secrets_even = 7;
}

enum AutocleanSubsystem {
Expand Down
23 changes: 23 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.

Loading
Loading