What changed, and why it matters
This commit adds a safety flag so that LND's in-memory channel graph cache is not read until it has been fully populated. It is a defensive correctness change that prevents callers from seeing incomplete or inconsistent graph data while the cache is still being built, especially once cache loading becomes asynchronous in the future. There is no direct evidence in the commit of an exploitable security vulnerability being fixed.
Treat as a routine defensive correctness improvement. Review related commits that introduce asynchronous cache population to confirm the race condition is fully mitigated, and continue normal monitoring. No emergency action is warranted based solely on this diff.
Security signals we found
Prevents use of partially populated in-memory cache, avoiding potential inconsistent graph state during startup
Atomic flag guards concurrent reads against a cache that may still be loading
Changes are defensive and preparatory; no bug or exploit is described in the commit
No vendor disclosure of security relevance, CVE, or researcher attribution present in the commit
Evidence from the diff
The patch introduces an atomic.Bool named cacheLoaded to ChannelGraph. It is set to true only at the end of populateCache(). Four read methods (ForEachNodeDirectedChannel, FetchNodeFeatures, GraphSession, and ForEachNodeCached) are updated to check cacheLoaded in addition to the existing graphCache != nil guard. Until the flag is set, these methods fall back to reading from the underlying Store (database) instead of the partially populated cache. The commit message explicitly states this is preparatory work for asynchronous cache population.
Changed components
graph/db/graph.goChannelGraphGraphCacheForEachNodeDirectedChannelFetchNodeFeaturesGraphSessionForEachNodeCachedpopulateCacheInspect captured patch +11 / −5
diff --git a/graph/db/graph.go b/graph/db/graph.go
index 5e74bfc..3568986 100644
--- a/graph/db/graph.go
+++ b/graph/db/graph.go
@@ -30,7 +30,11 @@ type ChannelGraph struct {
started atomic.Bool
stopped atomic.Bool
- graphCache *GraphCache
+ // cacheLoaded is true if the initial graphCache population has
+ // finished. We use this to ensure that when performing any reads,
+ // we only read from the graphCache if it has been fully populated.
+ cacheLoaded atomic.Bool
+ graphCache *GraphCache
db Store
*topologyManager
@@ -206,6 +210,8 @@ func (c *ChannelGraph) populateCache(ctx context.Context) error {
}
}
+ c.cacheLoaded.Store(true)
+
log.Infof("Finished populating in-memory channel graph (took %v, %s)",
time.Since(startTime), c.graphCache.Stats())
@@ -226,7 +232,7 @@ func (c *ChannelGraph) ForEachNodeDirectedChannel(ctx context.Context,
node route.Vertex, cb func(channel *DirectedChannel) error,
reset func()) error {
- if c.graphCache != nil {
+ if c.graphCache != nil && c.cacheLoaded.Load() {
return c.graphCache.ForEachChannel(node, cb)
}
@@ -247,7 +253,7 @@ func (c *ChannelGraph) ForEachNodeDirectedChannel(ctx context.Context,
func (c *ChannelGraph) FetchNodeFeatures(ctx context.Context,
node route.Vertex) (*lnwire.FeatureVector, error) {
- if c.graphCache != nil {
+ if c.graphCache != nil && c.cacheLoaded.Load() {
return c.graphCache.GetFeatures(node), nil
}
@@ -261,7 +267,7 @@ func (c *ChannelGraph) FetchNodeFeatures(ctx context.Context,
func (c *ChannelGraph) GraphSession(ctx context.Context,
cb func(graph NodeTraverser) error, reset func()) error {
- if c.graphCache != nil {
+ if c.graphCache != nil && c.cacheLoaded.Load() {
return cb(c)
}
@@ -277,7 +283,7 @@ func (c *ChannelGraph) ForEachNodeCached(ctx context.Context,
cb func(ctx context.Context, node route.Vertex, addrs []net.Addr,
chans map[uint64]*DirectedChannel) error, reset func()) error {
- if !withAddrs && c.graphCache != nil {
+ if !withAddrs && c.graphCache != nil && c.cacheLoaded.Load() {
return c.graphCache.ForEachNode(
func(node route.Vertex,
channels map[uint64]*DirectedChannel) error {
Why this scored 31/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.