graph/db: version FilterChannelRange

Add a gossip version parameter to FilterChannelRange in the Store interface,
both KV and SQL implementations, and the ChannelGraph wrapper.

KVStore guards against non-v1 versions with ErrVersionNotSupportedForKVDB.
SQLStore accepts any known gossip version, filtering the channel results by
version and using it in policy lookups. The SQL query still uses
GetPublicV1ChannelsBySCID for now (a TODO marks where a version-aware query
will be substituted in a follow-up).

VersionedGraph.FilterChannelRange shadows the ChannelGraph method with a
version-free signature, passing its baked-in version to the store. This keeps
the ChannelGraphTimeSeries interface and ChanSeries implementation unchanged.

Add TestFilterChannelRangeVersionGuard to verify that the KV store returns
ErrVersionNotSupportedForKVDB for v2 requests while the SQL store handles
them gracefully.
This commit is contained in:
Elle Mouton 2026-03-03 14:12:53 +02:00
parent 23288dce6f
commit 3c06daed8b
No known key found for this signature in database
GPG key ID: D7D916376026F177
5 changed files with 72 additions and 22 deletions

View file

@ -717,13 +717,25 @@ func (c *ChannelGraph) ChanUpdatesInHorizon(ctx context.Context,
return c.db.ChanUpdatesInHorizon(ctx, startTime, endTime, opts...)
}
// FilterChannelRange returns channel IDs within the passed block height range.
// FilterChannelRange returns channel IDs within the passed block height range
// for the given gossip version.
func (c *ChannelGraph) FilterChannelRange(ctx context.Context,
startHeight, endHeight uint32, withTimestamps bool) (
[]BlockChannelRange, error) {
v lnwire.GossipVersion, startHeight, endHeight uint32,
withTimestamps bool) ([]BlockChannelRange, error) {
return c.db.FilterChannelRange(
ctx, startHeight, endHeight, withTimestamps,
ctx, v, startHeight, endHeight, withTimestamps,
)
}
// FilterChannelRange returns channel IDs within the passed block height range
// for this graph's gossip version.
func (c *VersionedGraph) FilterChannelRange(ctx context.Context,
startHeight, endHeight uint32,
withTimestamps bool) ([]BlockChannelRange, error) {
return c.db.FilterChannelRange(
ctx, c.v, startHeight, endHeight, withTimestamps,
)
}

View file

