From fea9c171ee0423ae0d4e533b72290f251ead96df Mon Sep 17 00:00:00 2001 From: ziggie Date: Fri, 15 May 2026 13:07:27 -0300 Subject: [PATCH 01/24] chanstate: use channel types in consumers Move channelnotifier and invoice hop-hint code to the chanstate channel types. These consumers already depend on the chanstate store interfaces, so they no longer need to refer to the channeldb compatibility aliases for OpenChannel and ChannelCloseSummary. --- channelnotifier/channelnotifier.go | 15 ++++--- channelnotifier/channelnotifier_test.go | 12 +++--- lnrpc/invoicesrpc/addinvoice.go | 13 +++--- lnrpc/invoicesrpc/addinvoice_test.go | 54 +++++++++++++------------ 4 files changed, 48 insertions(+), 46 deletions(-) diff --git a/channelnotifier/channelnotifier.go b/channelnotifier/channelnotifier.go index 733242167..c9430dd72 100644 --- a/channelnotifier/channelnotifier.go +++ b/channelnotifier/channelnotifier.go @@ -4,7 +4,6 @@ import ( "sync" "github.com/btcsuite/btcd/wire/v2" - "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/subscribe" ) @@ -31,14 +30,14 @@ type PendingOpenChannelEvent struct { // channel. This might not have been persisted to the channel DB yet // because we are still waiting for the final message from the remote // peer. - PendingChannel *channeldb.OpenChannel + PendingChannel *chanstate.OpenChannel } // OpenChannelEvent represents a new event where a channel goes from pending // open to open. type OpenChannelEvent struct { // Channel is the channel that has become open. - Channel *channeldb.OpenChannel + Channel *chanstate.OpenChannel } // ActiveLinkEvent represents a new event where the link becomes active in the @@ -70,13 +69,13 @@ type InactiveChannelEvent struct { // ClosedChannelEvent represents a new event where a channel becomes closed. type ClosedChannelEvent struct { // CloseSummary is the summary of the channel close that has occurred. - CloseSummary *channeldb.ChannelCloseSummary + CloseSummary *chanstate.ChannelCloseSummary } // ChannelUpdateEvent represents a new event where a channel's state is updated. type ChannelUpdateEvent struct { // Channel is the channel that has been updated. - Channel *channeldb.OpenChannel + Channel *chanstate.OpenChannel } // FullyResolvedChannelEvent represents a new event where a channel becomes @@ -148,7 +147,7 @@ func (c *ChannelNotifier) SubscribeChannelEvents() (*subscribe.Client, error) { // persisted to the DB because we still wait for the final message from the // remote peer. func (c *ChannelNotifier) NotifyPendingOpenChannelEvent(chanPoint wire.OutPoint, - pendingChan *channeldb.OpenChannel) { + pendingChan *chanstate.OpenChannel) { event := PendingOpenChannelEvent{ ChannelPoint: &chanPoint, @@ -200,7 +199,7 @@ func (c *ChannelNotifier) NotifyClosedChannelEvent(chanPoint wire.OutPoint) { // IsPending field will typically be true at this point; callers should set it // accordingly. func (c *ChannelNotifier) NotifyEarlyClosedChannelEvent( - summary *channeldb.ChannelCloseSummary) { + summary *chanstate.ChannelCloseSummary) { event := ClosedChannelEvent{CloseSummary: summary} if err := c.ntfnServer.SendUpdate(event); err != nil { @@ -271,7 +270,7 @@ func (c *ChannelNotifier) NotifyInactiveChannelEvent(chanPoint wire.OutPoint) { // NotifyChannelUpdateEvent notifies subscribers that a channel's state has been // updated. func (c *ChannelNotifier) NotifyChannelUpdateEvent( - channel *channeldb.OpenChannel) { + channel *chanstate.OpenChannel) { event := ChannelUpdateEvent{Channel: channel} if err := c.ntfnServer.SendUpdate(event); err != nil { diff --git a/channelnotifier/channelnotifier_test.go b/channelnotifier/channelnotifier_test.go index afeea30f8..165d0e34d 100644 --- a/channelnotifier/channelnotifier_test.go +++ b/channelnotifier/channelnotifier_test.go @@ -6,7 +6,7 @@ import ( "github.com/btcsuite/btcd/chainhash/v2" "github.com/btcsuite/btcd/wire/v2" - "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/stretchr/testify/require" ) @@ -27,7 +27,7 @@ func TestChannelUpdateEvent(t *testing.T) { defer sub.Cancel() // Create a mock channel state. - channel := &channeldb.OpenChannel{} + channel := &chanstate.OpenChannel{} // Notify the server of a channel update event. ntfnServer.NotifyChannelUpdateEvent(channel) @@ -69,9 +69,9 @@ func TestNotifyEarlyClosedChannelEvent(t *testing.T) { Hash: chainhash.Hash{0x01, 0x02, 0x03}, Index: 4, } - summary := &channeldb.ChannelCloseSummary{ + summary := &chanstate.ChannelCloseSummary{ ChanPoint: chanPoint, - CloseType: channeldb.CooperativeClose, + CloseType: chanstate.CooperativeClose, IsPending: true, } @@ -111,9 +111,9 @@ func TestNotifyEarlyClosedChannelEventSingleEvent(t *testing.T) { require.NoError(t, err) t.Cleanup(sub.Cancel) - summary := &channeldb.ChannelCloseSummary{ + summary := &chanstate.ChannelCloseSummary{ ChanPoint: wire.OutPoint{Index: 7}, - CloseType: channeldb.CooperativeClose, + CloseType: chanstate.CooperativeClose, IsPending: true, } ntfnServer.NotifyEarlyClosedChannelEvent(summary) diff --git a/lnrpc/invoicesrpc/addinvoice.go b/lnrpc/invoicesrpc/addinvoice.go index 761055265..42839d851 100644 --- a/lnrpc/invoicesrpc/addinvoice.go +++ b/lnrpc/invoicesrpc/addinvoice.go @@ -17,7 +17,6 @@ import ( "github.com/btcsuite/btcd/chaincfg/v2" "github.com/btcsuite/btcd/chainhash/v2" "github.com/btcsuite/btcd/wire/v2" - "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/graph/db/models" "github.com/lightningnetwork/lnd/invoices" @@ -732,7 +731,7 @@ type HopHintInfo struct { ScidAliasFeature bool } -func newHopHintInfo(c *channeldb.OpenChannel, isActive bool) *HopHintInfo { +func newHopHintInfo(c *chanstate.OpenChannel, isActive bool) *HopHintInfo { isPublic := c.ChannelFlags&lnwire.FFAnnounceChannel != 0 return &HopHintInfo{ @@ -783,7 +782,7 @@ type SelectHopHintsCfg struct { // FetchAllChannels retrieves all open channels currently stored // within the database. - FetchAllChannels func() ([]*channeldb.OpenChannel, error) + FetchAllChannels func() ([]*chanstate.OpenChannel, error) // IsChannelActive checks whether the channel identified by the provided // ChannelID is considered active. @@ -846,7 +845,7 @@ func sufficientHints(nHintsLeft int, currentAmount, // getPotentialHints returns a slice of open channels that should be considered // for the hopHint list in an invoice. The slice is sorted in descending order // based on the remote balance. -func getPotentialHints(cfg *SelectHopHintsCfg) ([]*channeldb.OpenChannel, +func getPotentialHints(cfg *SelectHopHintsCfg) ([]*chanstate.OpenChannel, error) { // TODO(positiveblue): get the channels slice already filtered by @@ -856,7 +855,7 @@ func getPotentialHints(cfg *SelectHopHintsCfg) ([]*channeldb.OpenChannel, return nil, err } - privateChannels := make([]*channeldb.OpenChannel, 0, len(openChannels)) + privateChannels := make([]*chanstate.OpenChannel, 0, len(openChannels)) for _, oc := range openChannels { isPublic := oc.ChannelFlags&lnwire.FFAnnounceChannel != 0 if !isPublic { @@ -878,7 +877,7 @@ func getPotentialHints(cfg *SelectHopHintsCfg) ([]*channeldb.OpenChannel, // shouldIncludeChannel returns true if the channel passes all the checks to // be a hopHint in a given invoice. func shouldIncludeChannel(cfg *SelectHopHintsCfg, - channel *channeldb.OpenChannel, + channel *chanstate.OpenChannel, alreadyIncluded map[uint64]bool) (zpay32.HopHint, lnwire.MilliSatoshi, bool) { @@ -924,7 +923,7 @@ func shouldIncludeChannel(cfg *SelectHopHintsCfg, // descending priority. func selectHopHints(cfg *SelectHopHintsCfg, nHintsLeft int, targetBandwidth lnwire.MilliSatoshi, - potentialHints []*channeldb.OpenChannel, + potentialHints []*chanstate.OpenChannel, alreadyIncluded map[uint64]bool) [][]zpay32.HopHint { currentBandwidth := lnwire.MilliSatoshi(0) diff --git a/lnrpc/invoicesrpc/addinvoice_test.go b/lnrpc/invoicesrpc/addinvoice_test.go index c4f402e48..ac8408f52 100644 --- a/lnrpc/invoicesrpc/addinvoice_test.go +++ b/lnrpc/invoicesrpc/addinvoice_test.go @@ -8,7 +8,7 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/chainhash/v2" "github.com/btcsuite/btcd/wire/v2" - "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/graph/db/models" "github.com/lightningnetwork/lnd/invoices" "github.com/lightningnetwork/lnd/lnwire" @@ -78,11 +78,15 @@ func (h *hopHintsConfigMock) GetAlias( // FetchAllChannels retrieves all open channels currently stored // within the database. -func (h *hopHintsConfigMock) FetchAllChannels() ([]*channeldb.OpenChannel, +func (h *hopHintsConfigMock) FetchAllChannels() ([]*chanstate.OpenChannel, error) { args := h.Mock.Called() - return args.Get(0).([]*channeldb.OpenChannel), args.Error(1) + + channels, ok := args.Get(0).([]*chanstate.OpenChannel) + require.True(h.t, ok) + + return channels, args.Error(1) } // FetchChannelEdgesByID attempts to lookup the two directed edges for @@ -121,7 +125,7 @@ func getTestPubKey() *btcec.PublicKey { var shouldIncludeChannelTestCases = []struct { name string setupMock func(*hopHintsConfigMock) - channel *channeldb.OpenChannel + channel *chanstate.OpenChannel alreadyIncluded map[uint64]bool cfg *SelectHopHintsCfg hopHint zpay32.HopHint @@ -131,7 +135,7 @@ var shouldIncludeChannelTestCases = []struct { name: "already included channels should not be included " + "again", alreadyIncluded: map[uint64]bool{1: true}, - channel: &channeldb.OpenChannel{ + channel: &chanstate.OpenChannel{ ShortChannelID: lnwire.NewShortChanIDFromInt(1), }, include: false, @@ -146,7 +150,7 @@ var shouldIncludeChannelTestCases = []struct { "IsChannelActive", chanID, ).Once().Return(true) }, - channel: &channeldb.OpenChannel{ + channel: &chanstate.OpenChannel{ FundingOutpoint: wire.OutPoint{ Index: 0, }, @@ -163,7 +167,7 @@ var shouldIncludeChannelTestCases = []struct { "IsChannelActive", chanID, ).Once().Return(false) }, - channel: &channeldb.OpenChannel{ + channel: &chanstate.OpenChannel{ FundingOutpoint: wire.OutPoint{ Index: 0, }, @@ -185,7 +189,7 @@ var shouldIncludeChannelTestCases = []struct { "IsPublicNode", mock.Anything, ).Once().Return(false, nil) }, - channel: &channeldb.OpenChannel{ + channel: &chanstate.OpenChannel{ FundingOutpoint: wire.OutPoint{ Index: 0, }, @@ -220,7 +224,7 @@ var shouldIncludeChannelTestCases = []struct { "FetchChannelEdgesByID", mock.Anything, ).Once().Return(nil, nil, nil, fmt.Errorf("no edge")) }, - channel: &channeldb.OpenChannel{ + channel: &chanstate.OpenChannel{ FundingOutpoint: wire.OutPoint{ Index: 0, }, @@ -256,12 +260,12 @@ var shouldIncludeChannelTestCases = []struct { "GetAlias", mock.Anything, ).Once().Return(lnwire.ShortChannelID{}, nil) }, - channel: &channeldb.OpenChannel{ + channel: &chanstate.OpenChannel{ FundingOutpoint: wire.OutPoint{ Index: 0, }, IdentityPub: getTestPubKey(), - ChanType: channeldb.ScidAliasFeatureBit, + ChanType: chanstate.ScidAliasFeatureBit, }, include: false, }, { @@ -294,12 +298,12 @@ var shouldIncludeChannelTestCases = []struct { "GetAlias", mock.Anything, ).Once().Return(alias, nil) }, - channel: &channeldb.OpenChannel{ + channel: &chanstate.OpenChannel{ FundingOutpoint: wire.OutPoint{ Index: 0, }, IdentityPub: getTestPubKey(), - ChanType: channeldb.ScidAliasFeatureBit, + ChanType: chanstate.ScidAliasFeatureBit, }, include: false, }, { @@ -347,7 +351,7 @@ var shouldIncludeChannelTestCases = []struct { nil, ) }, - channel: &channeldb.OpenChannel{ + channel: &chanstate.OpenChannel{ FundingOutpoint: wire.OutPoint{ Index: 1, }, @@ -394,7 +398,7 @@ var shouldIncludeChannelTestCases = []struct { }, nil, ) }, - channel: &channeldb.OpenChannel{ + channel: &chanstate.OpenChannel{ FundingOutpoint: wire.OutPoint{ Index: 1, }, @@ -447,13 +451,13 @@ var shouldIncludeChannelTestCases = []struct { "GetAlias", mock.Anything, ).Once().Return(aliasSCID, nil) }, - channel: &channeldb.OpenChannel{ + channel: &chanstate.OpenChannel{ FundingOutpoint: wire.OutPoint{ Index: 1, }, IdentityPub: getTestPubKey(), ShortChannelID: lnwire.NewShortChanIDFromInt(12), - ChanType: channeldb.ScidAliasFeatureBit, + ChanType: chanstate.ScidAliasFeatureBit, }, hopHint: zpay32.HopHint{ NodeID: getTestPubKey(), @@ -571,7 +575,7 @@ var populateHopHintsTestCases = []struct { setupMock: func(h *hopHintsConfigMock) { fundingOutpoint := wire.OutPoint{Index: 9} chanID := lnwire.NewChanIDFromOutPoint(fundingOutpoint) - allChannels := []*channeldb.OpenChannel{ + allChannels := []*chanstate.OpenChannel{ { FundingOutpoint: fundingOutpoint, ShortChannelID: lnwire.NewShortChanIDFromInt(9), @@ -618,9 +622,9 @@ var populateHopHintsTestCases = []struct { fundingOutpoint := wire.OutPoint{Index: 9} chanID := lnwire.NewChanIDFromOutPoint(fundingOutpoint) remoteBalance := lnwire.MilliSatoshi(10_000_000) - allChannels := []*channeldb.OpenChannel{ + allChannels := []*chanstate.OpenChannel{ { - LocalCommitment: channeldb.ChannelCommitment{ + LocalCommitment: chanstate.ChannelCommitment{ RemoteBalance: remoteBalance, }, FundingOutpoint: fundingOutpoint, @@ -669,12 +673,12 @@ var populateHopHintsTestCases = []struct { fundingOutpoint := wire.OutPoint{Index: 9} chanID := lnwire.NewChanIDFromOutPoint(fundingOutpoint) remoteBalance := lnwire.MilliSatoshi(10_000_000) - allChannels := []*channeldb.OpenChannel{ + allChannels := []*chanstate.OpenChannel{ // Because the channels with higher remote balance have // enough bandwidth we should never use this one. {}, { - LocalCommitment: channeldb.ChannelCommitment{ + LocalCommitment: chanstate.ChannelCommitment{ RemoteBalance: remoteBalance, }, FundingOutpoint: fundingOutpoint, @@ -868,11 +872,11 @@ func setupMockTwoChannels(h *hopHintsConfigMock) (lnwire.ChannelID, chanID2 := lnwire.NewChanIDFromOutPoint(fundingOutpoint2) remoteBalance2 := lnwire.MilliSatoshi(1_000_000) - allChannels := []*channeldb.OpenChannel{ + allChannels := []*chanstate.OpenChannel{ // After sorting we will first process chanID1 and then // chanID2. { - LocalCommitment: channeldb.ChannelCommitment{ + LocalCommitment: chanstate.ChannelCommitment{ RemoteBalance: remoteBalance2, }, FundingOutpoint: fundingOutpoint2, @@ -880,7 +884,7 @@ func setupMockTwoChannels(h *hopHintsConfigMock) (lnwire.ChannelID, IdentityPub: getTestPubKey(), }, { - LocalCommitment: channeldb.ChannelCommitment{ + LocalCommitment: chanstate.ChannelCommitment{ RemoteBalance: remoteBalance1, }, FundingOutpoint: fundingOutpoint1, From a3fdcba511432f76bebd4996405e09cc6d1cd0fc Mon Sep 17 00:00:00 2001 From: ziggie Date: Fri, 15 May 2026 13:10:12 -0300 Subject: [PATCH 02/24] chanstate: use channel types in interfaces Move small interface-only consumers to chanstate.OpenChannel. These packages only expose open-channel values through callback or store interfaces, so they can depend on the channel-state package without pulling in channeldb compatibility aliases. --- discovery/ban.go | 4 ++-- netann/interface.go | 4 ++-- routing/blindedpath/blinded_path.go | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/discovery/ban.go b/discovery/ban.go index 0425948cb..80f5f8f7e 100644 --- a/discovery/ban.go +++ b/discovery/ban.go @@ -10,7 +10,7 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/lightninglabs/neutrino/cache" "github.com/lightninglabs/neutrino/cache/lru" - "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/lnwire" ) @@ -67,7 +67,7 @@ type GraphCloser interface { type NodeInfoInquirer interface { // FetchOpenChannels returns the set of channels that we have with the // peer identified by the passed-in public key. - FetchOpenChannels(*btcec.PublicKey) ([]*channeldb.OpenChannel, error) + FetchOpenChannels(*btcec.PublicKey) ([]*chanstate.OpenChannel, error) } // ScidCloserMan helps the gossiper handle closed channels that are in the diff --git a/netann/interface.go b/netann/interface.go index 291eb01cc..366805bfc 100644 --- a/netann/interface.go +++ b/netann/interface.go @@ -4,7 +4,7 @@ import ( "context" "github.com/btcsuite/btcd/wire/v2" - "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/graph/db/models" ) @@ -13,7 +13,7 @@ import ( type DB interface { // FetchAllOpenChannels returns a slice of all open channels known to // the daemon. This may include private or pending channels. - FetchAllOpenChannels() ([]*channeldb.OpenChannel, error) + FetchAllOpenChannels() ([]*chanstate.OpenChannel, error) } // ChannelGraph abstracts the required channel graph queries used by the diff --git a/routing/blindedpath/blinded_path.go b/routing/blindedpath/blinded_path.go index 77676e3ac..044a87e65 100644 --- a/routing/blindedpath/blinded_path.go +++ b/routing/blindedpath/blinded_path.go @@ -10,7 +10,7 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil/v2" sphinx "github.com/lightningnetwork/lightning-onion" - "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/graph/db/models" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/record" @@ -46,7 +46,7 @@ type BuildBlindedPathCfg struct { *models.ChannelEdgePolicy, *models.ChannelEdgePolicy, error) // FetchOurOpenChannels fetches this node's set of open channels. - FetchOurOpenChannels func() ([]*channeldb.OpenChannel, error) + FetchOurOpenChannels func() ([]*chanstate.OpenChannel, error) // BestHeight can be used to fetch the best block height that this node // is aware of. @@ -529,7 +529,7 @@ func buildDummyRouteData(node route.Vertex, relayInfo *record.PaymentRelayInfo, // we use the provided default policy values, and we get the average capacity of // this node's channels to compute a MaxHTLC value. func computeDummyHopPolicy(defaultPolicy *BlindedHopPolicy, - fetchOurChannels func() ([]*channeldb.OpenChannel, error), + fetchOurChannels func() ([]*chanstate.OpenChannel, error), policies map[uint64]*BlindedHopPolicy) (*BlindedHopPolicy, error) { numPolicies := len(policies) From 19e656b2bb32ea1feb212a9a729d742beabc9a46 Mon Sep 17 00:00:00 2001 From: ziggie Date: Fri, 15 May 2026 13:12:56 -0300 Subject: [PATCH 03/24] chanstate: use channel types in fitness Move channel event payloads in chanfitness to chanstate types. The event store still uses channeldb for flap-count persistence and related errors, but open-channel and close-summary values now come from the channel-state package. --- chanfitness/chaneventstore.go | 3 ++- chanfitness/chaneventstore_test.go | 5 +++-- chanfitness/chaneventstore_testctx_test.go | 7 ++++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/chanfitness/chaneventstore.go b/chanfitness/chaneventstore.go index 9fc429640..016f67d6a 100644 --- a/chanfitness/chaneventstore.go +++ b/chanfitness/chaneventstore.go @@ -20,6 +20,7 @@ import ( "github.com/btcsuite/btcd/wire/v2" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channelnotifier" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/clock" "github.com/lightningnetwork/lnd/peernotifier" "github.com/lightningnetwork/lnd/routing/route" @@ -84,7 +85,7 @@ type Config struct { // GetOpenChannels provides a list of existing open channels which is // used to populate the ChannelEventStore with a set of channels on // startup. - GetOpenChannels func() ([]*channeldb.OpenChannel, error) + GetOpenChannels func() ([]*chanstate.OpenChannel, error) // IsPeerOnline returns whether the peer with the given pubkey is // currently connected. It is used to seed the initial online state of a diff --git a/chanfitness/chaneventstore_test.go b/chanfitness/chaneventstore_test.go index 09dc1ce72..eed1d12cc 100644 --- a/chanfitness/chaneventstore_test.go +++ b/chanfitness/chaneventstore_test.go @@ -8,6 +8,7 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/wire/v2" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/clock" "github.com/lightningnetwork/lnd/routing/route" "github.com/lightningnetwork/lnd/subscribe" @@ -35,7 +36,7 @@ func TestStartStoreError(t *testing.T) { name string ChannelEvents func() (subscribe.Subscription, error) PeerEvents func() (subscribe.Subscription, error) - GetChannels func() ([]*channeldb.OpenChannel, error) + GetChannels func() ([]*chanstate.OpenChannel, error) }{ { name: "Channel events fail", @@ -50,7 +51,7 @@ func TestStartStoreError(t *testing.T) { name: "Get open channels fails", ChannelEvents: okSubscribeFunc, PeerEvents: okSubscribeFunc, - GetChannels: func() ([]*channeldb.OpenChannel, error) { + GetChannels: func() ([]*chanstate.OpenChannel, error) { return nil, errors.New("intentional test err") }, }, diff --git a/chanfitness/chaneventstore_testctx_test.go b/chanfitness/chaneventstore_testctx_test.go index dca7580cc..72a2530c3 100644 --- a/chanfitness/chaneventstore_testctx_test.go +++ b/chanfitness/chaneventstore_testctx_test.go @@ -9,6 +9,7 @@ import ( "github.com/btcsuite/btcd/wire/v2" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channelnotifier" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/clock" "github.com/lightningnetwork/lnd/peernotifier" "github.com/lightningnetwork/lnd/routing/route" @@ -83,7 +84,7 @@ func newChanEventStoreTestCtx(t *testing.T) *chanEventStoreTestCtx { SubscribePeerEvents: func() (subscribe.Subscription, error) { return testCtx.peerSubscription, nil }, - GetOpenChannels: func() ([]*channeldb.OpenChannel, error) { + GetOpenChannels: func() ([]*chanstate.OpenChannel, error) { return nil, nil }, WriteFlapCount: func(updates map[route.Vertex]*channeldb.FlapCount) error { @@ -192,7 +193,7 @@ func (c *chanEventStoreTestCtx) closeChannel(channel wire.OutPoint, peer *btcec.PublicKey) { update := channelnotifier.ClosedChannelEvent{ - CloseSummary: &channeldb.ChannelCloseSummary{ + CloseSummary: &chanstate.ChannelCloseSummary{ ChanPoint: channel, RemotePub: peer, }, @@ -232,7 +233,7 @@ func (c *chanEventStoreTestCtx) sendChannelOpenedUpdate(pubkey *btcec.PublicKey, channel wire.OutPoint) { update := channelnotifier.OpenChannelEvent{ - Channel: &channeldb.OpenChannel{ + Channel: &chanstate.OpenChannel{ FundingOutpoint: channel, IdentityPub: pubkey, }, From 471c9b696fc7be5c172496d16a3a0b74cc59ea84 Mon Sep 17 00:00:00 2001 From: ziggie Date: Fri, 15 May 2026 13:18:35 -0300 Subject: [PATCH 04/24] chanstate: use htlc type in beacon Move the witness subscription HTLC parameter to chanstate.HTLC. The beacon still depends on channeldb for witness-cache errors, but it no longer needs the channel DB compatibility alias for HTLC payloads. --- witness_beacon.go | 3 ++- witness_beacon_test.go | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/witness_beacon.go b/witness_beacon.go index 68c096a85..c1ccc63db 100644 --- a/witness_beacon.go +++ b/witness_beacon.go @@ -5,6 +5,7 @@ import ( "sync" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/contractcourt" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/graph/db/models" @@ -64,7 +65,7 @@ func newPreimageBeacon(wCache witnessCache, // SubscribeUpdates returns a channel that will be sent upon *each* time a new // preimage is discovered. func (p *preimageBeacon) SubscribeUpdates( - chanID lnwire.ShortChannelID, htlc *channeldb.HTLC, + chanID lnwire.ShortChannelID, htlc *chanstate.HTLC, payload *hop.Payload, nextHopOnionBlob []byte) (*contractcourt.WitnessSubscription, error) { diff --git a/witness_beacon_test.go b/witness_beacon_test.go index 1edbada93..7ba22db55 100644 --- a/witness_beacon_test.go +++ b/witness_beacon_test.go @@ -4,7 +4,7 @@ import ( "errors" "testing" - "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/graph/db/models" "github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/htlcswitch/hop" @@ -38,7 +38,7 @@ func TestWitnessBeaconIntercept(t *testing.T) { subscription, err := p.SubscribeUpdates( lnwire.NewShortChanIDFromInt(1), - &channeldb.HTLC{ + &chanstate.HTLC{ RHash: hash, }, &hop.Payload{}, @@ -76,7 +76,7 @@ func TestWitnessBeaconInterceptErrorCancels(t *testing.T) { ) chanID := lnwire.NewShortChanIDFromInt(1) - htlc := &channeldb.HTLC{ + htlc := &chanstate.HTLC{ HtlcIndex: 2, RHash: lntypes.Hash{3}, } From cd0383450a0d9b41a6a1af0c403dea2ac3d36119 Mon Sep 17 00:00:00 2001 From: ziggie Date: Fri, 15 May 2026 13:22:23 -0300 Subject: [PATCH 05/24] chanstate: use channel type in status manager Move the channel status manager working set to chanstate.OpenChannel. The DB interface already returns channelstate channel values, so this removes another channeldb compatibility alias from the consumer path while keeping the graph and announcement behavior unchanged. --- netann/chan_status_manager.go | 6 ++-- netann/chan_status_manager_test.go | 50 +++++++++++++++--------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/netann/chan_status_manager.go b/netann/chan_status_manager.go index f1b592fcd..3b642c43f 100644 --- a/netann/chan_status_manager.go +++ b/netann/chan_status_manager.go @@ -8,7 +8,7 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/wire/v2" - "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" graphdb "github.com/lightningnetwork/lnd/graph/db" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet" @@ -600,14 +600,14 @@ func (m *ChanStatusManager) disableInactiveChannels() { // fetchChannels returns the working set of channels managed by the // ChanStatusManager. The returned channels are filtered to only contain public // channels. -func (m *ChanStatusManager) fetchChannels() ([]*channeldb.OpenChannel, error) { +func (m *ChanStatusManager) fetchChannels() ([]*chanstate.OpenChannel, error) { allChannels, err := m.cfg.DB.FetchAllOpenChannels() if err != nil { return nil, err } // Filter out private channels. - var channels []*channeldb.OpenChannel + var channels []*chanstate.OpenChannel for _, c := range allChannels { // We'll skip any private channels, as they aren't used for // routing within the network by other nodes. diff --git a/netann/chan_status_manager_test.go b/netann/chan_status_manager_test.go index aaaf9344b..16d43fb3a 100644 --- a/netann/chan_status_manager_test.go +++ b/netann/chan_status_manager_test.go @@ -15,7 +15,7 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/chainhash/v2" "github.com/btcsuite/btcd/wire/v2" - "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" graphdb "github.com/lightningnetwork/lnd/graph/db" "github.com/lightningnetwork/lnd/graph/db/models" "github.com/lightningnetwork/lnd/keychain" @@ -51,14 +51,14 @@ func randOutpoint(t *testing.T) wire.OutPoint { var shortChanIDs uint64 -// createChannel generates a channeldb.OpenChannel with a random chanpoint and +// createChannel generates a chanstate.OpenChannel with a random chanpoint and // short channel id. -func createChannel(t *testing.T) *channeldb.OpenChannel { +func createChannel(t *testing.T) *chanstate.OpenChannel { t.Helper() sid := atomic.AddUint64(&shortChanIDs, 1) - return &channeldb.OpenChannel{ + return &chanstate.OpenChannel{ ShortChannelID: lnwire.NewShortChanIDFromInt(sid), ChannelFlags: lnwire.FFAnnounceChannel, FundingOutpoint: randOutpoint(t), @@ -69,7 +69,7 @@ func createChannel(t *testing.T) *channeldb.OpenChannel { // The remote party's public key is generated randomly, and then sorted against // our `pubkey` with the direction bit set appropriately in the policies. Our // update will be created with the disabled bit set if startEnabled is false. -func createEdgePolicies(t *testing.T, channel *channeldb.OpenChannel, +func createEdgePolicies(t *testing.T, channel *chanstate.OpenChannel, pubkey *btcec.PublicKey, startEnabled bool) (*models.ChannelEdgeInfo, *models.ChannelEdgePolicy, *models.ChannelEdgePolicy) { @@ -134,7 +134,7 @@ func createEdgePolicies(t *testing.T, channel *channeldb.OpenChannel, type mockGraph struct { mu sync.Mutex - channels []*channeldb.OpenChannel + channels []*chanstate.OpenChannel chanInfos map[wire.OutPoint]*models.ChannelEdgeInfo chanPols1 map[wire.OutPoint]*models.ChannelEdgePolicy chanPols2 map[wire.OutPoint]*models.ChannelEdgePolicy @@ -147,7 +147,7 @@ func newMockGraph(t *testing.T, numChannels int, startEnabled bool, pubKey *btcec.PublicKey) *mockGraph { g := &mockGraph{ - channels: make([]*channeldb.OpenChannel, 0, numChannels), + channels: make([]*chanstate.OpenChannel, 0, numChannels), chanInfos: make(map[wire.OutPoint]*models.ChannelEdgeInfo), chanPols1: make(map[wire.OutPoint]*models.ChannelEdgePolicy), chanPols2: make(map[wire.OutPoint]*models.ChannelEdgePolicy), @@ -169,7 +169,7 @@ func newMockGraph(t *testing.T, numChannels int, startEnabled bool, return g } -func (g *mockGraph) FetchAllOpenChannels() ([]*channeldb.OpenChannel, error) { +func (g *mockGraph) FetchAllOpenChannels() ([]*chanstate.OpenChannel, error) { return g.chans(), nil } @@ -246,24 +246,24 @@ func (g *mockGraph) ApplyChannelUpdate(update *lnwire.ChannelUpdate1, return nil } -func (g *mockGraph) chans() []*channeldb.OpenChannel { +func (g *mockGraph) chans() []*chanstate.OpenChannel { g.mu.Lock() defer g.mu.Unlock() - channels := make([]*channeldb.OpenChannel, 0, len(g.channels)) + channels := make([]*chanstate.OpenChannel, 0, len(g.channels)) channels = append(channels, g.channels...) return channels } -func (g *mockGraph) addChannel(channel *channeldb.OpenChannel) { +func (g *mockGraph) addChannel(channel *chanstate.OpenChannel) { g.mu.Lock() defer g.mu.Unlock() g.channels = append(g.channels, channel) } -func (g *mockGraph) addEdgePolicy(c *channeldb.OpenChannel, +func (g *mockGraph) addEdgePolicy(c *chanstate.OpenChannel, info *models.ChannelEdgeInfo, pol1, pol2 *models.ChannelEdgePolicy) { @@ -276,7 +276,7 @@ func (g *mockGraph) addEdgePolicy(c *channeldb.OpenChannel, g.sidToCid[c.ShortChanID()] = c.FundingOutpoint } -func (g *mockGraph) removeChannel(channel *channeldb.OpenChannel) { +func (g *mockGraph) removeChannel(channel *chanstate.OpenChannel) { g.mu.Lock() defer g.mu.Unlock() @@ -401,7 +401,7 @@ func newHarness(t *testing.T, numChannels int, // markActive updates the active status of the passed channels within the mock // switch to active. -func (h *testHarness) markActive(channels []*channeldb.OpenChannel) { +func (h *testHarness) markActive(channels []*chanstate.OpenChannel) { h.t.Helper() for _, channel := range channels { @@ -412,7 +412,7 @@ func (h *testHarness) markActive(channels []*channeldb.OpenChannel) { // markInactive updates the active status of the passed channels within the mock // switch to inactive. -func (h *testHarness) markInactive(channels []*channeldb.OpenChannel) { +func (h *testHarness) markInactive(channels []*chanstate.OpenChannel) { h.t.Helper() for _, channel := range channels { @@ -423,8 +423,8 @@ func (h *testHarness) markInactive(channels []*channeldb.OpenChannel) { // assertEnables requests enables for all of the passed channels, and asserts // that the errors returned from RequestEnable matches expErr. -func (h *testHarness) assertEnables(channels []*channeldb.OpenChannel, expErr error, - manual bool) { +func (h *testHarness) assertEnables(channels []*chanstate.OpenChannel, + expErr error, manual bool) { h.t.Helper() @@ -435,8 +435,8 @@ func (h *testHarness) assertEnables(channels []*channeldb.OpenChannel, expErr er // assertDisables requests disables for all of the passed channels, and asserts // that the errors returned from RequestDisable matches expErr. -func (h *testHarness) assertDisables(channels []*channeldb.OpenChannel, expErr error, - manual bool) { +func (h *testHarness) assertDisables(channels []*chanstate.OpenChannel, + expErr error, manual bool) { h.t.Helper() @@ -447,7 +447,7 @@ func (h *testHarness) assertDisables(channels []*channeldb.OpenChannel, expErr e // assertAutos requests auto state management for all of the passed channels, and // asserts that the errors returned from RequestAuto matches expErr. -func (h *testHarness) assertAutos(channels []*channeldb.OpenChannel, +func (h *testHarness) assertAutos(channels []*chanstate.OpenChannel, expErr error) { h.t.Helper() @@ -506,7 +506,7 @@ func (h *testHarness) assertNoUpdates(duration time.Duration) { // are receive on the network for each of the passed OpenChannels, and that all // of their disable bits are set to match expEnabled. The expEnabled parameter // is ignored if channels is nil. -func (h *testHarness) assertUpdates(channels []*channeldb.OpenChannel, +func (h *testHarness) assertUpdates(channels []*chanstate.OpenChannel, expEnabled bool, duration time.Duration) { h.t.Helper() @@ -554,7 +554,7 @@ func (h *testHarness) assertUpdates(channels []*channeldb.OpenChannel, // sidsFromChans returns an index contain the short channel ids of each channel // provided in the list of OpenChannels. func sidsFromChans( - channels []*channeldb.OpenChannel) map[lnwire.ShortChannelID]struct{} { + channels []*chanstate.OpenChannel) map[lnwire.ShortChannelID]struct{} { sids := make(map[lnwire.ShortChannelID]struct{}) for _, channel := range channels { @@ -703,7 +703,7 @@ var stateMachineTests = []stateMachineTest{ startEnabled: false, fn: func(h testHarness) { // Create channels unknown to the graph. - unknownChans := []*channeldb.OpenChannel{ + unknownChans := []*chanstate.OpenChannel{ createChannel(h.t), createChannel(h.t), createChannel(h.t), @@ -723,7 +723,7 @@ var stateMachineTests = []stateMachineTest{ startEnabled: false, fn: func(h testHarness) { // Create channels unknown to the graph. - unknownChans := []*channeldb.OpenChannel{ + unknownChans := []*chanstate.OpenChannel{ createChannel(h.t), createChannel(h.t), createChannel(h.t), @@ -749,7 +749,7 @@ var stateMachineTests = []stateMachineTest{ // Add a new channels to the graph, but don't yet add // the edge policies. We should see no updates sent // since the manager can't access the policies. - newChans := []*channeldb.OpenChannel{ + newChans := []*chanstate.OpenChannel{ createChannel(h.t), createChannel(h.t), createChannel(h.t), From c58d51d59394a177992ba42dfd2526939ccd23f7 Mon Sep 17 00:00:00 2001 From: ziggie Date: Fri, 15 May 2026 13:27:07 -0300 Subject: [PATCH 06/24] chanstate: use channel type in lnpeer Move lnpeer.NewChannel to embed chanstate.OpenChannel. This keeps the peer-facing channel event type independent of the channeldb compatibility alias while preserving the existing embedded OpenChannel field shape for callers. --- lnpeer/peer.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lnpeer/peer.go b/lnpeer/peer.go index 51f7f29b0..ef18ddab7 100644 --- a/lnpeer/peer.go +++ b/lnpeer/peer.go @@ -5,7 +5,7 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/wire/v2" - "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" ) @@ -14,7 +14,7 @@ import ( // with the set of channel options that may change how the channel is created. // This can be used to pass along the nonce state needed for taproot channels. type NewChannel struct { - *channeldb.OpenChannel + *chanstate.OpenChannel // ChanOpts can be used to change how the channel is created. ChanOpts []lnwallet.ChannelOpt From dac8a8fe53938fe0d78747b94a83cad8cc3700a0 Mon Sep 17 00:00:00 2001 From: ziggie Date: Fri, 15 May 2026 13:37:24 -0300 Subject: [PATCH 07/24] chanstate: use channel types in backups Move static channel backup construction to chanstate channel types. The package still imports channeldb where it uses real database concerns, including address sourcing and duplicate-channel recovery. The backup payload and live-channel source boundaries no longer depend on the channeldb compatibility aliases. --- chanbackup/backup.go | 9 +++++---- chanbackup/backup_test.go | 14 ++++++++------ chanbackup/pubsub.go | 4 ++-- chanbackup/single.go | 8 ++++---- chanbackup/single_test.go | 22 +++++++++++----------- 5 files changed, 30 insertions(+), 27 deletions(-) diff --git a/chanbackup/backup.go b/chanbackup/backup.go index 88b8a0ce4..6af6f381c 100644 --- a/chanbackup/backup.go +++ b/chanbackup/backup.go @@ -6,6 +6,7 @@ import ( "github.com/btcsuite/btcd/wire/v2" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/fn/v2" ) @@ -14,11 +15,11 @@ import ( // commitment transaction broadcast. type LiveChannelSource interface { // FetchAllChannels returns all known live channels. - FetchAllChannels() ([]*channeldb.OpenChannel, error) + FetchAllChannels() ([]*chanstate.OpenChannel, error) // FetchChannel attempts to locate a live channel identified by the // passed chanPoint. Optionally an existing db tx can be supplied. - FetchChannel(chanPoint wire.OutPoint) (*channeldb.OpenChannel, error) + FetchChannel(chanPoint wire.OutPoint) (*chanstate.OpenChannel, error) } // assembleChanBackup attempts to assemble a static channel backup for the @@ -26,7 +27,7 @@ type LiveChannelSource interface { // the channel, as well as addressing information so we can find the peer and // reconnect to them to initiate the protocol. func assembleChanBackup(ctx context.Context, addrSource channeldb.AddrSource, - openChan *channeldb.OpenChannel) (*Single, error) { + openChan *chanstate.OpenChannel) (*Single, error) { log.Debugf("Crafting backup for ChannelPoint(%v)", openChan.FundingOutpoint) @@ -55,7 +56,7 @@ func assembleChanBackup(ctx context.Context, addrSource channeldb.AddrSource, // in loss of funds! This may happen if an outdated channel backup is attempted // to be used to force close the channel. func buildCloseTxInputs( - targetChan *channeldb.OpenChannel) fn.Option[CloseTxInputs] { + targetChan *chanstate.OpenChannel) fn.Option[CloseTxInputs] { log.Debugf("Crafting CloseTxInputs for ChannelPoint(%v)", targetChan.FundingOutpoint) diff --git a/chanbackup/backup_test.go b/chanbackup/backup_test.go index f2a10c21d..264649b07 100644 --- a/chanbackup/backup_test.go +++ b/chanbackup/backup_test.go @@ -8,12 +8,12 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/wire/v2" - "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/stretchr/testify/require" ) type mockChannelSource struct { - chans map[wire.OutPoint]*channeldb.OpenChannel + chans map[wire.OutPoint]*chanstate.OpenChannel failQuery bool @@ -22,17 +22,19 @@ type mockChannelSource struct { func newMockChannelSource() *mockChannelSource { return &mockChannelSource{ - chans: make(map[wire.OutPoint]*channeldb.OpenChannel), + chans: make(map[wire.OutPoint]*chanstate.OpenChannel), addrs: make(map[[33]byte][]net.Addr), } } -func (m *mockChannelSource) FetchAllChannels() ([]*channeldb.OpenChannel, error) { +func (m *mockChannelSource) FetchAllChannels() ( + []*chanstate.OpenChannel, error) { + if m.failQuery { return nil, fmt.Errorf("fail") } - chans := make([]*channeldb.OpenChannel, 0, len(m.chans)) + chans := make([]*chanstate.OpenChannel, 0, len(m.chans)) for _, channel := range m.chans { chans = append(chans, channel) } @@ -41,7 +43,7 @@ func (m *mockChannelSource) FetchAllChannels() ([]*channeldb.OpenChannel, error) } func (m *mockChannelSource) FetchChannel(chanPoint wire.OutPoint) ( - *channeldb.OpenChannel, error) { + *chanstate.OpenChannel, error) { if m.failQuery { return nil, fmt.Errorf("fail") diff --git a/chanbackup/pubsub.go b/chanbackup/pubsub.go index 19151275f..4b5493f9d 100644 --- a/chanbackup/pubsub.go +++ b/chanbackup/pubsub.go @@ -10,7 +10,7 @@ import ( "sync/atomic" "github.com/btcsuite/btcd/wire/v2" - "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnutils" ) @@ -31,7 +31,7 @@ type Swapper interface { // ChannelWithAddrs bundles an open channel along with all the addresses for // the channel peer. type ChannelWithAddrs struct { - *channeldb.OpenChannel + *chanstate.OpenChannel // Addrs is the set of addresses that we can use to reach the target // peer. diff --git a/chanbackup/single.go b/chanbackup/single.go index 4acd64930..5cf654789 100644 --- a/chanbackup/single.go +++ b/chanbackup/single.go @@ -11,7 +11,7 @@ import ( "github.com/btcsuite/btcd/btcutil/v2" "github.com/btcsuite/btcd/chainhash/v2" "github.com/btcsuite/btcd/wire/v2" - "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnencrypt" @@ -169,7 +169,7 @@ type Single struct { // // NOTE: Of the items in the ChannelConstraints, we only write the CSV // delay. - LocalChanCfg channeldb.ChannelConfig + LocalChanCfg chanstate.ChannelConfig // RemoteChanCfg is the remote channel confirmation. We store this as // well since we'll need some of their keys to re-derive things like @@ -178,7 +178,7 @@ type Single struct { // // NOTE: Of the items in the ChannelConstraints, we only write the CSV // delay. - RemoteChanCfg channeldb.ChannelConfig + RemoteChanCfg chanstate.ChannelConfig // ShaChainRootDesc describes how to derive the private key that was // used as the shachain root for this channel. @@ -234,7 +234,7 @@ type CloseTxInputs struct { // connect to the channel peer. If possible, we include the data needed to // produce a force close transaction from the most recent state using externally // provided private key. -func NewSingle(channel *channeldb.OpenChannel, +func NewSingle(channel *chanstate.OpenChannel, nodeAddrs []net.Addr) Single { var shaChainRootDesc keychain.KeyDescriptor diff --git a/chanbackup/single_test.go b/chanbackup/single_test.go index 881aedf8a..3468fb086 100644 --- a/chanbackup/single_test.go +++ b/chanbackup/single_test.go @@ -12,7 +12,7 @@ import ( "github.com/btcsuite/btcd/chainhash/v2" "github.com/btcsuite/btcd/wire/v2" "github.com/davecgh/go-spew/spew" - "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnencrypt" @@ -135,7 +135,7 @@ func assertSingleEqual(t *testing.T, a, b Single) { } } -func genRandomOpenChannelShell() (*channeldb.OpenChannel, error) { +func genRandomOpenChannelShell() (*chanstate.OpenChannel, error) { var testPriv [32]byte if _, err := rand.Read(testPriv[:]); err != nil { return nil, err @@ -162,11 +162,11 @@ func genRandomOpenChannelShell() (*channeldb.OpenChannel, error) { isInitiator = true } - chanType := channeldb.ChannelType(rand.Intn(1 << 12)) + chanType := chanstate.ChannelType(rand.Intn(1 << 12)) - localCfg := channeldb.ChannelConfig{ - ChannelStateBounds: channeldb.ChannelStateBounds{}, - CommitmentParams: channeldb.CommitmentParams{ + localCfg := chanstate.ChannelConfig{ + ChannelStateBounds: chanstate.ChannelStateBounds{}, + CommitmentParams: chanstate.CommitmentParams{ CsvDelay: uint16(rand.Int63()), }, MultiSigKey: keychain.KeyDescriptor{ @@ -201,8 +201,8 @@ func genRandomOpenChannelShell() (*channeldb.OpenChannel, error) { }, } - remoteCfg := channeldb.ChannelConfig{ - CommitmentParams: channeldb.CommitmentParams{ + remoteCfg := chanstate.ChannelConfig{ + CommitmentParams: chanstate.CommitmentParams{ CsvDelay: uint16(rand.Int63()), }, MultiSigKey: keychain.KeyDescriptor{ @@ -222,14 +222,14 @@ func genRandomOpenChannelShell() (*channeldb.OpenChannel, error) { }, } - var localCommit channeldb.ChannelCommitment + var localCommit chanstate.ChannelCommitment if chanType.IsTaproot() { var commitSig [64]byte if _, err := rand.Read(commitSig[:]); err != nil { return nil, err } - localCommit = channeldb.ChannelCommitment{ + localCommit = chanstate.ChannelCommitment{ CommitTx: sampleCommitTx, CommitSig: commitSig[:], CommitHeight: rand.Uint64(), @@ -245,7 +245,7 @@ func genRandomOpenChannelShell() (*channeldb.OpenChannel, error) { tapscriptRootOption = fn.Some(tapscriptRoot) } - return &channeldb.OpenChannel{ + return &chanstate.OpenChannel{ ChainHash: chainHash, ChanType: chanType, IsInitiator: isInitiator, From 897f2ce54c5fe501e63c35a2c920bd260156ffe3 Mon Sep 17 00:00:00 2001 From: ziggie Date: Fri, 15 May 2026 13:39:02 -0300 Subject: [PATCH 08/24] chanstate: use channel type in backup notifier Move the root backup notifier adapter to chanstate.OpenChannel. The adapter still depends on channeldb for address sourcing and close type handling, but the new-channel payload it forwards into chanbackup now matches the chanstate-owned backup interfaces. --- channel_notifier.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/channel_notifier.go b/channel_notifier.go index 360fb6c58..c700e7ba6 100644 --- a/channel_notifier.go +++ b/channel_notifier.go @@ -8,6 +8,7 @@ import ( "github.com/lightningnetwork/lnd/chanbackup" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channelnotifier" + "github.com/lightningnetwork/lnd/chanstate" ) // channelNotifier is an implementation of the chanbackup.ChannelNotifier @@ -46,7 +47,7 @@ func (c *channelNotifier) SubscribeChans(ctx context.Context, // sendChanOpenUpdate is a closure that sends a ChannelEvent to the // chanUpdates channel to inform subscribers about new pending or // confirmed channels. - sendChanOpenUpdate := func(newOrPendingChan *channeldb.OpenChannel) { + sendChanOpenUpdate := func(newOrPendingChan *chanstate.OpenChannel) { _, nodeAddrs, err := c.addrs.AddrsForNode( ctx, newOrPendingChan.IdentityPub, ) From 8a215d9e2a984913c2ef23ac227d819454080d4c Mon Sep 17 00:00:00 2001 From: ziggie Date: Fri, 15 May 2026 13:40:31 -0300 Subject: [PATCH 09/24] chanstate: use channel type in wallet rpc Move the waiting-close channel helper to chanstate.OpenChannel. The helper consumes channel state returned by the store interface, so it should not spell the channeldb compatibility alias. Other database errors and APIs in the wallet RPC server remain on channeldb. --- lnrpc/walletrpc/walletkit_server.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lnrpc/walletrpc/walletkit_server.go b/lnrpc/walletrpc/walletkit_server.go index 456fb2046..8480f62d0 100644 --- a/lnrpc/walletrpc/walletkit_server.go +++ b/lnrpc/walletrpc/walletkit_server.go @@ -33,6 +33,7 @@ import ( "github.com/btcsuite/btcwallet/wtxmgr" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/contractcourt" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/input" @@ -1270,7 +1271,7 @@ func (w *WalletKit) BumpFee(ctx context.Context, // getWaitingCloseChannel returns the waiting close channel in case it does // exist in the underlying channel state database. func (w *WalletKit) getWaitingCloseChannel( - chanPoint wire.OutPoint) (*channeldb.OpenChannel, error) { + chanPoint wire.OutPoint) (*chanstate.OpenChannel, error) { // Fetch all channels, which still have their commitment transaction not // confirmed (waiting close channels). @@ -1279,7 +1280,7 @@ func (w *WalletKit) getWaitingCloseChannel( return nil, err } - channel := fn.Find(chans, func(c *channeldb.OpenChannel) bool { + channel := fn.Find(chans, func(c *chanstate.OpenChannel) bool { return c.FundingOutpoint == chanPoint }) From 3724915820d7e799bd388d4507656702244b299a Mon Sep 17 00:00:00 2001 From: ziggie Date: Fri, 15 May 2026 13:46:57 -0300 Subject: [PATCH 10/24] chanstate: use channel types in switch config Move the htlcswitch channel fetch callbacks to chanstate types. The switch still depends on channeldb for its KV circuit storage and forwarding package access. This commit only moves channel-state payloads at the switch and circuit-map boundaries. --- htlcswitch/circuit_map.go | 6 +++--- htlcswitch/circuit_map_test.go | 7 ++++--- htlcswitch/mock.go | 3 ++- htlcswitch/switch.go | 7 ++++--- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/htlcswitch/circuit_map.go b/htlcswitch/circuit_map.go index 15d4b5ffc..299abffcd 100644 --- a/htlcswitch/circuit_map.go +++ b/htlcswitch/circuit_map.go @@ -6,7 +6,7 @@ import ( "fmt" "sync" - "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/htlcswitch/hop" "github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/lnutils" @@ -203,12 +203,12 @@ type CircuitMapConfig struct { // FetchAllOpenChannels is a function that fetches all currently open // channels from the channel database. - FetchAllOpenChannels func() ([]*channeldb.OpenChannel, error) + FetchAllOpenChannels func() ([]*chanstate.OpenChannel, error) // FetchClosedChannels is a function that fetches all closed channels // from the channel database. FetchClosedChannels func( - pendingOnly bool) ([]*channeldb.ChannelCloseSummary, error) + pendingOnly bool) ([]*chanstate.ChannelCloseSummary, error) // ExtractErrorEncrypter derives the shared secret used to encrypt // errors from the obfuscator's ephemeral public key. diff --git a/htlcswitch/circuit_map_test.go b/htlcswitch/circuit_map_test.go index c4b6e442f..7b93fb0be 100644 --- a/htlcswitch/circuit_map_test.go +++ b/htlcswitch/circuit_map_test.go @@ -9,6 +9,7 @@ import ( "github.com/btcsuite/btcd/btcutil/v2" "github.com/btcsuite/btcd/wire/v2" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/lnwire" @@ -362,7 +363,7 @@ func createTestCloseChannelSummery(tx kvdb.RwTx, isPending bool, } outputPoint := wire.OutPoint{Hash: hash1, Index: 1} - ccs := &channeldb.ChannelCloseSummary{ + ccs := &chanstate.ChannelCloseSummary{ ChanPoint: outputPoint, ShortChanID: chanID, ChainHash: hash1, @@ -371,7 +372,7 @@ func createTestCloseChannelSummery(tx kvdb.RwTx, isPending bool, RemotePub: testEphemeralKey, Capacity: btcutil.Amount(10000), SettledBalance: btcutil.Amount(50000), - CloseType: channeldb.RemoteForceClose, + CloseType: chanstate.RemoteForceClose, IsPending: isPending, } var b bytes.Buffer @@ -389,7 +390,7 @@ func createTestCloseChannelSummery(tx kvdb.RwTx, isPending bool, func serializeChannelCloseSummary( w io.Writer, - cs *channeldb.ChannelCloseSummary) error { + cs *chanstate.ChannelCloseSummary) error { err := channeldb.WriteElements( w, diff --git a/htlcswitch/mock.go b/htlcswitch/mock.go index e9be926fd..a3079e696 100644 --- a/htlcswitch/mock.go +++ b/htlcswitch/mock.go @@ -22,6 +22,7 @@ import ( sphinx "github.com/lightningnetwork/lightning-onion" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/clock" "github.com/lightningnetwork/lnd/contractcourt" "github.com/lightningnetwork/lnd/fn/v2" @@ -74,7 +75,7 @@ func (m *mockPreimageCache) AddPreimages(preimages ...lntypes.Preimage) error { } func (m *mockPreimageCache) SubscribeUpdates( - chanID lnwire.ShortChannelID, htlc *channeldb.HTLC, + chanID lnwire.ShortChannelID, htlc *chanstate.HTLC, payload *hop.Payload, nextHopOnionBlob []byte) (*contractcourt.WitnessSubscription, error) { diff --git a/htlcswitch/switch.go b/htlcswitch/switch.go index b86d7be71..2c0bbddb6 100644 --- a/htlcswitch/switch.go +++ b/htlcswitch/switch.go @@ -15,6 +15,7 @@ import ( "github.com/btcsuite/btcd/wire/v2" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/clock" "github.com/lightningnetwork/lnd/contractcourt" "github.com/lightningnetwork/lnd/fn/v2" @@ -150,16 +151,16 @@ type Config struct { // FetchAllOpenChannels is a function that fetches all currently open // channels from the channel database. - FetchAllOpenChannels func() ([]*channeldb.OpenChannel, error) + FetchAllOpenChannels func() ([]*chanstate.OpenChannel, error) // FetchAllChannels is a function that fetches all pending open, open, // and waiting close channels from the database. - FetchAllChannels func() ([]*channeldb.OpenChannel, error) + FetchAllChannels func() ([]*chanstate.OpenChannel, error) // FetchClosedChannels is a function that fetches all closed channels // from the channel database. FetchClosedChannels func( - pendingOnly bool) ([]*channeldb.ChannelCloseSummary, error) + pendingOnly bool) ([]*chanstate.ChannelCloseSummary, error) // SwitchPackager provides access to the forwarding packages of all // active channels. This gives the switch the ability to read arbitrary From a546c7591d67aa01b300cb7639508a4eb41bffd3 Mon Sep 17 00:00:00 2001 From: ziggie Date: Fri, 15 May 2026 14:00:19 -0300 Subject: [PATCH 11/24] chanstate: use channel types in local chans Move the local channel manager fetch boundary to chanstate types. The manager still imports channeldb for the concrete not-found error, but channel state values and config constraints now use the chanstate package directly. --- routing/localchans/manager.go | 9 +++++---- routing/localchans/manager_test.go | 26 ++++++++++++++------------ 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/routing/localchans/manager.go b/routing/localchans/manager.go index 0eda60411..1c9f906bb 100644 --- a/routing/localchans/manager.go +++ b/routing/localchans/manager.go @@ -11,6 +11,7 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/wire/v2" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/discovery" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/funding" @@ -48,7 +49,7 @@ type Manager struct { // FetchChannel is used to query local channel parameters. Optionally an // existing db tx can be supplied. - FetchChannel func(chanPoint wire.OutPoint) (*channeldb.OpenChannel, + FetchChannel func(chanPoint wire.OutPoint) (*chanstate.OpenChannel, error) // AddEdge is used to add edge/channel to the topology of the router. @@ -247,7 +248,7 @@ func (r *Manager) UpdatePolicy(ctx context.Context, } func (r *Manager) createMissingEdge(ctx context.Context, - channel *channeldb.OpenChannel, + channel *chanstate.OpenChannel, newSchema routing.ChannelPolicy) (*models.ChannelEdgeInfo, *models.ChannelEdgePolicy, *lnrpc.FailedUpdate) { @@ -294,7 +295,7 @@ func (r *Manager) createMissingEdge(ctx context.Context, } // createEdge recreates an edge and policy from an open channel in-memory. -func (r *Manager) createEdge(channel *channeldb.OpenChannel, +func (r *Manager) createEdge(channel *chanstate.OpenChannel, timestamp time.Time) (*models.ChannelEdgeInfo, *models.ChannelEdgePolicy, error) { @@ -475,7 +476,7 @@ func (r *Manager) updateEdge(chanPoint wire.OutPoint, // getHtlcAmtLimits retrieves the negotiated channel min and max htlc amount // constraints. -func (r *Manager) getHtlcAmtLimits(ch *channeldb.OpenChannel) ( +func (r *Manager) getHtlcAmtLimits(ch *chanstate.OpenChannel) ( lnwire.MilliSatoshi, lnwire.MilliSatoshi, error) { // The max htlc policy field must be less than or equal to the channel diff --git a/routing/localchans/manager_test.go b/routing/localchans/manager_test.go index a23499f8a..4f0bc78ae 100644 --- a/routing/localchans/manager_test.go +++ b/routing/localchans/manager_test.go @@ -12,6 +12,7 @@ import ( "github.com/btcsuite/btcd/chainhash/v2" "github.com/btcsuite/btcd/wire/v2" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/discovery" "github.com/lightningnetwork/lnd/funding" "github.com/lightningnetwork/lnd/graph/db/models" @@ -138,28 +139,29 @@ func TestManager(t *testing.T) { return nil } - fetchChannel := func(chanPoint wire.OutPoint) (*channeldb.OpenChannel, + fetchChannel := func(chanPoint wire.OutPoint) (*chanstate.OpenChannel, error) { if chanPoint == chanPointMissing { - return &channeldb.OpenChannel{}, channeldb.ErrChannelNotFound + return &chanstate.OpenChannel{}, + channeldb.ErrChannelNotFound } - bounds := channeldb.ChannelStateBounds{ + bounds := chanstate.ChannelStateBounds{ MaxPendingAmount: maxPendingAmount, MinHTLC: minHTLC, } - return &channeldb.OpenChannel{ + return &chanstate.OpenChannel{ FundingOutpoint: chanPointValid, IdentityPub: remotepub, - LocalChanCfg: channeldb.ChannelConfig{ + LocalChanCfg: chanstate.ChannelConfig{ ChannelStateBounds: bounds, MultiSigKey: keychain.KeyDescriptor{ PubKey: localMultisigKey, }, }, - RemoteChanCfg: channeldb.ChannelConfig{ + RemoteChanCfg: chanstate.ChannelConfig{ ChannelStateBounds: bounds, MultiSigKey: keychain.KeyDescriptor{ PubKey: remoteMultisigKey, @@ -413,14 +415,14 @@ func TestCreateEdgeLower(t *testing.T) { TimeLockDelta: 7, } - channel := &channeldb.OpenChannel{ + channel := &chanstate.OpenChannel{ IdentityPub: remotepub, - LocalChanCfg: channeldb.ChannelConfig{ + LocalChanCfg: chanstate.ChannelConfig{ MultiSigKey: keychain.KeyDescriptor{ PubKey: localMultisigKey, }, }, - RemoteChanCfg: channeldb.ChannelConfig{ + RemoteChanCfg: chanstate.ChannelConfig{ MultiSigKey: keychain.KeyDescriptor{ PubKey: remoteMultisigKey, }, @@ -504,14 +506,14 @@ func TestCreateEdgeHigher(t *testing.T) { TimeLockDelta: 7, } - channel := &channeldb.OpenChannel{ + channel := &chanstate.OpenChannel{ IdentityPub: remotepub, - LocalChanCfg: channeldb.ChannelConfig{ + LocalChanCfg: chanstate.ChannelConfig{ MultiSigKey: keychain.KeyDescriptor{ PubKey: localMultisigKey, }, }, - RemoteChanCfg: channeldb.ChannelConfig{ + RemoteChanCfg: chanstate.ChannelConfig{ MultiSigKey: keychain.KeyDescriptor{ PubKey: remoteMultisigKey, }, From 7bd24477575fe379f9d91fc42fa3ff34fa29ee3a Mon Sep 17 00:00:00 2001 From: ziggie Date: Fri, 15 May 2026 14:05:12 -0300 Subject: [PATCH 12/24] chanstate: use channel type in gossiper Move the gossiper channel lookup callback to chanstate.OpenChannel. Discovery still depends on channeldb for waiting-proof persistence. This commit only removes the channeldb compatibility alias from the channel state lookup boundary. --- discovery/gossiper.go | 3 ++- discovery/gossiper_test.go | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/discovery/gossiper.go b/discovery/gossiper.go index 615dd7c4e..7f0874216 100644 --- a/discovery/gossiper.go +++ b/discovery/gossiper.go @@ -26,6 +26,7 @@ import ( "github.com/lightningnetwork/lnd/batch" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/graph" graphdb "github.com/lightningnetwork/lnd/graph/db" @@ -384,7 +385,7 @@ type Config struct { // FindChannel allows the gossiper to find a channel that we're party // to without iterating over the entire set of open channels. FindChannel func(node *btcec.PublicKey, chanID lnwire.ChannelID) ( - *channeldb.OpenChannel, error) + *chanstate.OpenChannel, error) // IsStillZombieChannel returns true if the channel described by info // should still be considered a zombie. diff --git a/discovery/gossiper_test.go b/discovery/gossiper_test.go index f9151ed09..eb045e8ed 100644 --- a/discovery/gossiper_test.go +++ b/discovery/gossiper_test.go @@ -27,6 +27,7 @@ import ( "github.com/lightningnetwork/lnd/batch" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/graph" graphdb "github.com/lightningnetwork/lnd/graph/db" "github.com/lightningnetwork/lnd/graph/db/models" @@ -889,7 +890,7 @@ func (ctx *testCtx) createChannelAnnouncement(blockHeight uint32, key1, } func mockFindChannel(node *btcec.PublicKey, chanID lnwire.ChannelID) ( - *channeldb.OpenChannel, error) { + *chanstate.OpenChannel, error) { return nil, nil } From 4bb78770ce94a8421c1aa64aa40532fc50cc2b8b Mon Sep 17 00:00:00 2001 From: ziggie Date: Fri, 15 May 2026 14:11:24 -0300 Subject: [PATCH 13/24] chanstate: use channel types in watchtower Move watchtower blob and client channel-type boundaries to chanstate. The wtclient manager still imports channeldb for closed-channel lookup errors, but the channel type and close-summary payloads now use the channel state package directly. --- watchtower/blob/type.go | 6 +++--- .../wtclient/backup_task_internal_test.go | 18 +++++++++--------- watchtower/wtclient/client.go | 4 ++-- watchtower/wtclient/client_test.go | 13 +++++++------ watchtower/wtclient/manager.go | 7 ++++--- 5 files changed, 25 insertions(+), 23 deletions(-) diff --git a/watchtower/blob/type.go b/watchtower/blob/type.go index 00415afc9..df04769ea 100644 --- a/watchtower/blob/type.go +++ b/watchtower/blob/type.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" ) // Flag represents a specify option that can be present in a Type. @@ -97,7 +97,7 @@ const ( // TypeFromChannel returns the appropriate blob Type for the given channel // type. -func TypeFromChannel(chanType channeldb.ChannelType) Type { +func TypeFromChannel(chanType chanstate.ChannelType) Type { switch { case chanType.IsTaprootFinal(): return TypeAltruistTaprootFinalCommit @@ -130,7 +130,7 @@ func (t Type) Identifier() (string, error) { // CommitmentType returns the appropriate CommitmentType for the given blob Type // and channel type. -func (t Type) CommitmentType(chanType *channeldb.ChannelType) (CommitmentType, +func (t Type) CommitmentType(chanType *chanstate.ChannelType) (CommitmentType, error) { switch { diff --git a/watchtower/wtclient/backup_task_internal_test.go b/watchtower/wtclient/backup_task_internal_test.go index 1fa16058f..5f725a8d5 100644 --- a/watchtower/wtclient/backup_task_internal_test.go +++ b/watchtower/wtclient/backup_task_internal_test.go @@ -10,7 +10,7 @@ import ( "github.com/btcsuite/btcd/chaincfg/v2" "github.com/btcsuite/btcd/txscript/v2" "github.com/btcsuite/btcd/wire/v2" - "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" @@ -65,7 +65,7 @@ type backupTaskTest struct { bindErr error expSweepScript []byte signer input.Signer - chanType channeldb.ChannelType + chanType chanstate.ChannelType commitType blob.CommitmentType } @@ -85,7 +85,7 @@ func genTaskTest( expSweepAmt int64, expRewardAmt int64, bindErr error, - chanType channeldb.ChannelType) backupTaskTest { + chanType chanstate.ChannelType) backupTaskTest { // Set the anchor or taproot flag in the blob type if the session needs // to support anchor or taproot channels. @@ -331,11 +331,11 @@ var ( func TestBackupTask(t *testing.T) { t.Parallel() - chanTypes := []channeldb.ChannelType{ - channeldb.SingleFunderBit, - channeldb.SingleFunderTweaklessBit, - channeldb.AnchorOutputsBit, - channeldb.SimpleTaprootFeatureBit, + chanTypes := []chanstate.ChannelType{ + chanstate.SingleFunderBit, + chanstate.SingleFunderTweaklessBit, + chanstate.AnchorOutputsBit, + chanstate.SimpleTaprootFeatureBit, } var backupTaskTests []backupTaskTest @@ -573,7 +573,7 @@ func testBackupTask(t *testing.T, test backupTaskTest) { // getBreachInfo is a helper closure that returns the breach retribution // info and channel type for the given channel and commit height. getBreachInfo := func(id lnwire.ChannelID, commitHeight uint64) ( - *lnwallet.BreachRetribution, channeldb.ChannelType, error) { + *lnwallet.BreachRetribution, chanstate.ChannelType, error) { return test.breachInfo, test.chanType, nil } diff --git a/watchtower/wtclient/client.go b/watchtower/wtclient/client.go index f8f3d3e61..41edb5988 100644 --- a/watchtower/wtclient/client.go +++ b/watchtower/wtclient/client.go @@ -13,7 +13,7 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btclog/v2" - "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" @@ -94,7 +94,7 @@ type RegisteredTower struct { // BreachRetribution from a channel ID and a commitment height. type BreachRetributionBuilder func(id lnwire.ChannelID, commitHeight uint64) (*lnwallet.BreachRetribution, - channeldb.ChannelType, error) + chanstate.ChannelType, error) // newTowerMsg is an internal message we'll use within the client to signal // that a new tower can be considered. diff --git a/watchtower/wtclient/client_test.go b/watchtower/wtclient/client_test.go index b44d3e2df..99c0e0a18 100644 --- a/watchtower/wtclient/client_test.go +++ b/watchtower/wtclient/client_test.go @@ -19,6 +19,7 @@ import ( "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channelnotifier" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" @@ -512,7 +513,7 @@ func newHarness(t *testing.T, cfg harnessCfg) *testHarness { }) fetchChannel := func(id lnwire.ChannelID) ( - *channeldb.ChannelCloseSummary, error) { + *chanstate.ChannelCloseSummary, error) { h.mu.Lock() defer h.mu.Unlock() @@ -522,7 +523,7 @@ func newHarness(t *testing.T, cfg harnessCfg) *testHarness { return nil, channeldb.ErrClosedChannelNotFound } - return &channeldb.ChannelCloseSummary{CloseHeight: height}, nil + return &chanstate.ChannelCloseSummary{CloseHeight: height}, nil } h.clientPolicy = cfg.policy @@ -550,11 +551,11 @@ func newHarness(t *testing.T, cfg harnessCfg) *testHarness { h.clientCfg.BuildBreachRetribution = func(id lnwire.ChannelID, commitHeight uint64) (*lnwallet.BreachRetribution, - channeldb.ChannelType, error) { + chanstate.ChannelType, error) { _, retribution := h.channelFromID(id).getState(commitHeight) - return retribution, channeldb.SimpleTaprootFeatureBit, nil + return retribution, chanstate.SimpleTaprootFeatureBit, nil } if !cfg.noServerStart { @@ -687,7 +688,7 @@ func (h *testHarness) closeChannel(id uint64, height uint32) { } h.channelEvents.sendUpdate(channelnotifier.ClosedChannelEvent{ - CloseSummary: &channeldb.ChannelCloseSummary{ + CloseSummary: &chanstate.ChannelCloseSummary{ ChanPoint: wire.OutPoint{ Hash: *chanPointHash, Index: 0, @@ -703,7 +704,7 @@ func (h *testHarness) registerChannel(id uint64) { chanID := chanIDFromInt(id) err := h.clientMgr.RegisterChannel( - chanID, channeldb.SimpleTaprootFeatureBit, + chanID, chanstate.SimpleTaprootFeatureBit, ) require.NoError(h.t, err) } diff --git a/watchtower/wtclient/manager.go b/watchtower/wtclient/manager.go index 03a344a15..6d9ae1849 100644 --- a/watchtower/wtclient/manager.go +++ b/watchtower/wtclient/manager.go @@ -12,6 +12,7 @@ import ( "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channelnotifier" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lnwire" @@ -67,7 +68,7 @@ type ClientManager interface { // parameters within the client. This should be called during link // startup to ensure that the client is able to support the link during // operation. - RegisterChannel(lnwire.ChannelID, channeldb.ChannelType) error + RegisterChannel(lnwire.ChannelID, chanstate.ChannelType) error // BackupState initiates a request to back up a particular revoked // state. If the method returns nil, the backup is guaranteed to be @@ -93,7 +94,7 @@ type Config struct { // channel. If the channel is not found or not yet closed then // channeldb.ErrClosedChannelNotFound will be returned. FetchClosedChannel func(cid lnwire.ChannelID) ( - *channeldb.ChannelCloseSummary, error) + *chanstate.ChannelCloseSummary, error) // ChainNotifier can be used to subscribe to block notifications. ChainNotifier chainntnfs.ChainNotifier @@ -597,7 +598,7 @@ func (m *Manager) Policy(blobType blob.Type) (wtpolicy.Policy, error) { // within the client. This should be called during link startup to ensure that // the client is able to support the link during operation. func (m *Manager) RegisterChannel(id lnwire.ChannelID, - chanType channeldb.ChannelType) error { + chanType chanstate.ChannelType) error { blobType := blob.TypeFromChannel(chanType) From da94a411c8e1fd136bbed58e4d5eb9cc8f22856f Mon Sep 17 00:00:00 2001 From: ziggie Date: Fri, 15 May 2026 14:15:16 -0300 Subject: [PATCH 14/24] chanstate: use channel types in link config Move htlcswitch link-facing channel state boundaries to chanstate. The link still uses channeldb for forwarding-package persistence, but channel update callbacks, tower registration, and dust helper channel types now use the channel state package directly. --- htlcswitch/interfaces.go | 3 ++- htlcswitch/link.go | 5 +++-- htlcswitch/link_test.go | 6 +++--- htlcswitch/mailbox_test.go | 3 ++- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/htlcswitch/interfaces.go b/htlcswitch/interfaces.go index 53d1c599e..ef62eb71d 100644 --- a/htlcswitch/interfaces.go +++ b/htlcswitch/interfaces.go @@ -6,6 +6,7 @@ import ( "github.com/btcsuite/btcd/btcutil/v2" "github.com/btcsuite/btcd/wire/v2" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/graph/db/models" "github.com/lightningnetwork/lnd/invoices" @@ -354,7 +355,7 @@ type TowerClient interface { // parameters within the client. This should be called during link // startup to ensure that the client is able to support the link during // operation. - RegisterChannel(lnwire.ChannelID, channeldb.ChannelType) error + RegisterChannel(lnwire.ChannelID, chanstate.ChannelType) error // BackupState initiates a request to back up a particular revoked // state. If the method returns nil, the backup is guaranteed to be diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 0b4516da9..9e3adf0bb 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -16,6 +16,7 @@ import ( "github.com/btcsuite/btcd/wire/v2" "github.com/btcsuite/btclog/v2" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/contractcourt" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/graph/db/models" @@ -258,7 +259,7 @@ type ChannelLinkConfig struct { // NotifyChannelUpdate allows the link to tell the ChannelNotifier when // a channel's state has been updated. - NotifyChannelUpdate func(*channeldb.OpenChannel) + NotifyChannelUpdate func(*chanstate.OpenChannel) // HtlcNotifier is an instance of a htlcNotifier which we will pipe htlc // events through. @@ -2372,7 +2373,7 @@ type dustClosure func(feerate chainfee.SatPerKWeight, incoming bool, whoseCommit lntypes.ChannelParty, amt btcutil.Amount) bool // dustHelper is used to construct the dustClosure. -func dustHelper(chantype channeldb.ChannelType, localDustLimit, +func dustHelper(chantype chanstate.ChannelType, localDustLimit, remoteDustLimit btcutil.Amount) dustClosure { isDust := func(feerate chainfee.SatPerKWeight, incoming bool, diff --git a/htlcswitch/link_test.go b/htlcswitch/link_test.go index d73a355c0..287608975 100644 --- a/htlcswitch/link_test.go +++ b/htlcswitch/link_test.go @@ -2240,7 +2240,7 @@ func newSingleLinkTestHarness(t *testing.T, chanAmt, MaxFeeAllocation: DefaultMaxLinkFeeAllocation, NotifyActiveLink: func(wire.OutPoint) {}, NotifyActiveChannel: func(wire.OutPoint) {}, - NotifyChannelUpdate: func(*channeldb.OpenChannel) {}, + NotifyChannelUpdate: func(*cstate.OpenChannel) {}, NotifyInactiveChannel: func(wire.OutPoint) {}, NotifyInactiveLinkEvent: func(wire.OutPoint) {}, HtlcNotifier: aliceSwitch.cfg.HtlcNotifier, @@ -4931,7 +4931,7 @@ func (h *persistentLinkHarness) restartLink( NotifyActiveChannel: func(wire.OutPoint) {}, NotifyInactiveChannel: func(wire.OutPoint) {}, NotifyInactiveLinkEvent: func(wire.OutPoint) {}, - NotifyChannelUpdate: func(*channeldb.OpenChannel) {}, + NotifyChannelUpdate: func(*cstate.OpenChannel) {}, HtlcNotifier: h.hSwitch.cfg.HtlcNotifier, SyncStates: syncStates, GetAliases: getAliases, @@ -5779,7 +5779,7 @@ type mockFailLoadFwdPkgStore struct { // failure handling while all other store methods delegate to the embedded // store. func (m *mockFailLoadFwdPkgStore) LoadFwdPkgs( - *channeldb.OpenChannel) ([]*channeldb.FwdPkg, error) { + *cstate.OpenChannel) ([]*channeldb.FwdPkg, error) { return nil, fmt.Errorf("failing LoadFwdPkgs") } diff --git a/htlcswitch/mailbox_test.go b/htlcswitch/mailbox_test.go index af4b192e7..b732694a6 100644 --- a/htlcswitch/mailbox_test.go +++ b/htlcswitch/mailbox_test.go @@ -9,6 +9,7 @@ import ( "github.com/btcsuite/btcd/btcutil/v2" "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/clock" "github.com/lightningnetwork/lnd/lnmock" "github.com/lightningnetwork/lnd/lnwallet/chainfee" @@ -586,7 +587,7 @@ func TestMailBoxDustHandling(t *testing.T) { }) } -func testMailBoxDust(t *testing.T, chantype channeldb.ChannelType) { +func testMailBoxDust(t *testing.T, chantype chanstate.ChannelType) { t.Parallel() ctx := newMailboxContext(t, time.Now(), testExpiry) From 03daaad4fcc943c5e04664ad99ebaf4d587678f4 Mon Sep 17 00:00:00 2001 From: ziggie Date: Fri, 15 May 2026 21:07:21 -0300 Subject: [PATCH 15/24] funding: use channel state open channel Update the funding manager callback and helper signatures to depend on the chanstate OpenChannel type instead of the channeldb alias. The funding manager already receives channel persistence through the chanstate Store interface, so this keeps its open-channel boundary aligned with the backend-independent package. --- funding/manager.go | 46 ++++++++++++++++++++--------------------- funding/manager_test.go | 9 ++++---- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/funding/manager.go b/funding/manager.go index 4efbb101a..40ac99aba 100644 --- a/funding/manager.go +++ b/funding/manager.go @@ -425,7 +425,7 @@ type Config struct { // channel ID. Providing the node's public key is an optimization that // prevents deserializing and scanning through all possible channels. FindChannel func(node *btcec.PublicKey, - chanID lnwire.ChannelID) (*channeldb.OpenChannel, error) + chanID lnwire.ChannelID) (*chanstate.OpenChannel, error) // TempChanIDSeed is a cryptographically random string of bytes that's // used as a seed to generate pending channel ID's. @@ -475,7 +475,7 @@ type Config struct { // the channel to the ChainArbitrator so it can watch for any on-chain // events related to the channel. We also provide the public key of the // node we're establishing a channel with for reconnection purposes. - WatchNewChannel func(*channeldb.OpenChannel, *btcec.PublicKey) error + WatchNewChannel func(*chanstate.OpenChannel, *btcec.PublicKey) error // ReportShortChanID allows the funding manager to report the confirmed // short channel ID of a formerly pending zero-conf channel to outside @@ -525,7 +525,7 @@ type Config struct { // NotifyPendingOpenChannelEvent informs the ChannelNotifier when // channels enter a pending state. NotifyPendingOpenChannelEvent func(wire.OutPoint, - *channeldb.OpenChannel, *btcec.PublicKey) + *chanstate.OpenChannel, *btcec.PublicKey) // NotifyFundingTimeout informs the ChannelNotifier when a pending-open // channel times out because the funding transaction hasn't confirmed. @@ -811,7 +811,7 @@ func (f *Manager) Stop() error { // rebroadcastFundingTx publishes the funding tx on startup for each // unconfirmed channel. -func (f *Manager) rebroadcastFundingTx(c *channeldb.OpenChannel) { +func (f *Manager) rebroadcastFundingTx(c *chanstate.OpenChannel) { var fundingTxBuf bytes.Buffer err := c.FundingTxn.Serialize(&fundingTxBuf) if err != nil { @@ -1090,7 +1090,7 @@ func (f *Manager) reservationCoordinator() { // OpenStatusUpdates. // // NOTE: This MUST be run as a goroutine. -func (f *Manager) advanceFundingState(channel *channeldb.OpenChannel, +func (f *Manager) advanceFundingState(channel *chanstate.OpenChannel, pendingChanID PendingChanID, updateChan chan<- *lnrpc.OpenStatusUpdate) { @@ -1171,7 +1171,7 @@ func (f *Manager) advanceFundingState(channel *channeldb.OpenChannel, // machine. This method is synchronous and the new channel opening state will // have been written to the database when it successfully returns. The // updateChan can be set non-nil to get OpenStatusUpdates. -func (f *Manager) stateStep(channel *channeldb.OpenChannel, +func (f *Manager) stateStep(channel *chanstate.OpenChannel, lnChannel *lnwallet.LightningChannel, shortChanID *lnwire.ShortChannelID, pendingChanID PendingChanID, channelState channelOpeningState, @@ -1296,7 +1296,7 @@ func (f *Manager) stateStep(channel *channeldb.OpenChannel, // advancePendingChannelState waits for a pending channel's funding tx to // confirm, and marks it open in the database when that happens. -func (f *Manager) advancePendingChannelState(channel *channeldb.OpenChannel, +func (f *Manager) advancePendingChannelState(channel *chanstate.OpenChannel, pendingChanID PendingChanID) error { if channel.IsZeroConf() { @@ -2962,7 +2962,7 @@ type confirmedChannel struct { // an ErrConfirmationTimeout. It is used to clean-up channel state and mark the // channel as closed. The error is only returned for the responder of the // channel flow. -func (f *Manager) fundingTimeout(c *channeldb.OpenChannel, +func (f *Manager) fundingTimeout(c *chanstate.OpenChannel, pendingID PendingChanID) error { // We'll get a timeout if the number of blocks mined since the channel @@ -3039,7 +3039,7 @@ func (f *Manager) fundingTimeout(c *channeldb.OpenChannel, // funding broadcast height. In case of confirmation, the short channel ID of // the channel and the funding transaction will be returned. func (f *Manager) waitForFundingWithTimeout( - ch *channeldb.OpenChannel) (*confirmedChannel, error) { + ch *chanstate.OpenChannel) (*confirmedChannel, error) { confChan := make(chan *confirmedChannel) timeoutChan := make(chan error, 1) @@ -3080,7 +3080,7 @@ func (f *Manager) waitForFundingWithTimeout( // MakeFundingScript re-creates the funding script for the funding transaction // of the target channel. -func MakeFundingScript(channel *channeldb.OpenChannel) ([]byte, error) { +func MakeFundingScript(channel *chanstate.OpenChannel) ([]byte, error) { localKey := channel.LocalChanCfg.MultiSigKey.PubKey remoteKey := channel.RemoteChanCfg.MultiSigKey.PubKey @@ -3118,7 +3118,7 @@ func MakeFundingScript(channel *channeldb.OpenChannel) ([]byte, error) { // // NOTE: This MUST be run as a goroutine. func (f *Manager) waitForFundingConfirmation( - completeChan *channeldb.OpenChannel, cancelChan <-chan struct{}, + completeChan *chanstate.OpenChannel, cancelChan <-chan struct{}, confChan chan<- *confirmedChannel) { defer f.wg.Done() @@ -3283,7 +3283,7 @@ func (f *Manager) waitForFundingConfirmation( // based on the confirmation details and sends this information, along with the // funding transaction, to the provided confirmation channel. func (f *Manager) handleConfirmation(confDetails *chainntnfs.TxConfirmation, - completeChan *channeldb.OpenChannel, + completeChan *chanstate.OpenChannel, confChan chan<- *confirmedChannel) error { fundingPoint := completeChan.FundingOutpoint @@ -3318,7 +3318,7 @@ func (f *Manager) handleConfirmation(confDetails *chainntnfs.TxConfirmation, // // NOTE: timeoutChan MUST be buffered. // NOTE: This MUST be run as a goroutine. -func (f *Manager) waitForTimeout(completeChan *channeldb.OpenChannel, +func (f *Manager) waitForTimeout(completeChan *chanstate.OpenChannel, cancelChan <-chan struct{}, timeoutChan chan<- error) { defer f.wg.Done() @@ -3390,7 +3390,7 @@ func (f *Manager) waitForTimeout(completeChan *channeldb.OpenChannel, // our short channel ID, which is known now that our funding transaction has // confirmed. We do not label transactions we did not publish, because our // wallet has no knowledge of them. -func (f *Manager) makeLabelForTx(c *channeldb.OpenChannel) { +func (f *Manager) makeLabelForTx(c *chanstate.OpenChannel) { if c.IsInitiator && c.ChanType.HasFundingTx() { shortChanID := c.ShortChanID() @@ -3416,7 +3416,7 @@ func (f *Manager) makeLabelForTx(c *channeldb.OpenChannel) { // decided short channel ID to the switch, and close the local discovery signal // for this channel. func (f *Manager) handleFundingConfirmation( - completeChan *channeldb.OpenChannel, + completeChan *chanstate.OpenChannel, confChannel *confirmedChannel) error { fundingPoint := completeChan.FundingOutpoint @@ -3495,7 +3495,7 @@ func (f *Manager) handleFundingConfirmation( // sendChannelReady creates and sends the channelReady message. // This should be called after the funding transaction has been confirmed, // and the channelState is 'markedOpen'. -func (f *Manager) sendChannelReady(completeChan *channeldb.OpenChannel, +func (f *Manager) sendChannelReady(completeChan *chanstate.OpenChannel, channel *lnwallet.LightningChannel) error { chanID := lnwire.NewChanIDFromOutPoint(completeChan.FundingOutpoint) @@ -3685,7 +3685,7 @@ func (f *Manager) receivedChannelReady(node *btcec.PublicKey, // extractAnnounceParams extracts the various channel announcement and update // parameters that will be needed to construct a ChannelAnnouncement and a // ChannelUpdate. -func (f *Manager) extractAnnounceParams(c *channeldb.OpenChannel) ( +func (f *Manager) extractAnnounceParams(c *chanstate.OpenChannel) ( lnwire.MilliSatoshi, lnwire.MilliSatoshi) { // We'll obtain the min HTLC value we can forward in our direction, as @@ -3744,7 +3744,7 @@ func mapGossipError(err error, msgType string) error { // The peerAlias is used for zero-conf channels to give the counter-party a // ChannelUpdate they understand. ourPolicy may be set for various // option-scid-alias channels to re-use the same policy. -func (f *Manager) addToGraph(completeChan *channeldb.OpenChannel, +func (f *Manager) addToGraph(completeChan *chanstate.OpenChannel, shortChanID *lnwire.ShortChannelID, peerAlias *lnwire.ShortChannelID, ourPolicy *models.ChannelEdgePolicy) error { @@ -3804,7 +3804,7 @@ func (f *Manager) addToGraph(completeChan *channeldb.OpenChannel, // 'addedToGraph') and the channel is ready to be used. This is the last // step in the channel opening process, and the opening state will be deleted // from the database if successful. -func (f *Manager) annAfterSixConfs(completeChan *channeldb.OpenChannel, +func (f *Manager) annAfterSixConfs(completeChan *chanstate.OpenChannel, shortChanID *lnwire.ShortChannelID) error { // If this channel is not meant to be announced to the greater network, @@ -3954,7 +3954,7 @@ func (f *Manager) annAfterSixConfs(completeChan *channeldb.OpenChannel, // waitForZeroConfChannel is called when the state is addedToGraph with // a zero-conf channel. This will wait for the real confirmation, add the // confirmed SCID to the router graph, and then announce after six confs. -func (f *Manager) waitForZeroConfChannel(c *channeldb.OpenChannel) error { +func (f *Manager) waitForZeroConfChannel(c *chanstate.OpenChannel) error { // First we'll check whether the channel is confirmed on-chain. If it // is already confirmed, the chainntnfs subsystem will return with the // confirmed tx. Otherwise, we'll wait here until confirmation occurs. @@ -4045,7 +4045,7 @@ func (f *Manager) waitForZeroConfChannel(c *channeldb.OpenChannel) error { // genFirstStateMusigNonce generates a nonces for the "first" local state. This // is the verification nonce for the state created for us after the initial // commitment transaction signed as part of the funding flow. -func genFirstStateMusigNonce(channel *channeldb.OpenChannel, +func genFirstStateMusigNonce(channel *chanstate.OpenChannel, ) (*musig2.Nonces, error) { musig2ShaChain, err := channeldb.DeriveMusig2Shachain( @@ -4420,7 +4420,7 @@ func (f *Manager) processChannelReady(peer lnpeer.Peer, // channelReady message, once the remote's channelReady is processed, the // channel is now active, thus we change its state to `addedToGraph` to // let the channel start handling routing. -func (f *Manager) handleChannelReadyReceived(channel *channeldb.OpenChannel, +func (f *Manager) handleChannelReadyReceived(channel *chanstate.OpenChannel, scid *lnwire.ShortChannelID, pendingChanID PendingChanID, updateChan chan<- *lnrpc.OpenStatusUpdate) error { @@ -4516,7 +4516,7 @@ func (f *Manager) handleChannelReadyReceived(channel *channeldb.OpenChannel, // policy set for the given channel. If we don't, we'll fall back to the default // values. func (f *Manager) ensureInitialForwardingPolicy(chanID lnwire.ChannelID, - channel *channeldb.OpenChannel) error { + channel *chanstate.OpenChannel) error { // Before we can add the channel to the peer, we'll need to ensure that // we have an initial forwarding policy set. This should always be the diff --git a/funding/manager_test.go b/funding/manager_test.go index 6e4b88814..ae7058c03 100644 --- a/funding/manager_test.go +++ b/funding/manager_test.go @@ -31,6 +31,7 @@ import ( acpt "github.com/lightningnetwork/lnd/chanacceptor" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channelnotifier" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/discovery" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/graph" @@ -250,7 +251,7 @@ func (m *mockChanEvent) NotifyOpenChannelEvent(outpoint wire.OutPoint, } func (m *mockChanEvent) NotifyPendingOpenChannelEvent(outpoint wire.OutPoint, - pendingChannel *channeldb.OpenChannel, + pendingChannel *chanstate.OpenChannel, remotePub *btcec.PublicKey) { m.pendingOpenEvent <- channelnotifier.PendingOpenChannelEvent{ @@ -499,7 +500,7 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey, }, TempChanIDSeed: chanIDSeed, FindChannel: func(node *btcec.PublicKey, - chanID lnwire.ChannelID) (*channeldb.OpenChannel, + chanID lnwire.ChannelID) (*chanstate.OpenChannel, error) { nodeChans, err := cdb.FetchOpenChannels(node) @@ -549,7 +550,7 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey, RequiredRemoteMaxHTLCs: func(chanAmt btcutil.Amount) uint16 { return uint16(input.MaxHTLCNumber / 2) }, - WatchNewChannel: func(*channeldb.OpenChannel, + WatchNewChannel: func(*chanstate.OpenChannel, *btcec.PublicKey) error { return nil @@ -5399,7 +5400,7 @@ func TestChannelReadyUnknownChannelID(t *testing.T) { cfg.FindChannel = func( node *btcec.PublicKey, chanID lnwire.ChannelID, - ) (*channeldb.OpenChannel, error) { + ) (*chanstate.OpenChannel, error) { findChannelCalls.Add(1) From 8e709678d9cb9f71735cf5b1b8b969eca8c74ae1 Mon Sep 17 00:00:00 2001 From: ziggie Date: Fri, 15 May 2026 21:08:03 -0300 Subject: [PATCH 16/24] server: use channel state open channel Update server callback wiring to use chanstate.OpenChannel at the funding manager boundary. This follows the funding package change and removes another consumer-facing dependency on the channeldb OpenChannel alias. --- server.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server.go b/server.go index bb6743e1f..a0312bb01 100644 --- a/server.go +++ b/server.go @@ -1661,7 +1661,7 @@ func newServer(ctx context.Context, cfg *Config, listenAddrs []net.Addr, } return delay }, - WatchNewChannel: func(channel *channeldb.OpenChannel, + WatchNewChannel: func(channel *chanstate.OpenChannel, peerKey *btcec.PublicKey) error { // First, we'll mark this new peer as a persistent peer @@ -3509,7 +3509,7 @@ func (s *server) createNewHiddenService(ctx context.Context) error { // optimization that is quicker than seeking for a channel given only the // ChannelID. func (s *server) findChannel(node *btcec.PublicKey, chanID lnwire.ChannelID) ( - *channeldb.OpenChannel, error) { + *chanstate.OpenChannel, error) { nodeChans, err := s.chanStateDB.FetchOpenChannels(node) if err != nil { @@ -4435,7 +4435,7 @@ func (s *server) notifyOpenChannelPeerEvent(op wire.OutPoint, // notifyPendingOpenChannelPeerEvent updates the access manager's maps and then // calls the channelNotifier's NotifyPendingOpenChannelEvent. func (s *server) notifyPendingOpenChannelPeerEvent(op wire.OutPoint, - pendingChan *channeldb.OpenChannel, remotePub *btcec.PublicKey) { + pendingChan *chanstate.OpenChannel, remotePub *btcec.PublicKey) { // Call newPendingOpenChan to update the access manager's maps for this // peer. From 4a34503ca5530f5dc9339270bece357f0ea098fc Mon Sep 17 00:00:00 2001 From: ziggie Date: Fri, 15 May 2026 21:08:48 -0300 Subject: [PATCH 17/24] rpcserver: use channel state open channel Update RPC helpers that format open channel data to accept chanstate.OpenChannel directly. These helpers only inspect channel state and do not need to name the channeldb OpenChannel alias. --- rpcserver.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/rpcserver.go b/rpcserver.go index 884dfa07a..52b682fbc 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -43,6 +43,7 @@ import ( "github.com/lightningnetwork/lnd/chanfitness" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channelnotifier" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/clock" "github.com/lightningnetwork/lnd/contractcourt" "github.com/lightningnetwork/lnd/discovery" @@ -4010,7 +4011,7 @@ type ( // 1. The current blockchain height // 2. The block height at which the funding transaction was first confirmed // 3. The total number of confirmations required for the channel. -func calcRemainingConfs(pendingChan *channeldb.OpenChannel, +func calcRemainingConfs(pendingChan *chanstate.OpenChannel, currentHeight uint32) uint32 { // If the funding transaction hasn't been confirmed yet, @@ -4315,7 +4316,7 @@ func (r *rpcServer) fetchWaitingCloseChannels( // getClosingTx is a helper closure that tries to find the closing tx of // a given waiting close channel. Notice that if the remote closes the // channel, we may not have the closing tx. - getClosingTx := func(c *channeldb.OpenChannel) (*wire.MsgTx, error) { + getClosingTx := func(c *chanstate.OpenChannel) (*wire.MsgTx, error) { var ( tx *wire.MsgTx err error @@ -4955,7 +4956,7 @@ func createChannelConstraint( // isPrivate evaluates the ChannelFlags of the db channel to determine if the // channel is private or not. -func isPrivate(dbChannel *channeldb.OpenChannel) bool { +func isPrivate(dbChannel *chanstate.OpenChannel) bool { if dbChannel == nil { return false } @@ -4964,7 +4965,7 @@ func isPrivate(dbChannel *channeldb.OpenChannel) bool { // encodeCustomChanData encodes the custom channel data for the open channel. // It encodes that data as a pair of var bytes blobs. -func encodeCustomChanData(lnChan *channeldb.OpenChannel) ([]byte, error) { +func encodeCustomChanData(lnChan *chanstate.OpenChannel) ([]byte, error) { customOpenChanData := lnChan.CustomBlob.UnwrapOr(nil) customLocalCommitData := lnChan.LocalCommitment.CustomBlob.UnwrapOr(nil) @@ -4995,7 +4996,7 @@ func encodeCustomChanData(lnChan *channeldb.OpenChannel) ([]byte, error) { // //nolint:funlen func createRPCOpenChannel(ctx context.Context, r *rpcServer, - dbChannel *channeldb.OpenChannel, + dbChannel *chanstate.OpenChannel, isActive, peerAliasLookup bool) (*lnrpc.Channel, error) { nodePub := dbChannel.IdentityPub From 05bea1dd69a9261d78ea594ed2bb2e0842be51bc Mon Sep 17 00:00:00 2001 From: ziggie Date: Fri, 15 May 2026 21:09:46 -0300 Subject: [PATCH 18/24] chanrestore: use channel state open channel Build restored channel shells with chanstate.OpenChannel instead of the channeldb alias. The restored shell is channel state data, so this keeps the constructor aligned with the package that now owns the type. --- chanrestore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chanrestore.go b/chanrestore.go index d97e50f8b..dfa1ea5f5 100644 --- a/chanrestore.go +++ b/chanrestore.go @@ -187,7 +187,7 @@ func (c *chanDBRestorer) openChannelShell(backup chanbackup.Single) ( chanShell := channeldb.ChannelShell{ NodeAddrs: backup.Addresses, - Chan: &channeldb.OpenChannel{ + Chan: &chanstate.OpenChannel{ ChanType: chanType, ChainHash: backup.ChainHash, IsInitiator: backup.IsInitiator, From e0b869135cada8d180f7b24d363e11c651ea456d Mon Sep 17 00:00:00 2001 From: ziggie Date: Fri, 15 May 2026 21:11:33 -0300 Subject: [PATCH 19/24] contractcourt: use channel state open channel Update contractcourt channel and resolver state boundaries to use chanstate.OpenChannel instead of the channeldb alias. This keeps the contract resolution package depending on channel state data through the package that now owns the type, while leaving channeldb references for store and error types that still belong there. --- contractcourt/anchor_resolver.go | 3 ++- contractcourt/breach_arbitrator_test.go | 5 +++-- contractcourt/breach_resolver.go | 4 ++-- contractcourt/chain_arbitrator.go | 19 +++++++++++-------- contractcourt/chain_arbitrator_test.go | 3 ++- contractcourt/chain_watcher.go | 7 ++++--- contractcourt/chain_watcher_test.go | 5 +++-- contractcourt/channel_arbitrator.go | 7 ++++--- contractcourt/channel_arbitrator_test.go | 13 ++++++++----- contractcourt/commit_sweep_resolver.go | 3 ++- contractcourt/contract_resolver.go | 3 ++- contractcourt/htlc_lease_resolver.go | 4 ++-- contractcourt/htlc_success_resolver.go | 3 ++- contractcourt/htlc_timeout_resolver.go | 3 ++- contractcourt/utils_test.go | 7 ++++--- 15 files changed, 53 insertions(+), 36 deletions(-) diff --git a/contractcourt/anchor_resolver.go b/contractcourt/anchor_resolver.go index 35b17607e..f0f8df96c 100644 --- a/contractcourt/anchor_resolver.go +++ b/contractcourt/anchor_resolver.go @@ -10,6 +10,7 @@ import ( "github.com/btcsuite/btcd/chainhash/v2" "github.com/btcsuite/btcd/wire/v2" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/sweep" @@ -159,7 +160,7 @@ func (c *anchorResolver) Stop() { // state required for the proper resolution of a contract. // // NOTE: Part of the ContractResolver interface. -func (c *anchorResolver) SupplementState(state *channeldb.OpenChannel) { +func (c *anchorResolver) SupplementState(state *chanstate.OpenChannel) { c.chanType = state.ChanType } diff --git a/contractcourt/breach_arbitrator_test.go b/contractcourt/breach_arbitrator_test.go index c8fa13f46..fd0997990 100644 --- a/contractcourt/breach_arbitrator_test.go +++ b/contractcourt/breach_arbitrator_test.go @@ -22,6 +22,7 @@ import ( "github.com/btcsuite/btcd/wire/v2" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" @@ -2315,7 +2316,7 @@ func createInitChannels(t *testing.T) ( binary.BigEndian.Uint64(chanIDBytes[:]), ) - aliceChannelState := &channeldb.OpenChannel{ + aliceChannelState := &chanstate.OpenChannel{ LocalChanCfg: aliceCfg, RemoteChanCfg: bobCfg, IdentityPub: aliceKeyPub, @@ -2332,7 +2333,7 @@ func createInitChannels(t *testing.T) ( Db: dbAlice.ChannelStateDB(), FundingTxn: channels.TestFundingTx, } - bobChannelState := &channeldb.OpenChannel{ + bobChannelState := &chanstate.OpenChannel{ LocalChanCfg: bobCfg, RemoteChanCfg: aliceCfg, IdentityPub: bobKeyPub, diff --git a/contractcourt/breach_resolver.go b/contractcourt/breach_resolver.go index f34112800..29a7f6bac 100644 --- a/contractcourt/breach_resolver.go +++ b/contractcourt/breach_resolver.go @@ -5,7 +5,7 @@ import ( "fmt" "io" - "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" ) // breachResolver is a resolver that will handle breached closes. In the @@ -88,7 +88,7 @@ func (b *breachResolver) Stop() { } // SupplementState adds additional state to the breachResolver. -func (b *breachResolver) SupplementState(_ *channeldb.OpenChannel) { +func (b *breachResolver) SupplementState(_ *chanstate.OpenChannel) { } // Encode encodes the breachResolver to the passed writer. diff --git a/contractcourt/chain_arbitrator.go b/contractcourt/chain_arbitrator.go index 40ef96cb3..e31b5a9bd 100644 --- a/contractcourt/chain_arbitrator.go +++ b/contractcourt/chain_arbitrator.go @@ -14,6 +14,7 @@ import ( "github.com/lightningnetwork/lnd/chainio" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/clock" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/graph/db/models" @@ -331,7 +332,7 @@ var _ chainio.Consumer = (*ChainArbitrator)(nil) // interact with. type arbChannel struct { // channel is the in-memory channel state. - channel *channeldb.OpenChannel + channel *chanstate.OpenChannel // c references the chain arbitrator and is used by arbChannel // internally. @@ -452,7 +453,7 @@ func shouldSuppressClosedChannelNotify(closeType channeldb.ClosureType, // newActiveChannelArbitrator creates a new instance of an active channel // arbitrator given the state of the target channel. -func newActiveChannelArbitrator(channel *channeldb.OpenChannel, +func newActiveChannelArbitrator(channel *chanstate.OpenChannel, c *ChainArbitrator, chanEvents *ChainEventSubscription) (*ChannelArbitrator, error) { // TODO(roasbeef): fetch best height (or pass in) so can ensure block @@ -518,7 +519,7 @@ func newActiveChannelArbitrator(channel *channeldb.OpenChannel, tx, c.cfg.ChainHash, &chanPoint, report, ) }, - FetchHistoricalChannel: func() (*channeldb.OpenChannel, error) { + FetchHistoricalChannel: func() (*chanstate.OpenChannel, error) { chanStateDB := c.chanSource.ChannelStateDB() return chanStateDB.FetchHistoricalChannel(&chanPoint) }, @@ -570,7 +571,7 @@ func newActiveChannelArbitrator(channel *channeldb.OpenChannel, // getArbChannel returns an open channel wrapper for use by channel arbitrators. func (c *ChainArbitrator) getArbChannel( - channel *channeldb.OpenChannel) *arbChannel { + channel *chanstate.OpenChannel) *arbChannel { return &arbChannel{ channel: channel, @@ -878,7 +879,7 @@ func (c *ChainArbitrator) notifyChannelResolved(cp wire.OutPoint) { // transactions and republish them. This helps ensure propagation of the // transactions in the event that prior publications failed. func (c *ChainArbitrator) republishClosingTxs( - channel *channeldb.OpenChannel) error { + channel *chanstate.OpenChannel) error { // If the channel has had its unilateral close broadcasted already, // republish it in case it didn't propagate. @@ -910,7 +911,7 @@ func (c *ChainArbitrator) republishClosingTxs( // // NOTE: There is no risk to calling this method if the channel isn't in either // CommitmentBroadcasted or CoopBroadcasted, but the logs will be misleading. -func (c *ChainArbitrator) rebroadcast(channel *channeldb.OpenChannel, +func (c *ChainArbitrator) rebroadcast(channel *chanstate.OpenChannel, state channeldb.ChannelStatus) error { chanPoint := channel.FundingOutpoint @@ -1169,7 +1170,9 @@ func (c *ChainArbitrator) ForceCloseContract(chanPoint wire.OutPoint) (*wire.Msg // ChannelArbitrator tasked with watching over a new channel. Once a new // channel has finished its final funding flow, it should be registered with // the ChainArbitrator so we can properly react to any on-chain events. -func (c *ChainArbitrator) WatchNewChannel(newChan *channeldb.OpenChannel) error { +func (c *ChainArbitrator) WatchNewChannel( + newChan *chanstate.OpenChannel) error { + c.Lock() defer c.Unlock() @@ -1454,7 +1457,7 @@ func (c *ChainArbitrator) loadPendingCloseChannels() error { tx, c.cfg.ChainHash, &chanPoint, report, ) }, - FetchHistoricalChannel: func() (*channeldb.OpenChannel, error) { + FetchHistoricalChannel: func() (*chanstate.OpenChannel, error) { return chanStateDB.FetchHistoricalChannel(&chanPoint) }, FindOutgoingHTLCDeadline: func( diff --git a/contractcourt/chain_arbitrator_test.go b/contractcourt/chain_arbitrator_test.go index 90d0e4bca..582faf49c 100644 --- a/contractcourt/chain_arbitrator_test.go +++ b/contractcourt/chain_arbitrator_test.go @@ -8,6 +8,7 @@ import ( "github.com/btcsuite/btcd/wire/v2" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/clock" "github.com/lightningnetwork/lnd/graph/db/models" "github.com/lightningnetwork/lnd/lntest/mock" @@ -26,7 +27,7 @@ func TestChainArbitratorRepublishCloses(t *testing.T) { // Create 10 test channels and sync them to the database. const numChans = 10 - var channels []*channeldb.OpenChannel + var channels []*chanstate.OpenChannel for i := 0; i < numChans; i++ { lChannel, _, err := lnwallet.CreateTestChannels( t, channeldb.SingleFunderTweaklessBit, diff --git a/contractcourt/chain_watcher.go b/contractcourt/chain_watcher.go index d40bdc56e..ee8f870ed 100644 --- a/contractcourt/chain_watcher.go +++ b/contractcourt/chain_watcher.go @@ -20,6 +20,7 @@ import ( "github.com/lightningnetwork/lnd/chainio" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lntypes" @@ -240,7 +241,7 @@ type chainWatcherConfig struct { // chanState is a snapshot of the persistent state of the channel that // we're watching. In the event of an on-chain event, we'll query the // database to ensure that we act using the most up to date state. - chanState *channeldb.OpenChannel + chanState *chanstate.OpenChannel // notifier is a reference to the channel notifier that we'll use to be // notified of output spends and when transactions are confirmed. @@ -658,7 +659,7 @@ type chainSet struct { // newChainSet creates a new chainSet given the current up to date channel // state. -func newChainSet(chanState *channeldb.OpenChannel) (*chainSet, error) { +func newChainSet(chanState *chanstate.OpenChannel) (*chainSet, error) { // First, we'll grab the current unrevoked commitments for ourselves // and the remote party. localCommit, remoteCommit, err := chanState.LatestCommitments() @@ -1836,7 +1837,7 @@ func (c *chainWatcher) waitForCommitmentPoint() *btcec.PublicKey { } // deriveFundingPkScript derives the script used in the funding output. -func deriveFundingPkScript(chanState *channeldb.OpenChannel) ([]byte, error) { +func deriveFundingPkScript(chanState *chanstate.OpenChannel) ([]byte, error) { localKey := chanState.LocalChanCfg.MultiSigKey.PubKey remoteKey := chanState.RemoteChanCfg.MultiSigKey.PubKey diff --git a/contractcourt/chain_watcher_test.go b/contractcourt/chain_watcher_test.go index d3289b753..27a810237 100644 --- a/contractcourt/chain_watcher_test.go +++ b/contractcourt/chain_watcher_test.go @@ -12,6 +12,7 @@ import ( "github.com/lightningnetwork/lnd/chainio" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/input" lnmock "github.com/lightningnetwork/lnd/lntest/mock" @@ -264,11 +265,11 @@ type dlpTestCase struct { // state) are returned. func executeStateTransitions(t *testing.T, htlcAmount lnwire.MilliSatoshi, aliceChannel, bobChannel *lnwallet.LightningChannel, - numUpdates uint8) ([]*channeldb.OpenChannel, error) { + numUpdates uint8) ([]*chanstate.OpenChannel, error) { // We'll make a copy of the channel state before each transition. var ( - chanStates []*channeldb.OpenChannel + chanStates []*chanstate.OpenChannel ) state, err := copyChannelState(t, aliceChannel.State()) diff --git a/contractcourt/channel_arbitrator.go b/contractcourt/channel_arbitrator.go index 380ccb90f..5435b705f 100644 --- a/contractcourt/channel_arbitrator.go +++ b/contractcourt/channel_arbitrator.go @@ -16,6 +16,7 @@ import ( "github.com/btcsuite/btcd/wire/v2" "github.com/lightningnetwork/lnd/chainio" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/graph/db/models" "github.com/lightningnetwork/lnd/htlcswitch/hop" @@ -166,7 +167,7 @@ type ChannelArbitratorConfig struct { // FetchHistoricalChannel retrieves the historical state of a channel. // This is mostly used to supplement the ContractResolvers with // additional information required for proper contract resolution. - FetchHistoricalChannel func() (*channeldb.OpenChannel, error) + FetchHistoricalChannel func() (*chanstate.OpenChannel, error) // FindOutgoingHTLCDeadline returns the deadline in absolute block // height for the specified outgoing HTLC. For an outgoing HTLC, its @@ -730,7 +731,7 @@ func (c *ChannelArbitrator) relaunchResolvers(commitSet *CommitSet, // We'll also fetch the historical state of this channel, as it should // have been marked as closed by now, and supplement it to each resolver // such that we can properly resolve our pending contracts. - var chanState *channeldb.OpenChannel + var chanState *chanstate.OpenChannel chanState, err = c.cfg.FetchHistoricalChannel() switch { // If we don't find this channel, then it may be the case that it @@ -2359,7 +2360,7 @@ func (c *ChannelArbitrator) prepContractResolutions( // We'll also fetch the historical state of this channel, as it should // have been marked as closed by now, and supplement it to each resolver // such that we can properly resolve our pending contracts. - var chanState *channeldb.OpenChannel + var chanState *chanstate.OpenChannel chanState, err := c.cfg.FetchHistoricalChannel() switch { // If we don't find this channel, then it may be the case that it diff --git a/contractcourt/channel_arbitrator_test.go b/contractcourt/channel_arbitrator_test.go index 4503b2e71..12e4619f3 100644 --- a/contractcourt/channel_arbitrator_test.go +++ b/contractcourt/channel_arbitrator_test.go @@ -17,6 +17,7 @@ import ( "github.com/lightningnetwork/lnd/chainio" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/clock" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/graph/db/models" @@ -447,8 +448,8 @@ func createTestChannelArbitrator(t *testing.T, log ArbitratorLog, return nil }, - FetchHistoricalChannel: func() (*channeldb.OpenChannel, error) { - return &channeldb.OpenChannel{}, nil + FetchHistoricalChannel: func() (*chanstate.OpenChannel, error) { + return &chanstate.OpenChannel{}, nil }, FindOutgoingHTLCDeadline: func( htlc channeldb.HTLC) fn.Option[int32] { @@ -2161,7 +2162,9 @@ func TestChannelArbitratorPendingExpiredHTLC(t *testing.T) { func TestRemoteCloseInitiator(t *testing.T) { // getCloseSummary returns a unilateral close summary for the channel // provided. - getCloseSummary := func(channel *channeldb.OpenChannel) *RemoteUnilateralCloseInfo { + getCloseSummary := func( + channel *chanstate.OpenChannel) *RemoteUnilateralCloseInfo { + return &RemoteUnilateralCloseInfo{ UnilateralCloseSummary: &lnwallet.UnilateralCloseSummary{ SpendDetail: &chainntnfs.SpendDetail{ @@ -2191,7 +2194,7 @@ func TestRemoteCloseInitiator(t *testing.T) { // is expected to be buffered, as is the default for test // channel arbitrators. notifyClose func(sub *ChainEventSubscription, - channel *channeldb.OpenChannel) + channel *chanstate.OpenChannel) // expectedStates is the set of states we expect the arbitrator // to progress through. @@ -2200,7 +2203,7 @@ func TestRemoteCloseInitiator(t *testing.T) { { name: "force close", notifyClose: func(sub *ChainEventSubscription, - channel *channeldb.OpenChannel) { + channel *chanstate.OpenChannel) { s := getCloseSummary(channel) sub.RemoteUnilateralClosure <- s diff --git a/contractcourt/commit_sweep_resolver.go b/contractcourt/commit_sweep_resolver.go index f47afa5df..fe625ec51 100644 --- a/contractcourt/commit_sweep_resolver.go +++ b/contractcourt/commit_sweep_resolver.go @@ -12,6 +12,7 @@ import ( "github.com/btcsuite/btcd/wire/v2" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lnwallet" @@ -210,7 +211,7 @@ func (c *commitSweepResolver) Stop() { // state required for the proper resolution of a contract. // // NOTE: Part of the ContractResolver interface. -func (c *commitSweepResolver) SupplementState(state *channeldb.OpenChannel) { +func (c *commitSweepResolver) SupplementState(state *chanstate.OpenChannel) { if state.ChanType.HasLeaseExpiration() { c.leaseExpiry = state.ThawHeight } diff --git a/contractcourt/contract_resolver.go b/contractcourt/contract_resolver.go index 62b8f1a13..66926b041 100644 --- a/contractcourt/contract_resolver.go +++ b/contractcourt/contract_resolver.go @@ -10,6 +10,7 @@ import ( "github.com/btcsuite/btcd/wire/v2" "github.com/btcsuite/btclog/v2" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/sweep" ) @@ -59,7 +60,7 @@ type ContractResolver interface { // SupplementState allows the user of a ContractResolver to supplement // it with state required for the proper resolution of a contract. - SupplementState(*channeldb.OpenChannel) + SupplementState(*chanstate.OpenChannel) // IsResolved returns true if the stored state in the resolve is fully // resolved. In this case the target output can be forgotten. diff --git a/contractcourt/htlc_lease_resolver.go b/contractcourt/htlc_lease_resolver.go index 438a09393..4cda5407a 100644 --- a/contractcourt/htlc_lease_resolver.go +++ b/contractcourt/htlc_lease_resolver.go @@ -3,7 +3,7 @@ package contractcourt import ( "github.com/btcsuite/btcd/wire/v2" "github.com/lightningnetwork/lnd/chainntnfs" - "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/tlv" @@ -76,7 +76,7 @@ func (h *htlcLeaseResolver) makeSweepInput(op *wire.OutPoint, // state required for the proper resolution of a contract. // // NOTE: Part of the ContractResolver interface. -func (h *htlcLeaseResolver) SupplementState(state *channeldb.OpenChannel) { +func (h *htlcLeaseResolver) SupplementState(state *chanstate.OpenChannel) { if state.ChanType.HasLeaseExpiration() { h.leaseExpiry = state.ThawHeight } diff --git a/contractcourt/htlc_success_resolver.go b/contractcourt/htlc_success_resolver.go index 2286ce26d..467641280 100644 --- a/contractcourt/htlc_success_resolver.go +++ b/contractcourt/htlc_success_resolver.go @@ -11,6 +11,7 @@ import ( "github.com/btcsuite/btcd/txscript/v2" "github.com/btcsuite/btcd/wire/v2" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/graph/db/models" "github.com/lightningnetwork/lnd/input" @@ -384,7 +385,7 @@ func (h *htlcSuccessResolver) HtlcPoint() wire.OutPoint { // production taproot channels after restart. // // NOTE: Part of the ContractResolver interface. -func (h *htlcSuccessResolver) SupplementState(state *channeldb.OpenChannel) { +func (h *htlcSuccessResolver) SupplementState(state *chanstate.OpenChannel) { h.htlcLeaseResolver.SupplementState(state) h.chanType = state.ChanType } diff --git a/contractcourt/htlc_timeout_resolver.go b/contractcourt/htlc_timeout_resolver.go index 5b89a345f..e6d20bf09 100644 --- a/contractcourt/htlc_timeout_resolver.go +++ b/contractcourt/htlc_timeout_resolver.go @@ -12,6 +12,7 @@ import ( "github.com/btcsuite/btcd/wire/v2" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lntypes" @@ -775,7 +776,7 @@ func (h *htlcTimeoutResolver) HtlcPoint() wire.OutPoint { // production taproot channels after restart. // // NOTE: Part of the ContractResolver interface. -func (h *htlcTimeoutResolver) SupplementState(state *channeldb.OpenChannel) { +func (h *htlcTimeoutResolver) SupplementState(state *chanstate.OpenChannel) { h.htlcLeaseResolver.SupplementState(state) h.chanType = state.ChanType } diff --git a/contractcourt/utils_test.go b/contractcourt/utils_test.go index 27b65b9bf..aaf17189b 100644 --- a/contractcourt/utils_test.go +++ b/contractcourt/utils_test.go @@ -10,11 +10,12 @@ import ( "time" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" ) // testChannelStateDB extracts the ChannelStateDB from the test channel state. func testChannelStateDB(t testing.TB, - state *channeldb.OpenChannel) *channeldb.ChannelStateDB { + state *chanstate.OpenChannel) *channeldb.ChannelStateDB { t.Helper() @@ -66,8 +67,8 @@ func copyFile(dest, src string) error { // copyChannelState copies the OpenChannel state by copying the database and // creating a new struct from it. The copied state is returned. -func copyChannelState(t *testing.T, state *channeldb.OpenChannel) ( - *channeldb.OpenChannel, error) { +func copyChannelState(t *testing.T, state *chanstate.OpenChannel) ( + *chanstate.OpenChannel, error) { // Make a copy of the DB. dbFile := filepath.Join( From fa31c56464d494db8e94b5153d108f0943776343 Mon Sep 17 00:00:00 2001 From: ziggie Date: Fri, 15 May 2026 21:12:34 -0300 Subject: [PATCH 20/24] lnwallet: use channel state open channel Update lnwallet channel, reservation, wallet, and test helpers to use chanstate.OpenChannel directly. The wallet package still imports channeldb for database APIs and other channel-state aliases, but the OpenChannel type boundary now points at the package that owns the type. --- lnwallet/aux_leaf_store.go | 9 +++--- lnwallet/channel.go | 41 ++++++++++++++------------- lnwallet/channel_test.go | 5 ++-- lnwallet/commitment.go | 11 ++++--- lnwallet/reservation.go | 15 +++++----- lnwallet/taproot_test_vectors_test.go | 5 ++-- lnwallet/test_utils.go | 5 ++-- lnwallet/transactions_test.go | 5 ++-- lnwallet/wallet.go | 9 +++--- 9 files changed, 59 insertions(+), 46 deletions(-) diff --git a/lnwallet/aux_leaf_store.go b/lnwallet/aux_leaf_store.go index fb645b1af..e4c22335f 100644 --- a/lnwallet/aux_leaf_store.go +++ b/lnwallet/aux_leaf_store.go @@ -5,6 +5,7 @@ import ( "github.com/btcsuite/btcd/chainhash/v2" "github.com/btcsuite/btcd/wire/v2" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lntypes" @@ -55,7 +56,7 @@ type CommitAuxLeaves struct { } // AuxChanState is a struct that holds certain fields of the -// channeldb.OpenChannel struct that are used by the aux components. The data +// chanstate.OpenChannel struct that are used by the aux components. The data // is copied over to prevent accidental mutation of the original channel state. type AuxChanState struct { // ChanType denotes which type of channel this is. @@ -110,7 +111,7 @@ type AuxChanState struct { } // NewAuxChanState creates a new AuxChanState from the given channel state. -func NewAuxChanState(chanState *channeldb.OpenChannel) AuxChanState { +func NewAuxChanState(chanState *chanstate.OpenChannel) AuxChanState { peerPub := chanState.IdentityPub.SerializeCompressed() return AuxChanState{ @@ -202,7 +203,7 @@ type AuxLeafStore interface { // auxLeavesFromView is used to derive the set of commit aux leaves (if any), // that are needed to create a new commitment transaction using the original // (unfiltered) htlc view. -func auxLeavesFromView(leafStore AuxLeafStore, chanState *channeldb.OpenChannel, +func auxLeavesFromView(leafStore AuxLeafStore, chanState *chanstate.OpenChannel, prevBlob fn.Option[tlv.Blob], originalView *HtlcView, whoseCommit lntypes.ChannelParty, ourBalance, theirBalance lnwire.MilliSatoshi, @@ -225,7 +226,7 @@ func auxLeavesFromView(leafStore AuxLeafStore, chanState *channeldb.OpenChannel, // updateAuxBlob is a helper function that attempts to update the aux blob // given the prior and current state information. -func updateAuxBlob(leafStore AuxLeafStore, chanState *channeldb.OpenChannel, +func updateAuxBlob(leafStore AuxLeafStore, chanState *chanstate.OpenChannel, prevBlob fn.Option[tlv.Blob], nextViewUnfiltered *HtlcView, whoseCommit lntypes.ChannelParty, ourBalance, theirBalance lnwire.MilliSatoshi, diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 73446c80d..ea243dd86 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -24,6 +24,7 @@ import ( "github.com/btcsuite/btclog/v2" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/graph/db/models" "github.com/lightningnetwork/lnd/input" @@ -793,7 +794,7 @@ type LightningChannel struct { // state, which we are able to broadcast safely. commitChains lntypes.Dual[*commitmentChain] - channelState *channeldb.OpenChannel + channelState *chanstate.OpenChannel commitBuilder *CommitmentBuilder @@ -951,7 +952,7 @@ func defaultChannelOpts() *channelOpts { // automatically persist pertinent state to the database in an efficient // manner. func NewLightningChannel(signer input.Signer, - state *channeldb.OpenChannel, + state *chanstate.OpenChannel, sigPool *SigPool, chanOpts ...ChannelOpt) (*LightningChannel, error) { opts := defaultChannelOpts() @@ -2093,7 +2094,9 @@ type BreachRetribution struct { // nil, then the revocation log will be checked to see if it contains the info // required to construct the BreachRetribution. If the revocation log is missing // the required fields then ErrRevLogDataMissing will be returned. -func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, +// +//nolint:funlen +func NewBreachRetribution(chanState *chanstate.OpenChannel, stateNum uint64, breachHeight uint32, spendTx *wire.MsgTx, leafStore fn.Option[AuxLeafStore], auxResolver fn.Option[AuxContractResolver]) (*BreachRetribution, @@ -2393,7 +2396,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, // createHtlcRetribution is a helper function to construct an HtlcRetribution // based on the passed params. -func createHtlcRetribution(chanState *channeldb.OpenChannel, +func createHtlcRetribution(chanState *chanstate.OpenChannel, keyRing *CommitmentKeyRing, commitHash chainhash.Hash, commitmentSecret *btcec.PrivateKey, leaseExpiry uint32, htlc *channeldb.HTLCEntry, @@ -2520,7 +2523,7 @@ func createHtlcRetribution(chanState *channeldb.OpenChannel, // see if these fields are present there. If they are not, then // ErrRevLogDataMissing is returned. func createBreachRetribution(revokedLog *channeldb.RevocationLog, - spendTx *wire.MsgTx, chanState *channeldb.OpenChannel, + spendTx *wire.MsgTx, chanState *chanstate.OpenChannel, keyRing *CommitmentKeyRing, commitmentSecret *btcec.PrivateKey, leaseExpiry uint32, auxLeaves fn.Option[CommitAuxLeaves]) (*BreachRetribution, int64, int64, @@ -2637,7 +2640,7 @@ func createBreachRetribution(revokedLog *channeldb.RevocationLog, // BreachRetribution using a ChannelCommitment. Returns the constructed // retribution, our amount, their amount, and a possible non-nil error. func createBreachRetributionLegacy(revokedLog *channeldb.ChannelCommitment, - chanState *channeldb.OpenChannel, keyRing *CommitmentKeyRing, + chanState *chanstate.OpenChannel, keyRing *CommitmentKeyRing, commitmentSecret *btcec.PrivateKey, ourScript, theirScript input.ScriptDescriptor, leaseExpiry uint32) (*BreachRetribution, int64, int64, error) { @@ -2990,7 +2993,7 @@ func (lc *LightningChannel) fetchCommitmentView( // fundingTxIn returns the funding output as a transaction input. The input // returned by this function uses a max sequence number, so it isn't able to be // used with RBF by default. -func fundingTxIn(chanState *channeldb.OpenChannel) wire.TxIn { +func fundingTxIn(chanState *chanstate.OpenChannel) wire.TxIn { return *wire.NewTxIn(&chanState.FundingOutpoint, nil, nil) } @@ -3246,7 +3249,7 @@ func (lc *LightningChannel) fetchParent(entry *paymentDescriptor, // configured reserve. It also uses the balance delta for the party, to account // for entry amounts that have been processed already. func balanceAboveReserve(party lntypes.ChannelParty, delta int64, - channel *channeldb.OpenChannel) bool { + channel *chanstate.OpenChannel) bool { // We're going to access the channel state, so let's make sure we're // holding the lock. @@ -3335,7 +3338,7 @@ func (lc *LightningChannel) evaluateNoOpHtlc(entry *paymentDescriptor, // signature can be submitted to the sigPool to generate all the signatures // asynchronously and in parallel. func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, - chanState *channeldb.OpenChannel, leaseExpiry uint32, + chanState *chanstate.OpenChannel, leaseExpiry uint32, remoteCommitView *commitment, leafStore fn.Option[AuxLeafStore]) ([]SignJob, []AuxSigJob, chan struct{}, error) { @@ -4965,7 +4968,7 @@ func (lc *LightningChannel) recordSettlement( // directly into the pool of workers. // //nolint:funlen -func genHtlcSigValidationJobs(chanState *channeldb.OpenChannel, +func genHtlcSigValidationJobs(chanState *chanstate.OpenChannel, localCommitmentView *commitment, keyRing *CommitmentKeyRing, htlcSigs []lnwire.Sig, leaseExpiry uint32, leafStore fn.Option[AuxLeafStore], auxSigner fn.Option[AuxSigner], @@ -6756,10 +6759,10 @@ func (lc *LightningChannel) ChannelPoint() wire.OutPoint { return lc.channelState.FundingOutpoint } -// ChannelState returns a copy of the internal channeldb.OpenChannel state +// ChannelState returns a copy of the internal chanstate.OpenChannel state // struct. Modifications to the returned struct will not be reflected within // the LightningChannel. -func (lc *LightningChannel) ChannelState() *channeldb.OpenChannel { +func (lc *LightningChannel) ChannelState() *chanstate.OpenChannel { return lc.channelState.Copy() } @@ -7069,7 +7072,7 @@ type UnilateralCloseSummary struct { // happen in case we have lost state) it should be set to an empty struct, in // which case we will attempt to sweep the non-HTLC output using the passed // commitPoint. -func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, //nolint:funlen +func NewUnilateralCloseSummary(chanState *chanstate.OpenChannel, signer input.Signer, commitSpend *chainntnfs.SpendDetail, remoteCommit channeldb.ChannelCommitment, commitPoint *btcec.PublicKey, leafStore fn.Option[AuxLeafStore], @@ -7410,7 +7413,7 @@ func newOutgoingHtlcResolution(signer input.Signer, commitTxHeight uint32, htlc *channeldb.HTLC, keyRing *CommitmentKeyRing, feePerKw chainfee.SatPerKWeight, csvDelay, leaseExpiry uint32, whoseCommit lntypes.ChannelParty, isCommitFromInitiator bool, - chanType channeldb.ChannelType, chanState *channeldb.OpenChannel, + chanType channeldb.ChannelType, chanState *chanstate.OpenChannel, auxLeaves fn.Option[CommitAuxLeaves], auxResolver fn.Option[AuxContractResolver], ) (*OutgoingHtlcResolution, error) { @@ -7784,7 +7787,7 @@ func newIncomingHtlcResolution(signer input.Signer, commitTxHeight uint32, htlc *channeldb.HTLC, keyRing *CommitmentKeyRing, feePerKw chainfee.SatPerKWeight, csvDelay, leaseExpiry uint32, whoseCommit lntypes.ChannelParty, isCommitFromInitiator bool, - chanType channeldb.ChannelType, chanState *channeldb.OpenChannel, + chanType channeldb.ChannelType, chanState *chanstate.OpenChannel, auxLeaves fn.Option[CommitAuxLeaves], auxResolver fn.Option[AuxContractResolver], ) (*IncomingHtlcResolution, error) { @@ -8169,7 +8172,7 @@ func extractHtlcResolutions(feePerKw chainfee.SatPerKWeight, localChanCfg, remoteChanCfg *channeldb.ChannelConfig, commitTx *wire.MsgTx, commitTxHeight uint32, chanType channeldb.ChannelType, isCommitFromInitiator bool, - leaseExpiry uint32, chanState *channeldb.OpenChannel, + leaseExpiry uint32, chanState *chanstate.OpenChannel, auxLeaves fn.Option[CommitAuxLeaves], auxResolver fn.Option[AuxContractResolver]) (*HtlcResolutions, error) { @@ -8383,7 +8386,7 @@ func (lc *LightningChannel) ForceClose(opts ...ForceCloseOpt) ( // NewLocalForceCloseSummary generates a LocalForceCloseSummary from the given // channel state. The passed commitTx must be a fully signed commitment // transaction corresponding to localCommit. -func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, +func NewLocalForceCloseSummary(chanState *chanstate.OpenChannel, signer input.Signer, commitTx *wire.MsgTx, commitTxHeight uint32, stateNum uint64, leafStore fn.Option[AuxLeafStore], auxResolver fn.Option[AuxContractResolver]) (*LocalForceCloseSummary, @@ -9038,7 +9041,7 @@ func (lc *LightningChannel) NewAnchorResolutions() (*AnchorResolutions, // NewAnchorResolution returns the information that is required to sweep the // local anchor. -func NewAnchorResolution(chanState *channeldb.OpenChannel, +func NewAnchorResolution(chanState *chanstate.OpenChannel, commitTx *wire.MsgTx, keyRing *CommitmentKeyRing, whoseCommit lntypes.ChannelParty) (*AnchorResolution, error) { @@ -10081,7 +10084,7 @@ func (lc *LightningChannel) IsPending() bool { } // State provides access to the channel's internal state. -func (lc *LightningChannel) State() *channeldb.OpenChannel { +func (lc *LightningChannel) State() *chanstate.OpenChannel { return lc.channelState } diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index f48f3d7db..5fd8cc08e 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -26,6 +26,7 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/graph/db/models" "github.com/lightningnetwork/lnd/input" @@ -9268,7 +9269,7 @@ func TestEvaluateView(t *testing.T) { t.Run(test.name, func(t *testing.T) { isInitiator := test.channelInitiator == lntypes.Local lc := LightningChannel{ - channelState: &channeldb.OpenChannel{ + channelState: &chanstate.OpenChannel{ IsInitiator: isInitiator, TotalMSatSent: 0, TotalMSatReceived: 0, @@ -10060,7 +10061,7 @@ func testGetDustSum(t *testing.T, chantype channeldb.ChannelType) { // deriveDummyRetributionParams is a helper function that derives a list of // dummy params to assist retribution creation related tests. -func deriveDummyRetributionParams(chanState *channeldb.OpenChannel) (uint32, +func deriveDummyRetributionParams(chanState *chanstate.OpenChannel) (uint32, *CommitmentKeyRing, chainhash.Hash) { config := chanState.RemoteChanCfg diff --git a/lnwallet/commitment.go b/lnwallet/commitment.go index ddb22c074..78b82125b 100644 --- a/lnwallet/commitment.go +++ b/lnwallet/commitment.go @@ -11,6 +11,7 @@ import ( "github.com/btcsuite/btcd/txscript/v2" "github.com/btcsuite/btcd/wire/v2" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lntypes" @@ -635,7 +636,7 @@ type CommitmentBuilder struct { // chanState is the underlying channel's state struct, used to // determine the type of channel we are dealing with, and relevant // parameters. - chanState *channeldb.OpenChannel + chanState *chanstate.OpenChannel // obfuscator is a 48-bit state hint that's used to obfuscate the // current state number on the commitment transactions. @@ -647,7 +648,7 @@ type CommitmentBuilder struct { } // NewCommitmentBuilder creates a new CommitmentBuilder from chanState. -func NewCommitmentBuilder(chanState *channeldb.OpenChannel, +func NewCommitmentBuilder(chanState *chanstate.OpenChannel, leafStore fn.Option[AuxLeafStore]) *CommitmentBuilder { // The anchor channel type MUST be tweakless. @@ -665,7 +666,9 @@ func NewCommitmentBuilder(chanState *channeldb.OpenChannel, // createStateHintObfuscator derives and assigns the state hint obfuscator for // the channel, which is used to encode the commitment height in the sequence // number of commitment transaction inputs. -func createStateHintObfuscator(state *channeldb.OpenChannel) [StateHintSize]byte { +func createStateHintObfuscator( + state *chanstate.OpenChannel) [StateHintSize]byte { + if state.IsInitiator { return DeriveStateHintObfuscator( state.LocalChanCfg.PaymentBasePoint.PubKey, @@ -1320,7 +1323,7 @@ func addHTLC(commitTx *wire.MsgTx, whoseCommit lntypes.ChannelParty, // output scripts and compares them against the outputs inside the commitment // to find the match. func findOutputIndexesFromRemote(revocationPreimage *chainhash.Hash, - chanState *channeldb.OpenChannel, + chanState *chanstate.OpenChannel, leafStore fn.Option[AuxLeafStore]) (uint32, uint32, error) { // Init the output indexes as empty. diff --git a/lnwallet/reservation.go b/lnwallet/reservation.go index 61addc109..a2c116566 100644 --- a/lnwallet/reservation.go +++ b/lnwallet/reservation.go @@ -12,6 +12,7 @@ import ( "github.com/btcsuite/btcd/chainhash/v2" "github.com/btcsuite/btcd/wire/v2" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" @@ -248,7 +249,7 @@ type ChannelReservation struct { ourContribution *ChannelContribution theirContribution *ChannelContribution - partialState *channeldb.OpenChannel + partialState *chanstate.OpenChannel nodeAddr net.Addr // The ID of this reservation, used to uniquely track the reservation @@ -494,7 +495,7 @@ func NewChannelReservation(capacity, localFundingAmt btcutil.Amount, FundingAmount: theirBalance.ToSatoshis(), ChannelConfig: &channeldb.ChannelConfig{}, }, - partialState: &channeldb.OpenChannel{ + partialState: &chanstate.OpenChannel{ ChanType: chanType, ChainHash: *chainHash, IsPending: true, @@ -777,11 +778,11 @@ func (r *ChannelReservation) OurSignatures() ([]*input.Script, // confirmations. Once the method unblocks, a LightningChannel instance is // returned, marking the channel available for updates. func (r *ChannelReservation) CompleteReservation(fundingInputScripts []*input.Script, - commitmentSig input.Signature) (*channeldb.OpenChannel, error) { + commitmentSig input.Signature) (*chanstate.OpenChannel, error) { // TODO(roasbeef): add flag for watch or not? errChan := make(chan error, 1) - completeChan := make(chan *channeldb.OpenChannel, 1) + completeChan := make(chan *chanstate.OpenChannel, 1) r.wallet.msgChan <- &addCounterPartySigsMsg{ pendingFundingID: r.reservationID, @@ -805,11 +806,11 @@ func (r *ChannelReservation) CompleteReservation(fundingInputScripts []*input.Sc // will be populated. func (r *ChannelReservation) CompleteReservationSingle( fundingPoint *wire.OutPoint, commitSig input.Signature, - auxFundingDesc fn.Option[AuxFundingDesc]) (*channeldb.OpenChannel, + auxFundingDesc fn.Option[AuxFundingDesc]) (*chanstate.OpenChannel, error) { errChan := make(chan error, 1) - completeChan := make(chan *channeldb.OpenChannel, 1) + completeChan := make(chan *chanstate.OpenChannel, 1) r.wallet.msgChan <- &addSingleFunderSigsMsg{ pendingFundingID: r.reservationID, @@ -903,7 +904,7 @@ func (r *ChannelReservation) Cancel() error { } // ChanState the current open channel state. -func (r *ChannelReservation) ChanState() *channeldb.OpenChannel { +func (r *ChannelReservation) ChanState() *chanstate.OpenChannel { r.RLock() defer r.RUnlock() diff --git a/lnwallet/taproot_test_vectors_test.go b/lnwallet/taproot_test_vectors_test.go index 23c72c439..57b702769 100644 --- a/lnwallet/taproot_test_vectors_test.go +++ b/lnwallet/taproot_test_vectors_test.go @@ -20,6 +20,7 @@ import ( "github.com/btcsuite/btcd/txscript/v2" "github.com/btcsuite/btcd/wire/v2" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" @@ -891,7 +892,7 @@ func createTaprootTestChannelsForVectors(tc *taprootTestContext, shortChanID := lnwire.NewShortChanIDFromInt(0xdeadbeef) - remoteChannelState := &channeldb.OpenChannel{ + remoteChannelState := &chanstate.OpenChannel{ LocalChanCfg: remoteCfg, RemoteChanCfg: localCfg, IdentityPub: tc.remoteFundingPrivkey.PubKey(), @@ -908,7 +909,7 @@ func createTaprootTestChannelsForVectors(tc *taprootTestContext, Db: dbRemote.ChannelStateDB(), FundingTxn: fundingTx, } - localChannelState := &channeldb.OpenChannel{ + localChannelState := &chanstate.OpenChannel{ LocalChanCfg: localCfg, RemoteChanCfg: remoteCfg, IdentityPub: tc.localFundingPrivkey.PubKey(), diff --git a/lnwallet/test_utils.go b/lnwallet/test_utils.go index 5723be7c4..22f8861aa 100644 --- a/lnwallet/test_utils.go +++ b/lnwallet/test_utils.go @@ -16,6 +16,7 @@ import ( "github.com/btcsuite/btcd/chainhash/v2" "github.com/btcsuite/btcd/wire/v2" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" @@ -308,7 +309,7 @@ func CreateTestChannels(t *testing.T, chanType channeldb.ChannelType, binary.BigEndian.Uint64(chanIDBytes[:]), ) - aliceChannelState := &channeldb.OpenChannel{ + aliceChannelState := &chanstate.OpenChannel{ LocalChanCfg: aliceCfg, RemoteChanCfg: bobCfg, IdentityPub: aliceKeys[0].PubKey(), @@ -325,7 +326,7 @@ func CreateTestChannels(t *testing.T, chanType channeldb.ChannelType, Db: dbAlice.ChannelStateDB(), FundingTxn: testTx, } - bobChannelState := &channeldb.OpenChannel{ + bobChannelState := &chanstate.OpenChannel{ LocalChanCfg: bobCfg, RemoteChanCfg: aliceCfg, IdentityPub: bobKeys[0].PubKey(), diff --git a/lnwallet/transactions_test.go b/lnwallet/transactions_test.go index 6f941ad1b..1ae21d887 100644 --- a/lnwallet/transactions_test.go +++ b/lnwallet/transactions_test.go @@ -21,6 +21,7 @@ import ( "github.com/btcsuite/btcd/txscript/v2" "github.com/btcsuite/btcd/wire/v2" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" @@ -967,7 +968,7 @@ func createTestChannelsForVectors(tc *testContext, chanType channeldb.ChannelTyp binary.BigEndian.Uint64(chanIDBytes[:]), ) - remoteChannelState := &channeldb.OpenChannel{ + remoteChannelState := &chanstate.OpenChannel{ LocalChanCfg: remoteCfg, RemoteChanCfg: localCfg, IdentityPub: remoteDummy2.PubKey(), @@ -984,7 +985,7 @@ func createTestChannelsForVectors(tc *testContext, chanType channeldb.ChannelTyp Db: dbRemote.ChannelStateDB(), FundingTxn: tc.fundingTx.MsgTx(), } - localChannelState := &channeldb.OpenChannel{ + localChannelState := &chanstate.OpenChannel{ LocalChanCfg: localCfg, RemoteChanCfg: remoteCfg, IdentityPub: localDummy2.PubKey(), diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index 6ae922cf3..54efab6a1 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -23,6 +23,7 @@ import ( "github.com/btcsuite/btcd/wire/v2" "github.com/btcsuite/btcwallet/wallet" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" @@ -335,7 +336,7 @@ type addCounterPartySigsMsg struct { // This channel is used to return the completed channel after the wallet // has completed all of its stages in the funding process. - completeChan chan *channeldb.OpenChannel + completeChan chan *chanstate.OpenChannel // NOTE: In order to avoid deadlocks, this channel MUST be buffered. err chan error @@ -364,7 +365,7 @@ type addSingleFunderSigsMsg struct { // This channel is used to return the completed channel after the wallet // has completed all of its stages in the funding process. - completeChan chan *channeldb.OpenChannel + completeChan chan *chanstate.OpenChannel // NOTE: In order to avoid deadlocks, this channel MUST be buffered. err chan error @@ -1152,7 +1153,7 @@ func (l *LightningWallet) CurrentNumAnchorChans() (int, error) { } var numAnchors int - cntChannel := func(c *channeldb.OpenChannel) { + cntChannel := func(c *chanstate.OpenChannel) { // We skip private channels, as we assume they won't be used // for routing. if c.ChannelFlags&lnwire.FFAnnounceChannel == 0 { @@ -2601,7 +2602,7 @@ func initStateHints(commit1, commit2 *wire.MsgTx, // ValidateChannel will attempt to fully validate a newly mined channel, given // its funding transaction and existing channel state. If this method returns // an error, then the mined channel is invalid, and shouldn't be used. -func (l *LightningWallet) ValidateChannel(channelState *channeldb.OpenChannel, +func (l *LightningWallet) ValidateChannel(channelState *chanstate.OpenChannel, fundingTx *wire.MsgTx) error { var chanOpts []ChannelOpt From a459bc4672ca909ffda855678ca8d1b7027e570d Mon Sep 17 00:00:00 2001 From: ziggie Date: Fri, 15 May 2026 21:13:52 -0300 Subject: [PATCH 21/24] peer: use channel state open channel Update peer channel loading, validation, and test helpers to use chanstate.OpenChannel directly. The peer package still depends on channeldb for store-level errors and helpers, but no longer needs the OpenChannel alias in its public channel-state boundary. --- peer/brontide.go | 16 +++++++++------- peer/brontide_test.go | 16 ++++++++-------- peer/test_utils.go | 9 +++++---- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/peer/brontide.go b/peer/brontide.go index 0a22005c8..193f20d8a 100644 --- a/peer/brontide.go +++ b/peer/brontide.go @@ -123,7 +123,7 @@ type outgoingMsg struct { errChan chan error // MUST be buffered. } -// newChannelMsg packages a channeldb.OpenChannel with a channel that allows +// newChannelMsg packages a chanstate.OpenChannel with a channel that allows // the receiver of the request to report when the channel creation process has // completed. type newChannelMsg struct { @@ -1142,7 +1142,9 @@ func (p *Brontide) addrWithInternalKey( // channels returned by the database. It returns a slice of channel reestablish // messages that should be sent to the peer immediately, in case we have borked // channels that haven't been closed yet. -func (p *Brontide) loadActiveChannels(chans []*channeldb.OpenChannel) ( +// +//nolint:funlen +func (p *Brontide) loadActiveChannels(chans []*chanstate.OpenChannel) ( []lnwire.Message, error) { // Return a slice of messages to send to the peers in case the channel @@ -1592,7 +1594,7 @@ func (p *Brontide) addLink(chanPoint *wire.OutPoint, // maybeSendNodeAnn sends our node announcement to the remote peer if at least // one confirmed public channel exists with them. -func (p *Brontide) maybeSendNodeAnn(channels []*channeldb.OpenChannel) { +func (p *Brontide) maybeSendNodeAnn(channels []*chanstate.OpenChannel) { defer p.cg.WgDone() hasConfirmedPublicChan := false @@ -5496,7 +5498,7 @@ func (p *Brontide) attachChannelEventSubscription() error { // updateNextRevocation updates the existing channel's next revocation if it's // nil. -func (p *Brontide) updateNextRevocation(c *channeldb.OpenChannel) error { +func (p *Brontide) updateNextRevocation(c *chanstate.OpenChannel) error { chanPoint := c.FundingOutpoint chanID := lnwire.NewChanIDFromOutPoint(chanPoint) @@ -5538,7 +5540,7 @@ func (p *Brontide) updateNextRevocation(c *channeldb.OpenChannel) error { } // addActiveChannel adds a new active channel to the `activeChannels` map. It -// takes a `channeldb.OpenChannel`, creates a `lnwallet.LightningChannel` from +// takes a `chanstate.OpenChannel`, creates a `lnwallet.LightningChannel` from // it and assembles it with a channel link. func (p *Brontide) addActiveChannel(c *lnpeer.NewChannel) error { chanPoint := c.FundingOutpoint @@ -5797,7 +5799,7 @@ func (p *Brontide) scaleTimeout(timeout time.Duration) time.Duration { // bandwidth against the traffic shaper. type auxHtlcValidator struct { peer *Brontide - dbChan *channeldb.OpenChannel + dbChan *chanstate.OpenChannel ts htlcswitch.AuxTrafficShaper } @@ -5873,7 +5875,7 @@ func (v *auxHtlcValidator) ValidateHtlc(amount, // createHtlcValidator creates an HTLC validator that performs final aux balance // validation before HTLCs are added to the channel state. -func (p *Brontide) createHtlcValidator(dbChan *channeldb.OpenChannel, +func (p *Brontide) createHtlcValidator(dbChan *chanstate.OpenChannel, ts htlcswitch.AuxTrafficShaper) lnwallet.AuxHtlcValidator { return &auxHtlcValidator{ diff --git a/peer/brontide_test.go b/peer/brontide_test.go index db2f35e81..ee6efbb45 100644 --- a/peer/brontide_test.go +++ b/peer/brontide_test.go @@ -13,7 +13,7 @@ import ( "github.com/btcsuite/btcd/txscript/v2" "github.com/btcsuite/btcd/wire/v2" "github.com/lightningnetwork/lnd/chainntnfs" - "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/contractcourt" "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/htlcswitch" @@ -765,7 +765,7 @@ func TestCustomShutdownScript(t *testing.T) { // setShutdown is a function which sets the upfront shutdown address for // the local channel. - setShutdown := func(a, b *channeldb.OpenChannel) { + setShutdown := func(a, b *chanstate.OpenChannel) { a.LocalShutdownScript = script b.RemoteShutdownScript = script } @@ -775,7 +775,7 @@ func TestCustomShutdownScript(t *testing.T) { // update is a function used to set values on the channel set up for the // test. It is used to set values for upfront shutdown addresses. - update func(a, b *channeldb.OpenChannel) + update func(a, b *chanstate.OpenChannel) // userCloseScript is the address specified by the user. userCloseScript lnwire.DeliveryAddress @@ -1222,8 +1222,8 @@ func assertMsgSent(t *testing.T, conn *mockMessageConn, func TestAlwaysSendChannelUpdate(t *testing.T) { require := require.New(t) - var channel *channeldb.OpenChannel - channelIntercept := func(a, b *channeldb.OpenChannel) { + var channel *chanstate.OpenChannel + channelIntercept := func(a, b *chanstate.OpenChannel) { channel = a } @@ -1432,8 +1432,8 @@ func TestStartupWriteMessageRace(t *testing.T) { // createTestPeerWithChannel, so we can mark it borked below. // We can't mark it borked within the callback, since the channel hasn't // been saved to the DB yet when the callback executes. - var channel *channeldb.OpenChannel - getChannels := func(a, b *channeldb.OpenChannel) { + var channel *chanstate.OpenChannel + getChannels := func(a, b *chanstate.OpenChannel) { channel = a } @@ -1633,7 +1633,7 @@ func TestCreateHtlcValidator(t *testing.T) { } // Create a mock channel with minimal required fields. - dbChan := &channeldb.OpenChannel{ + dbChan := &chanstate.OpenChannel{ ShortChannelID: lnwire.NewShortChanIDFromInt(123), } diff --git a/peer/test_utils.go b/peer/test_utils.go index f2c00979d..10af39668 100644 --- a/peer/test_utils.go +++ b/peer/test_utils.go @@ -18,6 +18,7 @@ import ( "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channelnotifier" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/fn/v2" graphdb "github.com/lightningnetwork/lnd/graph/db" "github.com/lightningnetwork/lnd/htlcswitch" @@ -55,7 +56,7 @@ var ( // noUpdate is a function which can be used as a parameter in // createTestPeerWithChannel to call the setup code with no custom values on // the channels set up. -var noUpdate = func(a, b *channeldb.OpenChannel) {} +var noUpdate = func(a, b *chanstate.OpenChannel) {} type peerTestCtx struct { peer *Brontide @@ -75,7 +76,7 @@ type peerTestCtx struct { // It takes an updateChan function which can be used to modify the default // values on the channel states for each peer. func createTestPeerWithChannel(t *testing.T, updateChan func(a, - b *channeldb.OpenChannel)) (*peerTestCtx, error) { + b *chanstate.OpenChannel)) (*peerTestCtx, error) { params := createTestPeer(t) @@ -238,7 +239,7 @@ func createTestPeerWithChannel(t *testing.T, updateChan func(a, binary.BigEndian.Uint64(chanIDBytes[:]), ) - aliceChannelState := &channeldb.OpenChannel{ + aliceChannelState := &chanstate.OpenChannel{ LocalChanCfg: aliceCfg, RemoteChanCfg: bobCfg, IdentityPub: aliceKeyPub, @@ -255,7 +256,7 @@ func createTestPeerWithChannel(t *testing.T, updateChan func(a, Db: dbAlice.ChannelStateDB(), FundingTxn: channels.TestFundingTx, } - bobChannelState := &channeldb.OpenChannel{ + bobChannelState := &chanstate.OpenChannel{ LocalChanCfg: bobCfg, RemoteChanCfg: aliceCfg, IdentityPub: bobKeyPub, From a4bec71b54818012bea7e72cc6d71fe7e59e46bd Mon Sep 17 00:00:00 2001 From: ziggie Date: Fri, 15 May 2026 21:15:55 -0300 Subject: [PATCH 22/24] htlcswitch: use channel state open channel Update htlcswitch test utilities to construct and pass chanstate.OpenChannel values directly. This removes another test-only dependency on the channeldb OpenChannel alias while leaving the test database helpers unchanged. --- htlcswitch/test_utils.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/htlcswitch/test_utils.go b/htlcswitch/test_utils.go index f0ebb5c2b..32d651f7e 100644 --- a/htlcswitch/test_utils.go +++ b/htlcswitch/test_utils.go @@ -24,6 +24,7 @@ import ( "github.com/btcsuite/btcd/wire/v2" sphinx "github.com/lightningnetwork/lightning-onion" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/chanstate" "github.com/lightningnetwork/lnd/contractcourt" "github.com/lightningnetwork/lnd/graph/db/models" "github.com/lightningnetwork/lnd/htlcswitch/hop" @@ -305,7 +306,7 @@ func createTestChannel(t *testing.T, alicePrivKey, bobPrivKey []byte, CommitSig: bytes.Repeat([]byte{1}, 71), } - aliceChannelState := &channeldb.OpenChannel{ + aliceChannelState := &chanstate.OpenChannel{ LocalChanCfg: aliceCfg, RemoteChanCfg: bobCfg, IdentityPub: aliceKeyPub, @@ -323,7 +324,7 @@ func createTestChannel(t *testing.T, alicePrivKey, bobPrivKey []byte, FundingTxn: channels.TestFundingTx, } - bobChannelState := &channeldb.OpenChannel{ + bobChannelState := &chanstate.OpenChannel{ LocalChanCfg: bobCfg, RemoteChanCfg: aliceCfg, IdentityPub: bobKeyPub, @@ -415,7 +416,7 @@ func createTestChannel(t *testing.T, alicePrivKey, bobPrivKey []byte, "channel: %w", err) } - var aliceStoredChannel *channeldb.OpenChannel + var aliceStoredChannel *chanstate.OpenChannel for _, channel := range aliceStoredChannels { if channel.FundingOutpoint.String() == prevOut.String() { aliceStoredChannel = channel @@ -463,7 +464,7 @@ func createTestChannel(t *testing.T, alicePrivKey, bobPrivKey []byte, "%w", err) } - var bobStoredChannel *channeldb.OpenChannel + var bobStoredChannel *chanstate.OpenChannel for _, channel := range bobStoredChannels { if channel.FundingOutpoint.String() == prevOut.String() { bobStoredChannel = channel @@ -1187,7 +1188,7 @@ func (h *hopNetwork) createChannelLink(server, peer *mockServer, NotifyActiveChannel: func(wire.OutPoint) {}, NotifyInactiveChannel: func(wire.OutPoint) {}, NotifyInactiveLinkEvent: func(wire.OutPoint) {}, - NotifyChannelUpdate: func(*channeldb.OpenChannel) {}, + NotifyChannelUpdate: func(*chanstate.OpenChannel) {}, HtlcNotifier: server.htlcSwitch.cfg.HtlcNotifier, GetAliases: getAliases, ShouldFwdExpAccountability: func() bool { return true }, From 04b7486fde27004b2b48930826ef44f81cb8bdd1 Mon Sep 17 00:00:00 2001 From: ziggie Date: Tue, 7 Jul 2026 07:34:26 -0300 Subject: [PATCH 23/24] chanstate: complete open channel copy The Copy method omitted the CloseConfirmationHeight and Db fields when cloning an OpenChannel, so the returned copy silently diverged from the original. Copy both fields over so the clone is a faithful copy, which consumers that operate on channel copies rely on. --- chanstate/open_channel.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/chanstate/open_channel.go b/chanstate/open_channel.go index 5aefc18cd..2b96a07aa 100644 --- a/chanstate/open_channel.go +++ b/chanstate/open_channel.go @@ -1118,6 +1118,7 @@ func (c *OpenChannel) Copy() *OpenChannel { chanStatus: c.chanStatus, FundingBroadcastHeight: c.FundingBroadcastHeight, ConfirmationHeight: c.ConfirmationHeight, + CloseConfirmationHeight: c.CloseConfirmationHeight, NumConfsRequired: c.NumConfsRequired, ChannelFlags: c.ChannelFlags, IdentityPub: c.IdentityPub, @@ -1139,6 +1140,7 @@ func (c *OpenChannel) Copy() *OpenChannel { RevocationKeyLocator: c.RevocationKeyLocator, confirmedScid: c.confirmedScid, TapscriptRoot: c.TapscriptRoot, + Db: c.Db, } if c.FundingTxn != nil { From 489a6dabd97cc944ca7ae958acf967f252e3204b Mon Sep 17 00:00:00 2001 From: ziggie Date: Tue, 7 Jul 2026 08:58:44 -0300 Subject: [PATCH 24/24] chanstate: match active htlcs by identity ActiveHtlcs previously matched HTLCs across the local and remote commitment snapshots by hashing the onion blob. The onion blob is routing payload data and can be duplicated by buggy or malicious senders, so it is not a reliable key for identifying the same HTLC on both commitments. Match on the HTLC's channel identity instead: the channel-level HTLC index combined with the direction of the offer uniquely identifies an offered HTLC within the channel state. A test is added to lock in the new matching behavior. --- chanstate/open_channel.go | 32 +++++++++++++++---- chanstate/open_channel_test.go | 58 ++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 7 deletions(-) create mode 100644 chanstate/open_channel_test.go diff --git a/chanstate/open_channel.go b/chanstate/open_channel.go index 2b96a07aa..2011ebff4 100644 --- a/chanstate/open_channel.go +++ b/chanstate/open_channel.go @@ -1,7 +1,6 @@ package chanstate import ( - "crypto/sha256" "errors" "fmt" "net" @@ -822,17 +821,33 @@ func (c *OpenChannel) ActiveHtlcs() []HTLC { c.RLock() defer c.RUnlock() + // htlcKey uniquely identifies an HTLC within the channel state by its + // channel-level HTLC index and the direction of the offer. This is used + // to match the same HTLC across the local and remote commitment + // snapshots. + type htlcKey struct { + index uint64 + incoming bool + } + // We'll only return HTLC's that are locked into *both* commitment // transactions. So we'll iterate through their set of HTLC's to note // which ones are present on their commitment. - remoteHtlcs := make(map[[32]byte]struct{}) + // + // HTLC identity is defined by the channel-level HTLC index plus the + // direction of the offer. The onion blob is routing payload data and + // can be duplicated by buggy or malicious senders, so it is not a + // robust key for matching the same HTLC across commitment snapshots. + remoteHtlcs := make(map[htlcKey]struct{}) for _, htlc := range c.RemoteCommitment.Htlcs { log.Tracef("RemoteCommitment has htlc: id=%v, update=%v "+ "incoming=%v", htlc.HtlcIndex, htlc.LogIndex, htlc.Incoming) - onionHash := sha256.Sum256(htlc.OnionBlob[:]) - remoteHtlcs[onionHash] = struct{}{} + remoteHtlcs[htlcKey{ + index: htlc.HtlcIndex, + incoming: htlc.Incoming, + }] = struct{}{} } // Now that we know which HTLC's they have, we'll only mark the HTLC's @@ -843,9 +858,12 @@ func (c *OpenChannel) ActiveHtlcs() []HTLC { "incoming=%v", htlc.HtlcIndex, htlc.LogIndex, htlc.Incoming) - onionHash := sha256.Sum256(htlc.OnionBlob[:]) - if _, ok := remoteHtlcs[onionHash]; !ok { - log.Tracef("Skipped htlc due to onion mismatched: "+ + _, ok := remoteHtlcs[htlcKey{ + index: htlc.HtlcIndex, + incoming: htlc.Incoming, + }] + if !ok { + log.Tracef("Skipped htlc due to identity mismatch: "+ "id=%v, update=%v incoming=%v", htlc.HtlcIndex, htlc.LogIndex, htlc.Incoming) diff --git a/chanstate/open_channel_test.go b/chanstate/open_channel_test.go new file mode 100644 index 000000000..ace3e2c04 --- /dev/null +++ b/chanstate/open_channel_test.go @@ -0,0 +1,58 @@ +package chanstate + +import ( + "testing" + + "github.com/lightningnetwork/lnd/lnwire" + "github.com/stretchr/testify/require" +) + +// TestActiveHtlcsMatchesByHTLCIdentity asserts that ActiveHtlcs matches HTLCs +// by their channel identity, not by their onion blob. Onion blobs are routing +// payload data and can be duplicated, while the HTLC index plus direction +// identifies an offered HTLC within the channel state. +func TestActiveHtlcsMatchesByHTLCIdentity(t *testing.T) { + t.Parallel() + + var onionBlob [lnwire.OnionPacketSize]byte + onionBlob[0] = 1 + + matchingHTLC := HTLC{ + HtlcIndex: 7, + LogIndex: 10, + Incoming: false, + OnionBlob: onionBlob, + } + duplicateOnionHTLC := HTLC{ + HtlcIndex: 8, + LogIndex: 11, + Incoming: false, + OnionBlob: onionBlob, + } + oppositeDirectionHTLC := HTLC{ + HtlcIndex: 7, + LogIndex: 12, + Incoming: true, + OnionBlob: onionBlob, + } + + channel := &OpenChannel{ + LocalCommitment: ChannelCommitment{ + Htlcs: []HTLC{ + matchingHTLC, + duplicateOnionHTLC, + oppositeDirectionHTLC, + }, + }, + RemoteCommitment: ChannelCommitment{ + Htlcs: []HTLC{ + matchingHTLC, + }, + }, + } + + activeHtlcs := channel.ActiveHtlcs() + require.Len(t, activeHtlcs, 1) + require.Equal(t, matchingHTLC.HtlcIndex, activeHtlcs[0].HtlcIndex) + require.Equal(t, matchingHTLC.Incoming, activeHtlcs[0].Incoming) +}