Repository navigation
Add option_independent_secrets channel type - #48
davelund110 wants to merge 13 commits into
Conversation
Proposed in a draft bLIP: a channel type add-on under which a node doesn't require its peer's per-commitment secrets to come from a shachain, so the peer can generate them k-of-n. This adds the bits (even in the channel type, odd in init and node_announcement), their name, the dependency on option_channel_type, and accepts the add-on as a variant of either basic channel type when we offer it. Nothing offers it yet.
…n one place How many per-commitment secrets the peer has revealed, and the last of them, come from the shachain in four places: the revocation check, channeld's init and channel_reestablish for a closing or long-closed channel. Put them behind channel_their_revocations() and channel_their_last_secret() (and closed_channel_their_last_secret() for closed ones), which on an option_independent_secrets channel whose secrets outgrew the shachain will read a count and the latest secret instead. No change for shachain channels.
…hain On a channel with option_independent_secrets the peer's per-commitment secrets needn't be a shachain. They still go in it for as long as they fit, as they always do from a peer which uses one: then nothing changes, down to the static channel backup, which carries the shachain and so can still punish. From the first secret which doesn't fit, peer_got_revoke() stores that one and every later one in a new table, channel_revocation_secrets, in the same db transaction as the rest of the revocation. channeld has already checked that each secret generates the point of the commitment it revokes. Loading a channel, open or closed, reads whether its secrets outgrew the shachain, and if so how many there are and the latest, for channel_reestablish. Once the channel is closed and resolved, wallet_channel_close() keeps only the latest: nothing can spend the funding output any more. The migration's revert refuses while a channel which hasn't closed uses the add-on, since an older version would expect the next secret to extend a shachain and fail the channel. test_splice_stored_feerate_repaired_on_upgrade re-runs the last migrations, so it now reaches back one more, and drops the new table first.
onchaind derives a revoked commitment's secret from the shachain it is given, which on an option_independent_secrets channel may hold only the secrets before the first which didn't fit. So lightningd unmasks the number of the commitment that spent the funding output, exactly as onchaind does, and if the peer revoked it and its secret is in the db, passes that secret in onchaind_init, along with the number of secrets held, which onchaind now takes from lightningd rather than counting the shachain. A mutual close or one of our own commitments can unmask to a revoked number too: onchaind recognizes those first and ignores the secret.
…nel types listpeerchannels, listclosedchannels and the opening commands report the channel type's names, which are a closed enum in the schemas, cln-rpc's ChannelTypeName and cln-grpc's primitives.proto: add the new one in each, so a channel with the add-on validates and deserializes. primitives_pb2.py is regenerated with protoc 31.1, which reproduces the previous file exactly.
With --independent-secrets we signal the optional bit, and every new channel with a peer which signals it too uses the add-on, including a channel type the caller names (it changes nothing but what the receiver accepts). Channels with other peers open as before. It is off by default: the receiver of such secrets stores each one, so an operator chooses to. It can't be used with an external signer, an hsmd replaced with --subdaemon such as VLS, which checks the peer's secrets against a shachain: lightningd refuses to start with both, or with an external signer while any channel with the add-on is open or being resolved, since a channel keeps it after the option is removed. Like option_blake2b and option_unified_sigs, it is not available on Liquid. Changelog-Added: Protocol: `option_independent_secrets` (266/267): a peer's per-commitment secrets need not come from a shachain, so it can generate them k-of-n; `--independent-secrets` offers it (off by default, and not with an external signer).
To exercise a peer's support for option_independent_secrets, this developer option makes hsmd derive our own per-commitment secrets as HMAC-SHA256(shaseed, "independent-per-commitment-secret" || n || counter), the same function as Lightning Fork's dev option, instead of from a shachain. Like the shachain it needs nothing but the seed, so every secret can be produced again. It applies to every channel, so lightningd refuses to start with it changed while any channel is open or being resolved, and it needs --independent-secrets, since only a peer with the add-on accepts them.
Negotiation, which only --independent-secrets offers, and the options it refuses (an external signer, also while a channel with the add-on is open, and the dev sender without it); that a channel keeps the add-on, storing and punishing, after a restart without the option; that a peer which uses a shachain costs nothing, including to a static backup (test_emergencyrecoverpenaltytxn now also runs with the add-on); storage across restarts and reconnections; that a shachain channel refuses the dev sender's secrets (which shows the other tests really give the receiver unrelated ones); the startup guard on the dev option; penalties from the db and from the shachain part, with and without an HTLC, and across restarts of onchaind; the peer's own unilateral close; pruning once the channel is forgotten, and channel_reestablish for it afterwards. Most run with both v1 and v2 opens.
What the add-on is for, what this node does with it, how to turn it on, its limits (static channel backups, external signers, downgrades) and the dev option.
On a channel with option_independent_secrets, listpeerchannels now shows independent_secrets_stored: how many of the peer's per-commitment secrets we store one by one, from the first which didn't fit the shachain. It stays 0 with a peer which uses one, and it is how an operator sees what the add-on costs on each channel. Changelog-Added: JSON-RPC: `listpeerchannels` shows `independent_secrets_stored` on channels with `option_independent_secrets`.
On a channel with option_independent_secrets, every update adds a secret we store one by one once they outgrow the shachain, and nothing else bounds them while the channel is open. --max-independent-secrets (default 1,000,000: 32MB of secrets, about 60MB in sqlite3; 0 for no limit) bounds them: at 90% we log a warning, and at the limit we start a mutual close, as `close` would, sending P2WPKH if the peer can't take our default P2TR address. After `shutdown` the peer only needs as many more as it takes to settle the HTLCs in flight, but it can keep a closing channel updating with update_fee, so once we store 10% more than the limit (at least one more) we close the channel unilaterally. Like `close`, we keep a lease we sold, and close at the first update after it expires. Changelog-Added: Config: `max-independent-secrets` closes a channel with `option_independent_secrets` once it stores that many of the peer's secrets (default 1000000).
kwsantiago
left a comment
There was a problem hiding this comment.
A sold lease skips the cap's backstop, so storage is unbounded until expiry; the cap's mutual close also needs a timeout.
… close Three fixes to max-independent-secrets, from review: - A channel the peer leased from us skipped the backstop as well as the mutual close, so until the lease expired a peer could make us store its secrets without limit. We still don't start a mutual close before the lease expires, but the 10% backstop now applies: our funds stay locked until then however the channel closes. - The mutual close started at the cap had no timeout, so a peer could leave it unfinished for ever. After 48 hours, as `close` waits by default, we now close unilaterally. The timer isn't saved, so at startup it is armed again for a channel that is closing and over the cap. --dev-independent-secrets-close-timeout shortens it for tests. - The 10% now counts from where we first saw the channel at or over the cap, so a cap lowered below what a channel already stores starts a mutual close at its next update instead of failing the channel at the one after. Also point the docs and comments at lightning-blake2b/bolts#17, where the spec now lives, instead of the draft bLIP.
Thanks, both fixed in b2e57b6: the backstop now applies during a lease (we still don’t start the mutual close before it expires), and the cap’s mutual close times out after 48h, like close’s default. A lowered cap also counts its 10% from where the channel was now. There are three new tests: lease, timeout, lowered cap. |
kwsantiago
left a comment
There was a problem hiding this comment.
Both fixed, thanks. A restart resets the slack and timer, and re-arms closes we didn't start.
From review: a restart reset the 10% backstop and the 48-hour timeout of the mutual close started at max-independent-secrets, and at startup the timeout was armed for any closing channel over the cap, including a close we didn't start. - How many secrets the channel stored when we started the close is now saved, in a new channels column, and the backstop counts from there. Without such a close it counts from the cap itself, so nothing resets on a restart. - At startup the timeout is armed only for a close we started, with whatever is left of its time since the channel moved to CHANNELD_SHUTTING_DOWN, as its state changes record.
|
Good catch, fixed in 4df42d5: the count at which our close started is now saved in the db (a new column), so the backstop doesn't reset on a restart, and at startup the timeout is armed only for a close we started, with the time it has left. There are two more tests: restart mid-close, and a user's close left alone. |
What
This PR is Core Lightning's side of
option_independent_secrets. The add-on is specified in the fork's BOLTs (lightning-blake2b/bolts#17), and Lightning Fork has it too (PR #7); the two were tested against each other.channel_revocation_secrets, in the same database transaction as the rest of the revocation.--independent-secretsturns it on, since the receiver of such secrets pays for storing them.--subdaemon, such as VLS), which checks the peer's secrets against a shachain: lightningd refuses to start with both, or with an external signer while any channel with the add-on is open or being resolved.listpeerchannelsshowsindependent_secrets_storedon such a channel: how many of the peer's secrets are stored one by one.--max-independent-secrets(default 1,000,000; 0 for no limit). At 90% the node logs a warning, and at the cap it starts a mutual close of the channel, asclosewould. A peer that keeps the closing channel updating has it closed unilaterally once it has made us store 10% more, and so does a close that hasn't finished in 48 hours.channel_type. A peer that doesn't signal it gets a normal channel.listpeerchannels,listclosedchannelsand the opening commands show it asindependent_secrets/even.Channels without the bit behave exactly as before. They keep their shachain, run the same checks, and store the same rows.
Size: about 1,040 added lines of C outside the tests; with tests, docs and regenerated bindings, 59 files, +2,350/−101. GitHub shows about 1,000 more lines each way, because it counts the regenerated
node_pb2.pyline by line; most of that is generated offsets that move whenever node.proto changes.Why
With a shachain, every signer of a k-of-n node must hold the seed, because any of them may need to revoke a commitment.
Independent secrets let the signers generate and reveal secrets jointly.
This PR doesn't add a threshold signer. It adds what every peer must support first: accepting secrets that don't come from a shachain. For testing, it also adds a developer-only way for this node to send such secrets.
Commits
Each commit builds and passes the unit tests, so they can be reviewed one at a time.
common: the feature bits, their name, the dependency onoption_channel_type, and acceptance as a channel type variant.lightningd: the count of the peer's revocations and its last secret come from one place. A refactor: shachain channels see no change.wallet, lightningd: keep the peer's secrets in the shachain while they fit, and store them from the first that doesn't. Reload the count and the latest; keep only the latest once the channel is closed. Includes the migration and its guarded revert.onchaind: take a revoked commitment's secret from lightningd.doc/schemas, cln-rpc, cln-grpc: the new channel type name.lightningd:--independent-secrets(off by default, refused with an external signer), docs,listconfigsand the regenerated bindings.hsmd, lightningd:--dev-independent-secrets-sender, which needs--independent-secrets.pytest: the new tests.doc:doc/independent-secrets.mdand the README.lightningd:independent_secrets_storedinlistpeerchannels.lightningd:--max-independent-secrets, the warning at 90% and the mutual close at the cap.lightningd: review fixes to the cap: the 10% backstop also applies while a lease we sold runs, the cap's mutual close times out after 48 hours, and a lowered cap counts its 10% from where the channel was.lightningd: keep the backstop and the close's timeout across a restart, and re-arm the timeout only for a close we started.How
Negotiation (
common,lightningd/channel.c,opening_control.c,dual_open_control.c)init,node_announcementandchannel_type, and depend onoption_channel_type.channel_type_accept()treats 266 as a variant of both basic types, and only when we offer it. So a node without--independent-secretsrefuses a channel type that includes it.desired_channel_type()adds the bit when both sides have it.fundchannel_startandopenchannel_initwhen the caller names a type, because it changes nothing but what the receiver accepts.option_unified_sigs.Receiving secrets (
peer_htlcs.c)peer_got_revoke()checks that the revocation number equals the count, then offers the secret to the shachain, as now.shachain_add_hash()checks a secret against the ones before it before it changes anything, so a refused secret leaves the shachain as it was.Reestablish (
channel.c,channel_control.c,peer_control.c)channel_their_revocations()andchannel_their_last_secret()give the count and the latest secret: from the stored secrets once there are any (kept in memory, and reloaded at startup), and from the shachain otherwise.CLOSINGD_COMPLETE), and the direct reply to a reestablish on any other closed channel, including a long-closed one, all go through them.Database (
wallet/)channel_revocation_secrets(channel_id, commitnum, secret).(channel_id, commitnum).ON DELETE CASCADE, as the other per-channel tables do.wallet_channel_close()deletes all but the latest stored secret, whichchannel_reestablishstill needs. It runs only once the channel is irrevocably resolved, never funded, or forgotten withdev-forget-channel.channels.independent_secrets_close_at(commit 13, with its own migration and revert) records how many secrets the channel stored when we started a mutual close at the cap, so the backstop survives a restart.Onchain (
onchain_control.c,onchaind/)onchaind_init, along with the count. Otherwise onchaind derives it from the shachain, as now.Count and cap (
channel.c,peer_control.c,peer_htlcs.c,closing_control.c)channel_their_stored_secrets()is the count of stored secrets: the total revocations less those the shachain holds, since the stored ones run without gaps from the first that didn't fit.listpeerchannelsreports it on channels with the add-on.peer_got_revoke()compares the count withmax-independent-secrets. It logs a warning (UNUSUAL) once the count reaches 90% of the cap (once per run, so also after a restart or a lowered cap), and at the cap callschannel_start_mutual_close().channel_start_mutual_close()does whatclosedoes with no options, without a command: it moves a channel inCHANNELD_NORMAL,CHANNELD_AWAITING_SPLICEorCHANNELD_AWAITING_LOCKINtoCHANNELD_SHUTTING_DOWNwith reasonlocal, and tells channeld to sendshutdownto the channel's close address. Likeclose, it doesn't send a script the peer won't accept: our default P2TR address becomes P2WPKH for a peer withoutoption_shutdown_anysegwit, and a script promised upfront that the peer can't take is logged and left alone. If channeld isn't running, it sendsshutdownwhen it next starts, as for any channel in that state.shutdown, the peer only needs as many more commitments as it takes to settle the HTLCs in flight. But it can keep a closing channel updating withupdate_fee, which needs no HTLCs, so once it has made us store 10% more than the cap (at least one more),channel_fail_permanent()closes the channel unilaterally. Storage stays bounded either way.channel_fail_permanent()closes the channel unilaterally, asclosedoes when its default timeout runs out. At startup the timeout is armed again only for a close we started (one withindependent_secrets_close_atset), with whatever is left of its time since the channel moved toCHANNELD_SHUTTING_DOWN, as its state changes record. A close the user or the peer started is left alone.close, it keeps a lease we sold: a channel the peer leased from us isn't closed mutually before the lease expires, but the 10% backstop still applies, since our funds stay locked until the lease expires however the channel closes. (Commit 12; before it, a peer could make us store without limit until expiry.)Dev sender (
hsmd,lightningd)--dev-independent-secrets-sender(developer mode only, sent inhsmd_dev_preinit), hsmd derives this node's secrets asHMAC-SHA256(shaseed, "independent-per-commitment-secret" || n || counter), the same function as Lightning Fork's dev sender. It applies to every channel, including ones without the option, so it is only for testing against peers that support the option. On a channel without it, the peer refuses our second secret (the first always fits an empty shachain), and the node breaks the spec's rule that a node whose secrets don't come from the seed must not open such a channel. That's acceptable for a developer-only switch; making hsmd aware of each channel's type would cost more than it's worth.--independent-secrets, since only a peer with the add-on accepts its secrets.Bindings
cln-rpcandcln-grpc, so it is added to all three. Without that, Rust clients and plugins would fail to deserialize a channel with the add-on.listconfigsgainsindependent-secretsandmax-independent-secrets, andlistpeerchannelsgainsindependent_secrets_stored..msggen.json,node.proto,model.rs,convert.rsandgrpc2py.py.*_pb2.pyfiles were generated with protoc and put in the layout of the pinned grpcio-tools 1.75.1 (protobuf 6.31.1). Applied to the previous.protofiles, the same method reproduces the committed*_pb2.pyfiles byte for byte.Prior work
This implements the "remove shachain" option that ZmnSCPxj first proposed on Delving Bitcoin as
no_more_shachains(Option 2A in his September summary, thread), negotiated per channel throughchannel_typerather than with node-level bits.His version has an odd bit meaning "I still send shachain secrets", so the other side can keep them compactly. Here the receiver finds that out for itself, from whether each secret still fits its shachain, so no bit is needed. The lnd PR covers the other approaches.
Review
An independent review found no defect that could lose funds, and nothing that breaks the protocol or the database. Its findings, all addressed:
hsmd_wire.csvchanged without a line incommon/hsm_version.h, whichcheck-hsm-versionswould catch. Added.openchannelmarkers, and pass both ways.Left as is: a caller who names a channel type gets the add-on whenever both peers support it, with no per-channel opt-out, as with
option_unified_sigs.One design change came after the review. The first version stored every secret in the table and left the shachain empty on these channels.
test_emergencyrecoverpenaltytxnshowed what that cost: a static channel backup carries the shachain, so a node restored from one could no longer punish any old state on such a channel. With the add-on on by default, as it was then, that would have been every new channel, including all those with peers that use a shachain. Secrets now stay in the shachain while they fit, which limits the loss to peers that really do send independent secrets. The spec PR was amended to allow this: the receiver MAY keep secrets in the compact form for as long as each one fits, and MUST store them individually from the first that doesn't.A second independent review then went through that design: the switch-over, the boundaries between the shachain's numbers and the table's, crashes mid-revocation, onchaind restarts and replays, pruning, and the backup path. It found no defect that could lose funds. Its low-severity notes are either documented limits (backups, storage growth) or behave as before this PR (a revocation still queued when the funding spend is seen). One more: a duplicate row would stop lightningd with a database error rather than fail the channel, but nothing a peer sends can cause one.
Maintainer review
Kyle's review found that a sold lease skipped the cap's backstop, leaving storage unbounded until the lease expired, and that the cap's mutual close needed a timeout. Commit 12 fixes both, and also counts a lowered cap's 10% from where the channel was. It comes with three new tests (lowered cap, close timeout, lease). An independent review had flagged the same two points.
His second review confirmed both and found that a restart reset the 10% and the timeout, and re-armed the timeout for closes we didn't start. Commit 13 saves the count at which our close started and arms the timeout only for our close, with the time it has left. It comes with two more tests (restart, a close we didn't start).
Maintainer decisions
After the first round, the fork's maintainers decided three design points, all in this version:
--independent-secretsto turn it on.--subdaemon.independent_secrets_storedinlistpeerchannels, and--max-independent-secrets, which closes a channel at the cap.Tests
After the bits moved from 516/517 to 264/265,
check-unitsand the pytest files that check feature bits and channel types were rerun (test_independent_secrets,test_misc,test_opening,test_plugin,test_connection,test_gossip,test_splicing,test_closing: 578 passed), along with all nine interop scenarios. Every failure was one of the environment-only ones listed below, apart fromtest_htlc_rexmit_while_closing, which failed once under load and passed on rerun, and the timing testrun-secret_eq_consttime, which passes when not run alongside the rest. The full-suite numbers below are from the run with 516/517: only constants, test expectations and docs changed since. The bits then moved once more, to 266/267, because an open bLIP PR already claims 264/265. That again changed only constants, test expectations and docs (8 files).The branch was then rebased, without conflicts, onto the fork's
blake2b-unifiedat1dd56e94d: the v26.06.9-blake2b.7 release (482c8c521) plus the fork's new test vectors and a doc change. On that base, with the bits at 266/267, it builds, and these were rerun: therun-channel_type,run-walletandrun-grind_feerateunit tests;test_independent_secrets.pyand the feature-bit and channel-type tests intest_misc,test_opening,test_plugin,test_connectionandtest_splicing(24 passed); and all nine interop scenarios.After the maintainers' decisions (off by default, refused with an external signer, the count and the cap), and a further review of that code whose findings are all addressed (the option on Liquid, an external signer with existing add-on channels, the backstop for a peer that keeps a closing channel updating, the close address and states, the warning), the branch was rebased, without conflicts, onto
blake2b-unifiedat13391ab17. On that base every commit builds and passes therun-channel_type,run-wallet,run-invoice-select-inchanandrun-grind_feerateunit tests, and these pass with the final code:test_independent_secrets.py, all oftest_splicing.py, and the reestablish, backup-recovery (with and without the add-on), feature-bit and channel-type tests intest_closing,test_connection,test_security_backport,test_misc,test_openingandtest_plugin(55 passed);test_independent_secrets.pyagain with dual-funded opens (all passed, the v1-only tests skipped); the penalty, onchain and closing tests intest_closing.py(61 passed, 1 skipped);test_independent_secrets.pyas it stands at each commit that changes it (the tests, the count and the cap commits); and all nine interop scenarios, with CLN started with--independent-secrets.The branch was then rebased again, without conflicts, onto
blake2b-unifiedat7974591c7(the fork's splice shared-input signature check, coinbase-input refusal, reserve and xpay fixes). On that base it builds and these pass again: the four unit tests above;test_independent_secrets.py, all oftest_splicing.pyandtest_splicing_disconnect.py, and the backup-recovery, feature-bit, channel-type and reestablish tests (58 passed);test_independent_secrets.pywith dual-funded opens (13 passed, the v1-only tests skipped); and all nine interop scenarios against Lightning Fork 0.21.4-beta-blake2b.20.Commits 12 and 13 (the review fixes) build and pass the four unit tests above and
run-db, andtest_independent_secrets.pypasses in full both ways: 24 passed with v1 opens, and 14 passed with dual-funded opens (the v1-only tests skipped), including the five new cap tests. So doestest_splice_stored_feerate_repaired_on_upgrade, which rewinds the database version and now drops the new column too.New (
tests/test_independent_secrets.py; most run with v1 and v2 opens):--independent-secrets, not without; added to a type the caller names; refused by a node that doesn't offer it; bit 267 ininitandnode_announcementonly with the option, and inlistconfigs.--independent-secretsand--subdaemon=hsmd:..., or with the dev sender but without--independent-secrets. Replacing hsmd without the option still works, but not while a channel with the add-on is open.--independent-secrets, keeps storing the peer's secrets and punishes a revoked commitment with one; the node no longer signals bit 267.test_emergencyrecoverpenaltytxnalso runs with the add-on: a peer that uses a shachain is still punished from a restored backup.independent_secrets_storedmatches the rows stored, across restarts; it is 0 with a shachain peer and absent without the add-on.max-independent-secrets=20, the warning comes once, at 18 stored secrets, with the channel still normal, and the 20th starts a mutual close while a payment is in flight. The payment completes (one more secret, within the slack), the close confirms, andstate_changesrecords the reason.max-independent-secrets=10the slack is one secret, so settling the payment in flight when the close starts is one too many: the channel is closed unilaterally, with its reason instate_changes.shutdownand doesn't come back; after the timeout (10 seconds in the test, through a developer option) we close unilaterally, with its reason instate_changes.your_last_per_commitment_secretis the one it revealed.Bad per_commitment_secret ... for 1). This shows the other tests really give the receiver secrets no shachain would accept.Also new:
run-wallet: the shachain taking secret 0 and refusing secret 1; storing, loading, looking up, pruning on close, the closed-channel loader, and the guarded revert. Eight mutations of that code (wrong number stored, wrong secret looked up, count off by one, either loader skipped, pruning everything or nothing, the revert unguarded) are each caught.run-channel_type: names and acceptance.Interop with Lightning Fork, on a Bitcoin Knots BLAKE2b regtest: nine scenarios, each on a fresh chain, all passing on v26.06.9-blake2b.7 and again on
blake2b-unifiedat 13391ab and 7974591 with the bits at 266/267 (scriptinterop.pyand its pluginhold.py, which I can share).lnd sends KDF secrets, CLN keeps them. This needs lnd's integration build, the only one with its dev sender. That build never signs with the unified sighash, so CLN ran with
--dev-force-features=-515: it requiresoption_unified_sigson new channels while it offers it. Channels opened as[12, 22, 266](static remote key, anchors, independent secrets).CLN sends KDF secrets, lnd keeps them. These use a release build of lnd and an unmodified CLN, so the channels also had
option_unified_sigs:[12, 22, 266, 514].your_last_per_commitment_secret, and keep paying. CLN's table stays empty, since lnd's own secrets come from its shachain. CLN broadcasts revoked state 13, and lnd's breach arbiter takes everything: it ends with 990,347 of the 1,000,000 sat.A peer that uses a shachain. shachain-peer: a release lnd sends ordinary secrets. CLN stores none, and punishes revoked state 12 from its shachain.
A pre-existing bug the interop found.
onchaind_tx_unsigned()leaves the witness's stack element (a revocation pubkey or a preimage) out of a spend's weight. When feerate estimates are at the floor, as on a regtest without any, an HTLC penalty or fulfill then pays less than the minimum relay fee, and bitcoind refuses it for as long as the estimates stay there. Upstream Core Lightning has the same line. It isn't part of this PR: A separate PR (PR #49) fixes it, with a test that fails without it.Existing tests: in the earlier version, with the add-on on by default, every CLN-to-CLN channel in the suite used it. Their secrets stayed in the shachain, so the existing penalty, onchain, reestablish and backup tests checked that nothing changed for shachain peers. The whole pytest suite (all 36 files) was run with that code: 1,070 passed. Now that it is off by default, those tests run as they do without this PR; the ones the earlier version had to adjust for the extra bit are back to upstream's.
test_closing.pyandtest_connection.py: 196 passed.test_opening.py,test_misc.py,test_plugin.py,test_splicing.pyand the new tests: 321 passed, includingtest_emergencyrecoverpenaltytxn, which punishes from a restored backup.test_currencyrate.py, thetest_bookkeeper.pyreports that use currency rates,test_reckless.py,test_announce_and_connect_via_dns), modules or Rust plugins this sandbox doesn't have (test_cln_lsps.py,test_cln_rs.py,test_clnrest.py), man pages (test_cli_multiline_help), a user other than root (test_setconfig_access), or IPv6 (test_ipv4_and_ipv6, andtest_websocket's teardown).test_channel_lease_lessor_cheat/test_channel_lease_lessee_cheat(dual-funded penalties), withEXPERIMENTAL_DUAL_FUND=1.check-unitsat every commit of the series;check-source-boltfor the changed files, and the quotes from the draft bLIP against its text;check-includes,check-config-docs,check-hsm-versions, flake8 andrustfmt(edition 2024).Not run here:
cln-rpcandcln-grpcwere only checked withrustfmt, andtest_cln_rs.py, which exercises them, couldn't import.check-doc-examples. The fork hasn't regenerated the doc examples forunified_sigs/eveneither. Thelistconfigsexample gained the two new options by hand.Limits
recoverchanneloremergencyrecovercan only punish states revoked before the first secret that didn't fit. With the dev sender, that is state 0 alone.hsmd_validate_revocationis a stub; channeld does the check). A signer that keeps them in a shachain of its own, such as VLS, would refuse the first that doesn't fit, so lightningd refuses an hsmd replaced by--subdaemonwith--independent-secrets, or while a channel with the add-on is open. A signer that supports the add-on later can lift that.max-independent-secretsis closed cooperatively, or unilaterally if the peer keeps it updating past 10% more or the close hasn't finished in 48 hours. A channel the peer leased from us is only closed mutually once the lease expires, but the 10% backstop applies before then. There is no limit across channels: a peer with several such channels can make us store the cap on each.lightning-downgradealready refuses wallets of this fork.Rollout
--independent-secrets; on by default in Lightning Fork. New channels use it when both sides offer it. Channels with older nodes open as before.Release note
Not in this PR
listconfigsschema and the config docs say "added in v26.06.9-blake2b.8", the release after the current v26.06.9-blake2b.7. Change that if the release is named differently.