Repository navigation
multi: add option_independent_secrets channel type - #7
Conversation
|
A review raised one point that’s your call, Paul, rather than a fix. With the bit on by default, any peer can move the database to version 39 just by starting an open, even one it never completes, because the fundee saves the pending channel before any funding transaction exists. After that the node can’t downgrade. The mark is still needed then, since that pending record can’t be read by an older version; the question is the default. Options: (1) keep it on and say so in the release notes (the docs now do); (2) ship it off by default at first, like the CLN fork; (3) add a guarded downgrade tool. Happy to do whichever you prefer. Commit 19 has the other review fixes. |
IndependentStore is a Store for per-commitment secrets that have no shachain relationship, as on a channel with option_independent_secrets. It accepts any secret, keeps the two most recent in memory (enough for channel_reestablish, including mid-revocation), and starts its encoding with a 0xff marker, which a RevocationStore encoding never does. NewStoreFromBytes decodes either kind.
The channel codec and the migration codecs now decode a revocation store with shachain.NewStoreFromBytes, so they read an IndependentStore as well as a RevocationStore. Legacy stores decode exactly as before. The optional revocation log migration (migration30) reads the revocation state of every channel, so without this it would fail once a channel with an IndependentStore exists.
Add IndependentSecretsBit, NewRemoteSecretStore, which picks the store for a channel type, and OpenChannel.UsesIndependentSecrets, which also trusts the store type so the two can't disagree silently. The bit is 1 << 40, far above upstream lnd's allocations, so a rebase onto upstream can't hand it another meaning. On such channels the secret that revokes a remote commitment is written to that commitment's revocation log entry (optional TLV 65537) in the same transaction that moves the commitment into the log. It must be the newest secret in the store; a store out of step with the commitment chain is refused rather than answered with the secret before it. ChanSyncMsg falls back to the revocation log when the in-memory store no longer holds the secret it needs, which a copy of the channel refreshed in two steps can run into.
ReceiveRevocation checks that a secret generates the expected point before storing it, since on these channels that is the only check a secret gets, and also requires it to be a valid private key as sent. NewBreachRetribution reads the secret from the revocation log on these channels and from the shachain store otherwise. Reservations take an IndependentSecrets flag that sets the channel type bit and the store.
The chain watcher's copy of the channel is loaded before the channel advances, as the chain arbitrator's is, so its in-memory store is stale and the retribution must come from the revocation log.
Bits 266/267, proposed in a draft bLIP. The add-on is only ever agreed through channel_type, so it depends on explicit channel type negotiation.
Mirrors option_unified_sigs: proposed whenever both peers signal it, stripped before the commitment type is matched, kept on the persisted type, refused explicitly by a peer without it, refused if a peer echoes it unproposed, and allowed on taproot channels. The RPC acceptor strips the required bit like the other add-ons.
Lightning on BLAKE2b Bitcoin is new, so the optional bit is signalled by default, like the fork's other XBT features, and every new channel with a peer that also signals it uses the add-on. Channels with peers that don't signal it open as before. protocol.no-independent-secrets turns it off. Downgrading to a version without it is not supported once any channel has used it: such a version can't read that channel's stored state, which lnd also keeps after the channel closes. The config help says so.
A static channel backup only lets the node ask its peer to force close, after which it never receives a revocation again, so the add-on has nothing to restore. Pin that a channel with it backs up exactly like the same channel without it.
Runs the watchtower path end to end on a channel whose peer reveals random secrets: the client builds the backup for a revoked state from the database, as lnd's server does, the tower decrypts it and builds the justice transaction, and every input must validate against the breached commitment. The test fails if the stored secret is off by a single bit.
Add independent_secrets to Channel and PendingChannelsResponse. PendingChannel, and fill it for open, pending open, waiting close and force closing channels, so operators can see which channels store every secret. The field is 2001, away from the fork's own fields, which are numbered on from 1001 (splicing took 1002 to 1006). The change to lightning.pb.go is only what protoc 3.21.12 and protoc-gen-go v1.36.11 generate for the new field; the rest of the file is left as it was.
An IndependentProducer derives the secret for index i as an HMAC of i keyed by the root, as option_independent_secrets allows. Like a shachain it needs nothing but its root, which it encodes exactly as a RevocationProducer does, but its secrets can't be compressed, so only a peer that stores every secret accepts them.
With --dev.independent-secrets-sender, available in integration builds only, a node generates its own per-commitment secrets with an IndependentProducer on every new channel with option_independent_secrets, so that a peer's support for them can be exercised by a real node. The channel records this in LocalIndependentSecretsBit, a local channel type bit, and the producer is rebuilt from the stored root when the channel is loaded; a type with the bit but without the add-on fails to load. The root is stored as before, and the taproot nonce producer is derived from the new producer exactly as after a restart, so nonces stay consistent.
Every TestChannelRetransmission case now also runs on channels with option_independent_secrets on which both sides send secrets without a shachain: links exchange real messages, are restarted from the database, reestablish and retransmit revoke_and_ack.
Two nodes, one sending independent secrets with the dev option, open a channel with option_independent_secrets by default, pay across restarts, and the sender then broadcasts a revoked state, which the other node sweeps. The fork's harness can't start lnd yet (it needs a BLAKE2b Bitcoin Knots backend), so this runs once it can.
What the add-on does, why, how to turn it off, the downgrade caveat, the RPC field and the dev option.
While a splice is pending, the peer has a commitment on each funding output at the same number, and its revoked ones on a splice's funding go into that splice's own revocation log. A breach on that funding, or a watchtower backup of it, is built from the splice's log entry. Those entries were written without the per-commitment secret, so on a channel with option_independent_secrets the commitment couldn't be punished. One secret revokes the commitment with that number on every funding output. AdvanceCommitChainTail now passes it to applySpliceWrites, which stores it with each splice's entry at that number. As a fallback for an entry without it, revokedCommitSecret takes the secret from the channel's own log at the same number, which is written in the same transaction. The new tests run the splice breach and watchtower tests on a channel with the add-on, where the peer reveals random secrets. They fail without this change.
…annel An older version can't read the revocation state of a channel with option_independent_secrets. It used to fail to decode the channel while it was open or being resolved, and once it had closed, start and fail its closedchannels and pendingchannels calls. Database version 39 now marks a database that has stored such a channel: syncNewChannel moves the database there, in the same transaction, when it saves a channel with the add-on, new or restored from a backup. So an older version refuses to start with the usual "Refusing to revert" error. Like the splicing mark (38), it changes no data. It is never run as a migration, so a node that never has such a channel stays at 37 or 38 and can still go back to an older release. A database at 39 opens with splicing on or off, and OpenableDBVersions lists it for copying tools.
targetVersions left out the independent secrets mark (39) and, with splicing off, the splicing mark (38) by their position at the end of dbVersions. The next migration appended there would have been taken for a mark and never run. Leave them out by number instead, and test that a migration appended after them still runs. Also correct two comments and the docs: a static backup doesn't carry the add-on's bit, so it's a channel being opened that marks the database, including the pending channel a fundee saves before any funding transaction exists. With the option on by default, a peer can therefore mark the database by starting an open; the docs now say so, and how to keep the option to downgrade.
Once a channel with the add-on is stored, the database moves to version 39, which an older release refuses to open. With the add-on on by default, two upgraded nodes would use it on every channel they open, so nearly every node would be marked right after upgrading and couldn't roll back if something else in the release went wrong. So it now ships off, and protocol.independent-secrets turns it on, replacing protocol.no-independent-secrets. A release that can read version 39 can then go out first; turning it on by default in a later release leaves a rollback landing on a release that already reads it. dev.independent-secrets-sender now needs protocol.independent-secrets, since its secrets are only accepted on channels with the add-on. The itest turns the option on for both nodes. The feature bit comment and docs now point to lightning-blake2b/bolts#17 instead of a draft bLIP.
The channel acceptor's commitment_type sets the add-on's bit aside, since it doesn't change the commitment's shape, so an acceptor couldn't tell whether accepting a channel meant storing every secret the initiator reveals. ChannelAcceptRequest gains wants_independent_secrets (field 2001), set when the proposed channel type carries the bit. The lightning.pb.go change is only what protoc 3.21.12 and protoc-gen-go v1.36.11 generate for the new field.
9c90126 to
679e8c0
Compare
|
Thanks Dave. As I said on Discord, I've read through this, and it builds and passes here, on its base and rebased onto blake2b-next. The care that went into the breach paths shows. On your question about the default: I'd like to ship it off by default first. Two upgraded nodes would use it on every channel, so nearly every node would move to database version 39 and lose the option to roll back, while no node sends secrets that need it yet. So for the first release: keep the code that reads version 39 and the new store, and turn the add-on on with A few other things before merging:
I'll approve the CI runs now. |
|
I wrote that before seeing your two new commits: off by default with |
|
A second, closer pass over 679e8c0 found nothing that affects funds. A few small things:
CI is green on this head. |
protocol.custom-init and protocol.custom-nodeann could set either bit of option_independent_secrets: 267 turned the add-on on with protocol.independent-secrets off, so the database could move to version 39, and 266 is not valid in init at all. The feature manager now refuses custom bits that lnd defines, as updates at runtime already do. A peer that set 266 in init counted as supporting the add-on, since HasFeature(267) is true when 266 is set, and a node with the option off no longer disconnected it, since lnd now knows the bit. Such a peer is now refused in init, and negotiation checks bit 267 itself.
From the maintainer's second pass: - syncNewChannel marks the database when UsesIndependentSecrets() is true, not on the channel-type bit alone, so a channel whose store and type disagree still marks it. Not reachable today; a test covers it. - revokedCommitSecret says plainly that a revoked state has no secret and can't be punished, and passes on the error when the channel's own log entry can't be read. - TestIndependentSecretsSpliceBreach also checks that each retribution's commitment point is the point of the secret Bob revealed, which the balances alone wouldn't catch. - closedchannels reports independent_secrets (field 2001 on ChannelCloseSummary), since a closed channel with the add-on still keeps the database at version 39. The lightning.pb.go change is only what protoc 3.21.12 and protoc-gen-go v1.36.11 generate for the new field. - The syncNewChannel comment no longer says a restored channel can't carry the bit.
- docs: the channel acceptor reports wants_independent_secrets, and closedchannels reports the add-on; a dev-sender node restored from a static backup gets a shachain producer back, so it can't sweep its own to_local with an external tool; rewrap a paragraph. - lnwire: IndependentSecretsRequired is only valid in channel_type. - itest: output 2 of the breach is Bob's revoked to_local, and the justice transaction's other input is Carol's to_remote; drop --hodl.exit-settle, which had no effect on Carol.
|
Thanks for the quick fixes; 9b0d0b3 covers everything from my second pass, and CI is green. Since the interop script wasn't available yet, I ran our own lab scenarios against privkeyio/lightning#48 (4df42d549) on a Knots BLAKE2b regtest, with lnd at 9b0d0b3 (a release build, and an integration build for the dev sender). All of these passed:
One thing for anyone repeating this with the dev sender: an integration build never opts into the unified sighash ( I'd still like to see |
|
Thanks, great to hear it all passed in your lab, splices and towers included. The scripts are here, with a README on the Knots version, the lnd and CLN builds and how the nodes start: https://github.com/davelund110/independent-secrets-interop On the unified sighash: we hit the same thing and handled it on the CLN side instead, running CLN with --dev-force-features=-515 in the scenarios where lnd's integration build sends (those channels are [12, 22, 266]); the others use a release lnd, so they carry 514 too. Happy to leave the integration build as it is. I'll go through your spec comments on bolts#17 next. |
|
Thanks for publishing the scripts. All nine |
What
This PR is the receiver side of
option_independent_secrets, which is specified in the fork's BOLTs ([lightning-blake2b/bolts#17](lightning-blake2b/bolts#17)). The CLN fork has it too ([PR #48](privkeyio/lightning#48)); the two were tested against each other.protocol.independent-secretsturns it on. As the maintainer proposed, the release that includes it ships it off, and can already read the new store and database version 39; a later release turns it on by default, so a rollback from there lands on a release that reads 39 and no downgrade tool is needed.channel_type, which lnd sends on every open since 0.21.4.listchannels,pendingchannelsandclosedchannelsreportindependent_secretsper channel, and the channel acceptor getswants_independent_secrets.Channels without the bit behave exactly as before. They use the same store type, write the same bytes on disk, and run the same checks.
Why
With 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 a node generate and reveal its secrets jointly.
This PR doesn't add a threshold signer. It adds what every peer has to support first: accepting secrets that aren't shachain-derived. For testing, it also adds a dev-only way for lnd to send such secrets.
Commits
Each commit builds and passes its own tests, so they can be reviewed one at a time.
Core (1–8)
shachain: addIndependentStorefor non-shachain secrets.channeldb: decode either revocation store kind (channel and migration codecs).channeldb: persist independent per-commitment secrets in the revocation log.lnwallet: accept these secrets, and punish breaches with them.contractcourt: test breach detection with independent secrets.lnwire+feature: add the feature bits and their dependency.funding: negotiate the channel type add-on.multi: signal it by default, with an opt-out flag (commit 20 turns it off by default).Coverage, visibility and testing tools (9–16)
chanbackup: test that backups ignore the add-on.wtclient: end-to-end test of the watchtower's justice transaction.lnrpc: reportindependent_secretsper channel.shachain: addIndependentProducer, a KDF secret generator.multi: add--dev.independent-secrets-sender, available in integration builds only.htlcswitch: run the link retransmission tests with independent secrets.itest: add a two-node test. It doesn't run yet; see Tests below.docs:docs/independent-secrets.md.Splicing (17)
channeldb, lnwallet: keep the secret for splices' revocation logs, so a revoked commitment on a splice's funding output can be punished.Downgrade marker (18)
channeldb: mark the database, as version 39, once it stores a channel with the add-on.Review fixes (19)
channeldb: pick the database version marks by number, so the next migration appended after them still runs; correct the docs on what marks the database.Maintainer review (20–21)
multi: turn it off by default, withprotocol.independent-secretsto turn it on; the dev sender now needs that option; the feature bit comment and docs point to bolts#17 instead of a draft bLIP.chanacceptor, lnrpc: report the add-on to the channel acceptor aswants_independent_secrets.Maintainer's second pass (22–24)
feature, peer, funding: refuse custom init/nodeann bits that lnd defines, refuse a peer that sets 266 in init, and negotiate on bit 267 itself.multi: mark the database byUsesIndependentSecrets(), a plainer error when a revoked state has no secret, a commitment point check in the splice breach test, andindependent_secretsinclosedchannels.docs, lnwire, itest: review nits.How
shachain
IndependentStoreimplementsshachain.Store. It keeps a count plus the two most recent secrets.channel_reestablishsends.ReceiveRevocation, betweenAddNextEntryand advancing the remote commitment.ErrSecretNotRetained.0xff. An encodedRevocationStorestarts with its bucket count, which is at most 48, so the two can't be confused.NewStoreFromBytesdecodes either kind and consumes exactly its own bytes.IndependentProducer, used only by the dev sender:iisHMAC-SHA256(root, tag || i || counter). The counter is bumped in the negligible case that the result isn't a valid key.RevocationProducer, so nothing new is stored, and taproot nonces derived from the root don't change.channeldb
IndependentSecretsBitis1 << 40, far above upstream lnd's allocations, so a rebase onto upstream can't hand it another meaning.NewRemoteSecretStore(chanType)picks the store for a channel type.OpenChannel.UsesIndependentSecrets()returns true if either the bit or the store type says so. A disagreement between the two then still finds its secrets in the log.RevocationLoghas an optionalCommitSecretrecord. It uses TLV type 65537, a high number chosen to avoid future upstream records.AdvanceCommitChainTailwrites this record in the same transaction that moves the commitment into the log.ChanSyncMsgfalls back to the revocation log if the in-memory store no longer holds the secret it needs. A copy of the channel that is refreshed in two steps can run into this.NewStoreFromBytes:migration_01_to_11codec, which is needed: the optional revocation-log migration (migration30) uses it to decode the revocation state of every channel, and it failed when an independent-secrets channel was present;migration21codec, for consistency.LocalIndependentSecretsBit(1 << 41) is a local bit, used only by the dev sender. When the channel is loaded, it rebuilds theIndependentProducerfrom the stored root. A channel type with this bit but without the add-on fails to load.lnwallet
ReceiveRevocationnow checks that the secret generates the expected point before storing it. On these channels, that check is all that ties a secret to the commitment it revokes.0 < s < n). BOLT 2 already requires a valid secret key.NewBreachRetributionreads the secret from the revocation log on these channels, and from the shachain store otherwise.InitFundingReserveMsg.IndependentSecrets: the reservation sets the bit, and the wallet creates the matching store.Config.IndependentSecretsSenderis set only by the dev option. The wallet then uses anIndependentProducerfor new channels with the add-on, and derives the taproot nonce producer from it, exactly as it is derived after a restart.funding
option_unified_sigs:option_unified_sigs, as every release build does, opens no taproot channels yet, because the unified bit isn't defined for MuSig2 signatures.)accept_channelwhose type differs from the one it proposed, so a reply that adds the bit is refused.splicing
AdvanceCommitChainTailpasses it toapplySpliceWrites, which stores it with each splice's entry at that number.revokedCommitSecrettakes the secret from the channel's own log at the same number, written in the same transaction.feature, lnwire, lncfg, server, chanacceptor, lnrpc
independent-secrets). Withprotocol.independent-secrets, the optional bit is signalled ininitandnode_announcement. Negotiation checks bit 267 itself, and a peer that sets 266 ininit, where the spec doesn't allow it, is refused.protocol.custom-initandcustom-nodeanncan no longer set a bit lnd defines, so they can't turn the add-on on behind its option's back.option_channel_type, stated in the spec.protocol.independent-secretsflag, with an entry insample-lnd.conf.dev.independent-secrets-senderis refused without it.option_unified_sigs, and the bit itself aswants_independent_secrets = 2001onChannelAcceptRequest.independent_secrets = 2001onChannel,PendingChannelsResponse.PendingChannelandChannelCloseSummary, filled for open, pending-open, waiting-close, force-closing and closed channels. 2001 keeps it apart from the fork's own fields, which are numbered on from 1001 (splicing took 1002 to 1006).lightning.pb.gois only what protoc 3.21.12 and protoc-gen-go v1.36.11 generate for the new field; the rest of the file is as upstream has it.Storage cost: each revoked state's log entry grows by 38 bytes: the 32-byte secret plus its TLV header. lnd already keeps a log entry for every revoked state. The spec lets a receiver keep a seed-based peer's secrets in the compact 49-entry form for as long as they fit it, as the CLN fork does. lnd doesn't need that: it writes a log entry per revoked state anyway, and its static channel backups never carried revocation state.
Database version (
channeldb/db.go,channeldb/channel.go,channeldb/migration39)IndependentStore). It used to fail to decode the channel while it was open or being resolved, and once it had closed, start and failclosedchannelsandpendingchannels.syncNewChannel, which saves every new channel (including the fundee's pending one, saved before any funding transaction exists), moves the database to 39 in the same transaction when the channel type has the bit. An older version then refuses to start with lnd's usual "Refusing to revert from db_version=39".targetVersionsleaves out 39, and 38 too with splicing off, picking them by number (commit 19), so a migration appended after them still runs. OnlymarkIndependentSecretsreaches 39. A node that never has such a channel stays at 37 or 38 and can still go back to an older release. A database at 39 opens with splicing on or off,OpenableDBVersionslists it for copying tools (lndinit's SQLite conversion), andWipestill initialises at 38.Prior work
This implements the "remove shachain" option first proposed by ZmnSCPxj on Delving Bitcoin as
no_more_shachains, which is Option 2A in his September summary of the options ([thread](https://delvingbitcoin.org/t/towards-a-k-of-n-lightning-network-node/2395)). The difference is in how it's negotiated:channel_type, asoption_unified_sigs, taproot and zero-conf are.channel_typelacks the add-on.Other approaches: Option 2B (several shachains, at most 5 signers) and Iceberg/shachain-mpc (shachain computed in MPC, no protocol change, not production-ready). The Iceberg authors write that Lightning may "drop the SHA-chains ... in the future". I found no existing BOLT PR or implementation of this change.
Review
The series had three independent reviews: one of the first version, one of the core series, and one of the additions. None found a critical or high-severity issue. Every finding was fixed except the three listed under "Left as is" below:
option_channel_typewasn't in the spec. It is now.channel_reestablishfalls back to the log for the secret it needs.1 << 40.noRevLogAmtDatawith a secret;Left as is, with the reason:
req.IndependentSecretsis set after the struct literal rather than inside it. Moving it would realign every line of two literals, which would make rebasing onto upstream harder.option_unified_sigs. There is no per-channel opt-out, only the global flag.Tests
Commits 20 to 24 come from the maintainer's two reviews, and the branch is now on this PR's base,
blake2b(0.21.4-beta-blake2b.21,eb0848bad, the same commit asblake2b-next), rebased without conflicts. On that base, with all of them:go build ./...and thecmd/lndandcmd/lncliintegration and release builds pass, and the itest compiles (go test -c).go build -tags=integration ./...stops atitest/config.go, which uses a function from a test file; it does the same on the untouched base.shachain,channeldb/...,feature,chanacceptor,lncfg,funding,contractcourt,watchtower/...,lnrpc,peer, andlnwalletwith its subpackages. The exceptions arelnwallet/test/{bitcoind,btcd,neutrino}, which need those backends installed.TestAcceptorReportsIndependentSecrets,TestCustomFeaturesRefuseKnownBitsandTestInitRefusesIndependentSecretsEvenBit(both fail without commit 22), a database-version case for a store that disagrees with the channel type, and a commitment point check inTestIndependentSecretsSpliceBreach.TestDevIndependentSecretsSenderNeedsProtocolOptionpasses in the integration build.protocol.independent-secretsit advertises it as optional.protocol.independent-secrets; the two cooperative-close scenarios also check thatclosedchannelsreports the add-on.The text below describes the earlier move to .20.
Commit 19 comes from a further review:
targetVersionspicked the marks by their position at the end ofdbVersions, so the next migration appended there would have been taken for a mark and never run. It now picks them by number, andTestTargetVersionschecks that a migration appended after the marks still runs. The same commit corrects the docs: a static backup doesn't carry the bit, so it's an open that marks the database, including the pending channel a fundee saves before any funding transaction exists. With the add-on on, a peer can therefore mark the database just by starting an open, which is why it now ships off by default.go build ./...and everychanneldb/...test pass with it.The branch was then moved to Lightning Fork 0.21.4-beta-blake2b.20 (
cee9561d5). The rebase had one conflict: upstream's "refine revocation validation order" now checks the secret against the commitment point before storing it, after a new check that the remote chain has an unacked commitment, so commit 4 only adds its note and the valid-private-key check there. Commit 18 (the database version) is new, as the maintainer asked. On this base every commit builds and passesgo vetfor the packages it touches (which compiles their tests), and with the final code:go build ./..., thecmd/lndandcmd/lncliintegration and release builds, andgo vetof every touched package pass.shachain,channeldb/...(with the newTestIndependentSecretsDBVersion, which fails if the marking is removed),feature,chanacceptor,lncfg,funding,contractcourt,watchtower/wtclientandlnwallet.lnwire,chanbackupandhtlcswitch, every test that fails also fails the same way on untouched 0.21.4-beta-blake2b.20:TestQueryShortChanIDsZlibCompatibility(Go 1.26.8),TestCreateArchiveFile(running as root), andTestChannelLinkBandwidthConsistency,TestChannelLinkTrimCircuitsNoCommit,TestPipelineSettle,TestChannelLinkCleanupSpuriousResponsesandTestSwitchDustForwarding. Our own tests in those packages pass, including all sixTestChannelRetransmissioncases, with and without the add-on.A database that already holds a channel with the add-on, saved by an earlier build of this branch, stays at 37 or 38 until another such channel is saved; the branch was never released, so no node should be in that state.
The text below describes the earlier move from .18 to .19.
This branch is on Lightning Fork 0.21.4-beta-blake2b.19 (
9cbe4083b), which adds splicing and needs Go 1.26.8 to build. Moving it there from .18 meant three small conflicts (the point check inReceiveRevocationnow follows upstream's splice-restore check; a field each inserver.goandlightning.proto, where our field moved to 2001) and commit 17 above, which an audit of the splice code found was needed. On .18, lnd 0.21.4 always proposes an explicit channel type and no longer falls back to the legacy type. That conflicted with the negotiation commit in two files, and resolving it removed code rather than adding any: the add-on now only goes into explicit types, the test case for an open withoutchannel_typeis gone, and the unproposed-echo test now checks the funder's general rule that the reply's type must equal the one it proposed. Every commit builds and compiles the tests of the packages it touches. Everything below was rerun on this base, with the bits at 266/267.New:
TestOpenableDBVersionsnow expect 39 as the latest.IndependentProducersecrets are deterministic, distinct and valid keys. A shachain store refuses them. The producer encodes the same root as a shachain producer.UsesIndependentSecretsfor every bit/store combination.s + nis rejected. The test fails without the range check.ChanSyncMsgworks mid-revocation, and from the log when the store has moved two secrets ahead.IndependentProduceron anchor and taproot channels, across a restart of both.wants_independent_secretsexactly when the proposed type carries the bit.initandnode_announcementonly when turned on, and only as optional.dev.independent-secrets-senderwithoutprotocol.independent-secretsis refused.TestChannelRetransmissioncase also runs on channels with the add-on where both sides send KDF secrets.revoke_and_ack.Passing:
shachain,channeldb(plus migrations 30, 01_to_11 and 21);lnwallet,contractcourt,funding,feature;chanacceptor,lncfg,peer,lnrpc,watchtower/....htlcswitchwith-tags dev.lnwire, exceptTestQueryShortChanIDsZlibCompatibility, which fails the same way on the untouched release built with Go 1.26.8. It checks zlib output sizes and doesn't touch this code.chanbackup, exceptTestCreateArchiveFile, which fails when run as root. That is environmental and unrelated.go build ./..., theintegration devbuild, the itest binary, and a release build (the release tags, without the kvdb backends) used for the interop below.Interop with the CLN fork ([PR #48](privkeyio/lightning#48)), on a Bitcoin Knots BLAKE2b regtest: nine scenarios, each on a fresh chain, all passing on this base (script
interop.pyand its CLN pluginhold.py, in [davelund110/independent-secrets-interop](https://github.com/davelund110/independent-secrets-interop) with a README on the setup; lnd runs withprotocol.independent-secrets).--dev.independent-secrets-sender; that build never signs with the unified sighash, so CLN ran with--dev-force-features=-515, and the channels were[12, 22, 266]):[12, 22, 266, 514], sooption_unified_sigstoo):your_last_per_commitment_secret, CLN broadcasts revoked state 13; the breach arbiter builds the justice transaction from the revocation log, and lnd ends with 990,347 of the 1,000,000 sat;Not run:
--bitcoin.blake2b-activation-heightaren't wired intolntest.Rollout
protocol.independent-secrets. That release reads the new store and database version 39. In the CLN fork ([PR #48](Add option_independent_secrets channel type privkeyio/lightning#48)) it is off by default too, turned on with--independent-secrets. A new channel uses it when both sides offer it. With older releases, channels open the normal way, so nothing breaks.Release note
Not in this PR