Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
11e0635
shachain: add IndependentStore for non-shachain secrets
davelund110 Oct 5, 2026
c0db491
channeldb: decode either revocation store kind
davelund110 Oct 5, 2026
d9d1732
channeldb: persist independent per-commitment secrets
davelund110 Oct 5, 2026
f784b5f
lnwallet: accept and punish with independent secrets
davelund110 Oct 5, 2026
d34a1d6
contractcourt: test breach detection with independent secrets
davelund110 Oct 5, 2026
1aa3e3f
lnwire+feature: add option_independent_secrets feature bits
davelund110 Oct 5, 2026
9895301
funding: negotiate option_independent_secrets as a channel type add-on
davelund110 Oct 5, 2026
d9f959b
multi: signal option_independent_secrets by default
davelund110 Oct 5, 2026
735c1e6
chanbackup: test that backups ignore option_independent_secrets
davelund110 Oct 5, 2026
6e48749
wtclient: test the tower's justice transaction with independent secrets
davelund110 Oct 5, 2026
362bd3c
lnrpc: report option_independent_secrets per channel
davelund110 Oct 16, 2026
3bd4108
shachain: add IndependentProducer
davelund110 Oct 5, 2026
0aa29d6
multi: add a dev option to send independent secrets
davelund110 Oct 5, 2026
f6cec6a
htlcswitch: run the retransmission tests with independent secrets
davelund110 Oct 5, 2026
7f64600
itest: add an independent secrets test
davelund110 Oct 5, 2026
f12ba93
docs: describe option_independent_secrets
davelund110 Oct 5, 2026
a4a09cf
channeldb, lnwallet: keep the secret for splices' revocation logs
davelund110 Oct 16, 2026
9b85c75
channeldb: mark the database once it stores an independent secrets ch…
davelund110 Oct 17, 2026
49c0aa3
channeldb: pick the database version marks by number
davelund110 Oct 18, 2026
7dbdb32
multi: turn option_independent_secrets off by default
davelund110 Oct 21, 2026
679e8c0
chanacceptor, lnrpc: report option_independent_secrets to the acceptor
davelund110 Oct 21, 2026
bda87f7
feature, peer, funding: keep the add-on's bits to their own option
davelund110 Oct 21, 2026
baa36d4
multi: harden marking and secret lookup, report closed channels
davelund110 Oct 21, 2026
9b0d0b3
docs, lnwire, itest: review nits
davelund110 Oct 21, 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
22 changes: 19 additions & 3 deletions chanacceptor/rpcacceptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,9 +262,10 @@ func (r *RPCAcceptor) sendAcceptRequests(errChan chan error,
// counterpart. Also determine whether the zero-conf or
// scid-alias channel types are set.
var (
commitmentType lnrpc.CommitmentType
wantsZeroConf bool
wantsScidAlias bool
commitmentType lnrpc.CommitmentType
wantsZeroConf bool
wantsScidAlias bool
wantsIndependentSecrets bool
)

if req.OpenChanMsg.ChannelType != nil {
Expand All @@ -289,6 +290,15 @@ func (r *RPCAcceptor) sendAcceptRequests(errChan chan error,

wantsScidAlias = true
}

// acceptorCommitmentType sets this bit aside,
// so it's reported on its own.
if channelFeatures.IsSet(
lnwire.IndependentSecretsRequired,
) {

wantsIndependentSecrets = true
}
}

acceptRequests[pendingChanID] = newRequest
Expand All @@ -311,6 +321,8 @@ func (r *RPCAcceptor) sendAcceptRequests(errChan chan error,
CommitmentType: commitmentType,
WantsZeroConf: wantsZeroConf,
WantsScidAlias: wantsScidAlias,

WantsIndependentSecrets: wantsIndependentSecrets,
}

if err := r.send(chanAcceptReq); err != nil {
Expand Down Expand Up @@ -457,6 +469,10 @@ func acceptorCommitmentType(
shape := channelFeatures.Clone()
shape.Unset(lnwire.UnifiedSigsRequired)

// option_independent_secrets changes how per-commitment secrets are
// stored, not the shape of the commitment, so it's set aside too.
shape.Unset(lnwire.IndependentSecretsRequired)

switch {
case shape.OnlyContains(
lnwire.ZeroConfRequired,
Expand Down
102 changes: 102 additions & 0 deletions chanacceptor/rpcacceptor_independent_secrets_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package chanacceptor

import (
"testing"

"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/chaincfg"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/stretchr/testify/require"
)

// TestAcceptorCommitmentTypeIgnoresIndependentSecrets checks that a channel
// carrying option_independent_secrets, alone or with option_unified_sigs, is
// reported by its commitment type, as the same channel without it is.
func TestAcceptorCommitmentTypeIgnoresIndependentSecrets(t *testing.T) {
t.Parallel()

base := []lnwire.FeatureBit{
lnwire.StaticRemoteKeyRequired,
lnwire.AnchorsZeroFeeHtlcTxRequired,
}

for _, extra := range [][]lnwire.FeatureBit{
{lnwire.IndependentSecretsRequired},
{lnwire.IndependentSecretsRequired, lnwire.UnifiedSigsRequired},
} {
bits := append(append([]lnwire.FeatureBit{}, base...), extra...)
vec := lnwire.NewRawFeatureVector(bits...)
require.Equal(t, lnrpc.CommitmentType_ANCHORS,
acceptorCommitmentType(vec), "%v", bits)

// The caller's vector is not changed.
require.True(t, vec.IsSet(lnwire.IndependentSecretsRequired))
}
}

// TestAcceptorReportsIndependentSecrets checks that the request sent to the
// channel acceptor says whether the proposed channel type carries
// option_independent_secrets, which commitment_type leaves out.
func TestAcceptorReportsIndependentSecrets(t *testing.T) {
t.Parallel()

node := btcec.NewPublicKey(
new(btcec.FieldVal).SetInt(1), new(btcec.FieldVal).SetInt(1),
)

for _, withBit := range []bool{false, true} {
bits := []lnwire.FeatureBit{
lnwire.StaticRemoteKeyRequired,
lnwire.AnchorsZeroFeeHtlcTxRequired,
}
if withBit {
bits = append(bits, lnwire.IndependentSecretsRequired)
}
chanType := lnwire.ChannelType(*lnwire.NewRawFeatureVector(
bits...,
))

// The acceptor sends each request once; the mock remote answers
// the pending channel it names, and the test reads what was sent.
sent := make(chan *lnrpc.ChannelAcceptRequest, 1)
pending := make(chan []byte, 1)
quit := make(chan struct{})
acceptor := NewRPCAcceptor(
func() (*lnrpc.ChannelAcceptResponse, error) {
select {
case id := <-pending:
return &lnrpc.ChannelAcceptResponse{
PendingChanId: id,
}, nil
case <-quit:
return nil, errShuttingDown
}
},
func(req *lnrpc.ChannelAcceptRequest) error {
sent <- req
pending <- req.PendingChanId
return nil
},
testTimeout*5, &chaincfg.RegressionNetParams, quit,
)
errChan := make(chan error, 1)
go func() { errChan <- acceptor.Run() }()

acceptor.Accept(&ChannelAcceptRequest{
Node: node,
OpenChanMsg: &lnwire.OpenChannel{
PendingChannelID: [32]byte{1},
ChannelType: &chanType,
},
})

req := <-sent
require.Equal(t, withBit, req.WantsIndependentSecrets)
require.Equal(t, lnrpc.CommitmentType_ANCHORS,
req.CommitmentType)

close(quit)
require.ErrorIs(t, <-errChan, errShuttingDown)
}
}
58 changes: 58 additions & 0 deletions chanbackup/independent_secrets_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package chanbackup

import (
"bytes"
"testing"

"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnencrypt"
"github.com/stretchr/testify/require"
)

// TestSingleIgnoresIndependentSecrets checks that a channel with
// option_independent_secrets backs up exactly like the same channel without
// it. 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 and the backup format stays unchanged.
func TestSingleIgnoresIndependentSecrets(t *testing.T) {
t.Parallel()

keyRing := &lnencrypt.MockKeyRing{}

channel, err := genRandomOpenChannelShell()
require.NoError(t, err)

base := channeldb.SingleFunderTweaklessBit |
channeldb.AnchorOutputsBit | channeldb.ZeroHtlcTxFeeBit

pack := func(chanType channeldb.ChannelType) Single {
channel.ChanType = chanType
single := NewSingle(channel, nil)

var b bytes.Buffer
require.NoError(t, single.PackToWriter(&b, keyRing))

var unpacked Single
require.NoError(t, unpacked.UnpackFromReader(
bytes.NewReader(b.Bytes()), keyRing,
))

return unpacked
}

for _, extra := range []channeldb.ChannelType{
0, channeldb.UnifiedSigsBit,
} {
plain := pack(base | extra)
independent := pack(
base | extra | channeldb.IndependentSecretsBit,
)

require.Equal(t, plain.Version, independent.Version)
require.Equal(t, plain.UnifiedSigs, independent.UnifiedSigs)
anchors := SingleBackupVersion(
AnchorsZeroFeeHtlcTxCommitVersion,
)
require.Equal(t, anchors, independent.Version)
}
}
Loading
Loading