What changed, and why it matters
This commit is a routine internal code cleanup in the Lightning Network Daemon (LND) graph database. It splits one method, HasNode, into two methods: a simple existence check (HasNode) and a version-specific check that also returns a timestamp (HasV1Node). There is no security fix or vulnerability here.
No security action required. Treat as normal refactoring; review as part of standard code review only.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change refactors the ChannelGraph/Store interface to separate V1-specific node-announcement timestamp behavior from a plain existence check. HasNode now returns only (bool, error), while HasV1Node preserves the old (time.Time, bool, error) signature. Call sites are updated accordingly. The SQL store adds a NodeExists query restricted to GossipVersion1. No logic bugs, race conditions, or security-sensitive changes are evident in the diff.
Changed components
graph/db/ChannelGraphgraph/db/Store interfacegraph/db/KVStoregraph/db/SQLStoregraph/builder.gorpcserver.goInspect captured patch +100 / −25
diff --git a/graph/builder.go b/graph/builder.go
index 91040dd..f9ce63e 100644
--- a/graph/builder.go
+++ b/graph/builder.go
@@ -874,7 +874,7 @@ func (b *Builder) assertNodeAnnFreshness(ctx context.Context, node route.Vertex,
// node announcements, we will ignore such nodes. If we do know about
// this node, check that this update brings info newer than what we
// already have.
- lastUpdate, exists, err := b.cfg.Graph.HasNode(ctx, node)
+ lastUpdate, exists, err := b.cfg.Graph.HasV1Node(ctx, node)
if err != nil {
return fmt.Errorf("unable to query for the "+
"existence of node: %w", err)
diff --git a/graph/db/graph.go b/graph/db/graph.go
index 13b9af3..e9bd4aa 100644
--- a/graph/db/graph.go
+++ b/graph/db/graph.go
@@ -664,10 +664,19 @@ func (c *ChannelGraph) FetchNode(ctx context.Context,
return c.db.FetchNode(ctx, nodePub)
}
-// HasNode determines if the graph has a vertex identified by the target node.
-func (c *ChannelGraph) HasNode(ctx context.Context,
+// HasV1Node determines if the graph has a vertex identified by the target node
+// in the V1 graph.
+func (c *ChannelGraph) HasV1Node(ctx context.Context,
nodePub [33]byte) (time.Time, bool, error) {
+ return c.db.HasV1Node(ctx, nodePub)
+}
+
+// HasNode determines if the graph has a vertex identified by the target node
+// in the V1 graph.
+func (c *ChannelGraph) HasNode(ctx context.Context, nodePub [33]byte) (bool,
+ error) {
+
return c.db.HasNode(ctx, nodePub)
}
diff --git a/graph/db/graph_test.go b/graph/db/graph_test.go
index 8972994..c147b09 100644
--- a/graph/db/graph_test.go
+++ b/graph/db/graph_test.go
@@ -138,7 +138,7 @@ func TestNodeInsertionAndDeletion(t *testing.T) {
dbNode, err := graph.FetchNode(ctx, testPub)
require.NoError(t, err, "unable to locate node")
- _, exists, err := graph.HasNode(ctx, dbNode.PubKeyBytes)
+ exists, err := graph.HasNode(ctx, dbNode.PubKeyBytes)
require.NoError(t, err)
require.True(t, exists)
@@ -309,7 +309,7 @@ func TestPartialNode(t *testing.T) {
dbNode2, err := graph.FetchNode(ctx, pubKey2)
require.NoError(t, err)
- _, exists, err := graph.HasNode(ctx, dbNode1.PubKeyBytes)
+ exists, err := graph.HasNode(ctx, dbNode1.PubKeyBytes)
require.NoError(t, err)
require.True(t, exists)
@@ -318,7 +318,7 @@ func TestPartialNode(t *testing.T) {
expectedNode1 := models.NewV1ShellNode(pubKey1)
compareNodes(t, expectedNode1, dbNode1)
- _, exists, err = graph.HasNode(ctx, dbNode2.PubKeyBytes)
+ exists, err = graph.HasNode(ctx, dbNode2.PubKeyBytes)
require.NoError(t, err)
require.True(t, exists)
diff --git a/graph/db/interfaces.go b/graph/db/interfaces.go
index 20408c9..5917283 100644
--- a/graph/db/interfaces.go
+++ b/graph/db/interfaces.go
@@ -119,12 +119,19 @@ type Store interface { //nolint:interfacebloat
FetchNode(ctx context.Context, nodePub route.Vertex) (*models.Node,
error)
+ // HasV1Node determines if the graph has a vertex identified by
+ // the target node identity public key in the V1 graph. If the node
+ // exists in the database, a timestamp of when the data for the node
+ // was lasted updated is returned along with a true boolean. Otherwise,
+ // an empty time.Time is returned with a false boolean.
+ // This is specific to the V1 graph since only V1 node announcements
+ // use timestamps for their latest update timestamp.
+ HasV1Node(ctx context.Context, nodePub [33]byte) (time.Time, bool,
+ error)
+
// HasNode determines if the graph has a vertex identified by
- // the target node identity public key. If the node exists in the
- // database, a timestamp of when the data for the node was lasted
- // updated is returned along with a true boolean. Otherwise, an empty
- // time.Time is returned with a false boolean.
- HasNode(ctx context.Context, nodePub [33]byte) (time.Time, bool, error)
+ // the target node identity public key.
+ HasNode(ctx context.Context, nodePub [33]byte) (bool, error)
// IsPublicNode is a helper method that determines whether the node with
// the given public key is seen as a public node in the graph from the
diff --git a/graph/db/kv_store.go b/graph/db/kv_store.go
index 27c3bba..a582fd5 100644
--- a/graph/db/kv_store.go
+++ b/graph/db/kv_store.go
@@ -3433,11 +3433,12 @@ func (c *KVStore) fetchLightningNode(tx kvdb.RTx,
return node, nil
}
-// HasNode determines if the graph has a vertex identified by the target node
-// identity public key. If the node exists in the database, a timestamp of when
-// the data for the node was lasted updated is returned along with a true
-// boolean. Otherwise, an empty time.Time is returned with a false boolean.
-func (c *KVStore) HasNode(_ context.Context,
+// HasV1Node determines if the graph has a vertex identified by the
+// target node identity public key. If the node exists in the database, a
+// timestamp of when the data for the node was lasted updated is returned along
+// with a true boolean. Otherwise, an empty time.Time is returned with a false
+// boolean.
+func (c *KVStore) HasV1Node(_ context.Context,
nodePub [33]byte) (time.Time, bool, error) {
var (
@@ -3457,7 +3458,6 @@ func (c *KVStore) HasNode(_ context.Context,
// exit early.
nodeBytes := nodes.Get(nodePub[:])
if nodeBytes == nil {
- exists = false
return nil
}
@@ -3485,6 +3485,38 @@ func (c *KVStore) HasNode(_ context.Context,
return updateTime, exists, nil
}
+// HasNode determines if the graph has a vertex identified by the target node
+// identity public key.
+func (c *KVStore) HasNode(_ context.Context, nodePub [33]byte) (bool, error) {
+ var exists bool
+ err := kvdb.View(c.db, func(tx kvdb.RTx) error {
+ // First grab the nodes bucket which stores the mapping from
+ // pubKey to node information.
+ nodes := tx.ReadBucket(nodeBucket)
+ if nodes == nil {
+ return ErrGraphNotFound
+ }
+
+ // If a key for this serialized public key isn't found, we can
+ // exit early.
+ nodeBytes := nodes.Get(nodePub[:])
+ if nodeBytes == nil {
+ return nil
+ }
+
+ exists = true
+
+ return nil
+ }, func() {
+ exists = false
+ })
+ if err != nil {
+ return exists, err
+ }
+
+ return exists, nil
+}
+
// nodeTraversal is used to traverse all channels of a node given by its
// public key and passes channel information into the specified callback.
//
diff --git a/graph/db/sql_store.go b/graph/db/sql_store.go
index 55efabf..d9fca51 100644
--- a/graph/db/sql_store.go
+++ b/graph/db/sql_store.go
@@ -53,6 +53,7 @@ type SQLQueries interface {
DeleteUnconnectedNodes(ctx context.Context) ([][]byte, error)
DeleteNodeByPubKey(ctx context.Context, arg sqlc.DeleteNodeByPubKeyParams) (sql.Result, error)
DeleteNode(ctx context.Context, id int64) error
+ NodeExists(ctx context.Context, arg sqlc.NodeExistsParams) (bool, error)
GetExtraNodeTypes(ctx context.Context, nodeID int64) ([]sqlc.GraphNodeExtraType, error)
GetNodeExtraTypesBatch(ctx context.Context, ids []int64) ([]sqlc.GraphNodeExtraType, error)
@@ -283,14 +284,14 @@ func (s *SQLStore) FetchNode(ctx context.Context,
return node, nil
}
-// HasNode determines if the graph has a vertex identified by the
+// HasV1Node determines if the graph has a vertex identified by the
// target node identity public key. If the node exists in the database, a
// timestamp of when the data for the node was lasted updated is returned along
// with a true boolean. Otherwise, an empty time.Time is returned with a false
// boolean.
//
// NOTE: part of the Store interface.
-func (s *SQLStore) HasNode(ctx context.Context,
+func (s *SQLStore) HasV1Node(ctx context.Context,
pubKey [33]byte) (time.Time, bool, error) {
var (
@@ -326,6 +327,32 @@ func (s *SQLStore) HasNode(ctx context.Context,
return lastUpdate, exists, nil
}
+// HasNode determines if the graph has a vertex identified by the
+// target node identity public key.
+//
+// NOTE: part of the Store interface.
+func (s *SQLStore) HasNode(ctx context.Context, pubKey [33]byte) (bool, error) {
+ var (
+ v = lnwire.GossipVersion1
+ exists bool
+ )
+ err := s.db.ExecTx(ctx, sqldb.ReadTxOpt(), func(db SQLQueries) error {
+ var err error
+ exists, err = db.NodeExists(ctx, sqlc.NodeExistsParams{
+ Version: int16(v),
+ PubKey: pubKey[:],
+ })
+
+ return err
+ }, sqldb.NoOpReset)
+ if err != nil {
+ return false, fmt.Errorf("unable to check if node (%x) "+
+ "exists: %w", pubKey, err)
+ }
+
+ return exists, nil
+}
+
// AddrsForNode returns all known addresses for the target node public key
// that the graph DB is aware of. The returned boolean indicates if the
// given node is unknown to the graph DB or not.
diff --git a/routing/router_test.go b/routing/router_test.go
index 9f08891..102afe8 100644
--- a/routing/router_test.go
+++ b/routing/router_test.go
@@ -2717,11 +2717,11 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
copy(pub2[:], priv2.PubKey().SerializeCompressed())
// The two nodes we are about to add should not exist yet.
- _, exists1, err := ctx.graph.HasNode(ctxb, pub1)
+ exists1, err := ctx.graph.HasNode(ctxb, pub1)
require.NoError(t, err, "unable to query graph")
require.False(t, exists1)
- _, exists2, err := ctx.graph.HasNode(ctxb, pub2)
+ exists2, err := ctx.graph.HasNode(ctxb, pub2)
require.NoError(t, err, "unable to query graph")
require.False(t, exists2)
@@ -2778,11 +2778,11 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
// After adding the edge between the two previously unknown nodes, they
// should have been added to the graph.
- _, exists1, err = ctx.graph.HasNode(ctxb, pub1)
+ exists1, err = ctx.graph.HasNode(ctxb, pub1)
require.NoError(t, err, "unable to query graph")
require.True(t, exists1)
- _, exists2, err = ctx.graph.HasNode(ctxb, pub2)
+ exists2, err = ctx.graph.HasNode(ctxb, pub2)
require.NoError(t, err, "unable to query graph")
require.True(t, exists2)
diff --git a/rpcserver.go b/rpcserver.go
index fe0e670..f092787 100644
--- a/rpcserver.go
+++ b/rpcserver.go
@@ -746,7 +746,7 @@ func (r *rpcServer) addDeps(ctx context.Context, s *server,
return info.NodeKey1Bytes, info.NodeKey2Bytes, nil
},
HasNode: func(nodePub route.Vertex) (bool, error) {
- _, exists, err := graph.HasNode(ctx, nodePub)
+ exists, err := graph.HasNode(ctx, nodePub)
return exists, err
},
@@ -1822,7 +1822,7 @@ func (r *rpcServer) VerifyMessage(ctx context.Context,
//
// TODO(phlip9): Require valid nodes to have capital in active channels.
graph := r.server.graphDB
- _, active, err := graph.HasNode(ctx, pub)
+ active, err := graph.HasNode(ctx, pub)
if err != nil {
return nil, fmt.Errorf("failed to query graph: %w", err)
}
Why this scored 15/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.