What changed, and why it matters
This is a routine internal code cleanup in LND's autopilot (automatic channel-opening assistant). It changes a callback to pass a node public key directly instead of wrapping it in a full Node object, and skips nodes with no channels during graph traversal. There is no security fix or externally visible behavior change.
No security action required. Treat as normal refactoring/technical-debt cleanup during code review.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit refactors the autopilot.ChannelGraph.ForEachNodesChannels callback signature from func(context.Context, Node, []ChannelEdge) error to func(context.Context, NodeID, []ChannelEdge) error. Implementations in databaseChannelGraph and databaseChannelGraphCached now skip vertices with len(chans)==0 and pass NodeID(node) directly, avoiding construction of dbNode/dbNodeCached objects whose Addrs field is unused or empty. Callers in prefattach.go and simple_graph.go are updated to use the NodeID directly. The change is purely structural and removes an unused import; no security-sensitive logic is altered.
Changed components
autopilot/graph.goautopilot/interface.goautopilot/prefattach.goautopilot/prefattach_test.goautopilot/simple_graph.goInspect captured patch +43 / −51
diff --git a/autopilot/graph.go b/autopilot/graph.go
index d82cdd3..19bd380 100644
--- a/autopilot/graph.go
+++ b/autopilot/graph.go
@@ -101,14 +101,14 @@ func (d *databaseChannelGraph) ForEachNode(ctx context.Context,
}, reset)
}
-// ForEachNodesChannels iterates through all connected nodes, and for each node,
-// all the channels that connect to it. The passed callback will be called with
-// the context, the Node itself, and a slice of ChannelEdge that connect to the
-// node.
+// ForEachNodesChannels iterates through all connected nodes, and for each
+// node, all the channels that connect to it. The passed callback will be
+// called with the context, the node's pubkey, and a slice of ChannelEdge
+// that connect to the node.
//
// NOTE: Part of the autopilot.ChannelGraph interface.
func (d *databaseChannelGraph) ForEachNodesChannels(ctx context.Context,
- cb func(context.Context, Node, []*ChannelEdge) error,
+ cb func(context.Context, NodeID, []*ChannelEdge) error,
reset func()) error {
// The channel-scoring callers only need topology data here. Address
@@ -117,6 +117,10 @@ func (d *databaseChannelGraph) ForEachNodesChannels(ctx context.Context,
ctx, func(ctx context.Context, node route.Vertex,
chans map[uint64]*graphdb.DirectedChannel) error {
+ if len(chans) == 0 {
+ return nil
+ }
+
edges := make([]*ChannelEdge, 0, len(chans))
for _, channel := range chans {
edges = append(edges, &ChannelEdge{
@@ -128,9 +132,7 @@ func (d *databaseChannelGraph) ForEachNodesChannels(ctx context.Context,
})
}
- return cb(ctx, &dbNode{
- pub: node,
- }, edges)
+ return cb(ctx, NodeID(node), edges)
}, reset,
)
}
@@ -206,20 +208,24 @@ func (dc *databaseChannelGraphCached) ForEachNode(ctx context.Context,
}, reset)
}
-// ForEachNodesChannels iterates through all connected nodes, and for each node,
-// all the channels that connect to it. The passed callback will be called with
-// the context, the Node itself, and a slice of ChannelEdge that connect to the
-// node.
+// ForEachNodesChannels iterates through all connected nodes, and for each
+// node, all the channels that connect to it. The passed callback will be
+// called with the context, the node's pubkey, and a slice of ChannelEdge
+// that connect to the node.
//
// NOTE: Part of the autopilot.ChannelGraph interface.
func (dc *databaseChannelGraphCached) ForEachNodesChannels(ctx context.Context,
- cb func(context.Context, Node, []*ChannelEdge) error,
+ cb func(context.Context, NodeID, []*ChannelEdge) error,
reset func()) error {
return dc.db.ForEachNodeCached(ctx, func(ctx context.Context,
n route.Vertex,
channels map[uint64]*graphdb.DirectedChannel) error {
+ if len(channels) == 0 {
+ return nil
+ }
+
edges := make([]*ChannelEdge, 0, len(channels))
for cid, channel := range channels {
edges = append(edges, &ChannelEdge{
@@ -229,18 +235,7 @@ func (dc *databaseChannelGraphCached) ForEachNodesChannels(ctx context.Context,
})
}
- if len(channels) > 0 {
- node := dbNodeCached{
- node: n,
- channels: channels,
- }
-
- if err := cb(ctx, node, edges); err != nil {
- return err
- }
- }
-
- return nil
+ return cb(ctx, NodeID(n), edges)
}, reset)
}
diff --git a/autopilot/interface.go b/autopilot/interface.go
index b3fc3de..7181323 100644
--- a/autopilot/interface.go
+++ b/autopilot/interface.go
@@ -84,10 +84,10 @@ type ChannelGraph interface {
// ForEachNodesChannels iterates through all connected nodes, and for
// each node, all the channels that connect to it. The passed callback
- // will be called with the context, the Node itself, and a slice of
+ // will be called with the context, the node's pubkey, and a slice of
// ChannelEdge that connect to the node.
ForEachNodesChannels(ctx context.Context,
- cb func(context.Context, Node, []*ChannelEdge) error,
+ cb func(context.Context, NodeID, []*ChannelEdge) error,
reset func()) error
}
diff --git a/autopilot/prefattach.go b/autopilot/prefattach.go
index 267c13d..7d36674 100644
--- a/autopilot/prefattach.go
+++ b/autopilot/prefattach.go
@@ -90,7 +90,7 @@ func (p *PrefAttachment) NodeScores(ctx context.Context, g ChannelGraph,
seenChans = make(map[uint64]struct{})
)
err := g.ForEachNodesChannels(
- ctx, func(_ context.Context, node Node,
+ ctx, func(_ context.Context, node NodeID,
channels []*ChannelEdge) error {
for _, e := range channels {
@@ -121,7 +121,7 @@ func (p *PrefAttachment) NodeScores(ctx context.Context, g ChannelGraph,
var maxChans int
nodeChanNum := make(map[NodeID]int)
err = g.ForEachNodesChannels(
- ctx, func(ctx context.Context, node Node,
+ ctx, func(ctx context.Context, node NodeID,
edges []*ChannelEdge) error {
var nodeChans int
@@ -154,17 +154,16 @@ func (p *PrefAttachment) NodeScores(ctx context.Context, g ChannelGraph,
// If this node is not among our nodes to score, we can
// return early.
- nID := NodeID(node.PubKey())
- if _, ok := nodes[nID]; !ok {
+ if _, ok := nodes[node]; !ok {
log.Tracef("Node %x not among nodes to score, "+
- "ignoring", nID[:])
+ "ignoring", node[:])
return nil
}
// Otherwise we'll record the number of channels.
- nodeChanNum[nID] = nodeChans
+ nodeChanNum[node] = nodeChans
log.Tracef("Counted %v channels for node %x", nodeChans,
- nID[:])
+ node[:])
return nil
}, func() {
diff --git a/autopilot/prefattach_test.go b/autopilot/prefattach_test.go
index efddfb8..cdaec87 100644
--- a/autopilot/prefattach_test.go
+++ b/autopilot/prefattach_test.go
@@ -246,11 +246,11 @@ func TestPrefAttachmentSelectGreedyAllocation(t *testing.T) {
twoChans := false
nodes := make(map[NodeID]struct{})
err = graph.ForEachNodesChannels(
- ctx, func(_ context.Context, node Node,
+ ctx, func(_ context.Context, node NodeID,
edges []*ChannelEdge) error {
numNodes++
- nodes[node.PubKey()] = struct{}{}
+ nodes[node] = struct{}{}
numChans := 0
for range edges {
@@ -619,14 +619,15 @@ func (m *memChannelGraph) ForEachNode(ctx context.Context,
return nil
}
-// ForEachNodesChannels iterates through all connected nodes, and for each node,
-// all the channels that connect to it. The passed callback will be called with
-// the context, the Node itself, and a slice of ChannelEdge that connect to the
-// node.
+// ForEachNodesChannels iterates through all connected nodes, and for each
+// node, all the channels that connect to it. The passed callback will be
+// called with the context, the node's pubkey, and a slice of ChannelEdge
+// that connect to the node.
//
// NOTE: Part of the autopilot.ChannelGraph interface.
func (m *memChannelGraph) ForEachNodesChannels(ctx context.Context,
- cb func(context.Context, Node, []*ChannelEdge) error, _ func()) error {
+ cb func(context.Context, NodeID, []*ChannelEdge) error,
+ _ func()) error {
for _, node := range m.graph {
edges := make([]*ChannelEdge, 0, len(node.chans))
@@ -634,7 +635,7 @@ func (m *memChannelGraph) ForEachNodesChannels(ctx context.Context,
edges = append(edges, &node.chans[i])
}
- if err := cb(ctx, node, edges); err != nil {
+ if err := cb(ctx, NewNodeID(node.pub), edges); err != nil {
return err
}
}
diff --git a/autopilot/simple_graph.go b/autopilot/simple_graph.go
index 44f5149..d6072cd 100644
--- a/autopilot/simple_graph.go
+++ b/autopilot/simple_graph.go
@@ -2,8 +2,6 @@ package autopilot
import (
"context"
-
- "github.com/lightningnetwork/lnd/routing/route"
)
// diameterCutoff is used to discard nodes in the diameter calculation.
@@ -35,12 +33,11 @@ func NewSimpleGraph(ctx context.Context, g ChannelGraph) (*SimpleGraph, error) {
// The returned index is then used to create a simplified adjacency list
// where each node is identified by its index instead of its pubkey, and
// also to create a mapping from node index to node pubkey.
- getNodeIndex := func(node route.Vertex) int {
- key := NodeID(node)
- nodeIndex, ok := nodes[key]
+ getNodeIndex := func(node NodeID) int {
+ nodeIndex, ok := nodes[node]
if !ok {
- nodes[key] = nextIndex
+ nodes[node] = nextIndex
nodeIndex = nextIndex
nextIndex++
}
@@ -51,12 +48,12 @@ func NewSimpleGraph(ctx context.Context, g ChannelGraph) (*SimpleGraph, error) {
// Iterate over each node and each channel and update the adj and the
// node index.
err := g.ForEachNodesChannels(ctx, func(_ context.Context,
- node Node, channels []*ChannelEdge) error {
+ node NodeID, channels []*ChannelEdge) error {
- u := getNodeIndex(node.PubKey())
+ u := getNodeIndex(node)
for _, edge := range channels {
- v := getNodeIndex(edge.Peer)
+ v := getNodeIndex(NodeID(edge.Peer))
adj[u] = append(adj[u], v)
}
Why this scored 14/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.