@@ -418,7 +418,8 @@ func CreateWithBackend(backend kvdb.Backend, modifiers ...OptionModifier) (*DB,
418418 linkNodeDB : & LinkNodeDB {
419419 backend : backend ,
420420 },
421- backend : backend ,
421+ backend : backend ,
422+ tombstoneClosedChannels : opts .tombstoneClosedChannels ,
422423 },
423424 clock : opts .clock ,
424425 dryRun : opts .dryRun ,
@@ -548,6 +549,12 @@ type ChannelStateDB struct {
548549 // backend points to the actual backend holding the channel state
549550 // database. This may be a real backend or a cache middleware.
550551 backend kvdb.Backend
552+
553+ // tombstoneClosedChannels is set by OptionTombstoneClosedChannels.
554+ // When true, CloseChannel skips deleting nested per-channel state and
555+ // relies on the outpointBucket flip to outpointClosed as the
556+ // authoritative closed-channel signal.
557+ tombstoneClosedChannels bool
551558}
552559
553560// GetParentDB returns the "main" channeldb.DB object that is the owner of this
@@ -621,7 +628,7 @@ func (c *ChannelStateDB) fetchOpenChannels(tx kvdb.RTx,
621628
622629 // Finally, we both of the necessary buckets retrieved, fetch
623630 // all the active channels related to this node.
624- nodeChannels , err := c .fetchNodeChannels (chainBucket )
631+ nodeChannels , err := c .fetchNodeChannels (tx , chainBucket )
625632 if err != nil {
626633 return fmt .Errorf ("unable to read channel for " +
627634 "chain_hash=%x, node_key=%x: %v" ,
@@ -637,12 +644,19 @@ func (c *ChannelStateDB) fetchOpenChannels(tx kvdb.RTx,
637644
638645// fetchNodeChannels retrieves all active channels from the target chainBucket
639646// which is under a node's dedicated channel bucket. This function is typically
640- // used to fetch all the active channels related to a particular node.
641- func (c * ChannelStateDB ) fetchNodeChannels (chainBucket kvdb.RBucket ) (
642- []* OpenChannel , error ) {
647+ // used to fetch all the active channels related to a particular node. Channels
648+ // already flipped to outpointClosed in the outpoint index are skipped silently
649+ // — readers see only channels that are still considered open.
650+ func (c * ChannelStateDB ) fetchNodeChannels (tx kvdb.RTx ,
651+ chainBucket kvdb.RBucket ) ([]* OpenChannel , error ) {
643652
644653 var channels []* OpenChannel
645654
655+ // Hoist the outpoint-bucket lookup so the closed-channel check inside
656+ // the loop is a per-iteration map probe rather than a tx-level bucket
657+ // resolve.
658+ opBucket := tx .ReadBucket (outpointBucket )
659+
646660 // A node may have channels on several chains, so for each known chain,
647661 // we'll extract all the channels.
648662 err := chainBucket .ForEach (func (chanPoint , v []byte ) error {
@@ -651,12 +665,24 @@ func (c *ChannelStateDB) fetchNodeChannels(chainBucket kvdb.RBucket) (
651665 return nil
652666 }
653667
668+ // Skip already-closed channels. The chanBucket still exists
669+ // on disk on tombstone-enabled backends; the outpoint flip is
670+ // the sole signal that the channel should be treated as
671+ // closed.
672+ isClosed , err := isOutpointClosed (opBucket , chanPoint )
673+ if err != nil {
674+ return err
675+ }
676+ if isClosed {
677+ return nil
678+ }
679+
654680 // Once we've found a valid channel bucket, we'll extract it
655681 // from the node's chain bucket.
656682 chanBucket := chainBucket .NestedReadBucket (chanPoint )
657683
658684 var outPoint wire.OutPoint
659- err : = graphdb .ReadOutpoint (
685+ err = graphdb .ReadOutpoint (
660686 bytes .NewReader (chanPoint ), & outPoint ,
661687 )
662688 if err != nil {
@@ -771,6 +797,11 @@ func (c *ChannelStateDB) FetchPermAndTempPeers(
771797 return ErrNoChanDBExists
772798 }
773799
800+ // Hoist the outpoint-bucket lookup so the closed-channel check
801+ // inside the nested chainBucket.ForEach below is a per-channel
802+ // map probe rather than a tx-level bucket resolve.
803+ opBucket := tx .ReadBucket (outpointBucket )
804+
774805 openChanErr := openChanBucket .ForEach (func (nodePub ,
775806 v []byte ) error {
776807
@@ -804,6 +835,22 @@ func (c *ChannelStateDB) FetchPermAndTempPeers(
804835 return nil
805836 }
806837
838+ // Skip already-closed channels: they are
839+ // logically closed even though their
840+ // per-channel state still resides under
841+ // chainBucket. The closed peer's protected
842+ // status is established below via the
843+ // historical-channel scan.
844+ isClosed , err := isOutpointClosed (
845+ opBucket , chanPoint ,
846+ )
847+ if err != nil {
848+ return err
849+ }
850+ if isClosed {
851+ return nil
852+ }
853+
807854 chanBucket := chainBucket .NestedReadBucket (
808855 chanPoint ,
809856 )
@@ -976,6 +1023,11 @@ func (c *ChannelStateDB) channelScanner(tx kvdb.RTx,
9761023 return ErrNoActiveChannels
9771024 }
9781025
1026+ // Hoist the outpoint-bucket lookup so the closed-channel
1027+ // check inside the per-chain ForEach below pays one tx-level
1028+ // bucket resolve total instead of one per visited chanKey.
1029+ opBucket := tx .ReadBucket (outpointBucket )
1030+
9791031 // Within the node channel bucket, are the set of node pubkeys
9801032 // we have channels with, we don't know the entire set, so we'll
9811033 // check them all.
@@ -1024,6 +1076,19 @@ func (c *ChannelStateDB) channelScanner(tx kvdb.RTx,
10241076 return err
10251077 }
10261078
1079+ // An already-closed channel is logically gone
1080+ // and must not be surfaced by lookup-style
1081+ // scans.
1082+ isClosed , err := isOutpointClosed (
1083+ opBucket , targetChanBytes ,
1084+ )
1085+ if err != nil {
1086+ return err
1087+ }
1088+ if isClosed {
1089+ return nil
1090+ }
1091+
10271092 chanBucket := chainBucket .NestedReadBucket (
10281093 targetChanBytes ,
10291094 )
@@ -1187,7 +1252,9 @@ func fetchChannels(c *ChannelStateDB, filters ...fetchChannelsFilter) (
11871252 "bucket for chain=%x" , chainHash [:])
11881253 }
11891254
1190- nodeChans , err := c .fetchNodeChannels (chainBucket )
1255+ nodeChans , err := c .fetchNodeChannels (
1256+ tx , chainBucket ,
1257+ )
11911258 if err != nil {
11921259 return fmt .Errorf ("unable to read " +
11931260 "channel for chain_hash=%x, " +
0 commit comments