@ -3505,7 +3505,9 @@ func TestFilterChannelRange(t *testing.T) {
// If we try to filter a channel range before we have any channels
// inserted, we should get an empty slice of results.
resp, err := graph.FilterChannelRange(ctx, 10, 100, false)
resp, err := graph.FilterChannelRange(
ctx, lnwire.GossipVersion1, 10, 100, false,
)
require.NoError(t, err)
require.Empty(t, resp)
@ -3676,7 +3678,8 @@ func TestFilterChannelRange(t *testing.T) {
// First, do the query without requesting timestamps.
resp, err := graph.FilterChannelRange(
ctx, test.startHeight, test.endHeight, false,
ctx, lnwire.GossipVersion1, test.startHeight,
test.endHeight, false,
)
require.NoError(t, err)
@ -3690,7 +3693,8 @@ func TestFilterChannelRange(t *testing.T) {
// Now, query the timestamps as well.
resp, err = graph.FilterChannelRange(
ctx, test.startHeight, test.endHeight, true,
ctx, lnwire.GossipVersion1, test.startHeight,
test.endHeight, true,
)
require.NoError(t, err)
@ -3705,6 +3709,29 @@ func TestFilterChannelRange(t *testing.T) {
}
}
// TestFilterChannelRangeVersionGuard checks that FilterChannelRange correctly
// handles version-specific requests. For gossip v1, the KV store returns
// results as normal; for v2, the KV store returns
// ErrVersionNotSupportedForKVDB while the SQL store returns empty results
// (a v2-aware query is a follow-up).
func TestFilterChannelRangeVersionGuard(t *testing.T) {
t.Parallel()
ctx := t.Context()
store := NewTestDB(t)
_, err := store.FilterChannelRange(
ctx, lnwire.GossipVersion2, 0, 1000, false,
)
// The KV store does not support v2 and must return the sentinel error.
// The SQL store accepts any known version (returning empty results
// since no v2 channels have been added).
if err != nil {
require.ErrorIs(t, err, ErrVersionNotSupportedForKVDB)
}
}
// TestFetchChanInfos tests that we're able to properly retrieve the full set
// of ChannelEdge structs for a given set of short channel ID's.
func testFetchChanInfos(t *testing.T, v lnwire.GossipVersion) {

View file

@ -273,15 +273,15 @@ type Store interface { //nolint:interfacebloat
error)
// FilterChannelRange returns the channel ID's of all known channels
// which were mined in a block height within the passed range. The
// channel IDs are grouped by their common block height. This method can
// be used to quickly share with a peer the set of channels we know of
// within a particular range to catch them up after a period of time
// offline. If withTimestamps is true then the timestamp info of the
// latest received channel update messages of the channel will be
// included in the response.
FilterChannelRange(ctx context.Context, startHeight,
endHeight uint32,
// which were mined in a block height within the passed range for the
// given gossip version. The channel IDs are grouped by their common
// block height. This method can be used to quickly share with a peer
// the set of channels we know of within a particular range to catch
// them up after a period of time offline. If withTimestamps is true
// then the timestamp info of the latest received channel update
// messages of the channel will be included in the response.
FilterChannelRange(ctx context.Context, v lnwire.GossipVersion,
startHeight, endHeight uint32,
withTimestamps bool) ([]BlockChannelRange, error)
// FetchChanInfos returns the set of channel edges that correspond to

View file

@ -2859,8 +2859,13 @@ type BlockChannelRange struct {
// up after a period of time offline. If withTimestamps is true then the
// timestamp info of the latest received channel update messages of the channel
// will be included in the response.
func (c *KVStore) FilterChannelRange(_ context.Context, startHeight,
endHeight uint32, withTimestamps bool) ([]BlockChannelRange, error) {
func (c *KVStore) FilterChannelRange(_ context.Context,
v lnwire.GossipVersion, startHeight, endHeight uint32,
withTimestamps bool) ([]BlockChannelRange, error) {
if v != lnwire.GossipVersion1 {
return nil, ErrVersionNotSupportedForKVDB
}
startChanID := &lnwire.ShortChannelID{
BlockHeight: startHeight,

View file

@ -1664,8 +1664,9 @@ func (s *SQLStore) ForEachChannel(ctx context.Context,
// will be included in the response.
//
// NOTE: This is part of the Store interface.
func (s *SQLStore) FilterChannelRange(ctx context.Context, startHeight,
endHeight uint32, withTimestamps bool) ([]BlockChannelRange, error) {
func (s *SQLStore) FilterChannelRange(ctx context.Context,
v lnwire.GossipVersion, startHeight, endHeight uint32,
withTimestamps bool) ([]BlockChannelRange, error) {
var (
startSCID = &lnwire.ShortChannelID{
@ -1687,6 +1688,7 @@ func (s *SQLStore) FilterChannelRange(ctx context.Context, startHeight,
// and add those timestamps to the collected channel.
channelsPerBlock := make(map[uint32][]ChannelUpdateInfo)
err := s.db.ExecTx(ctx, sqldb.ReadTxOpt(), func(db SQLQueries) error {
// TODO(elle): replace with a version-aware query.
dbChans, err := db.GetPublicV1ChannelsBySCID(
ctx, sqlc.GetPublicV1ChannelsBySCIDParams{
StartScid: chanIDStart,
@ -1699,6 +1701,10 @@ func (s *SQLStore) FilterChannelRange(ctx context.Context, startHeight,
}
for _, dbChan := range dbChans {
if v != lnwire.GossipVersion(dbChan.Version) {
continue
}
cid := lnwire.NewShortChanIDFromInt(
byteOrder.Uint64(dbChan.Scid),
)
@ -1718,7 +1724,7 @@ func (s *SQLStore) FilterChannelRange(ctx context.Context, startHeight,
//nolint:ll
node1Policy, err := db.GetChannelPolicyByChannelAndNode(
ctx, sqlc.GetChannelPolicyByChannelAndNodeParams{
Version: int16(lnwire.GossipVersion1),
Version: int16(v),
ChannelID: dbChan.ID,
NodeID: dbChan.NodeID1,
},
@ -1735,7 +1741,7 @@ func (s *SQLStore) FilterChannelRange(ctx context.Context, startHeight,
//nolint:ll
node2Policy, err := db.GetChannelPolicyByChannelAndNode(
ctx, sqlc.GetChannelPolicyByChannelAndNodeParams{
Version: int16(lnwire.GossipVersion1),
Version: int16(v),
ChannelID: dbChan.ID,
NodeID: dbChan.NodeID2,
},