What changed, and why it matters
This commit is a routine code-quality refactor: it threads a request-scoped cancellation signal (a 'context') through the IsPublicNode database lookup. Previously the SQL backend used a placeholder TODO context, which meant long-running queries could not be cancelled. The change does not alter what the function returns, only how it handles timeouts and cancellation. It is not a security patch in itself, though better context plumbing can help limit resource exhaustion in future hardening.
No immediate action required. Treat as normal maintenance. If auditing for DoS resilience, verify that all SQL callers eventually pass a real, deadline-bearing context rather than context.TODO().
Security signals we found
Context propagation refactor only
No change to authorization, authentication, or cryptographic checks
No change to data returned by IsPublicNode
SQL backend now respects caller-provided cancellation/timeout context
No vendor disclosure or CVE references present
Evidence from the diff
The diff updates the IsPublicNode method signature across the graph database interfaces and implementations (Store, ChannelGraph, VersionedGraph, KVStore, SQLStore) and callers (graph.Builder, invoicesrpc.addinvoice) to accept a context.Context. The SQL implementation removes its internal context.TODO() and now uses the supplied context. The KV implementation ignores the context (underscore receiver). Callers that lack a context pass context.TODO(). No logic changes to the public-node determination itself; this is pure context propagation.
Changed components
graph/db/graph.gograph/db/interfaces.gograph/db/kv_store.gograph/db/sql_store.gograph/builder.golnrpc/invoicesrpc/addinvoice.golnrpc/invoicesrpc/interfaces.goInspect captured patch +26 / −18
diff --git a/graph/builder.go b/graph/builder.go
index 654a842..c2121d8 100644
--- a/graph/builder.go
+++ b/graph/builder.go
@@ -1331,7 +1331,7 @@ func (b *Builder) IsStaleNode(ctx context.Context, node route.Vertex,
//
// NOTE: This method is part of the ChannelGraphSource interface.
func (b *Builder) IsPublicNode(node route.Vertex) (bool, error) {
- return b.v1Graph.IsPublicNode(node)
+ return b.v1Graph.IsPublicNode(context.TODO(), node)
}
// IsKnownEdge returns true if the graph source already knows of the passed
diff --git a/graph/db/graph.go b/graph/db/graph.go
index 0051e8d..0a1f517 100644
--- a/graph/db/graph.go
+++ b/graph/db/graph.go
@@ -652,8 +652,10 @@ func (c *ChannelGraph) HasV1Node(ctx context.Context,
}
// IsPublicNode determines whether the node is seen as public in the graph.
-func (c *ChannelGraph) IsPublicNode(pubKey [33]byte) (bool, error) {
- return c.db.IsPublicNode(lnwire.GossipVersion1, pubKey)
+func (c *ChannelGraph) IsPublicNode(ctx context.Context,
+ pubKey [33]byte) (bool, error) {
+
+ return c.db.IsPublicNode(ctx, lnwire.GossipVersion1, pubKey)
}
// ForEachChannel iterates through all channel edges stored within the graph.
@@ -993,8 +995,10 @@ func (c *VersionedGraph) ChannelID(ctx context.Context,
}
// IsPublicNode determines whether the node is seen as public in the graph.
-func (c *VersionedGraph) IsPublicNode(pubKey [33]byte) (bool, error) {
- return c.db.IsPublicNode(c.v, pubKey)
+func (c *VersionedGraph) IsPublicNode(ctx context.Context,
+ pubKey [33]byte) (bool, error) {
+
+ return c.db.IsPublicNode(ctx, c.v, pubKey)
}
// MakeTestGraph creates a new instance of the ChannelGraph for testing
diff --git a/graph/db/graph_test.go b/graph/db/graph_test.go
index d162611..7ff5cde 100644
--- a/graph/db/graph_test.go
+++ b/graph/db/graph_test.go
@@ -4225,7 +4225,7 @@ func testNodeIsPublic(t *testing.T, v lnwire.GossipVersion) {
for _, node := range nodes {
for _, graph := range graphs {
isPublic, err := graph.IsPublicNode(
- node.PubKeyBytes,
+ ctx, node.PubKeyBytes,
)
require.NoError(t, err)
@@ -4330,7 +4330,7 @@ func testIsPublicNodeEmptyChannelSignature(t *testing.T,
// node1 should NOT be considered public because the
// channel announcement has empty signatures.
- isPublic, err := graph.IsPublicNode(node1.PubKeyBytes)
+ isPublic, err := graph.IsPublicNode(ctx, node1.PubKeyBytes)
require.NoError(t, err)
require.False(t, isPublic)
}
@@ -4354,7 +4354,7 @@ func BenchmarkIsPublicNode(b *testing.B) {
// Query random nodes to avoid query caching and better
// represent real-world query patterns.
nodePub := nodes[rng.Intn(len(nodes))].PubKeyBytes
- _, err := graph.IsPublicNode(nodePub)
+ _, err := graph.IsPublicNode(b.Context(), nodePub)
require.NoError(b, err)
}
}
diff --git a/graph/db/interfaces.go b/graph/db/interfaces.go
index 4d52fbf..3b4175b 100644
--- a/graph/db/interfaces.go
+++ b/graph/db/interfaces.go
@@ -150,7 +150,8 @@ type Store interface { //nolint:interfacebloat
// 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
// graph's source node's point of view.
- IsPublicNode(v lnwire.GossipVersion, pubKey [33]byte) (bool, error)
+ IsPublicNode(ctx context.Context, v lnwire.GossipVersion,
+ pubKey [33]byte) (bool, error)
// GraphSession will provide the call-back with access to a
// NodeTraverser instance which can be used to perform queries against
diff --git a/graph/db/kv_store.go b/graph/db/kv_store.go
index 72d63f5..84c293e 100644
--- a/graph/db/kv_store.go
+++ b/graph/db/kv_store.go
@@ -4103,8 +4103,8 @@ func (c *KVStore) FetchChannelEdgesByID(_ context.Context,
// 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 graph's
// source node's point of view.
-func (c *KVStore) IsPublicNode(v lnwire.GossipVersion, pubKey [33]byte) (bool,
- error) {
+func (c *KVStore) IsPublicNode(_ context.Context, v lnwire.GossipVersion,
+ pubKey [33]byte) (bool, error) {
if v != lnwire.GossipVersion1 {
return false, ErrVersionNotSupportedForKVDB
diff --git a/graph/db/sql_store.go b/graph/db/sql_store.go
index 06f099d..cd4626b 100644
--- a/graph/db/sql_store.go
+++ b/graph/db/sql_store.go
@@ -2546,10 +2546,9 @@ func (s *SQLStore) ChannelID(ctx context.Context, v lnwire.GossipVersion,
// source node's point of view.
//
// NOTE: part of the Store interface.
-func (s *SQLStore) IsPublicNode(v lnwire.GossipVersion, pubKey [33]byte) (bool,
- error) {
+func (s *SQLStore) IsPublicNode(ctx context.Context, v lnwire.GossipVersion,
+ pubKey [33]byte) (bool, error) {
- ctx := context.TODO()
if !isKnownGossipVersion(v) {
return false, fmt.Errorf("unsupported gossip version: %d", v)
}
diff --git a/lnrpc/invoicesrpc/addinvoice.go b/lnrpc/invoicesrpc/addinvoice.go
index 7d8ece0..9afba76 100644
--- a/lnrpc/invoicesrpc/addinvoice.go
+++ b/lnrpc/invoicesrpc/addinvoice.go
@@ -795,9 +795,13 @@ func newSelectHopHintsCfg(invoicesCfg *AddInvoiceConfig,
maxHopHints int) *SelectHopHintsCfg {
return &SelectHopHintsCfg{
- FetchAllChannels: invoicesCfg.ChanDB.FetchAllChannels,
- IsChannelActive: invoicesCfg.IsChannelActive,
- IsPublicNode: invoicesCfg.Graph.IsPublicNode,
+ FetchAllChannels: invoicesCfg.ChanDB.FetchAllChannels,
+ IsChannelActive: invoicesCfg.IsChannelActive,
+ IsPublicNode: func(pubKey [33]byte) (bool, error) {
+ return invoicesCfg.Graph.IsPublicNode(
+ context.TODO(), pubKey,
+ )
+ },
FetchChannelEdgesByID: func(chanID uint64) (
*models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
*models.ChannelEdgePolicy, error) {
diff --git a/lnrpc/invoicesrpc/interfaces.go b/lnrpc/invoicesrpc/interfaces.go
index d350af6..99a8bef 100644
--- a/lnrpc/invoicesrpc/interfaces.go
+++ b/lnrpc/invoicesrpc/interfaces.go
@@ -18,5 +18,5 @@ type GraphSource interface {
// 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
// graph's source node's point of view.
- IsPublicNode(pubKey [33]byte) (bool, error)
+ IsPublicNode(ctx context.Context, pubKey [33]byte) (bool, error)
}
Why this scored 19/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.