graph/db: version MarkEdgeLive

Add a gossip version parameter to MarkEdgeLive throughout the stack:

- Store interface and KVStore/SQLStore implementations now take
  lnwire.GossipVersion; KVStore rejects non-v1 with
  ErrVersionNotSupportedForKVDB, SQLStore uses the version in the
  DeleteZombieChannel query and cache invalidation.
- ChannelGraph.MarkEdgeLive passes the version through to both the
  store call and the FetchChanInfos cache repopulation.
- FilterKnownChanIDs uses GossipVersion1 explicitly for its internal
  MarkEdgeLive call; this site will be properly versioned when
  FilterKnownChanIDs itself is versioned.
- graph.ChannelGraphSource interface and Builder.MarkEdgeLive updated
  accordingly.
- Discovery gossiper and test mock updated to pass GossipVersion1 at
  their (v1-only) call sites.
This commit is contained in:
Elle Mouton 2026-03-03 12:51:24 +02:00
parent 787dbcf7b3
commit e9eff6297f
No known key found for this signature in database
GPG key ID: D7D916376026F177
9 changed files with 58 additions and 34 deletions

View file

@ -2340,7 +2340,7 @@ func (d *AuthenticatedGossiper) processZombieUpdate(_ context.Context,
// With the signature valid, we'll proceed to mark the
// edge as live and wait for the channel announcement to
// come through again.
err = d.cfg.Graph.MarkEdgeLive(scid)
err = d.cfg.Graph.MarkEdgeLive(lnwire.GossipVersion1, scid)
switch {
case errors.Is(err, graphdb.ErrZombieEdgeNotFound):
log.Errorf("edge with chan_id=%v was not found in the "+

View file

@ -413,7 +413,9 @@ func (r *mockGraphSource) IsStaleEdgePolicy(chanID lnwire.ShortChannelID,
// MarkEdgeLive clears an edge from our zombie index, deeming it as live.
//
// NOTE: This method is part of the ChannelGraphSource interface.
func (r *mockGraphSource) MarkEdgeLive(chanID lnwire.ShortChannelID) error {
func (r *mockGraphSource) MarkEdgeLive(_ lnwire.GossipVersion,
chanID lnwire.ShortChannelID) error {
r.mu.Lock()
defer r.mu.Unlock()
delete(r.zombies, chanID.ToUint64())
@ -2440,7 +2442,8 @@ func TestRejectZombieEdge(t *testing.T) {
// If we then mark the edge as live, the edge's zombie status should be
// overridden and the announcements should be processed.
if err := tCtx.router.MarkEdgeLive(chanID); err != nil {
err = tCtx.router.MarkEdgeLive(lnwire.GossipVersion1, chanID)
if err != nil {
t.Fatalf("unable mark channel %v as zombie: %v", chanID, err)
}
@ -4811,7 +4814,7 @@ func TestChanAnnBanningNonChanPeer(t *testing.T) {
// as a zombie if any error occurs in the chanvalidate.Validate call.
// For the sake of the rest of the test, however, we mark it as live
// here.
_ = tCtx.router.MarkEdgeLive(ca.ShortChannelID)
_ = tCtx.router.MarkEdgeLive(lnwire.GossipVersion1, ca.ShortChannelID)
select {
case err = <-tCtx.gossiper.ProcessRemoteAnnouncement(

View file

@ -1450,11 +1450,14 @@ func (b *Builder) IsStaleEdgePolicy(chanID lnwire.ShortChannelID,
return false
}
// MarkEdgeLive clears an edge from our zombie index, deeming it as live.
// MarkEdgeLive clears an edge from our zombie index for the given gossip
// version, deeming it as live.
//
// NOTE: This method is part of the ChannelGraphSource interface.
func (b *Builder) MarkEdgeLive(chanID lnwire.ShortChannelID) error {
func (b *Builder) MarkEdgeLive(v lnwire.GossipVersion,
chanID lnwire.ShortChannelID) error {
return b.cfg.Graph.MarkEdgeLive(
context.TODO(), chanID.ToUint64(),
context.TODO(), v, chanID.ToUint64(),
)
}

View file

@ -345,11 +345,13 @@ func (c *ChannelGraph) AddChannelEdge(ctx context.Context,
return nil
}
// MarkEdgeLive clears an edge from our zombie index, deeming it as live.
// If the cache is enabled, the edge will be added back to the graph cache if
// we still have a record of this channel in the DB.
func (c *ChannelGraph) MarkEdgeLive(ctx context.Context, chanID uint64) error {
err := c.db.MarkEdgeLive(ctx, chanID)
// MarkEdgeLive clears an edge from our zombie index for the given gossip
// version, deeming it as live. If the cache is enabled, the edge will be added
// back to the graph cache if we still have a record of this channel in the DB.
func (c *ChannelGraph) MarkEdgeLive(ctx context.Context,
v lnwire.GossipVersion, chanID uint64) error {
err := c.db.MarkEdgeLive(ctx, v, chanID)
if err != nil {
return err
}
@ -357,9 +359,7 @@ func (c *ChannelGraph) MarkEdgeLive(ctx context.Context, chanID uint64) error {
if c.graphCache != nil {
// We need to add the channel back into our graph cache,
// otherwise we won't use it for path finding.
infos, err := c.db.FetchChanInfos(
ctx, lnwire.GossipVersion1, []uint64{chanID},
)
infos, err := c.db.FetchChanInfos(ctx, v, []uint64{chanID})
if err != nil {
return err
}
@ -554,7 +554,8 @@ func (c *ChannelGraph) FilterKnownChanIDs(ctx context.Context,
// alive, and we let it be added to the set of IDs to query our
// peer for.
err := c.db.MarkEdgeLive(
ctx, info.ShortChannelID.ToUint64(),
ctx, lnwire.GossipVersion1,
info.ShortChannelID.ToUint64(),
)
// Since there is a chance that the edge could have been marked
// as "live" between the FilterKnownChanIDs call and the

View file

@ -4610,12 +4610,16 @@ func TestGraphZombieIndex(t *testing.T) {
// Similarly, if we mark the same edge as live, we should no longer see
// it within the index.
require.NoError(t, graph.MarkEdgeLive(ctx, edge.ChannelID))
require.NoError(
t, graph.MarkEdgeLive(ctx, lnwire.GossipVersion1, edge.ChannelID),
)
// Attempting to mark the edge as live again now that it is no longer
// in the zombie index should fail.
require.ErrorIs(
t, graph.MarkEdgeLive(ctx, edge.ChannelID),
t, graph.MarkEdgeLive(
ctx, lnwire.GossipVersion1, edge.ChannelID,
),
ErrZombieEdgeNotFound,
)

View file

@ -332,9 +332,10 @@ type Store interface { //nolint:interfacebloat
MarkEdgeZombie(ctx context.Context, chanID uint64,
pubKey1, pubKey2 [33]byte) error
// MarkEdgeLive clears an edge from our zombie index, deeming it as
// live.
MarkEdgeLive(ctx context.Context, chanID uint64) error
// MarkEdgeLive clears an edge from our zombie index for the given
// gossip version, deeming it as live.
MarkEdgeLive(ctx context.Context, v lnwire.GossipVersion,
chanID uint64) error
// IsZombieEdge returns whether the edge is considered zombie. If it is
// a zombie, then the two node public keys corresponding to this edge

View file

@ -4291,8 +4291,15 @@ func markEdgeZombie(zombieIndex kvdb.RwBucket, chanID uint64, pubKey1,
return zombieIndex.Put(k[:], v[:])
}
// MarkEdgeLive clears an edge from our zombie index, deeming it as live.
func (c *KVStore) MarkEdgeLive(_ context.Context, chanID uint64) error {
// MarkEdgeLive clears an edge from our zombie index for the given gossip
// version, deeming it as live.
func (c *KVStore) MarkEdgeLive(_ context.Context, v lnwire.GossipVersion,
chanID uint64) error {
if v != lnwire.GossipVersion1 {
return ErrVersionNotSupportedForKVDB
}
c.cacheMu.Lock()
defer c.cacheMu.Unlock()

View file

@ -1812,22 +1812,27 @@ func (s *SQLStore) MarkEdgeZombie(ctx context.Context, chanID uint64,
return nil
}
// MarkEdgeLive clears an edge from our zombie index, deeming it as live.
// MarkEdgeLive clears an edge from our zombie index for the given gossip
// version, deeming it as live.
//
// NOTE: part of the Store interface.
func (s *SQLStore) MarkEdgeLive(ctx context.Context, chanID uint64) error {
func (s *SQLStore) MarkEdgeLive(ctx context.Context,
v lnwire.GossipVersion, chanID uint64) error {
s.cacheMu.Lock()
defer s.cacheMu.Unlock()
var (
chanIDB = channelIDToBytes(chanID)
)
if !isKnownGossipVersion(v) {
return fmt.Errorf("unsupported gossip version: %d", v)
}
chanIDB := channelIDToBytes(chanID)
err := s.db.ExecTx(ctx, sqldb.WriteTxOpt(), func(db SQLQueries) error {
res, err := db.DeleteZombieChannel(
ctx, sqlc.DeleteZombieChannelParams{
Scid: chanIDB,
Version: int16(lnwire.GossipVersion1),
Version: int16(v),
},
)
if err != nil {
@ -1854,8 +1859,8 @@ func (s *SQLStore) MarkEdgeLive(ctx context.Context, chanID uint64) error {
"(channel_id=%d): %w", chanID, err)
}
s.rejectCache.remove(lnwire.GossipVersion1, chanID)
s.chanCache.remove(lnwire.GossipVersion1, chanID)
s.rejectCache.remove(v, chanID)
s.chanCache.remove(v, chanID)
return err
}

View file

@ -60,9 +60,9 @@ type ChannelGraphSource interface {
IsStaleEdgePolicy(chanID lnwire.ShortChannelID, timestamp time.Time,
flags lnwire.ChanUpdateChanFlags) bool
// MarkEdgeLive clears an edge from our zombie index, deeming it as
// live.
MarkEdgeLive(chanID lnwire.ShortChannelID) error
// MarkEdgeLive clears an edge from our zombie index for the given
// gossip version, deeming it as live.
MarkEdgeLive(v lnwire.GossipVersion, chanID lnwire.ShortChannelID) error
// ForAllOutgoingChannels is used to iterate over all channels
// emanating from the "source" node which is the center of the