graph/db: add gossip version parameter to ForEachNodeChannel

Update ForEachNodeChannel to accept a gossip version parameter,
allowing callers to specify which gossip version's channels should be
iterated. This change mirrors the approach taken in ForEachChannel and
prepares the graph database for supporting multiple gossip versions
while maintaining backward compatibility.

The Store interface is updated to include the version parameter, and
both KVStore and SQLStore implementations are updated accordingly:

- KVStore validates that only GossipVersion1 is requested, returning
  ErrVersionNotSupportedForKVDB for other versions.
- SQLStore passes the version through to the underlying node query,
  enabling version-specific channel iteration.

The ChannelGraph wrapper is updated to accept and pass through the
version parameter. VersionedGraph gains a ForEachNodeChannel method
that automatically uses its configured gossip version, providing a
clean interface for version-specific operations.

Update all call sites to explicitly pass lnwire.GossipVersion1, except
for the local channel manager in server.go which now uses the v1Graph
directly (matching the pattern used in other parts of the codebase).
This commit is contained in:
Elle Mouton committed 2026-02-13 07:21:24 +02:00
1 parent d948c5bfad
commit 1e35f258e4
7 files changed
+68 -66

No files matched your search

+1 -1
View File
@@ -1276,7 +1276,7 @@ func (b *Builder) ForAllOutgoingChannels(ctx context.Context,
reset func()) error {
return b.cfg.Graph.ForEachNodeChannel(
ctx, b.cfg.SelfNode,
ctx, lnwire.GossipVersion1, b.cfg.SelfNode,
func(c *models.ChannelEdgeInfo, e *models.ChannelEdgePolicy,
_ *models.ChannelEdgePolicy) error {
+12 -2
View File
@@ -597,11 +597,12 @@ func (c *ChannelGraph) ForEachSourceNodeChannel(ctx context.Context,
// ForEachNodeChannel iterates through all channels of the given node.
func (c *ChannelGraph) ForEachNodeChannel(ctx context.Context,
nodePub route.Vertex, cb func(*models.ChannelEdgeInfo,
v lnwire.GossipVersion, nodePub route.Vertex,
cb func(*models.ChannelEdgeInfo,
*models.ChannelEdgePolicy,
*models.ChannelEdgePolicy) error, reset func()) error {
return c.db.ForEachNodeChannel(ctx, nodePub, cb, reset)
return c.db.ForEachNodeChannel(ctx, v, nodePub, cb, reset)
}
// ForEachNode iterates through all stored vertices/nodes in the graph.
@@ -905,6 +906,15 @@ func (c *VersionedGraph) HasChannelEdge(chanID uint64) (bool, bool, error) {
return c.db.HasChannelEdge(c.v, chanID)
}
// ForEachNodeChannel iterates through all channels of the given node.
func (c *VersionedGraph) ForEachNodeChannel(ctx context.Context,
nodePub route.Vertex, cb func(*models.ChannelEdgeInfo,
*models.ChannelEdgePolicy,
*models.ChannelEdgePolicy) error, reset func()) error {
return c.db.ForEachNodeChannel(ctx, c.v, nodePub, cb, reset)
}
// ForEachChannel iterates through all channel edges stored within the graph.
func (c *VersionedGraph) ForEachChannel(ctx context.Context,
cb func(*models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
+35 -52
View File
@@ -150,6 +150,10 @@ var versionedTests = []versionedTest{
name: "edge policy crud",
test: testEdgePolicyCRUD,
},
{
name: "incomplete channel policies",
test: testIncompleteChannelPolicies,
},
{
name: "partial node",
test: testPartialNode,
@@ -1753,7 +1757,7 @@ func TestGraphTraversal(t *testing.T) {
numNodeChans := 0
firstNode, secondNode := nodeList[0], nodeList[1]
err = graph.ForEachNodeChannel(
ctx, firstNode.PubKeyBytes,
ctx, lnwire.GossipVersion1, firstNode.PubKeyBytes,
func(_ *models.ChannelEdgeInfo, outEdge,
inEdge *models.ChannelEdgePolicy) error {
@@ -3810,32 +3814,26 @@ func TestFetchChanInfos(t *testing.T) {
}
}
// TestIncompleteChannelPolicies tests that a channel that only has a policy
// testIncompleteChannelPolicies tests that a channel that only has a policy
// specified on one end is properly returned in ForEachChannel calls from
// both sides.
func TestIncompleteChannelPolicies(t *testing.T) {
func testIncompleteChannelPolicies(t *testing.T, v lnwire.GossipVersion) {
t.Parallel()
ctx := t.Context()
graph := MakeTestGraph(t)
graph := NewVersionedGraph(MakeTestGraph(t), v)
// Create two nodes.
node1 := createTestVertex(t, lnwire.GossipVersion1)
if err := graph.AddNode(ctx, node1); err != nil {
t.Fatalf("unable to add node: %v", err)
}
node2 := createTestVertex(t, lnwire.GossipVersion1)
if err := graph.AddNode(ctx, node2); err != nil {
t.Fatalf("unable to add node: %v", err)
}
node1 := createTestVertex(t, v)
require.NoError(t, graph.AddNode(ctx, node1))
node2 := createTestVertex(t, v)
require.NoError(t, graph.AddNode(ctx, node2))
channel, chanID := createEdge(
lnwire.GossipVersion1, uint32(0), 0, 0, 0, node1, node2,
v, uint32(0), 0, 0, 0, node1, node2,
)
if err := graph.AddChannelEdge(ctx, channel); err != nil {
t.Fatalf("unable to create channel edge: %v", err)
}
require.NoError(t, graph.AddChannelEdge(ctx, channel))
// Ensure that channel is reported with unknown policies.
checkPolicies := func(node *models.Node, expectedIn,
@@ -3847,21 +3845,8 @@ func TestIncompleteChannelPolicies(t *testing.T) {
func(_ *models.ChannelEdgeInfo, outEdge,
inEdge *models.ChannelEdgePolicy) error {
if !expectedOut && outEdge != nil {
t.Fatalf("Expected no outgoing policy")
}
if expectedOut && outEdge == nil {
t.Fatalf("Expected an outgoing policy")
}
if !expectedIn && inEdge != nil {
t.Fatalf("Expected no incoming policy")
}
if expectedIn && inEdge == nil {
t.Fatalf("Expected an incoming policy")
}
require.Equal(t, expectedOut, outEdge != nil)
require.Equal(t, expectedIn, inEdge != nil)
calls++
@@ -3874,34 +3859,30 @@ func TestIncompleteChannelPolicies(t *testing.T) {
checkPolicies(node2, false, false)
newTestEdgePolicy := func(isNode1 bool,
toNode route.Vertex) *models.ChannelEdgePolicy {
policy := newEdgePolicy(
v, chanID.ToUint64(), nextUpdateTime().Unix(), isNode1,
)
policy.ToNode = toNode
policy.SigBytes = testSig.Serialize()
return policy
}
// Only create an edge policy for node1 and leave the policy for node2
// unknown.
updateTime := time.Unix(1234, 0)
edgePolicy := newEdgePolicy(
lnwire.GossipVersion1, chanID.ToUint64(), updateTime.Unix(),
true,
)
edgePolicy.ToNode = node2.PubKeyBytes
edgePolicy.SigBytes = testSig.Serialize()
if err := graph.UpdateEdgePolicy(ctx, edgePolicy); err != nil {
t.Fatalf("unable to update edge: %v", err)
}
edgePolicy := newTestEdgePolicy(true, node2.PubKeyBytes)
require.NoError(t, graph.UpdateEdgePolicy(ctx, edgePolicy))
checkPolicies(node1, false, true)
checkPolicies(node2, true, false)
// Create second policy and assert that both policies are reported
// as present.
edgePolicy = newEdgePolicy(
lnwire.GossipVersion1, chanID.ToUint64(), updateTime.Unix(),
false,
)
edgePolicy.ToNode = node1.PubKeyBytes
edgePolicy.SigBytes = testSig.Serialize()
if err := graph.UpdateEdgePolicy(ctx, edgePolicy); err != nil {
t.Fatalf("unable to update edge: %v", err)
}
edgePolicy = newTestEdgePolicy(false, node1.PubKeyBytes)
require.NoError(t, graph.UpdateEdgePolicy(ctx, edgePolicy))
checkPolicies(node1, true, true)
checkPolicies(node2, true, true)
@@ -5024,7 +5005,9 @@ func BenchmarkForEachChannel(b *testing.B) {
return nil
}
err := graph.ForEachNodeChannel(ctx, n, cb, func() {})
err := graph.ForEachNodeChannel(
ctx, lnwire.GossipVersion1, n, cb, func() {},
)
require.NoError(b, err)
}
}
+3 -2
View File
@@ -73,8 +73,9 @@ type Store interface { //nolint:interfacebloat
// to the caller.
//
// Unknown policies are passed into the callback as nil values.
ForEachNodeChannel(ctx context.Context, nodePub route.Vertex,
cb func(*models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
ForEachNodeChannel(ctx context.Context, v lnwire.GossipVersion,
nodePub route.Vertex, cb func(*models.ChannelEdgeInfo,
*models.ChannelEdgePolicy,
*models.ChannelEdgePolicy) error, reset func()) error
// ForEachNodeCached is similar to forEachNode, but it returns
+6 -1
View File
@@ -3690,10 +3690,15 @@ func nodeTraversal(tx kvdb.RTx, nodePub []byte, db kvdb.Backend,
// halted with the error propagated back up to the caller.
//
// Unknown policies are passed into the callback as nil values.
func (c *KVStore) ForEachNodeChannel(_ context.Context, nodePub route.Vertex,
func (c *KVStore) ForEachNodeChannel(_ context.Context,
v lnwire.GossipVersion, nodePub route.Vertex,
cb func(*models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
*models.ChannelEdgePolicy) error, reset func()) error {
if v != lnwire.GossipVersion1 {
return ErrVersionNotSupportedForKVDB
}
return nodeTraversal(
nil, nodePub[:], c.db, func(_ kvdb.RTx,
info *models.ChannelEdgeInfo, policy,
+9 -7
View File
@@ -913,7 +913,7 @@ func (s *SQLStore) ForEachSourceNodeChannel(ctx context.Context,
}
return forEachNodeChannel(
ctx, db, s.cfg, nodeID,
ctx, db, s.cfg, lnwire.GossipVersion1, nodeID,
func(info *models.ChannelEdgeInfo,
outPolicy *models.ChannelEdgePolicy,
_ *models.ChannelEdgePolicy) error {
@@ -1027,14 +1027,15 @@ func (s *SQLStore) ForEachNodeCacheable(ctx context.Context,
// Unknown policies are passed into the callback as nil values.
//
// NOTE: part of the Store interface.
func (s *SQLStore) ForEachNodeChannel(ctx context.Context, nodePub route.Vertex,
func (s *SQLStore) ForEachNodeChannel(ctx context.Context,
v lnwire.GossipVersion, nodePub route.Vertex,
cb func(*models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
*models.ChannelEdgePolicy) error, reset func()) error {
return s.db.ExecTx(ctx, sqldb.ReadTxOpt(), func(db SQLQueries) error {
dbNode, err := db.GetNodeByPubKey(
ctx, sqlc.GetNodeByPubKeyParams{
Version: int16(lnwire.GossipVersion1),
Version: int16(v),
PubKey: nodePub[:],
},
)
@@ -1044,7 +1045,7 @@ func (s *SQLStore) ForEachNodeChannel(ctx context.Context, nodePub route.Vertex,
return fmt.Errorf("unable to fetch node: %w", err)
}
return forEachNodeChannel(ctx, db, s.cfg, dbNode.ID, cb)
return forEachNodeChannel(ctx, db, s.cfg, v, dbNode.ID, cb)
}, reset)
}
@@ -3524,14 +3525,15 @@ func forEachNodeCacheable(ctx context.Context, cfg *sqldb.QueryConfig,
// edge information, the outgoing policy and the incoming policy for the
// channel and node combo.
func forEachNodeChannel(ctx context.Context, db SQLQueries,
cfg *SQLStoreConfig, id int64, cb func(*models.ChannelEdgeInfo,
cfg *SQLStoreConfig, v lnwire.GossipVersion, id int64,
cb func(*models.ChannelEdgeInfo,
*models.ChannelEdgePolicy,
*models.ChannelEdgePolicy) error) error {
// Get all the V1 channels for this node.
// Get all the channels for this node.
rows, err := db.ListChannelsByNodeID(
ctx, sqlc.ListChannelsByNodeIDParams{
Version: int16(lnwire.GossipVersion1),
Version: int16(v),
NodeID1: id,
},
)
+2 -1
View File
@@ -1131,7 +1131,8 @@ func newServer(ctx context.Context, cfg *Config, listenAddrs []net.Addr,
*models.ChannelEdgePolicy) error,
reset func()) error {
return s.graphDB.ForEachNodeChannel(ctx, selfVertex,
return s.v1Graph.ForEachNodeChannel(
ctx, selfVertex,
func(c *models.ChannelEdgeInfo,
e *models.ChannelEdgePolicy,
_ *models.ChannelEdgePolicy) error {