From 97143a74ab94d9ae286e599d59b17f478daae6b5 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Sun, 26 Oct 2025 16:38:29 +0200 Subject: [PATCH] graph/db: split HasNode into two methods HasNode currently is very v1 specific since it returns a time.Time timestamp which is specific to V1 node announcements. However, it is mostly only used for the "exists" return value. So here we split it up into HasNode which just checks existence and HasV1Node which retains the same behavaiour as before. --- graph/builder.go | 2 +- graph/db/graph.go | 13 +++++++++++-- graph/db/graph_test.go | 6 +++--- graph/db/interfaces.go | 17 +++++++++++----- graph/db/kv_store.go | 44 ++++++++++++++++++++++++++++++++++++------ graph/db/sql_store.go | 31 +++++++++++++++++++++++++++-- routing/router_test.go | 8 ++++---- rpcserver.go | 4 ++-- 8 files changed, 100 insertions(+), 25 deletions(-) diff --git a/graph/builder.go b/graph/builder.go index 91040dd08..f9ce63eb8 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 13b9af300..e9bd4aafb 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 89729944c..c147b09fd 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 20408c904..59172833e 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 27c3bbadc..a582fd5e4 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 55efabf15..d9fca5155 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 9f088917d..102afe85b 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 fe0e6702b..f09278777 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) }