@@ -38,14 +38,22 @@ type ChannelRestrictMgr struct {
3838 // chanPointToID is a map from channel point to channel ID's for our
3939 // known set of channels.
4040 chanPointToID map [string ]uint64
41- mu sync.Mutex
41+
42+ // checkedIDs tracks channel IDs that we have already attempted to find
43+ // in LND's list of open channels but were not present.
44+ checkedIDs map [uint64 ]bool
45+
46+ // mu is a mutex used to protect the maps and other state in the manager
47+ // from concurrent access.
48+ mu sync.Mutex
4249}
4350
4451// NewChannelRestrictMgr constructs a new instance of a ChannelRestrictMgr.
4552func NewChannelRestrictMgr () * ChannelRestrictMgr {
4653 return & ChannelRestrictMgr {
4754 chanIDToPoint : make (map [uint64 ]string ),
4855 chanPointToID : make (map [string ]uint64 ),
56+ checkedIDs : make (map [uint64 ]bool ),
4957 }
5058}
5159
@@ -72,10 +80,13 @@ func (c *ChannelRestrictMgr) NewEnforcer(ctx context.Context, cfg Config,
7280 chanMap := make (map [uint64 ]bool , len (channels .DenyList ))
7381 for _ , chanID := range channels .DenyList {
7482 chanMap [chanID ] = true
75- err := c .maybeUpdateChannelMaps (ctx , cfg , chanID )
76- if err != nil {
77- return nil , err
78- }
83+ }
84+
85+ // We'll attempt to update our internal channel maps for any IDs in our
86+ // deny list that we don't already know about and haven't checked yet.
87+ err := c .maybeUpdateChannelMaps (ctx , cfg , channels .DenyList )
88+ if err != nil {
89+ return nil , err
7990 }
8091
8192 return & ChannelRestrictEnforcer {
@@ -118,17 +129,26 @@ func (c *ChannelRestrictMgr) EmptyValue() Values {
118129}
119130
120131// maybeUpdateChannelMaps updates the ChannelRestrictMgrs set of known channels
121- // iff the channel given by the caller is not found in the current map set.
132+ // iff any of the channels given by the caller are not found in the current
133+ // map set and have not been checked previously.
122134func (c * ChannelRestrictMgr ) maybeUpdateChannelMaps (ctx context.Context ,
123- cfg Config , chanID uint64 ) error {
135+ cfg Config , chanIDs [] uint64 ) error {
124136
125137 c .mu .Lock ()
126138 defer c .mu .Unlock ()
127139
128- // If we already know of this channel, we don't need to go update our
129- // maps.
130- _ , ok := c .chanIDToPoint [chanID ]
131- if ok {
140+ var needsSync bool
141+ for _ , id := range chanIDs {
142+ _ , known := c .chanIDToPoint [id ]
143+ if ! known && ! c .checkedIDs [id ] {
144+ needsSync = true
145+ break
146+ }
147+ }
148+
149+ // If we already know about all these channels or have checked them,
150+ // then we don't need to do anything.
151+ if ! needsSync {
132152 return nil
133153 }
134154
@@ -139,39 +159,59 @@ func (c *ChannelRestrictMgr) maybeUpdateChannelMaps(ctx context.Context,
139159 return err
140160 }
141161
142- var (
143- found bool
144- point string
145- id uint64
146- )
147-
148- // Update our set of maps and also make sure that the channel specified
149- // by the caller is valid given our set of open channels.
162+ // Update our set of maps with all currently open channels.
150163 for _ , channel := range chans {
151- point = channel .ChannelPoint
152- id = channel .ChannelID
153-
154- c .chanPointToID [point ] = id
155- c .chanIDToPoint [id ] = point
156-
157- if id == chanID {
158- found = true
159- }
164+ c .chanPointToID [channel .ChannelPoint ] = channel .ChannelID
165+ c .chanIDToPoint [channel .ChannelID ] = channel .ChannelPoint
160166 }
161167
162- if ! found {
163- return fmt .Errorf ("invalid channel ID" )
168+ // For every ID we were looking for, if it's still not in our known
169+ // maps, we mark it as checked so we don't trigger another sync for it.
170+ for _ , id := range chanIDs {
171+ if _ , ok := c .chanIDToPoint [id ]; ! ok {
172+ c .checkedIDs [id ] = true
173+ }
164174 }
165175
166176 return nil
167177}
168178
169- func (c * ChannelRestrictMgr ) getChannelID (point string ) (uint64 , bool ) {
179+ // getChannelIDWithRetryCheck performs an atomic lookup of a channel point
180+ // while also determining whether the caller should retry their request to allow
181+ // for channel mapping resynchronization. When a channel point is not found in
182+ // our known mappings, we must decide whether to allow the operation or request
183+ // a retry. If any entries in the deny list remain unmapped to channel points,
184+ // we cannot be certain whether the unknown channel is restricted, so we signal
185+ // for a retry to trigger a fresh synchronization with the node's current
186+ // channel state.
187+ func (c * ChannelRestrictMgr ) getChannelIDWithRetryCheck (point string ,
188+ denyList []uint64 ) (id uint64 , found bool , shouldRetry bool ) {
189+
170190 c .mu .Lock ()
171191 defer c .mu .Unlock ()
172192
173- id , ok := c .chanPointToID [point ]
174- return id , ok
193+ // First, check if we know this channel point.
194+ id , found = c .chanPointToID [point ]
195+ if found {
196+ return id , true , false
197+ }
198+
199+ // Channel not found. Check if we have any unmapped deny list entries.
200+ // If we do, we can't be sure whether this unknown channel is
201+ // restricted or not, so we must trigger a retry to refresh our
202+ // mappings.
203+ for _ , restrictedID := range denyList {
204+ if _ , mapped := c .chanIDToPoint [restrictedID ]; ! mapped {
205+ // We have unmapped restrictions - clear the negative
206+ // cache to force a fresh sync on retry.
207+ c .checkedIDs = make (map [uint64 ]bool )
208+ return 0 , false , true
209+ }
210+ }
211+
212+ // Channel not found, but all deny list entries are mapped, so this
213+ // unknown channel is definitely not in our restriction list.
214+ return 0 , false , false
175215}
176216
177217// ChannelRestrictEnforcer enforces requests and responses against a
@@ -278,8 +318,23 @@ func (c *ChannelRestrictEnforcer) checkers() map[string]mid.RoundTripChecker {
278318 "%s:%d" , txid .String (), index ,
279319 )
280320
281- id , ok := c .mgr .getChannelID (point )
282- if ! ok {
321+ // Atomically check if we know this channel and
322+ // whether we need to retry.
323+ id , found , shouldRetry := c .mgr .
324+ getChannelIDWithRetryCheck (
325+ point , c .DenyList ,
326+ )
327+
328+ if shouldRetry {
329+ return fmt .Errorf ("unknown channel " +
330+ "point, please retry the " +
331+ "request" )
332+ }
333+
334+ if ! found {
335+ // Channel is unknown but all deny list
336+ // entries are mapped, so this channel
337+ // is definitely not restricted.
283338 return nil
284339 }
285340
0 commit comments