What changed, and why it matters
This commit refactors how LND fetches multiple channel records from its database. Instead of building each result one-by-one inside a loop, it first collects all raw rows and then builds them in a single batch. The change appears to be a performance optimization with no obvious security impact. There is no mention of fixing a vulnerability, and the diff does not introduce new input handling or access controls.
No security action required. Treat as a routine performance refactor. If reviewing further, verify that batchBuildChannelEdges preserves the same error handling and nil-policy behavior as the previous inline code.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch modifies FetchChanInfos in graph/db/sql_store.go to defer construction of ChannelEdge objects. A callback now appends each sqlc.GetChannelsBySCIDWithPoliciesRow to a slice, and after the iteration completes it calls a new batchBuildChannelEdges helper. The old inline per-row logic (buildNodes, getAndBuildEdgeInfo, extractChannelPolicies, getAndBuildChanPolicies) is removed from the callback. A helper function buildNodes is deleted, and accessor methods Node1/Node2 are added to GetChannelsBySCIDWithPoliciesRow in sqldb/sqlc/db_custom.go to support the ChannelAndNodes interface used by the batch builder. The change is structural/performance-oriented.
Changed components
graph/db/sql_store.gosqldb/sqlc/db_custom.goFetchChanInfosbatchBuildChannelEdgesChannelAndNodes interfaceInspect captured patch +39 / −59
diff --git a/graph/db/sql_store.go b/graph/db/sql_store.go
index 33e3e1c..3276f67 100644
--- a/graph/db/sql_store.go
+++ b/graph/db/sql_store.go
@@ -2055,55 +2055,40 @@ func (s *SQLStore) FetchChanInfos(chanIDs []uint64) ([]ChannelEdge, error) {
edges = make(map[uint64]ChannelEdge)
)
err := s.db.ExecTx(ctx, sqldb.ReadTxOpt(), func(db SQLQueries) error {
+ // First, collect all channel rows.
+ var channelRows []sqlc.GetChannelsBySCIDWithPoliciesRow
chanCallBack := func(ctx context.Context,
row sqlc.GetChannelsBySCIDWithPoliciesRow) error {
- node1, node2, err := buildNodes(
- ctx, db, row.GraphNode, row.GraphNode_2,
- )
- if err != nil {
- return fmt.Errorf("unable to fetch nodes: %w",
- err)
- }
-
- edge, err := getAndBuildEdgeInfo(
- ctx, db, s.cfg.ChainHash, row.GraphChannel,
- node1.PubKeyBytes, node2.PubKeyBytes,
- )
- if err != nil {
- return fmt.Errorf("unable to build "+
- "channel info: %w", err)
- }
-
- dbPol1, dbPol2, err := extractChannelPolicies(row)
- if err != nil {
- return fmt.Errorf("unable to extract channel "+
- "policies: %w", err)
- }
-
- p1, p2, err := getAndBuildChanPolicies(
- ctx, db, dbPol1, dbPol2, edge.ChannelID,
- node1.PubKeyBytes, node2.PubKeyBytes,
- )
- if err != nil {
- return fmt.Errorf("unable to build channel "+
- "policies: %w", err)
- }
+ channelRows = append(channelRows, row)
+ return nil
+ }
- edges[edge.ChannelID] = ChannelEdge{
- Info: edge,
- Policy1: p1,
- Policy2: p2,
- Node1: node1,
- Node2: node2,
- }
+ err := s.forEachChanWithPoliciesInSCIDList(
+ ctx, db, chanCallBack, chanIDs,
+ )
+ if err != nil {
+ return err
+ }
+ if len(channelRows) == 0 {
return nil
}
- return s.forEachChanWithPoliciesInSCIDList(
- ctx, db, chanCallBack, chanIDs,
+ // Batch build all channel edges.
+ chans, err := batchBuildChannelEdges(
+ ctx, s.cfg, db, channelRows,
)
+ if err != nil {
+ return fmt.Errorf("unable to build channel edges: %w",
+ err)
+ }
+
+ for _, c := range chans {
+ edges[c.Info.ChannelID] = c
+ }
+
+ return err
}, func() {
clear(edges)
})
@@ -4199,25 +4184,6 @@ func buildChanPolicy(dbPolicy sqlc.GraphChannelPolicy, channelID uint64,
}, nil
}
-// buildNodes builds the models.LightningNode instances for the
-// given row which is expected to be a sqlc type that contains node information.
-func buildNodes(ctx context.Context, db SQLQueries, dbNode1,
- dbNode2 sqlc.GraphNode) (*models.LightningNode, *models.LightningNode,
- error) {
-
- node1, err := buildNode(ctx, db, dbNode1)
- if err != nil {
- return nil, nil, err
- }
-
- node2, err := buildNode(ctx, db, dbNode2)
- if err != nil {
- return nil, nil, err
- }
-
- return node1, node2, nil
-}
-
// extractChannelPolicies extracts the sqlc.GraphChannelPolicy records from the give
// row which is expected to be a sqlc type that contains channel policy
// information. It returns two policies, which may be nil if the policy
diff --git a/sqldb/sqlc/db_custom.go b/sqldb/sqlc/db_custom.go
index 7c9825e..f7bc499 100644
--- a/sqldb/sqlc/db_custom.go
+++ b/sqldb/sqlc/db_custom.go
@@ -106,6 +106,20 @@ func (r GetChannelsBySCIDWithPoliciesRow) Node2Pub() []byte {
return r.GraphNode_2.PubKey
}
+// Node1 returns the first GraphNode associated with this channel.
+//
+// NOTE: This method is part of the ChannelAndNodes interface.
+func (r GetChannelsBySCIDWithPoliciesRow) Node1() GraphNode {
+ return r.GraphNode
+}
+
+// Node2 returns the second GraphNode associated with this channel.
+//
+// NOTE: This method is part of the ChannelAndNodes interface.
+func (r GetChannelsBySCIDWithPoliciesRow) Node2() GraphNode {
+ return r.GraphNode_2
+}
+
// Channel returns the GraphChannel associated with this interface.
//
// NOTE: This method is part of the ChannelAndNodeIDs interface.
Why this scored 17/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.