From e6bf3e9d6811413f6f87d5eb845d92c9cbbd955b Mon Sep 17 00:00:00 2001 From: onmax Date: Fri, 22 Nov 2024 14:06:31 -0600 Subject: [PATCH 1/5] fix: update reliability return values to -1 for NaN and negative discriminant cases --- packages/nimiq-validators-trustscore/src/score.ts | 4 ++-- server/utils/validators.ts | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/nimiq-validators-trustscore/src/score.ts b/packages/nimiq-validators-trustscore/src/score.ts index 3d02e3f..8bebd87 100644 --- a/packages/nimiq-validators-trustscore/src/score.ts +++ b/packages/nimiq-validators-trustscore/src/score.ts @@ -56,12 +56,12 @@ export function getReliability({ inherentsPerEpoch, weightFactor = 0.5, curveCen // Could be the case that the division is NaN, so we return 0 in that case. That means there's no inherents, so no blocks, so not reliable because there's no data if (Number.isNaN(reliability)) - return 0 + return -1 // Ensure the expression under the square root is non-negative const discriminant = -(reliability ** 2) + 2 * curveCenter * reliability + (curveCenter - 1) ** 2 if (discriminant < 0) - return 0 + return -1 // Plot into the curve return Math.max(-curveCenter + 1 - Math.sqrt(discriminant), 1) diff --git a/server/utils/validators.ts b/server/utils/validators.ts index bde7f79..3f28c84 100644 --- a/server/utils/validators.ts +++ b/server/utils/validators.ts @@ -193,6 +193,8 @@ export async function fetchValidators(params: FetchValidatorsOptions): Result { if (!v.score) return + if (v.score.reliability === -1) + v.score.reliability = null if ( (v.score.dominance === null || v.score.dominance < 0) || (v.score.reliability === null || v.score.reliability < 0) From f3fd186ce91a16e9a71aa4860945ddb7db3f5c48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren?= Date: Fri, 22 Nov 2024 14:09:59 -0600 Subject: [PATCH 2/5] Convert validators endpoint to Drizzle query API --- server/database/schema.ts | 21 ++++++++++- server/utils/validators.ts | 76 +++++++++++++++++--------------------- 2 files changed, 54 insertions(+), 43 deletions(-) diff --git a/server/database/schema.ts b/server/database/schema.ts index 601a2f8..e59133d 100644 --- a/server/database/schema.ts +++ b/server/database/schema.ts @@ -1,4 +1,4 @@ -import { sql } from 'drizzle-orm' +import { relations, sql } from 'drizzle-orm' import { check, index, integer, primaryKey, real, sqliteTable, text, uniqueIndex } from 'drizzle-orm/sqlite-core' import { PayoutType } from '../utils/types' @@ -24,6 +24,11 @@ export const validators = sqliteTable('validators', { ), })) +export const validatorRelations = relations(validators, ({ many }) => ({ + scores: many(scores), + activity: many(activity), +})) + // The scores only for the default window dominance export const scores = sqliteTable('scores', { validatorId: integer('validator_id').notNull().references(() => validators.id, { onDelete: 'cascade' }), @@ -38,6 +43,13 @@ export const scores = sqliteTable('scores', { compositePrimaryKey: primaryKey({ columns: [table.validatorId, table.epochNumber] }), })) +export const scoresRelations = relations(scores, ({ one }) => ({ + validator: one(validators, { + fields: [scores.validatorId], + references: [validators.id], + }), +})) + export const activity = sqliteTable('activity', { validatorId: integer('validator_id').notNull().references(() => validators.id, { onDelete: 'cascade' }), epochNumber: integer('epoch_number').notNull(), @@ -51,3 +63,10 @@ export const activity = sqliteTable('activity', { idxElectionBlock: index('idx_election_block').on(table.epochNumber), compositePrimaryKey: primaryKey({ columns: [table.validatorId, table.epochNumber] }), })) + +export const activityRelations = relations(activity, ({ one }) => ({ + validator: one(validators, { + fields: [activity.validatorId], + references: [validators.id], + }), +})) diff --git a/server/utils/validators.ts b/server/utils/validators.ts index bde7f79..e4ad7fb 100644 --- a/server/utils/validators.ts +++ b/server/utils/validators.ts @@ -142,49 +142,41 @@ export async function fetchValidators(params: FetchValidatorsOptions): Result { + const {scores, logo, contact, activity, ...rest} = validator + + return { + ...rest, + score: scores[0] || null, + logo, + dominanceRatioViaBalance: activity[0]?.dominanceRatioViaBalance, + dominanceRatioViaSlots: activity[0]?.dominanceRatioViaSlots, + } as FetchedValidator + }) if (!withIdenticons) validators.filter(v => v.hasDefaultLogo).forEach(v => delete v.logo) From 8f54df377b68200fd099d534ac7cef11c06cc325 Mon Sep 17 00:00:00 2001 From: onmax Date: Fri, 22 Nov 2024 14:27:02 -0600 Subject: [PATCH 3/5] refactor: reorganize validator relations and clean up imports in schema and utils --- server/database/schema.ts | 10 +++++----- server/utils/validators.ts | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/server/database/schema.ts b/server/database/schema.ts index e59133d..34c76c1 100644 --- a/server/database/schema.ts +++ b/server/database/schema.ts @@ -24,11 +24,6 @@ export const validators = sqliteTable('validators', { ), })) -export const validatorRelations = relations(validators, ({ many }) => ({ - scores: many(scores), - activity: many(activity), -})) - // The scores only for the default window dominance export const scores = sqliteTable('scores', { validatorId: integer('validator_id').notNull().references(() => validators.id, { onDelete: 'cascade' }), @@ -70,3 +65,8 @@ export const activityRelations = relations(activity, ({ one }) => ({ references: [validators.id], }), })) + +export const validatorRelations = relations(validators, ({ many }) => ({ + scores: many(scores), + activity: many(activity), +})) diff --git a/server/utils/validators.ts b/server/utils/validators.ts index 94d70b1..13f00d7 100644 --- a/server/utils/validators.ts +++ b/server/utils/validators.ts @@ -5,7 +5,7 @@ import type { Result, ValidatorScore } from './types' import { readdir, readFile } from 'node:fs/promises' import path from 'node:path' import { consola } from 'consola' -import { desc, inArray, isNotNull } from 'drizzle-orm' +import { desc, inArray } from 'drizzle-orm' import { handleValidatorLogo } from './logo' import { defaultValidatorJSON, validatorSchema } from './schemas' @@ -167,15 +167,15 @@ export async function fetchValidators(params: FetchValidatorsOptions): Result { - const {scores, logo, contact, activity, ...rest} = validator - - return { - ...rest, - score: scores[0] || null, - logo, - dominanceRatioViaBalance: activity[0]?.dominanceRatioViaBalance, - dominanceRatioViaSlots: activity[0]?.dominanceRatioViaSlots, - } as FetchedValidator + const { scores, logo, contact, activity, ...rest } = validator + + return { + ...rest, + score: scores[0] || null, + logo, + dominanceRatioViaBalance: activity[0]?.dominanceRatioViaBalance, + dominanceRatioViaSlots: activity[0]?.dominanceRatioViaSlots, + } as FetchedValidator }) if (!withIdenticons) From b1c0a00827012714500523dfa9404c36753a4829 Mon Sep 17 00:00:00 2001 From: onmax Date: Fri, 22 Nov 2024 14:33:01 -0600 Subject: [PATCH 4/5] fix: update validators db handling --- server/utils/validators.ts | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/server/utils/validators.ts b/server/utils/validators.ts index 13f00d7..b31f449 100644 --- a/server/utils/validators.ts +++ b/server/utils/validators.ts @@ -1,5 +1,5 @@ import type { SQLWrapper } from 'drizzle-orm' -import type { Validator } from './drizzle' +import type { Score, Validator } from './drizzle' import type { ValidatorJSON } from './schemas' import type { Result, ValidatorScore } from './types' import { readdir, readFile } from 'node:fs/promises' @@ -147,6 +147,7 @@ export async function fetchValidators(params: FetchValidatorsOptions): Result { const { scores, logo, contact, activity, ...rest } = validator + const { availability, dominance, reliability, total } = scores[0] + const score = { total, availability, dominance, reliability } as Record + if (reliability === -1 || reliability === null) { + score.reliability = null + score.total = null + } + if (availability === -1 || availability === null) { + score.availability = null + score.total = null + } + if (dominance === -1 || dominance === null) { + score.dominance = null + score.total = null + } + return { ...rest, score: scores[0] || null, - logo, + logo: withIdenticons ? logo : undefined, dominanceRatioViaBalance: activity[0]?.dominanceRatioViaBalance, dominanceRatioViaSlots: activity[0]?.dominanceRatioViaSlots, } as FetchedValidator }) - if (!withIdenticons) - validators.filter(v => v.hasDefaultLogo).forEach(v => delete v.logo) - - // Don't return score if any of the values not [0, 1] - validators.forEach((v) => { - if (!v.score) - return - if (v.score.reliability === -1) - v.score.reliability = null - if ( - (v.score.dominance === null || v.score.dominance < 0) - || (v.score.reliability === null || v.score.reliability < 0) - || (v.score.availability === null || v.score.availability < 0) - ) { - v.score.total = null - } - }) - return { data: validators, error: undefined } } catch (error) { From 7cb734c6d2056c6b11b663596bb2e7645a6e0de6 Mon Sep 17 00:00:00 2001 From: onmax Date: Fri, 22 Nov 2024 14:45:44 -0600 Subject: [PATCH 5/5] fix: update fetched validator structure and adjust activity query parameters --- server/utils/validators.ts | 54 +++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/server/utils/validators.ts b/server/utils/validators.ts index b31f449..727712c 100644 --- a/server/utils/validators.ts +++ b/server/utils/validators.ts @@ -126,8 +126,8 @@ export type FetchValidatorsOptions = Zod.infer & { addre type FetchedValidator = Omit & { logo?: string score?: { total: number | null, availability: number | null, reliability: number | null, dominance: number | null } - dominanceRatioViaBalance?: number - dominanceRatioViaSlots?: number + dominanceRatio?: number + balance?: number } export async function fetchValidators(params: FetchValidatorsOptions): Result { @@ -156,42 +156,48 @@ export async function fetchValidators(params: FetchValidatorsOptions): Result { - const { scores, logo, contact, activity, ...rest } = validator - - const { availability, dominance, reliability, total } = scores[0] - const score = { total, availability, dominance, reliability } as Record - if (reliability === -1 || reliability === null) { - score.reliability = null - score.total = null - } - if (availability === -1 || availability === null) { - score.availability = null - score.total = null - } - if (dominance === -1 || dominance === null) { - score.dominance = null - score.total = null + const { scores, logo, contact, activity, hasDefaultLogo, ...rest } = validator + + const score = scores[0] + if (score) { + const { availability, dominance, reliability, total } = scores[0] + const score = { total, availability, dominance, reliability } as Record + if (reliability === -1 || reliability === null) { + score.reliability = null + score.total = null + } + if (availability === -1 || availability === null) { + score.availability = null + score.total = null + } + if (dominance === -1 || dominance === null) { + score.dominance = null + score.total = null + } } + const { dominanceRatioViaBalance, dominanceRatioViaSlots, balance } = activity?.[0] || {} + return { ...rest, - score: scores[0] || null, - logo: withIdenticons ? logo : undefined, - dominanceRatioViaBalance: activity[0]?.dominanceRatioViaBalance, - dominanceRatioViaSlots: activity[0]?.dominanceRatioViaSlots, - } as FetchedValidator + score, + hasDefaultLogo, + logo: !withIdenticons && hasDefaultLogo ? undefined : logo, + dominanceRatio: dominanceRatioViaBalance || dominanceRatioViaSlots, + balance, + } satisfies FetchedValidator }) return { data: validators, error: undefined }