Skip to content

Commit da15108

Browse files
committed
fix(api): restore safe validator listing
1 parent 886d078 commit da15108

File tree

5 files changed

+28
-27
lines changed

5 files changed

+28
-27
lines changed

server/utils/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export interface SnapshotEpochValidators {
2525
type Nullable<T> = {
2626
[K in keyof T]: T[K] | null
2727
}
28-
export type FetchedValidator = Omit<Validator, 'logo' | 'contact' | 'isListed'> & Pick<Activity, 'balance' | 'stakers'> & {
28+
export type FetchedValidator = Omit<Validator, 'logo' | 'contact'> & Pick<Activity, 'balance' | 'stakers'> & {
2929
logo?: string
3030
score: Nullable<Pick<Score, 'total' | 'availability' | 'reliability' | 'dominance' | 'epochNumber'>>
3131
dominanceRatio: number | null

server/utils/validator-listing.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
const UNKNOWN_VALIDATOR_NAME = 'Unknown validator'
2+
13
interface ValidatorListState {
24
isListed: boolean | null
5+
name: string
36
}
47

58
interface ValidatorAddress {
@@ -10,8 +13,10 @@ interface StoredValidatorAddressState extends ValidatorAddress {
1013
isListed: boolean | null
1114
}
1215

13-
export function isKnownValidatorProfile({ isListed }: ValidatorListState) {
14-
return isListed === true
16+
export function isKnownValidatorProfile({ isListed, name }: ValidatorListState) {
17+
if (typeof isListed === 'boolean')
18+
return isListed
19+
return name.toLowerCase() !== UNKNOWN_VALIDATOR_NAME.toLowerCase()
1520
}
1621

1722
export function getUnlistedAddresses(storedAddresses: string[], bundledAddresses: Set<string>) {

server/utils/validator-public.ts

Lines changed: 0 additions & 4 deletions
This file was deleted.

server/utils/validators-bundle.ts

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import type { Result } from 'nimiq-validator-trustscore/types'
22
import type { ValidatorJSON } from './schemas'
3+
import { bundledValidatorsByNetwork } from '../generated/validators-bundle.generated'
34
import { validatorSchema } from './schemas'
45
import { getUnlistedAddresses } from './validator-listing'
5-
import { getStoredValidatorsAddress, markValidatorsAsUnlisted, storeValidator } from './validators'
66

77
interface ImportValidatorsBundledOptions {
88
shouldStore?: boolean
@@ -13,27 +13,22 @@ export async function importValidatorsBundled(nimiqNetwork?: string, options: Im
1313
return [false, 'Nimiq network is required', undefined]
1414

1515
const { shouldStore = true } = options
16-
const storage = useStorage('assets:public')
17-
const keys = await storage.getKeys(`validators/${nimiqNetwork}`)
16+
const bundledValidators = bundledValidatorsByNetwork[nimiqNetwork as keyof typeof bundledValidatorsByNetwork]
17+
if (!bundledValidators || bundledValidators.length === 0)
18+
return [false, `No bundled validators found for network: ${nimiqNetwork}`, undefined]
1819

1920
const validators: ValidatorJSON[] = []
20-
for (const key of keys) {
21-
if (!key.endsWith('.json') || key.endsWith('.example.json'))
22-
continue
23-
24-
const data = await storage.getItem(key)
21+
for (const data of bundledValidators) {
2522
const parsed = validatorSchema.safeParse(data)
2623
if (!parsed.success)
27-
return [false, `Invalid validator data at ${key}: ${parsed.error}`, undefined]
24+
return [false, `Invalid bundled validator data: ${parsed.error}`, undefined]
2825
validators.push(parsed.data)
2926
}
3027

3128
if (!shouldStore)
3229
return [true, undefined, validators]
3330

34-
if (validators.length === 0)
35-
return [false, `No bundled validators found for network: ${nimiqNetwork}`, undefined]
36-
31+
const { getStoredValidatorsAddress, markValidatorsAsUnlisted, storeValidator } = await import('./validators')
3732
const bundledAddresses = new Set(validators.map(v => v.address))
3833
const storedAddresses = await getStoredValidatorsAddress()
3934
const unlistedAddresses = getUnlistedAddresses(storedAddresses, bundledAddresses)

server/utils/validators.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import { tables, useDrizzle } from './drizzle'
1111
import { handleValidatorLogo } from './logo'
1212
import { defaultValidatorJSON } from './schemas'
1313
import { getUnlistedActiveValidatorAddresses, isKnownValidatorProfile } from './validator-listing'
14-
import { stripInternalValidatorFields } from './validator-public'
1514

1615
export const getStoredValidatorsId = () => useDrizzle().select({ id: tables.validators.id }).from(tables.validators).execute().then(r => r.map(v => v.id))
1716
export const getStoredValidatorsAddress = () => useDrizzle().select({ address: tables.validators.address }).from(tables.validators).execute().then(r => r.map(v => v.address))
@@ -39,6 +38,13 @@ async function selectValidatorsWithListState(filters: SQLWrapper[] = []) {
3938
: await query.execute()
4039
}
4140

41+
export function filterVisibleValidators<T extends { isListed: boolean | null, name: string }>(validators: T[], onlyKnown: boolean): T[] {
42+
if (!onlyKnown)
43+
return validators
44+
45+
return validators.filter(isKnownValidatorProfile)
46+
}
47+
4248
export async function getStoredValidatorsListState() {
4349
return useDrizzle()
4450
.select({ address: tables.validators.address, isListed: tables.validators.isListed })
@@ -137,7 +143,7 @@ export async function markValidatorsAsUnlisted(addresses: string[]) {
137143
export type FetchValidatorsOptions = MainQuerySchema & { epochNumber: number }
138144

139145
export async function fetchValidators(_event: H3Event, params: FetchValidatorsOptions): Result<FetchedValidator[]> {
140-
const { 'payout-type': payoutType, 'only-known': onlyKnown = false, 'with-identicons': withIdenticons, epochNumber } = params
146+
const { 'payout-type': payoutType, 'only-known': onlyKnown = true, 'with-identicons': withIdenticons, epochNumber } = params
141147

142148
// Add safety check for epochNumber
143149
if (epochNumber === null || epochNumber === undefined || !Number.isInteger(epochNumber)) {
@@ -152,7 +158,7 @@ export async function fetchValidators(_event: H3Event, params: FetchValidatorsOp
152158
try {
153159
const dbValidators = await selectValidatorsWithListState(filters)
154160

155-
const visibleValidators = onlyKnown ? dbValidators.filter(isKnownValidatorProfile) : dbValidators
161+
const visibleValidators = filterVisibleValidators(dbValidators, onlyKnown)
156162
const validatorIds = visibleValidators.map(v => v.id)
157163
if (validatorIds.length === 0)
158164
return [true, undefined, []]
@@ -219,8 +225,7 @@ export async function fetchValidators(_event: H3Event, params: FetchValidatorsOp
219225
const activityByValidatorId = new Map(activityRows.map(row => [row.validatorId, row]))
220226

221227
const validators = visibleValidators.map((validator) => {
222-
const { logo, hasDefaultLogo } = validator
223-
const rest = stripInternalValidatorFields(validator)
228+
const { logo, hasDefaultLogo, contact: _contact, isListed, ...rest } = validator
224229
const scoreRow = scoresByValidatorId.get(validator.id)
225230
const activityRow = activityByValidatorId.get(validator.id)
226231

@@ -243,6 +248,7 @@ export async function fetchValidators(_event: H3Event, params: FetchValidatorsOp
243248

244249
return {
245250
...rest,
251+
isListed,
246252
score,
247253
hasDefaultLogo,
248254
logo: withIdenticons === false && hasDefaultLogo ? undefined : logo,
@@ -278,7 +284,7 @@ export const cachedFetchValidators = defineCachedFunction((_event: H3Event, para
278284
})
279285

280286
export interface FetchValidatorOptions { address: string, range: Range }
281-
export type FetchedValidatorDetails = Omit<Validator, 'isListed'> & { activity: Activity[], scores: Score[], score?: Score }
287+
export type FetchedValidatorDetails = Validator & { activity: Activity[], scores: Score[], score?: Score }
282288

283289
export async function fetchValidator(_event: H3Event, params: FetchValidatorOptions): Result<FetchedValidatorDetails> {
284290
const { address, range: { fromEpoch, toEpoch } } = params
@@ -321,8 +327,7 @@ export async function fetchValidator(_event: H3Event, params: FetchValidatorOpti
321327
))
322328
.execute()
323329

324-
const publicValidator = stripInternalValidatorFields(validator)
325-
return [true, undefined, { ...publicValidator, scores, activity, score }]
330+
return [true, undefined, { ...validator, scores, activity, score }]
326331
}
327332
catch (error) {
328333
consola.error(`Error fetching validator ${address}: ${error}`)

0 commit comments

Comments
 (0)