mirror of
https://github.com/MeshCore-Beacon/beacon-server.git
synced 2026-10-22 18:51:25 +00:00
Add bounded topology snapshots and validate TRACE route evidence
This commit is contained in:
1 parent
bc84be577c
commit
1d3f7ce621
43 files changed
+1746
-144
No files matched your search
+31
-1
@@ -3,7 +3,7 @@
|
||||
//
|
||||
// Generated by this command:
|
||||
//
|
||||
// mockgen -source=db/sqlc/querier.go -destination=db/sqlc/mock/querier.go -package=mockdb
|
||||
// mockgen -source db/sqlc/querier.go -destination db/sqlc/mock/querier.go -package mockdb
|
||||
//
|
||||
|
||||
// Package mockdb is a generated GoMock package.
|
||||
@@ -1166,6 +1166,21 @@ func (mr *MockQuerierMockRecorder) GetTopNodes(ctx, arg any) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTopNodes", reflect.TypeOf((*MockQuerier)(nil).GetTopNodes), ctx, arg)
|
||||
}
|
||||
|
||||
// GetTopologyLinks mocks base method.
|
||||
func (m *MockQuerier) GetTopologyLinks(ctx context.Context, arg db.GetTopologyLinksParams) ([]db.GetTopologyLinksRow, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetTopologyLinks", ctx, arg)
|
||||
ret0, _ := ret[0].([]db.GetTopologyLinksRow)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetTopologyLinks indicates an expected call of GetTopologyLinks.
|
||||
func (mr *MockQuerierMockRecorder) GetTopologyLinks(ctx, arg any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTopologyLinks", reflect.TypeOf((*MockQuerier)(nil).GetTopologyLinks), ctx, arg)
|
||||
}
|
||||
|
||||
// GetTransportScopeByName mocks base method.
|
||||
func (m *MockQuerier) GetTransportScopeByName(ctx context.Context, name string) (int32, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -2029,6 +2044,21 @@ func (mr *MockQuerierMockRecorder) ResolveRelayHashPairsP4(ctx, arg any) *gomock
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResolveRelayHashPairsP4", reflect.TypeOf((*MockQuerier)(nil).ResolveRelayHashPairsP4), ctx, arg)
|
||||
}
|
||||
|
||||
// ResolveTraceHashes mocks base method.
|
||||
func (m *MockQuerier) ResolveTraceHashes(ctx context.Context, arg db.ResolveTraceHashesParams) ([]db.ResolveTraceHashesRow, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ResolveTraceHashes", ctx, arg)
|
||||
ret0, _ := ret[0].([]db.ResolveTraceHashesRow)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// ResolveTraceHashes indicates an expected call of ResolveTraceHashes.
|
||||
func (mr *MockQuerierMockRecorder) ResolveTraceHashes(ctx, arg any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResolveTraceHashes", reflect.TypeOf((*MockQuerier)(nil).ResolveTraceHashes), ctx, arg)
|
||||
}
|
||||
|
||||
// RollAdvertHearings mocks base method.
|
||||
func (m *MockQuerier) RollAdvertHearings(ctx context.Context, hour pgtype.Timestamptz) error {
|
||||
m.ctrl.T.Helper()
|
||||
|
||||
@@ -162,6 +162,8 @@ type Querier interface {
|
||||
// Nodes by ADVERT hearings since the given hour. Live names win over the rolled snapshot;
|
||||
// node_id is NULL once the node row is gone. iata is a representative one.
|
||||
GetTopNodes(ctx context.Context, arg GetTopNodesParams) ([]GetTopNodesRow, error)
|
||||
// Reduce route history to unique adjacent pairs before transferring it to a browser.
|
||||
GetTopologyLinks(ctx context.Context, arg GetTopologyLinksParams) ([]GetTopologyLinksRow, error)
|
||||
GetTransportScopeByName(ctx context.Context, name string) (int32, error)
|
||||
GetTransportScopes(ctx context.Context) ([]GetTransportScopesRow, error)
|
||||
// ============================================================
|
||||
@@ -335,6 +337,9 @@ type Querier interface {
|
||||
ResolveRelayHashPairsP3(ctx context.Context, arg ResolveRelayHashPairsP3Params) ([]ResolveRelayHashPairsP3Row, error)
|
||||
// Batch form of ResolvePathHashesP4 across IATAs; callers check uniqueness per IATA.
|
||||
ResolveRelayHashPairsP4(ctx context.Context, arg ResolveRelayHashPairsP4Params) ([]ResolveRelayHashPairsP4Row, error)
|
||||
// TRACE can terminate at companions. Preserve the full prefix (including eight
|
||||
// bytes), and return every candidate across heard regions instead of picking one.
|
||||
ResolveTraceHashes(ctx context.Context, arg ResolveTraceHashesParams) ([]ResolveTraceHashesRow, error)
|
||||
RollAdvertHearings(ctx context.Context, hour pgtype.Timestamptz) error
|
||||
// Each distinct ADVERT packet counts once, under the exact set of IATAs that heard it this hour.
|
||||
RollAdvertSets(ctx context.Context, hour pgtype.Timestamptz) error
|
||||
|
||||
+146
-16
@@ -1422,6 +1422,8 @@ func (q *Queries) GetPacketByHash(ctx context.Context, packetHash []byte) (GetPa
|
||||
|
||||
const getPacketsByTraceTag = `-- name: GetPacketsByTraceTag :many
|
||||
SELECT encode(p.packet_hash, 'hex') AS packet_hash_hex,
|
||||
p.raw_payload, p.payload_version,
|
||||
EXISTS(SELECT 1 FROM packet_observations po WHERE po.packet_hash=p.packet_hash AND po.path_length_byte>=64) AS invalid_snr_path,
|
||||
p.route_type,
|
||||
p.first_heard_at,
|
||||
p.last_heard_at,
|
||||
@@ -1442,14 +1444,17 @@ ORDER BY p.first_heard_at ASC
|
||||
`
|
||||
|
||||
type GetPacketsByTraceTagRow struct {
|
||||
PacketHashHex string `json:"packet_hash_hex"`
|
||||
RouteType int16 `json:"route_type"`
|
||||
FirstHeardAt pgtype.Timestamptz `json:"first_heard_at"`
|
||||
LastHeardAt pgtype.Timestamptz `json:"last_heard_at"`
|
||||
ParsedPayload []byte `json:"parsed_payload"`
|
||||
ScopeID *int32 `json:"scope_id"`
|
||||
ScopeName *string `json:"scope_name"`
|
||||
Iatas []string `json:"iatas"`
|
||||
PacketHashHex string `json:"packet_hash_hex"`
|
||||
RawPayload []byte `json:"raw_payload"`
|
||||
PayloadVersion int16 `json:"payload_version"`
|
||||
InvalidSnrPath bool `json:"invalid_snr_path"`
|
||||
RouteType int16 `json:"route_type"`
|
||||
FirstHeardAt pgtype.Timestamptz `json:"first_heard_at"`
|
||||
LastHeardAt pgtype.Timestamptz `json:"last_heard_at"`
|
||||
ParsedPayload []byte `json:"parsed_payload"`
|
||||
ScopeID *int32 `json:"scope_id"`
|
||||
ScopeName *string `json:"scope_name"`
|
||||
Iatas []string `json:"iatas"`
|
||||
}
|
||||
|
||||
// Return distinct observation IATAs in first-heard order for path resolution,
|
||||
@@ -1465,6 +1470,9 @@ func (q *Queries) GetPacketsByTraceTag(ctx context.Context, decode string) ([]Ge
|
||||
var i GetPacketsByTraceTagRow
|
||||
if err := rows.Scan(
|
||||
&i.PacketHashHex,
|
||||
&i.RawPayload,
|
||||
&i.PayloadVersion,
|
||||
&i.InvalidSnrPath,
|
||||
&i.RouteType,
|
||||
&i.FirstHeardAt,
|
||||
&i.LastHeardAt,
|
||||
@@ -2254,6 +2262,59 @@ func (q *Queries) GetTopNodes(ctx context.Context, arg GetTopNodesParams) ([]Get
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getTopologyLinks = `-- name: GetTopologyLinks :many
|
||||
WITH pairs AS (
|
||||
SELECT h.node_id AS a, r.node_ids[(h.ord+1)::int] AS b
|
||||
FROM known_routes r CROSS JOIN LATERAL unnest(r.node_ids) WITH ORDINALITY h(node_id,ord)
|
||||
WHERE r.last_seen >= $2::timestamptz AND r.last_seen < $3::timestamptz
|
||||
AND (COALESCE(cardinality($4::bpchar[]),0)=0 OR r.iata=ANY($4::bpchar[]))
|
||||
AND h.ord < cardinality(r.node_ids)
|
||||
)
|
||||
SELECT LEAST(a,b)::uuid AS from_id, GREATEST(a,b)::uuid AS to_id
|
||||
FROM pairs WHERE a IS NOT NULL AND b IS NOT NULL AND a<>b
|
||||
GROUP BY LEAST(a,b), GREATEST(a,b)
|
||||
ORDER BY LEAST(a,b), GREATEST(a,b)
|
||||
LIMIT $1::int
|
||||
`
|
||||
|
||||
type GetTopologyLinksParams struct {
|
||||
LinkLimit int32 `json:"link_limit"`
|
||||
Since pgtype.Timestamptz `json:"since"`
|
||||
Until pgtype.Timestamptz `json:"until"`
|
||||
Iatas []string `json:"iatas"`
|
||||
}
|
||||
|
||||
type GetTopologyLinksRow struct {
|
||||
FromID uuid.UUID `json:"from_id"`
|
||||
ToID uuid.UUID `json:"to_id"`
|
||||
}
|
||||
|
||||
// Reduce route history to unique adjacent pairs before transferring it to a browser.
|
||||
func (q *Queries) GetTopologyLinks(ctx context.Context, arg GetTopologyLinksParams) ([]GetTopologyLinksRow, error) {
|
||||
rows, err := q.db.Query(ctx, getTopologyLinks,
|
||||
arg.LinkLimit,
|
||||
arg.Since,
|
||||
arg.Until,
|
||||
arg.Iatas,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []GetTopologyLinksRow{}
|
||||
for rows.Next() {
|
||||
var i GetTopologyLinksRow
|
||||
if err := rows.Scan(&i.FromID, &i.ToID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getTransportScopeByName = `-- name: GetTransportScopeByName :one
|
||||
SELECT id FROM transport_scopes WHERE name = $1
|
||||
`
|
||||
@@ -4194,7 +4255,11 @@ SELECT
|
||||
WHERE ti.trace_tag = t.trace_tag
|
||||
AND (COALESCE(cardinality($1::bpchar[]), 0) = 0 OR ti.iata = ANY($1::bpchar[]))) AS iata_count,
|
||||
COALESCE(t.trace_type, '')::text AS trace_type,
|
||||
t.best_payload
|
||||
t.best_payload,
|
||||
ARRAY(SELECT ti.iata FROM trace_iatas ti WHERE ti.trace_tag=t.trace_tag)::bpchar[] AS iatas,
|
||||
EXISTS(SELECT 1 FROM packets p WHERE p.trace_tag=t.trace_tag AND p.payload_version<>0) AS unsupported_version,
|
||||
EXISTS(SELECT 1 FROM packets p WHERE p.trace_tag=t.trace_tag AND p.route_type NOT IN (2,3)) AS non_direct,
|
||||
EXISTS(SELECT 1 FROM packets p JOIN packet_observations po ON po.packet_hash=p.packet_hash WHERE p.trace_tag=t.trace_tag AND po.path_length_byte>=64) AS invalid_snr_path
|
||||
FROM trace_tags t
|
||||
WHERE (COALESCE(cardinality($1::bpchar[]), 0) = 0 OR EXISTS (
|
||||
SELECT 1 FROM trace_iatas ti WHERE ti.trace_tag = t.trace_tag AND ti.iata = ANY($1::bpchar[])))
|
||||
@@ -4222,13 +4287,17 @@ type ListTraceTagsParams struct {
|
||||
}
|
||||
|
||||
type ListTraceTagsRow struct {
|
||||
TraceTag string `json:"trace_tag"`
|
||||
FirstHeardAt pgtype.Timestamptz `json:"first_heard_at"`
|
||||
LastHeardAt pgtype.Timestamptz `json:"last_heard_at"`
|
||||
PacketCount int64 `json:"packet_count"`
|
||||
IataCount int64 `json:"iata_count"`
|
||||
TraceType string `json:"trace_type"`
|
||||
BestPayload []byte `json:"best_payload"`
|
||||
TraceTag string `json:"trace_tag"`
|
||||
FirstHeardAt pgtype.Timestamptz `json:"first_heard_at"`
|
||||
LastHeardAt pgtype.Timestamptz `json:"last_heard_at"`
|
||||
PacketCount int64 `json:"packet_count"`
|
||||
IataCount int64 `json:"iata_count"`
|
||||
TraceType string `json:"trace_type"`
|
||||
BestPayload []byte `json:"best_payload"`
|
||||
Iatas []string `json:"iatas"`
|
||||
UnsupportedVersion bool `json:"unsupported_version"`
|
||||
NonDirect bool `json:"non_direct"`
|
||||
InvalidSnrPath bool `json:"invalid_snr_path"`
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
@@ -4261,6 +4330,10 @@ func (q *Queries) ListTraceTags(ctx context.Context, arg ListTraceTagsParams) ([
|
||||
&i.IataCount,
|
||||
&i.TraceType,
|
||||
&i.BestPayload,
|
||||
&i.Iatas,
|
||||
&i.UnsupportedVersion,
|
||||
&i.NonDirect,
|
||||
&i.InvalidSnrPath,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -5103,6 +5176,63 @@ func (q *Queries) ResolveRelayHashPairsP4(ctx context.Context, arg ResolveRelayH
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const resolveTraceHashes = `-- name: ResolveTraceHashes :many
|
||||
SELECT h.hash::bytea AS hash, si.iata, n.id AS node_id, n.name, n.latitude, n.longitude, n.public_key
|
||||
FROM unnest($1::bytea[]) AS h(hash)
|
||||
JOIN node_short_ids si ON si.iata = ANY($2::bpchar[]) AND
|
||||
((octet_length(h.hash)=1 AND si.prefix_1=h.hash) OR
|
||||
(octet_length(h.hash)=2 AND si.prefix_2=h.hash) OR
|
||||
(octet_length(h.hash) IN (4,8) AND si.prefix_4=substring(h.hash FROM 1 FOR 4)))
|
||||
JOIN nodes n ON n.id=si.node_id
|
||||
WHERE substring(n.public_key FROM 1 FOR octet_length(h.hash))=h.hash
|
||||
ORDER BY si.iata, h.hash, n.id
|
||||
`
|
||||
|
||||
type ResolveTraceHashesParams struct {
|
||||
Hashes [][]byte `json:"hashes"`
|
||||
Iatas []string `json:"iatas"`
|
||||
}
|
||||
|
||||
type ResolveTraceHashesRow struct {
|
||||
Hash []byte `json:"hash"`
|
||||
Iata string `json:"iata"`
|
||||
NodeID uuid.UUID `json:"node_id"`
|
||||
Name *string `json:"name"`
|
||||
Latitude *float64 `json:"latitude"`
|
||||
Longitude *float64 `json:"longitude"`
|
||||
PublicKey []byte `json:"public_key"`
|
||||
}
|
||||
|
||||
// TRACE can terminate at companions. Preserve the full prefix (including eight
|
||||
// bytes), and return every candidate across heard regions instead of picking one.
|
||||
func (q *Queries) ResolveTraceHashes(ctx context.Context, arg ResolveTraceHashesParams) ([]ResolveTraceHashesRow, error) {
|
||||
rows, err := q.db.Query(ctx, resolveTraceHashes, arg.Hashes, arg.Iatas)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []ResolveTraceHashesRow{}
|
||||
for rows.Next() {
|
||||
var i ResolveTraceHashesRow
|
||||
if err := rows.Scan(
|
||||
&i.Hash,
|
||||
&i.Iata,
|
||||
&i.NodeID,
|
||||
&i.Name,
|
||||
&i.Latitude,
|
||||
&i.Longitude,
|
||||
&i.PublicKey,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const saveChannelCatalogue = `-- name: SaveChannelCatalogue :exec
|
||||
INSERT INTO meshmapper_channel_catalogues (iata, url, payload, etag, checked_at, attempted_at, next_attempt, last_error)
|
||||
VALUES ($1, $2, $3::jsonb, $4::text,
|
||||
|
||||
Reference in new issue
Block a user