rules: allow non-present channels in restriction#1215
rules: allow non-present channels in restriction#1215bitromortac wants to merge 3 commits intolightninglabs:masterfrom
Conversation
Summary of ChangesHello @bitromortac, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request modifies the channel restriction logic to enhance its resilience against dynamic channel states. It specifically removes a strict check that previously required all channels in a deny list to be currently open, preventing unnecessary failures when channels are closed. This ensures the firewall remains operational and robust to routine channel changes without compromising security, as operations on closed channels are inherently impossible. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request modifies the channel-restriction rule to be more resilient to channel closures. By removing the strict check that required a restricted channel to be open, the firewall can now handle deny lists containing closed or non-existent channels without failing. This is a good improvement for operational stability. The code change is clean and also improves variable scoping. I've noted one potential performance side-effect of this change with a suggestion for how to mitigate it.
368c7e4 to
a8fa7e5
Compare
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request modifies the channel restriction rule to allow non-present channels in the deny list. The changes include adding a checkedIDs map to track channel IDs that have been checked, updating the maybeUpdateChannelMaps function to handle a list of channel IDs, and modifying the checkers function to clear the negative cache and return an error if a channel ID is not found and has been checked. This ensures that the firewall remains resilient to channel changes and that the next request will trigger a fresh sync.
I am having trouble creating individual review comments. Click here to see my feedback.
rules/channel_restrictions.go (137-144)
This logic checks if any of the channel IDs need a sync. If one ID needs a sync, needsSync is set to true and the loop breaks. This is good. However, the mutex is unlocked before calling LND, which means another goroutine could modify chanIDToPoint or checkedIDs before LND returns the channel list. This could lead to a race condition where the same channel ID is checked multiple times, or a channel ID is missed entirely.
c.mu.Lock()
var needsSync bool
for _, id := range chanIDs {
_, known := c.chanIDToPoint[id]
if !known && !c.checkedIDs[id] {
needsSync = true
break
}
}
rules/channel_restrictions.go (75-78)
The original code iterated through each chanID in channels.DenyList and called maybeUpdateChannelMaps individually. This could lead to multiple calls to LND to fetch the list of open channels if multiple chanIDs are unknown. It's more efficient to update channel maps once for the entire deny list.
err := c.maybeUpdateChannelMaps(ctx, cfg, chanID)
if err != nil {
return nil, err
}
rules/channel_restrictions.go (131-133)
The description of the function maybeUpdateChannelMaps is not accurate anymore. It should reflect that the function now accepts a list of channel IDs and updates the maps if any of them are not found or have not been checked previously.
// iff any of the channels given by the caller are not found in the current
// map set and have not been checked previously.
rules/channel_restrictions.go (162-169)
The code iterates through the channels returned by LND and updates the chanPointToID and chanIDToPoint maps. However, there's no check to see if the channel already exists in the maps. This could lead to unnecessary updates and potential performance issues.
for _, channel := range chans {
c.chanPointToID[channel.ChannelPoint] = channel.ChannelID
c.chanIDToPoint[channel.ChannelID] = channel.ChannelPoint
}
rules/channel_restrictions.go (328-334)
If hasCheckedIDs returns true, the code clears the checkedIDs map and returns an error, prompting the user to retry the request. This could lead to a retry loop if the channel is still not found in LND's list of open channels. Consider adding a delay or a maximum number of retries to prevent the loop from running indefinitely.
return fmt.Errorf("unknown " +
"channel point, " +
"please retry the " +
"request")a8fa7e5 to
c3f366f
Compare
|
@claude review |
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
ViktorT-11
left a comment
There was a problem hiding this comment.
I think this looks good, despite this code being quite complex and hard to follow fully. Leaving one comment regarding locking of the mutex.
This also introduces the use of testify mock to check which calls were made.
26c9cde to
30443a4
Compare
|
@claude review |
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
30443a4 to
c565ac8
Compare
ViktorT-11
left a comment
There was a problem hiding this comment.
Thanks for the changes, I think that definitely makes things simpler to reason about :)! I think this looks good after all the linter issues have been addressed.
The channel-restriction rule was previously fragile because it failed to initialize when a restricted channel was closed while the session was inactive. This often caused unnecessary session invalidation and blocked users from managing their nodes. This change makes the rule resilient by allowing it to start even if some channels in the deny-list are missing from the node's current active set. To maintain high performance, this implements a negative cache that tracks unknown channel IDs, shielding LND from redundant RPC calls during request evaluation. Only having a negative cache without invalidation can be a security problem. Someone could apply a rule with a future guessed channel id such that the channel restriction populates the checkedIDs map with it. After the channel was opened, we'd then allow making actions on the channel because we don't know about the channel's id in the getChannelID check. To ensure security isn't compromised by the cache, this adds a self-healing retry mechanism. If the firewall encounters an unknown channel outpoint while it still has unmapped restricted IDs, it clears the negative cache and forces a single retry in the next RPC call. This ensures that any newly opened restricted channels are correctly identified and blocked without adding latency to the common path. Note: This approach deliberately accepts potential cache thrashing in the edge case where a user repeatedly requests an unknown channel point while a permanently missing ID exists in the deny list. This trade-off is accepted to prioritize security (fail close) over performance in this specific invalid state.
c565ac8 to
93cb4dc
Compare
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request improves the resilience of the channel-restriction rule by allowing it to handle cases where channels in the deny list are not currently open. The changes are well-implemented, introducing a caching mechanism (checkedIDs) and a retry logic to gracefully handle unknown channels without failing initialization. The core logic in maybeUpdateChannelMaps and the new getChannelIDWithRetryCheck function is sound and robust. The accompanying new test, TestChannelRestrictResilience, is very thorough and covers the complex interaction of states well. The refactoring of tests to use testify/mock is also a good improvement. Overall, this is a high-quality change that effectively addresses the stated problem.
Concerning this, it will be fixed by |
|
|
||
|
|
||
|
|
There was a problem hiding this comment.
will fix this in with the next push
The
channel-restrictionrule previously failed if any channel in its deny list wasn't currently open. A routine channel closure could block a request withinvalid channel ID. This change removes that strict check to ensure the firewall remains resilient to channel changes. It is fine to remove the check, because we can't operate on closed channels anyhow.