chanevents+db: add scid-to-peer index

Add ScidToPeerMap, which materialises a snapshot of every short channel
id paired with the pubkey of the channel's remote peer. Forwarding-data
sources index events by short channel id, but downstream analyses need
to attribute behaviour to the peer, not the channel. The map skips
channels whose short channel id is still zero (unconfirmed), so callers
see only fully advertised channels. Coverage extends TestStore with a
two-channel fixture pinning the join.
This commit is contained in:
bitromortac 2026-05-06 07:05:20 +02:00
parent 7431bb5d67
commit 2363173325
5 changed files with 73 additions and 0 deletions

View file

@ -49,6 +49,8 @@ type Queries interface {
sqlc.ChannelEvent,
error,
)
GetChannels(ctx context.Context) ([]sqlc.GetChannelsRow, error)
}
// Store provides access to the db for channel events.
@ -263,6 +265,29 @@ func (s *Store) GetChannelEvents(ctx context.Context, channelID, afterID int64,
return events, nil
}
// ScidToPeerMap returns the historic scid→peer index, including channels that
// have since closed. Unconfirmed channels (scid still zero) are not part of
// the contract.
func (s *Store) ScidToPeerMap(ctx context.Context) (map[uint64]string, error) {
dbChannels, err := s.db.GetChannels(ctx)
if err != nil {
return nil, err
}
scidToPeer := make(map[uint64]string, len(dbChannels))
for _, dbChannel := range dbChannels {
// The short channel ID can be zero if it's not known yet. We
// should just ignore those.
if dbChannel.ShortChannelID == 0 {
continue
}
scidToPeer[uint64(dbChannel.ShortChannelID)] = dbChannel.Pubkey
}
return scidToPeer, nil
}
// GetLatestChannelUpdateBefore returns the latest channel event before a given
// time (exclusive). If no event is found, it returns (nil, nil).
func (s *Store) GetLatestChannelUpdateBefore(ctx context.Context,

View file

@ -100,6 +100,13 @@ func TestStore(t *testing.T) {
require.NoError(t, err)
require.NotZero(t, channel2ID)
// Get the historic channel to peer map.
chanToPeer, err := store.ScidToPeerMap(ctx)
require.NoError(t, err)
require.Len(t, chanToPeer, 2)
require.Equal(t, testPubKey, chanToPeer[testShortChanID1])
require.Equal(t, testPubKey, chanToPeer[testShortChanID2])
// Add an online event for the channel.
onlineEvent := &ChannelEvent{
ChannelID: channelID,

View file

@ -98,6 +98,41 @@ func (q *Queries) GetChannelEvents(ctx context.Context, arg GetChannelEventsPara
return items, nil
}
const getChannels = `-- name: GetChannels :many
SELECT c.id, c.short_channel_id, p.pubkey
FROM channels c
JOIN peers p ON c.peer_id = p.id
`
type GetChannelsRow struct {
ID int64
ShortChannelID int64
Pubkey string
}
func (q *Queries) GetChannels(ctx context.Context) ([]GetChannelsRow, error) {
rows, err := q.db.QueryContext(ctx, getChannels)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetChannelsRow
for rows.Next() {
var i GetChannelsRow
if err := rows.Scan(&i.ID, &i.ShortChannelID, &i.Pubkey); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getLatestChannelEventBefore = `-- name: GetLatestChannelEventBefore :one
SELECT id, channel_id, event_type, timestamp, local_balance_sat, remote_balance_sat, is_sync FROM channel_events
WHERE channel_id = $1 AND event_type = $2 AND timestamp < $3

View file

@ -12,6 +12,7 @@ type Querier interface {
GetChannelByChanPoint(ctx context.Context, channelPoint string) (Channel, error)
GetChannelByShortChanID(ctx context.Context, shortChannelID int64) (Channel, error)
GetChannelEvents(ctx context.Context, arg GetChannelEventsParams) ([]ChannelEvent, error)
GetChannels(ctx context.Context) ([]GetChannelsRow, error)
GetLatestChannelEventBefore(ctx context.Context, arg GetLatestChannelEventBeforeParams) (ChannelEvent, error)
GetPeerByPubKey(ctx context.Context, pubkey string) (Peer, error)
InsertChannel(ctx context.Context, arg InsertChannelParams) (int64, error)

View file

@ -33,3 +33,8 @@ SELECT * FROM channel_events
WHERE channel_id = $1 AND event_type = $2 AND timestamp < $3
ORDER BY timestamp DESC, id DESC
LIMIT 1;
-- name: GetChannels :many
SELECT c.id, c.short_channel_id, p.pubkey
FROM channels c
JOIN peers p ON c.peer_id = p.id;