graph/db: move gossip versions up one layer

Update some of the node related graph CRUD methods to take a version
rather than hardcoding them in the SQLStore layer. Move the version up
one layer instead. This will make it easier to make it configurable
later on.
This commit is contained in:
Elle Mouton committed 2026-02-13 07:21:20 +02:00
1 parent 97143a74ab
commit c281810ab9
5 files changed
+111 -53

No files matched your search

+7 -7
View File
@@ -226,7 +226,7 @@ func (c *ChannelGraph) FetchNodeFeatures(node route.Vertex) (
return c.graphCache.GetFeatures(node), nil
}
return c.db.FetchNodeFeatures(node)
return c.db.FetchNodeFeatures(lnwire.GossipVersion1, node)
}
// GraphSession will provide the call-back with access to a NodeTraverser
@@ -298,7 +298,7 @@ func (c *ChannelGraph) AddNode(ctx context.Context,
func (c *ChannelGraph) DeleteNode(ctx context.Context,
nodePub route.Vertex) error {
err := c.db.DeleteNode(ctx, nodePub)
err := c.db.DeleteNode(ctx, lnwire.GossipVersion1, nodePub)
if err != nil {
return err
}
@@ -607,7 +607,7 @@ func (c *ChannelGraph) UpdateEdgePolicy(ctx context.Context,
func (c *ChannelGraph) AddrsForNode(ctx context.Context,
nodePub *btcec.PublicKey) (bool, []net.Addr, error) {
return c.db.AddrsForNode(ctx, nodePub)
return c.db.AddrsForNode(ctx, lnwire.GossipVersion1, nodePub)
}
// ForEachSourceNodeChannel iterates through all channels of the source node.
@@ -646,7 +646,7 @@ func (c *ChannelGraph) ForEachNodeCacheable(ctx context.Context,
func (c *ChannelGraph) LookupAlias(ctx context.Context,
pub *btcec.PublicKey) (string, error) {
return c.db.LookupAlias(ctx, pub)
return c.db.LookupAlias(ctx, lnwire.GossipVersion1, pub)
}
// NodeUpdatesInHorizon returns all known lightning nodes with updates in the
@@ -661,7 +661,7 @@ func (c *ChannelGraph) NodeUpdatesInHorizon(startTime, endTime time.Time,
func (c *ChannelGraph) FetchNode(ctx context.Context,
nodePub route.Vertex) (*models.Node, error) {
return c.db.FetchNode(ctx, nodePub)
return c.db.FetchNode(ctx, lnwire.GossipVersion1, nodePub)
}
// HasV1Node determines if the graph has a vertex identified by the target node
@@ -677,7 +677,7 @@ func (c *ChannelGraph) HasV1Node(ctx context.Context,
func (c *ChannelGraph) HasNode(ctx context.Context, nodePub [33]byte) (bool,
error) {
return c.db.HasNode(ctx, nodePub)
return c.db.HasNode(ctx, lnwire.GossipVersion1, nodePub)
}
// IsPublicNode determines whether the node is seen as public in the graph.
@@ -796,7 +796,7 @@ func (c *ChannelGraph) IsClosedScid(scid lnwire.ShortChannelID) (bool, error) {
// SourceNode returns the source node of the graph.
func (c *ChannelGraph) SourceNode(ctx context.Context) (*models.Node, error) {
return c.db.SourceNode(ctx)
return c.db.SourceNode(ctx, lnwire.GossipVersion1)
}
// SetSourceNode sets the source node within the graph database.
+3 -1
View File
@@ -153,7 +153,9 @@ func TestNodeInsertionAndDeletion(t *testing.T) {
// Check that the node's features are fetched correctly. This check
// will check the database directly.
features, err = graph.db.FetchNodeFeatures(node.PubKeyBytes)
features, err = graph.db.FetchNodeFeatures(
lnwire.GossipVersion1, node.PubKeyBytes,
)
require.NoError(t, err)
require.Equal(t, testFeatures, features)
+19 -8
View File
@@ -31,7 +31,14 @@ type NodeTraverser interface {
// Store represents the main interface for the channel graph database for all
// channels and nodes gossiped via the V1 gossip protocol as defined in BOLT 7.
type Store interface { //nolint:interfacebloat
NodeTraverser
// ForEachNodeDirectedChannel calls the callback for every channel of
// the given node.
ForEachNodeDirectedChannel(nodePub route.Vertex,
cb func(channel *DirectedChannel) error, reset func()) error
// FetchNodeFeatures returns the features of the given node.
FetchNodeFeatures(v lnwire.GossipVersion,
nodePub route.Vertex) (*lnwire.FeatureVector, error)
// AddNode adds a vertex/node to the graph database. If the
// node is not in the database from before, this will add a new,
@@ -45,7 +52,7 @@ type Store interface { //nolint:interfacebloat
// 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.
AddrsForNode(ctx context.Context,
AddrsForNode(ctx context.Context, v lnwire.GossipVersion,
nodePub *btcec.PublicKey) (bool, []net.Addr, error)
// ForEachSourceNodeChannel iterates through all channels of the source
@@ -100,11 +107,13 @@ type Store interface { //nolint:interfacebloat
// LookupAlias attempts to return the alias as advertised by the target
// node.
LookupAlias(ctx context.Context, pub *btcec.PublicKey) (string, error)
LookupAlias(ctx context.Context, v lnwire.GossipVersion,
pub *btcec.PublicKey) (string, error)
// DeleteNode starts a new database transaction to remove a
// vertex/node from the database according to the node's public key.
DeleteNode(ctx context.Context, nodePub route.Vertex) error
DeleteNode(ctx context.Context, v lnwire.GossipVersion,
nodePub route.Vertex) error
// NodeUpdatesInHorizon returns all the known lightning node which have
// an update timestamp within the passed range. This method can be used
@@ -116,8 +125,8 @@ type Store interface { //nolint:interfacebloat
// FetchNode attempts to look up a target node by its identity
// public key. If the node isn't found in the database, then
// ErrGraphNodeNotFound is returned.
FetchNode(ctx context.Context, nodePub route.Vertex) (*models.Node,
error)
FetchNode(ctx context.Context, v lnwire.GossipVersion,
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
@@ -131,7 +140,8 @@ type Store interface { //nolint:interfacebloat
// HasNode determines if the graph has a vertex identified by
// the target node identity public key.
HasNode(ctx context.Context, nodePub [33]byte) (bool, error)
HasNode(ctx context.Context, v lnwire.GossipVersion,
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
@@ -334,7 +344,8 @@ type Store interface { //nolint:interfacebloat
// treated as the center node within a star-graph. This method may be
// used to kick off a path finding algorithm in order to explore the
// reachability of another node based off the source node.
SourceNode(ctx context.Context) (*models.Node, error)
SourceNode(ctx context.Context, v lnwire.GossipVersion) (*models.Node,
error)
// SetSourceNode sets the source node within the graph database. The
// source node is to be used as the center of a star-graph within path
+50 -9
View File
@@ -164,6 +164,11 @@ var (
//
// maps: scid -> []byte{}
closedScidBucket = []byte("closed-scid")
// ErrVersionNotSupportedForKVDB is returned with KVStore queries are
// made using a gossip version other than V1.
ErrVersionNotSupportedForKVDB = errors.New("only gossip v1 is " +
"supported for kvdb graph store")
)
const (
@@ -376,7 +381,7 @@ func initKVStore(db kvdb.Backend) error {
// unknown to the graph DB or not.
//
// NOTE: this is part of the channeldb.AddrSource interface.
func (c *KVStore) AddrsForNode(ctx context.Context,
func (c *KVStore) AddrsForNode(ctx context.Context, v lnwire.GossipVersion,
nodePub *btcec.PublicKey) (bool, []net.Addr, error) {
pubKey, err := route.NewVertexFromBytes(nodePub.SerializeCompressed())
@@ -384,7 +389,7 @@ func (c *KVStore) AddrsForNode(ctx context.Context,
return false, nil, err
}
node, err := c.FetchNode(ctx, pubKey)
node, err := c.FetchNode(ctx, v, pubKey)
// We don't consider it an error if the graph is unaware of the node.
switch {
case err != nil && !errors.Is(err, ErrGraphNodeNotFound):
@@ -666,8 +671,12 @@ func (c *KVStore) ForEachNodeDirectedChannel(nodePub route.Vertex,
// known for the node, an empty feature vector is returned.
//
// NOTE: this is part of the graphdb.NodeTraverser interface.
func (c *KVStore) FetchNodeFeatures(nodePub route.Vertex) (
*lnwire.FeatureVector, error) {
func (c *KVStore) FetchNodeFeatures(v lnwire.GossipVersion,
nodePub route.Vertex) (*lnwire.FeatureVector, error) {
if v != lnwire.GossipVersion1 {
return nil, ErrVersionNotSupportedForKVDB
}
return c.fetchNodeFeatures(nil, nodePub)
}
@@ -901,7 +910,13 @@ func (c *KVStore) ForEachNodeCacheable(_ context.Context,
// as the center node within a star-graph. This method may be used to kick off
// a path finding algorithm in order to explore the reachability of another
// node based off the source node.
func (c *KVStore) SourceNode(_ context.Context) (*models.Node, error) {
func (c *KVStore) SourceNode(_ context.Context,
v lnwire.GossipVersion) (*models.Node, error) {
if v != lnwire.GossipVersion1 {
return nil, ErrVersionNotSupportedForKVDB
}
return sourceNode(c.db)
}
@@ -955,6 +970,10 @@ func sourceNodeWithTx(nodes kvdb.RBucket) (*models.Node, error) {
func (c *KVStore) SetSourceNode(_ context.Context,
node *models.Node) error {
if node.Version != lnwire.GossipVersion1 {
return ErrVersionNotSupportedForKVDB
}
nodePubBytes := node.PubKeyBytes[:]
return kvdb.Update(c.db, func(tx kvdb.RwTx) error {
@@ -1021,9 +1040,13 @@ func addLightningNode(tx kvdb.RwTx, node *models.Node) error {
// LookupAlias attempts to return the alias as advertised by the target node.
// TODO(roasbeef): currently assumes that aliases are unique...
func (c *KVStore) LookupAlias(_ context.Context,
func (c *KVStore) LookupAlias(_ context.Context, v lnwire.GossipVersion,
pub *btcec.PublicKey) (string, error) {
if v != lnwire.GossipVersion1 {
return "", ErrVersionNotSupportedForKVDB
}
var alias string
err := kvdb.View(c.db, func(tx kvdb.RTx) error {
@@ -1060,9 +1083,13 @@ func (c *KVStore) LookupAlias(_ context.Context,
// DeleteNode starts a new database transaction to remove a vertex/node
// from the database according to the node's public key.
func (c *KVStore) DeleteNode(_ context.Context,
func (c *KVStore) DeleteNode(_ context.Context, v lnwire.GossipVersion,
nodePub route.Vertex) error {
if v != lnwire.GossipVersion1 {
return ErrVersionNotSupportedForKVDB
}
// TODO(roasbeef): ensure dangling edges are removed...
return kvdb.Update(c.db, func(tx kvdb.RwTx) error {
nodes := tx.ReadWriteBucket(nodeBucket)
@@ -3370,9 +3397,13 @@ func (c *KVStore) fetchNodeTx(tx kvdb.RTx, nodePub route.Vertex) (*models.Node,
// FetchNode attempts to look up a target node by its identity public
// key. If the node isn't found in the database, then ErrGraphNodeNotFound is
// returned.
func (c *KVStore) FetchNode(_ context.Context,
func (c *KVStore) FetchNode(_ context.Context, v lnwire.GossipVersion,
nodePub route.Vertex) (*models.Node, error) {
if v != lnwire.GossipVersion1 {
return nil, ErrVersionNotSupportedForKVDB
}
return c.fetchLightningNode(nil, nodePub)
}
@@ -3487,7 +3518,13 @@ func (c *KVStore) HasV1Node(_ context.Context,
// 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) {
func (c *KVStore) HasNode(_ context.Context, v lnwire.GossipVersion,
nodePub [33]byte) (bool, error) {
if v != lnwire.GossipVersion1 {
return false, ErrVersionNotSupportedForKVDB
}
var exists bool
err := kvdb.View(c.db, func(tx kvdb.RTx) error {
// First grab the nodes bucket which stores the mapping from
@@ -4366,6 +4403,10 @@ func (c *nodeTraverserSession) FetchNodeFeatures(nodePub route.Vertex) (
func putLightningNode(nodeBucket, aliasBucket, updateIndex kvdb.RwBucket,
node *models.Node) error {
if node.Version != lnwire.GossipVersion1 {
return ErrVersionNotSupportedForKVDB
}
var (
scratch [16]byte
b bytes.Buffer
+32 -28
View File
@@ -267,13 +267,15 @@ func (s *SQLStore) AddNode(ctx context.Context,
// returned.
//
// NOTE: part of the Store interface.
func (s *SQLStore) FetchNode(ctx context.Context,
func (s *SQLStore) FetchNode(ctx context.Context, v lnwire.GossipVersion,
pubKey route.Vertex) (*models.Node, error) {
var node *models.Node
err := s.db.ExecTx(ctx, sqldb.ReadTxOpt(), func(db SQLQueries) error {
var err error
_, node, err = getNodeByPubKey(ctx, s.cfg.QueryCfg, db, pubKey)
_, node, err = getNodeByPubKey(
ctx, s.cfg.QueryCfg, db, v, pubKey,
)
return err
}, sqldb.NoOpReset)
@@ -331,11 +333,10 @@ func (s *SQLStore) HasV1Node(ctx context.Context,
// 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
)
func (s *SQLStore) HasNode(ctx context.Context, v lnwire.GossipVersion,
pubKey [33]byte) (bool, error) {
var exists bool
err := s.db.ExecTx(ctx, sqldb.ReadTxOpt(), func(db SQLQueries) error {
var err error
exists, err = db.NodeExists(ctx, sqlc.NodeExistsParams{
@@ -358,7 +359,7 @@ func (s *SQLStore) HasNode(ctx context.Context, pubKey [33]byte) (bool, error) {
// given node is unknown to the graph DB or not.
//
// NOTE: part of the Store interface.
func (s *SQLStore) AddrsForNode(ctx context.Context,
func (s *SQLStore) AddrsForNode(ctx context.Context, v lnwire.GossipVersion,
nodePub *btcec.PublicKey) (bool, []net.Addr, error) {
var (
@@ -370,7 +371,7 @@ func (s *SQLStore) AddrsForNode(ctx context.Context,
// does.
dbID, err := db.GetNodeIDByPubKey(
ctx, sqlc.GetNodeIDByPubKeyParams{
Version: int16(lnwire.GossipVersion1),
Version: int16(v),
PubKey: nodePub.SerializeCompressed(),
},
)
@@ -400,13 +401,13 @@ func (s *SQLStore) AddrsForNode(ctx context.Context,
// from the database according to the node's public key.
//
// NOTE: part of the Store interface.
func (s *SQLStore) DeleteNode(ctx context.Context,
func (s *SQLStore) DeleteNode(ctx context.Context, v lnwire.GossipVersion,
pubKey route.Vertex) error {
err := s.db.ExecTx(ctx, sqldb.WriteTxOpt(), func(db SQLQueries) error {
res, err := db.DeleteNodeByPubKey(
ctx, sqlc.DeleteNodeByPubKeyParams{
Version: int16(lnwire.GossipVersion1),
Version: int16(v),
PubKey: pubKey[:],
},
)
@@ -438,12 +439,12 @@ func (s *SQLStore) DeleteNode(ctx context.Context,
// known for the node, an empty feature vector is returned.
//
// NOTE: this is part of the graphdb.NodeTraverser interface.
func (s *SQLStore) FetchNodeFeatures(nodePub route.Vertex) (
*lnwire.FeatureVector, error) {
func (s *SQLStore) FetchNodeFeatures(v lnwire.GossipVersion,
nodePub route.Vertex) (*lnwire.FeatureVector, error) {
ctx := context.TODO()
return fetchNodeFeatures(ctx, s.db, nodePub)
return fetchNodeFeatures(ctx, s.db, v, nodePub)
}
// DisabledChannelIDs returns the channel ids of disabled channels.
@@ -478,14 +479,14 @@ func (s *SQLStore) DisabledChannelIDs() ([]uint64, error) {
// LookupAlias attempts to return the alias as advertised by the target node.
//
// NOTE: part of the Store interface.
func (s *SQLStore) LookupAlias(ctx context.Context,
func (s *SQLStore) LookupAlias(ctx context.Context, v lnwire.GossipVersion,
pub *btcec.PublicKey) (string, error) {
var alias string
err := 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: pub.SerializeCompressed(),
},
)
@@ -516,20 +517,20 @@ func (s *SQLStore) LookupAlias(ctx context.Context,
// node based off the source node.
//
// NOTE: part of the Store interface.
func (s *SQLStore) SourceNode(ctx context.Context) (*models.Node,
error) {
func (s *SQLStore) SourceNode(ctx context.Context,
v lnwire.GossipVersion) (*models.Node, error) {
var node *models.Node
err := s.db.ExecTx(ctx, sqldb.ReadTxOpt(), func(db SQLQueries) error {
_, nodePub, err := s.getSourceNode(
ctx, db, lnwire.GossipVersion1,
)
_, nodePub, err := s.getSourceNode(ctx, db, v)
if err != nil {
return fmt.Errorf("unable to fetch V1 source node: %w",
err)
}
_, node, err = getNodeByPubKey(ctx, s.cfg.QueryCfg, db, nodePub)
_, node, err = getNodeByPubKey(
ctx, s.cfg.QueryCfg, db, v, nodePub,
)
return err
}, sqldb.NoOpReset)
@@ -919,7 +920,8 @@ func (s *SQLStore) ForEachSourceNodeChannel(ctx context.Context,
}
_, otherNode, err := getNodeByPubKey(
ctx, s.cfg.QueryCfg, db, otherNodePub,
ctx, s.cfg.QueryCfg, db,
lnwire.GossipVersion1, otherNodePub,
)
if err != nil {
return fmt.Errorf("unable to fetch "+
@@ -3104,7 +3106,7 @@ func (s *sqlNodeTraverser) FetchNodeFeatures(nodePub route.Vertex) (
ctx := context.TODO()
return fetchNodeFeatures(ctx, s.db, nodePub)
return fetchNodeFeatures(ctx, s.db, lnwire.GossipVersion1, nodePub)
}
// forEachNodeDirectedChannel iterates through all channels of a given
@@ -3466,11 +3468,12 @@ func updateChanEdgePolicy(ctx context.Context, tx SQLQueries,
// getNodeByPubKey attempts to look up a target node by its public key.
func getNodeByPubKey(ctx context.Context, cfg *sqldb.QueryConfig, db SQLQueries,
pubKey route.Vertex) (int64, *models.Node, error) {
v lnwire.GossipVersion, pubKey route.Vertex) (int64, *models.Node,
error) {
dbNode, err := db.GetNodeByPubKey(
ctx, sqlc.GetNodeByPubKeyParams{
Version: int16(lnwire.GossipVersion1),
Version: int16(v),
PubKey: pubKey[:],
},
)
@@ -3926,12 +3929,13 @@ func upsertNodeFeatures(ctx context.Context, db SQLQueries, nodeID int64,
// fetchNodeFeatures fetches the features for a node with the given public key.
func fetchNodeFeatures(ctx context.Context, queries SQLQueries,
nodePub route.Vertex) (*lnwire.FeatureVector, error) {
v lnwire.GossipVersion, nodePub route.Vertex) (*lnwire.FeatureVector,
error) {
rows, err := queries.GetNodeFeaturesByPubKey(
ctx, sqlc.GetNodeFeaturesByPubKeyParams{
PubKey: nodePub[:],
Version: int16(lnwire.GossipVersion1),
Version: int16(v),
},
)
if err != nil {