Skip to content

Commit 3687171

Browse files
committed
sqldb+graph/db: implement FilterChannelRange
This lets us run `TestFilterChannelRange` against the SQL backends.
1 parent ff84fa1 commit 3687171

File tree

6 files changed

+235
-1
lines changed

6 files changed

+235
-1
lines changed

docs/release-notes/release-notes-0.20.0.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ circuit. The indices are only available for forwarding events saved after v0.20.
8585
* [3](https://github.com/lightningnetwork/lnd/pull/9887)
8686
* [4](https://github.com/lightningnetwork/lnd/pull/9931)
8787
* [5](https://github.com/lightningnetwork/lnd/pull/9935)
88+
* [6](https://github.com/lightningnetwork/lnd/pull/9936)
8889

8990
## RPC Updates
9091

graph/db/graph_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2727,7 +2727,7 @@ func TestFilterChannelRange(t *testing.T) {
27272727
t.Parallel()
27282728
ctx := context.Background()
27292729

2730-
graph := MakeTestGraph(t)
2730+
graph := MakeTestGraphNew(t)
27312731

27322732
// We'll first populate our graph with two nodes. All channels created
27332733
// below will be made between these two nodes.

graph/db/sql_store.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ import (
77
"encoding/hex"
88
"errors"
99
"fmt"
10+
"maps"
1011
"math"
1112
"net"
13+
"slices"
1214
"strconv"
1315
"sync"
1416
"time"
@@ -92,6 +94,7 @@ type SQLQueries interface {
9294
ListChannelsByNodeID(ctx context.Context, arg sqlc.ListChannelsByNodeIDParams) ([]sqlc.ListChannelsByNodeIDRow, error)
9395
ListChannelsWithPoliciesPaginated(ctx context.Context, arg sqlc.ListChannelsWithPoliciesPaginatedParams) ([]sqlc.ListChannelsWithPoliciesPaginatedRow, error)
9496
GetChannelsByPolicyLastUpdateRange(ctx context.Context, arg sqlc.GetChannelsByPolicyLastUpdateRangeParams) ([]sqlc.GetChannelsByPolicyLastUpdateRangeRow, error)
97+
GetPublicV1ChannelsBySCID(ctx context.Context, arg sqlc.GetPublicV1ChannelsBySCIDParams) ([]sqlc.Channel, error)
9598

9699
CreateChannelExtraType(ctx context.Context, arg sqlc.CreateChannelExtraTypeParams) error
97100
InsertChannelFeature(ctx context.Context, arg sqlc.InsertChannelFeatureParams) error
@@ -100,6 +103,7 @@ type SQLQueries interface {
100103
Channel Policy table queries.
101104
*/
102105
UpsertEdgePolicy(ctx context.Context, arg sqlc.UpsertEdgePolicyParams) (int64, error)
106+
GetChannelPolicyByChannelAndNode(ctx context.Context, arg sqlc.GetChannelPolicyByChannelAndNodeParams) (sqlc.ChannelPolicy, error)
103107

104108
InsertChanPolicyExtraType(ctx context.Context, arg sqlc.InsertChanPolicyExtraTypeParams) error
105109
GetChannelPolicyExtraTypes(ctx context.Context, arg sqlc.GetChannelPolicyExtraTypesParams) ([]sqlc.GetChannelPolicyExtraTypesRow, error)
@@ -1262,6 +1266,133 @@ func (s *SQLStore) ForEachChannel(cb func(*models.ChannelEdgeInfo,
12621266
}, sqldb.NoOpReset)
12631267
}
12641268

1269+
// FilterChannelRange returns the channel ID's of all known channels which were
1270+
// mined in a block height within the passed range. The channel IDs are grouped
1271+
// by their common block height. This method can be used to quickly share with a
1272+
// peer the set of channels we know of within a particular range to catch them
1273+
// up after a period of time offline. If withTimestamps is true then the
1274+
// timestamp info of the latest received channel update messages of the channel
1275+
// will be included in the response.
1276+
//
1277+
// NOTE: This is part of the V1Store interface.
1278+
func (s *SQLStore) FilterChannelRange(startHeight, endHeight uint32,
1279+
withTimestamps bool) ([]BlockChannelRange, error) {
1280+
1281+
var (
1282+
ctx = context.TODO()
1283+
startSCID = &lnwire.ShortChannelID{
1284+
BlockHeight: startHeight,
1285+
}
1286+
endSCID = lnwire.ShortChannelID{
1287+
BlockHeight: endHeight,
1288+
TxIndex: math.MaxUint32 & 0x00ffffff,
1289+
TxPosition: math.MaxUint16,
1290+
}
1291+
)
1292+
1293+
var chanIDStart [8]byte
1294+
byteOrder.PutUint64(chanIDStart[:], startSCID.ToUint64())
1295+
var chanIDEnd [8]byte
1296+
byteOrder.PutUint64(chanIDEnd[:], endSCID.ToUint64())
1297+
1298+
// 1) get all channels where channelID is between start and end chan ID.
1299+
// 2) skip if not public (ie, no channel_proof)
1300+
// 3) collect that channel.
1301+
// 4) if timestamps are wanted, fetch both policies for node 1 and node2
1302+
// and add those timestamps to the collected channel.
1303+
channelsPerBlock := make(map[uint32][]ChannelUpdateInfo)
1304+
err := s.db.ExecTx(ctx, sqldb.ReadTxOpt(), func(db SQLQueries) error {
1305+
dbChans, err := db.GetPublicV1ChannelsBySCID(
1306+
ctx, sqlc.GetPublicV1ChannelsBySCIDParams{
1307+
StartScid: chanIDStart[:],
1308+
EndScid: chanIDEnd[:],
1309+
},
1310+
)
1311+
if err != nil {
1312+
return fmt.Errorf("unable to fetch channel range: %w",
1313+
err)
1314+
}
1315+
1316+
for _, dbChan := range dbChans {
1317+
cid := lnwire.NewShortChanIDFromInt(
1318+
byteOrder.Uint64(dbChan.Scid),
1319+
)
1320+
chanInfo := NewChannelUpdateInfo(
1321+
cid, time.Time{}, time.Time{},
1322+
)
1323+
1324+
if !withTimestamps {
1325+
channelsPerBlock[cid.BlockHeight] = append(
1326+
channelsPerBlock[cid.BlockHeight],
1327+
chanInfo,
1328+
)
1329+
1330+
continue
1331+
}
1332+
1333+
//nolint:ll
1334+
node1Policy, err := db.GetChannelPolicyByChannelAndNode(
1335+
ctx, sqlc.GetChannelPolicyByChannelAndNodeParams{
1336+
Version: int16(ProtocolV1),
1337+
ChannelID: dbChan.ID,
1338+
NodeID: dbChan.NodeID1,
1339+
},
1340+
)
1341+
if err != nil && !errors.Is(err, sql.ErrNoRows) {
1342+
return fmt.Errorf("unable to fetch node1 "+
1343+
"policy: %w", err)
1344+
} else if err == nil {
1345+
chanInfo.Node1UpdateTimestamp = time.Unix(
1346+
node1Policy.LastUpdate.Int64, 0,
1347+
)
1348+
}
1349+
1350+
//nolint:ll
1351+
node2Policy, err := db.GetChannelPolicyByChannelAndNode(
1352+
ctx, sqlc.GetChannelPolicyByChannelAndNodeParams{
1353+
Version: int16(ProtocolV1),
1354+
ChannelID: dbChan.ID,
1355+
NodeID: dbChan.NodeID2,
1356+
},
1357+
)
1358+
if err != nil && !errors.Is(err, sql.ErrNoRows) {
1359+
return fmt.Errorf("unable to fetch node2 "+
1360+
"policy: %w", err)
1361+
} else if err == nil {
1362+
chanInfo.Node2UpdateTimestamp = time.Unix(
1363+
node2Policy.LastUpdate.Int64, 0,
1364+
)
1365+
}
1366+
1367+
channelsPerBlock[cid.BlockHeight] = append(
1368+
channelsPerBlock[cid.BlockHeight], chanInfo,
1369+
)
1370+
}
1371+
1372+
return nil
1373+
}, func() {
1374+
channelsPerBlock = make(map[uint32][]ChannelUpdateInfo)
1375+
})
1376+
if err != nil {
1377+
return nil, fmt.Errorf("unable to fetch channel range: %w", err)
1378+
}
1379+
1380+
if len(channelsPerBlock) == 0 {
1381+
return nil, nil
1382+
}
1383+
1384+
// Return the channel ranges in ascending block height order.
1385+
blocks := slices.Collect(maps.Keys(channelsPerBlock))
1386+
slices.Sort(blocks)
1387+
1388+
return fn.Map(blocks, func(block uint32) BlockChannelRange {
1389+
return BlockChannelRange{
1390+
Height: block,
1391+
Channels: channelsPerBlock[block],
1392+
}
1393+
}), nil
1394+
}
1395+
12651396
// forEachNodeDirectedChannel iterates through all channels of a given
12661397
// node, executing the passed callback on the directed edge representing the
12671398
// channel and its incoming policy. If the node is not found, no error is

sqldb/sqlc/graph.sql.go

Lines changed: 86 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sqldb/sqlc/querier.go

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sqldb/sqlc/queries/graph.sql

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,13 @@ FROM channels c
317317
WHERE c.version = $1
318318
AND (c.node_id_1 = $2 OR c.node_id_2 = $2);
319319

320+
-- name: GetPublicV1ChannelsBySCID :many
321+
SELECT *
322+
FROM channels
323+
WHERE node_1_signature IS NOT NULL
324+
AND scid >= @start_scid
325+
AND scid < @end_scid;
326+
320327
-- name: ListChannelsWithPoliciesPaginated :many
321328
SELECT
322329
sqlc.embed(c),
@@ -420,6 +427,13 @@ ON CONFLICT (channel_id, node_id, version)
420427
WHERE EXCLUDED.last_update > channel_policies.last_update
421428
RETURNING id;
422429

430+
-- name: GetChannelPolicyByChannelAndNode :one
431+
SELECT *
432+
FROM channel_policies
433+
WHERE channel_id = $1
434+
AND node_id = $2
435+
AND version = $3;
436+
423437
/* ─────────────────────────────────────────────
424438
channel_policy_extra_types table queries
425439
─────────────────────────────────────────────

0 commit comments

Comments
 (0)