From 4d2b896ddaac618f3a0b54dffbefeeddd6a44020 Mon Sep 17 00:00:00 2001 From: pablomendezroyo Date: Tue, 8 Oct 2024 18:00:18 +0200 Subject: [PATCH] return to frontend raw data --- .../calls/fetchValidatorsPerformanceData.ts | 20 +- .../src/modules/apiClients/postgres/index.ts | 67 +++--- .../src/modules/apiClients/postgres/types.ts | 10 +- ...fetchAndInsertValidatorsPerformanceData.ts | 20 +- .../insertPerformanceData.ts | 15 +- .../getAttestationSuccessRate.ts | 39 ---- .../getAttestationSuccessRatePerClients.ts | 35 --- .../getClientsUsedPerIntervalsMap.ts | 27 --- .../getIntervalsEpochs.ts | 41 ---- .../src/modules/validatorsDataIngest/index.ts | 72 +----- .../src/modules/validatorsDataIngest/types.ts | 40 ++-- .../getAttestationSuccessRate.unit.test.ts | 112 --------- ...estationSuccessRatePerClients.unit.test.ts | 177 -------------- ...getClientsUsedPerIntervalsMap.unit.test.ts | 219 ------------------ .../getIntervalsEpochs.unit.test.ts | 108 --------- .../validatorsDataIngest/index.unit.test.ts | 4 +- 16 files changed, 93 insertions(+), 913 deletions(-) delete mode 100644 packages/brain/src/modules/validatorsDataIngest/getAttestationSuccessRate.ts delete mode 100644 packages/brain/src/modules/validatorsDataIngest/getAttestationSuccessRatePerClients.ts delete mode 100644 packages/brain/src/modules/validatorsDataIngest/getClientsUsedPerIntervalsMap.ts delete mode 100644 packages/brain/src/modules/validatorsDataIngest/getIntervalsEpochs.ts delete mode 100644 packages/brain/test/unit/modules/validatorsDataIngest/getAttestationSuccessRate.unit.test.ts delete mode 100644 packages/brain/test/unit/modules/validatorsDataIngest/getAttestationSuccessRatePerClients.unit.test.ts delete mode 100644 packages/brain/test/unit/modules/validatorsDataIngest/getClientsUsedPerIntervalsMap.unit.test.ts delete mode 100644 packages/brain/test/unit/modules/validatorsDataIngest/getIntervalsEpochs.unit.test.ts diff --git a/packages/brain/src/calls/fetchValidatorsPerformanceData.ts b/packages/brain/src/calls/fetchValidatorsPerformanceData.ts index 67d39d0a..f0d642d5 100644 --- a/packages/brain/src/calls/fetchValidatorsPerformanceData.ts +++ b/packages/brain/src/calls/fetchValidatorsPerformanceData.ts @@ -1,34 +1,22 @@ import { fetchAndProcessValidatorsData } from "../modules/validatorsDataIngest/index.js"; import { minGenesisTime, secondsPerSlot } from "../index.js"; -import type { - ValidatorsDataProcessed, - Granularity, - NumberOfDaysToQuery -} from "../modules/validatorsDataIngest/types.js"; +import type { EpochsValidatorsMap, NumberOfDaysToQuery } from "../modules/validatorsDataIngest/types.js"; import { PostgresClient } from "../modules/apiClients/index.js"; export async function fetchValidatorsPerformanceData({ postgresClient, validatorIndexes, - numberOfDaysToQuery, - granularity + numberOfDaysToQuery }: { postgresClient: PostgresClient; validatorIndexes: string[]; numberOfDaysToQuery?: NumberOfDaysToQuery; - granularity?: Granularity; -}): Promise< - Map< - number, // validatorIndex - ValidatorsDataProcessed // processed data of the validator - > -> { +}): Promise { return await fetchAndProcessValidatorsData({ validatorIndexes, postgresClient, minGenesisTime, secondsPerSlot, - numberOfDaysToQuery, - granularity + numberOfDaysToQuery }); } diff --git a/packages/brain/src/modules/apiClients/postgres/index.ts b/packages/brain/src/modules/apiClients/postgres/index.ts index bd31beac..dae99200 100644 --- a/packages/brain/src/modules/apiClients/postgres/index.ts +++ b/packages/brain/src/modules/apiClients/postgres/index.ts @@ -1,7 +1,8 @@ import postgres from "postgres"; import logger from "../../logger/index.js"; -import { BlockProposalStatus, Columns, ValidatorPerformance, ValidatorPerformancePostgres } from "./types.js"; +import { BlockProposalStatus, Columns, EpochData, ValidatorPerformancePostgres } from "./types.js"; import { ConsensusClient, ExecutionClient } from "@stakingbrain/common"; +import { EpochsValidatorsMap, DataPerEpoch, ValidatorsEpochMap } from "../../validatorsDataIngest/types.js"; export class PostgresClient { private readonly tableName = "validators_performance"; @@ -111,7 +112,7 @@ CREATE TABLE IF NOT EXISTS ${this.tableName} ( * * @param data - The performance data to insert. */ - public async insertPerformanceData(data: ValidatorPerformance): Promise { + public async insertPerformanceData(data: EpochData): Promise { const query = ` INSERT INTO ${this.tableName} (${Columns.validatorIndex}, ${Columns.epoch}, ${Columns.executionClient}, ${Columns.consensusClient}, ${Columns.slot}, ${Columns.liveness}, ${Columns.blockProposalStatus}, ${Columns.syncCommitteeRewards}, ${Columns.attestationsTotalRewards}, ${Columns.attestationsIdealRewards}, ${Columns.error}) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) @@ -151,7 +152,7 @@ DO UPDATE SET * @param validatorIndexes - The indexes of the validators to get the data for. * @returns The performance data for the given validators. */ - public async getValidatorsDataFromAllEpochs(validatorIndexes: string[]): Promise { + public async getValidatorsDataFromAllEpochs(validatorIndexes: string[]): Promise { const query = ` SELECT * FROM ${this.tableName} WHERE ${Columns.validatorIndex} = ANY($1) @@ -174,15 +175,20 @@ WHERE ${Columns.validatorIndex} = ANY($1) } /** - * Get the validators data for the given validator indexes and an epoch start and end range. In order to improve data process - * it will return a map with the validator index as key and the performance data as value. + * Get the validators data for the given validator indexes and an epoch start and end range. + * This function will return a nested map where the outer map is indexed by epoch, and + * each entry contains another map indexed by validator index. The inner map contains the performance data + * for each validator at that epoch. + * + * The performance data returned will be organized into attestation, block, and sync committee + * sections to provide a more structured view of the data per epoch. * * @param validatorIndexes - The indexes of the validators to get the data for. * @param startEpoch - The start epoch number. * @param endEpoch - The end epoch number. - * @returns The performance data for the given validators. + * @returns A nested map with epoch as the key, validator index as the secondary key, and the performance data as value. */ - public async getValidatorsDataMapForEpochRange({ + public async getEpochsDataMapForEpochRange({ validatorIndexes, startEpoch, endEpoch @@ -190,7 +196,7 @@ WHERE ${Columns.validatorIndex} = ANY($1) validatorIndexes: string[]; startEpoch: number; endEpoch: number; - }): Promise> { + }): Promise { const query = ` SELECT * FROM ${this.tableName} WHERE ${Columns.validatorIndex} = ANY($1) @@ -204,28 +210,37 @@ AND ${Columns.epoch} <= $3 endEpoch ])) as ValidatorPerformancePostgres[]; - return result.reduce((map: Map, row) => { - const key = row.validator_index; - - const performanceData = { - validatorIndex: row.validator_index, - epoch: row.epoch, - executionClient: row.execution_client, - consensusClient: row.consensus_client, - slot: row.slot, - liveness: row.liveness, - blockProposalStatus: row.block_proposal_status, - syncCommitteeRewards: row.sync_comittee_rewards, - attestationsTotalRewards: JSON.parse(row.attestations_total_rewards), - attestationsIdealRewards: JSON.parse(row.attestations_ideal_rewards), - error: JSON.parse(row.error) + return result.reduce((map: EpochsValidatorsMap, row) => { + const epoch = row.epoch; + const validatorIndex = row.validator_index; + + // Define the performance data in the new format. + const epochData: DataPerEpoch = { + attestation: { + totalRewards: JSON.parse(row.attestations_total_rewards), + idealRewards: JSON.parse(row.attestations_ideal_rewards) + }, + block: { + status: row.block_proposal_status, // Assuming row.block_proposal_status will provide either 'proposed' or 'missed' + slot: row.slot, + graffiti: undefined, // Assuming there's no graffiti info in the existing data + reward: undefined // Assuming there's no reward info in the existing data + }, + syncCommittee: { + reward: row.sync_comittee_rewards + }, + tag: "solo" // TODO fix this }; - if (map.has(key)) map.get(key)?.push(performanceData); - else map.set(key, [performanceData]); + // If the outer map doesn't have the epoch, add it. + if (!map.has(epoch)) map.set(epoch, new Map()); + + const validatorsEpochMap = map.get(epoch); + // Add or update the validator data for this epoch. + if (validatorsEpochMap) validatorsEpochMap.set(validatorIndex, epochData); return map; - }, new Map()); + }, new Map()); } /** diff --git a/packages/brain/src/modules/apiClients/postgres/types.ts b/packages/brain/src/modules/apiClients/postgres/types.ts index f8e619b2..6eba2df2 100644 --- a/packages/brain/src/modules/apiClients/postgres/types.ts +++ b/packages/brain/src/modules/apiClients/postgres/types.ts @@ -38,7 +38,7 @@ export enum BlockProposalStatus { } // Interface data return from Postgres client -export interface ValidatorPerformance { +export interface EpochData { validatorIndex: number; epoch: number; executionClient: ExecutionClient; @@ -49,10 +49,10 @@ export interface ValidatorPerformance { slot?: number; liveness?: boolean; syncCommitteeRewards?: number; - error?: ValidatorPerformanceError; + error?: EpochError; } -export enum ValidatorPerformanceErrorCode { +export enum EpochErrorCode { BEACONCHAIN_API_ERROR = "BEACONCHAIN_API_ERROR", EXECUTION_OFFLINE = "EXECUTION_OFFLINE", CONSENSUS_SYNCING = "CONSENSUS_SYNCING", @@ -62,7 +62,7 @@ export enum ValidatorPerformanceErrorCode { UNKNOWN_ERROR = "UNKNOWN_ERROR" } -export interface ValidatorPerformanceError { - code: ValidatorPerformanceErrorCode; +export interface EpochError { + code: EpochErrorCode; message: string; } diff --git a/packages/brain/src/modules/cron/trackValidatorsPerformance/fetchAndInsertValidatorsPerformanceData.ts b/packages/brain/src/modules/cron/trackValidatorsPerformance/fetchAndInsertValidatorsPerformanceData.ts index 59767466..8a2e64a6 100644 --- a/packages/brain/src/modules/cron/trackValidatorsPerformance/fetchAndInsertValidatorsPerformanceData.ts +++ b/packages/brain/src/modules/cron/trackValidatorsPerformance/fetchAndInsertValidatorsPerformanceData.ts @@ -9,11 +9,7 @@ import { getActiveValidatorsLoadedInBrain } from "./getActiveValidatorsLoadedInB import { logPrefix } from "./logPrefix.js"; import { ConsensusClient, ExecutionClient } from "@stakingbrain/common"; import { IdealRewards, TotalRewards } from "../../apiClients/types.js"; -import { - BlockProposalStatus, - ValidatorPerformanceError, - ValidatorPerformanceErrorCode -} from "../../apiClients/postgres/types.js"; +import { BlockProposalStatus, EpochError, EpochErrorCode } from "../../apiClients/postgres/types.js"; import { BeaconchainApiError } from "../../apiClients/beaconchain/error.js"; import { BrainDbError } from "../../db/error.js"; import { ExecutionOfflineError, NodeSyncingError } from "./error.js"; @@ -43,7 +39,7 @@ export async function fetchAndInsertValidatorsPerformanceData({ }): Promise { if (currentEpoch === lastProcessedEpoch) return; - let validatorPerformanceError: ValidatorPerformanceError | undefined; + let validatorPerformanceError: EpochError | undefined; let activeValidatorsIndexes: string[] = []; let validatorBlockStatusMap: Map | undefined; let validatorAttestationsRewards: { totalRewards: TotalRewards[]; idealRewards: IdealRewards } | undefined; @@ -110,29 +106,29 @@ async function ensureNodeStatus({ beaconchainApi }: { beaconchainApi: Beaconchai if (el_offline) throw new ExecutionOfflineError("Execution layer is offline"); } -function getValidatorPerformanceError(e: Error): ValidatorPerformanceError { +function getValidatorPerformanceError(e: Error): EpochError { if (e instanceof BeaconchainApiError) return { - code: ValidatorPerformanceErrorCode.BEACONCHAIN_API_ERROR, + code: EpochErrorCode.BEACONCHAIN_API_ERROR, message: e.message }; if (e instanceof BrainDbError) return { - code: ValidatorPerformanceErrorCode.BRAINDDB_ERROR, + code: EpochErrorCode.BRAINDDB_ERROR, message: e.message }; if (e instanceof ExecutionOfflineError) return { - code: ValidatorPerformanceErrorCode.EXECUTION_OFFLINE, + code: EpochErrorCode.EXECUTION_OFFLINE, message: e.message }; if (e instanceof NodeSyncingError) return { - code: ValidatorPerformanceErrorCode.CONSENSUS_SYNCING, + code: EpochErrorCode.CONSENSUS_SYNCING, message: e.message }; return { - code: ValidatorPerformanceErrorCode.UNKNOWN_ERROR, + code: EpochErrorCode.UNKNOWN_ERROR, message: e.message }; } diff --git a/packages/brain/src/modules/cron/trackValidatorsPerformance/insertPerformanceData.ts b/packages/brain/src/modules/cron/trackValidatorsPerformance/insertPerformanceData.ts index 185ac9bf..05d2134f 100644 --- a/packages/brain/src/modules/cron/trackValidatorsPerformance/insertPerformanceData.ts +++ b/packages/brain/src/modules/cron/trackValidatorsPerformance/insertPerformanceData.ts @@ -1,11 +1,6 @@ import { ConsensusClient, ExecutionClient } from "@stakingbrain/common"; import { PostgresClient } from "../../apiClients/index.js"; -import { - BlockProposalStatus, - ValidatorPerformance, - ValidatorPerformanceError, - ValidatorPerformanceErrorCode -} from "../../apiClients/postgres/types.js"; +import { BlockProposalStatus, EpochData, EpochError, EpochErrorCode } from "../../apiClients/postgres/types.js"; import { IdealRewards, TotalRewards } from "../../apiClients/types.js"; import logger from "../../logger/index.js"; import { logPrefix } from "./logPrefix.js"; @@ -32,7 +27,7 @@ export async function insertPerformanceData({ activeValidatorsIndexes: string[]; validatorBlockStatusMap?: Map; validatorAttestationsRewards?: { totalRewards: TotalRewards[]; idealRewards: IdealRewards }; - error?: ValidatorPerformanceError; + error?: EpochError; }): Promise { for (const validatorIndex of activeValidatorsIndexes) { if (error) { @@ -61,7 +56,7 @@ export async function insertPerformanceData({ executionClient, consensusClient, error: { - code: ValidatorPerformanceErrorCode.MISSING_ATT_DATA, + code: EpochErrorCode.MISSING_ATT_DATA, message: `Missing attestation data for validator ${validatorIndex}` } } @@ -79,7 +74,7 @@ export async function insertPerformanceData({ executionClient, consensusClient, error: { - code: ValidatorPerformanceErrorCode.MISSING_BLOCK_DATA, + code: EpochErrorCode.MISSING_BLOCK_DATA, message: `Missing block proposal data for validator ${validatorIndex}` } } @@ -110,7 +105,7 @@ async function insertDataNotThrow({ validatorPerformance }: { postgresClient: PostgresClient; - validatorPerformance: ValidatorPerformance; + validatorPerformance: EpochData; }): Promise { try { logger.debug(`${logPrefix}Inserting data for validator ${validatorPerformance.validatorIndex}`); diff --git a/packages/brain/src/modules/validatorsDataIngest/getAttestationSuccessRate.ts b/packages/brain/src/modules/validatorsDataIngest/getAttestationSuccessRate.ts deleted file mode 100644 index 3210be4f..00000000 --- a/packages/brain/src/modules/validatorsDataIngest/getAttestationSuccessRate.ts +++ /dev/null @@ -1,39 +0,0 @@ -import type { ValidatorPerformance } from "../apiClients/postgres/types.js"; -import logger from "../logger/index.js"; - -/** - * Calculates the attestation success rate for a given validator. The attestation success rate is the percentage of successful attestations - * Being the total attestation opportunities the number of epochs between the first and last epoch in the data set of a specific validator. - * And the total successful attestations the number of epochs where the validator successfully attested: source must be >= 0. - * - * The epoch must be greater or equal to the startEpoch and less than the endEpoch. - * - * @param validatorData the data of the validator from the postgres database - * @param startEpoch the start epoch of the data set - * @param endEpoch the end epoch of the data set - */ -export function getAttestationSuccessRate({ - validatorData, - startEpoch, - endEpoch -}: { - validatorData: ValidatorPerformance[]; - startEpoch: number; - endEpoch: number; -}): number { - // Calculate the total attestation opportunities - const totalAttestationOpportunities = endEpoch - startEpoch; - if (totalAttestationOpportunities <= 0) { - logger.warn("totalAttestationOpportunities is less than or equal to 0"); - return 0; - } - - // Calculate the total successful attestations - const totalSuccessfulAttestations = validatorData.filter((data) => { - const attestationsTotalRewards = data.attestationsTotalRewards; - if (!attestationsTotalRewards) return false; - return data.epoch >= startEpoch && data.epoch < endEpoch && parseInt(attestationsTotalRewards.source) >= 0; - }).length; - - return Math.round((totalSuccessfulAttestations / totalAttestationOpportunities) * 100); -} diff --git a/packages/brain/src/modules/validatorsDataIngest/getAttestationSuccessRatePerClients.ts b/packages/brain/src/modules/validatorsDataIngest/getAttestationSuccessRatePerClients.ts deleted file mode 100644 index f7fd9fe6..00000000 --- a/packages/brain/src/modules/validatorsDataIngest/getAttestationSuccessRatePerClients.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { getAttestationSuccessRate } from "./getAttestationSuccessRate.js"; -import type { ExecutionConsensusConcatenated } from "./types.js"; -import type { ValidatorPerformance } from "../apiClients/postgres/types.js"; - -/** - * Calculates the attestation success rate for a given validator per Execution and Consensus client. - * - * @param validatorData the data of the validator from the postgres database - * @param startEpoch the start epoch of the data set - * @param endEpoch the end epoch of the data set - */ -export function getAttestationSuccessRatePerClients({ - validatorData, - startEpoch, - endEpoch -}: { - validatorData: ValidatorPerformance[]; - startEpoch: number; - endEpoch: number; -}): Map { - const attestationSuccessRatePerClients = new Map(); - - const dataByClient = new Map(); - for (const data of validatorData) { - const key: ExecutionConsensusConcatenated = `${data.executionClient}-${data.consensusClient}`; - if (!dataByClient.has(key)) dataByClient.set(key, []); - dataByClient.get(key)?.push(data); - } - - // calculate the attestation success rate for each client combination - for (const [key, data] of dataByClient.entries()) - attestationSuccessRatePerClients.set(key, getAttestationSuccessRate({ validatorData: data, startEpoch, endEpoch })); - - return attestationSuccessRatePerClients; -} diff --git a/packages/brain/src/modules/validatorsDataIngest/getClientsUsedPerIntervalsMap.ts b/packages/brain/src/modules/validatorsDataIngest/getClientsUsedPerIntervalsMap.ts deleted file mode 100644 index 6ca32e23..00000000 --- a/packages/brain/src/modules/validatorsDataIngest/getClientsUsedPerIntervalsMap.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { ValidatorPerformance } from "../apiClients/postgres/types.js"; -import type { ExecutionConsensusConcatenated } from "./types.js"; - -export function getClientsUsedPerIntervalsMap({ - validatorData, - startEpoch, - endEpoch -}: { - validatorData: ValidatorPerformance[]; - startEpoch: number; - endEpoch: number; -}): Map { - const clientsUsedInInterval = new Map(); - - const dataByClient = new Map(); - for (const data of validatorData) { - const key: ExecutionConsensusConcatenated = `${data.executionClient}-${data.consensusClient}`; - if (!dataByClient.has(key)) dataByClient.set(key, []); - dataByClient.get(key)?.push(data); - } - - // calculate the number of epochs the client was used in the interval - for (const [key, data] of dataByClient.entries()) - clientsUsedInInterval.set(key, data.filter((data) => data.epoch >= startEpoch && data.epoch < endEpoch).length); - - return clientsUsedInInterval; -} diff --git a/packages/brain/src/modules/validatorsDataIngest/getIntervalsEpochs.ts b/packages/brain/src/modules/validatorsDataIngest/getIntervalsEpochs.ts deleted file mode 100644 index 10bef2c9..00000000 --- a/packages/brain/src/modules/validatorsDataIngest/getIntervalsEpochs.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { getStartAndEndEpochs } from "./getStartAndEndEpochs.js"; -import { Granularity } from "./types.js"; - -export function getIntervalsEpochs({ - startDate, - endDate, - granularity, - minGenesisTime, - secondsPerSlot -}: { - startDate: Date; - endDate: Date; - granularity: Granularity; - minGenesisTime: number; - secondsPerSlot: number; -}): { startEpoch: number; endEpoch: number }[] { - // Calculate the number of intervals based on the granularity - const numberOfIntervals = getNumberOfIntervals({ startDate, endDate, granularity }); - return Array.from({ length: numberOfIntervals }, (_, idx) => { - return getStartAndEndEpochs({ - minGenesisTime, - secondsPerSlot, - startDate: new Date(startDate.getTime() + idx * granularity), - endDate: new Date(startDate.getTime() + (idx + 1) * granularity) - }); - }); -} - -function getNumberOfIntervals({ - startDate, - endDate, - granularity -}: { - startDate: Date; - endDate: Date; - granularity: Granularity; -}): number { - // Calculate the total amount of time based on the granularity - const totalAmountOfTime = endDate.getTime() - startDate.getTime(); - return Math.floor(totalAmountOfTime / granularity); // Use Math.floor for proper interval count -} diff --git a/packages/brain/src/modules/validatorsDataIngest/index.ts b/packages/brain/src/modules/validatorsDataIngest/index.ts index 878d7a8f..42159707 100644 --- a/packages/brain/src/modules/validatorsDataIngest/index.ts +++ b/packages/brain/src/modules/validatorsDataIngest/index.ts @@ -1,24 +1,7 @@ import { PostgresClient } from "../apiClients/index.js"; import logger from "../logger/index.js"; import { getStartAndEndEpochs } from "./getStartAndEndEpochs.js"; -import { getAttestationSuccessRate } from "./getAttestationSuccessRate.js"; -import { Granularity, NumberOfDaysToQuery, ValidatorsDataProcessed } from "./types.js"; -import { getIntervalsEpochs } from "./getIntervalsEpochs.js"; -import { getAttestationSuccessRatePerClients } from "./getAttestationSuccessRatePerClients.js"; -import { getClientsUsedPerIntervalsMap } from "./getClientsUsedPerIntervalsMap.js"; - -// Module in charge of querying and processin the data of the validators to get the performance metrics: -// - Attestation success rate -// - Blocks proposed success rate -// - Mean attestation success rate -// - Mean blocks proposed success rate - -// Note: It is overkill to store in db the attestation success rate for each epoch since it is only useful froma a global perspective -// taking into account the historical data. As for now we will calculate dynamicall the attestation success rate with the arguments: epoch start and epoch end. - -// TODO: return current validator balance: 2 ways of doing it: 1) **get the balance from the beaconchain API**, 2) store the ideal rewards with the effective balance and get the balance from the postgres DB. The second option is more efficient but it is not real time. -// TODO: return to the frontend the remaining seconds to next epoch. In the frontend use this parameter to query the backend every time the epoch changes. -// TODO: add to block proposed epoch and slot +import { NumberOfDaysToQuery, EpochsValidatorsMap } from "./types.js"; /** * Get the processed data for the validators in the given date range and the given validators indexes. @@ -35,23 +18,15 @@ export async function fetchAndProcessValidatorsData({ postgresClient, minGenesisTime, secondsPerSlot, - numberOfDaysToQuery = 1, - granularity = Granularity.Hourly + numberOfDaysToQuery = 1 }: { validatorIndexes: string[]; postgresClient: PostgresClient; // import from backend index minGenesisTime: number; // import from backend index secondsPerSlot: number; // immport from backend index numberOfDaysToQuery?: NumberOfDaysToQuery; - granularity?: Granularity; -}): Promise< - Map< - number, // validatorIndex - ValidatorsDataProcessed // processed data of the validator - > -> { - logger.info("Processing validators data"); - const mapValidatorPerformance = new Map(); +}): Promise { + logger.info("Processing epochs data"); // Get start timestamp and end timestamp const endDate = new Date(); @@ -61,42 +36,9 @@ export async function fetchAndProcessValidatorsData({ // Calculate the epochs for the given dates const { startEpoch, endEpoch } = getStartAndEndEpochs({ minGenesisTime, secondsPerSlot, startDate, endDate }); - // Get the start and end epochs for each interval - const intervals = getIntervalsEpochs({ startDate, endDate, granularity, minGenesisTime, secondsPerSlot }); - - // Get the validators data from the postgres database with the start and end epoch - const validatorsDataMap = await postgresClient.getValidatorsDataMapForEpochRange({ - validatorIndexes, + return await postgresClient.getEpochsDataMapForEpochRange({ startEpoch, - endEpoch + endEpoch, + validatorIndexes }); - - // Calculate the attestation success rate for each validator - for (const [validatorIndex, validatorData] of validatorsDataMap.entries()) - mapValidatorPerformance.set(validatorIndex, { - attestationSuccessRate: getAttestationSuccessRate({ validatorData, startEpoch, endEpoch }), - attestationSuccessRatePerClients: getAttestationSuccessRatePerClients({ validatorData, startEpoch, endEpoch }), - attestationSuccessRatePerInterval: intervals.map(({ startEpoch, endEpoch }) => { - return { - startEpoch, - endEpoch, - attestationSuccessRate: getAttestationSuccessRate({ validatorData, startEpoch, endEpoch }), - clientsUsedInInterval: getClientsUsedPerIntervalsMap({ validatorData, startEpoch, endEpoch }) - }; - }), - blocks: { - proposed: validatorData - .filter((data) => data.blockProposalStatus === "Proposed") - .map((data) => ({ epoch: data.epoch })), - missed: validatorData - .filter((data) => data.blockProposalStatus === "Missed") - .map((data) => ({ epoch: data.epoch })), - unchosen: validatorData - .filter((data) => data.blockProposalStatus === "Unchosen") - .map((data) => ({ epoch: data.epoch })) - } - }); - - // Return the processed data - return mapValidatorPerformance; } diff --git a/packages/brain/src/modules/validatorsDataIngest/types.ts b/packages/brain/src/modules/validatorsDataIngest/types.ts index 1c9e5132..0616c140 100644 --- a/packages/brain/src/modules/validatorsDataIngest/types.ts +++ b/packages/brain/src/modules/validatorsDataIngest/types.ts @@ -1,24 +1,28 @@ -import { ConsensusClient, ExecutionClient } from "@stakingbrain/common"; +import { Tag } from "@stakingbrain/common"; +import { BlockProposalStatus } from "../apiClients/postgres/types"; +import { IdealRewards, TotalRewards } from "../apiClients/types"; -export type ExecutionConsensusConcatenated = `${ExecutionClient}-${ConsensusClient}`; +// TODO: index epoch data per slot -// TODO: use ideal rewards to return calculated attestation efficiency -export interface ValidatorsDataProcessed { - attestationSuccessRate: number; // mean attestationSuccessRate of the validator - attestationSuccessRatePerClients: Map; - // attestationSuccessRate in each interval - attestationSuccessRatePerInterval: { - startEpoch: number; // start epoch of the interval - endEpoch: number; // end epoch of the interval - attestationSuccessRate: number | null; // attestationSuccessRate in the interval - clientsUsedInInterval: Map; // Map indexed by ["execution-consensus"] (i.e "geth-lighthouse") with the number of epochs the client was used in the interval - }[]; - blocks: { - // TODO: add slot { epoch: number, slot: number } - proposed: { epoch: number }[]; - missed: { epoch: number }[]; - unchosen: { epoch: number }[]; +// Indexed by epoch number +export type EpochsValidatorsMap = Map; +// Indexed by validator index +export type ValidatorsEpochMap = Map; +export interface DataPerEpoch { + attestation: { + totalRewards: TotalRewards; + idealRewards: IdealRewards; }; + block: { + status: BlockProposalStatus; // todo: pick only proposed and missed + slot?: number; + graffiti?: string; + reward?: number; + }; + syncCommittee: { + reward: number; + }; + tag: Tag; } export enum Granularity { diff --git a/packages/brain/test/unit/modules/validatorsDataIngest/getAttestationSuccessRate.unit.test.ts b/packages/brain/test/unit/modules/validatorsDataIngest/getAttestationSuccessRate.unit.test.ts deleted file mode 100644 index 43b674d5..00000000 --- a/packages/brain/test/unit/modules/validatorsDataIngest/getAttestationSuccessRate.unit.test.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { ExecutionClient, ConsensusClient } from "@stakingbrain/common"; -import { expect } from "chai"; -import { ValidatorPerformance, BlockProposalStatus } from "../../../../src/modules/apiClients/postgres/types.js"; -import { getAttestationSuccessRate } from "../../../../src/modules/validatorsDataIngest/getAttestationSuccessRate"; - -describe("validatorsDataIngest - getAttestationSuccessRate", () => { - it("should return the attestation success rate for a given validator", () => { - const validatorData: ValidatorPerformance[] = [ - { - validatorIndex: 0, - epoch: 1, - executionClient: ExecutionClient.Geth, - consensusClient: ConsensusClient.Lighthouse, - blockProposalStatus: BlockProposalStatus.Proposed, - attestationsTotalRewards: { - validator_index: "0", - head: "someHead", - target: "someTarget", - source: "1", - inclusion_delay: "0", - inactivity: "0" - } - }, - { - validatorIndex: 0, - epoch: 2, - executionClient: ExecutionClient.Geth, - consensusClient: ConsensusClient.Lighthouse, - blockProposalStatus: BlockProposalStatus.Proposed, - attestationsTotalRewards: { - validator_index: "0", - head: "someHead", - target: "someTarget", - source: "0", - inclusion_delay: "0", - inactivity: "0" - } - }, - { - validatorIndex: 0, - epoch: 3, - executionClient: ExecutionClient.Geth, - consensusClient: ConsensusClient.Lighthouse, - blockProposalStatus: BlockProposalStatus.Proposed, - attestationsTotalRewards: { - validator_index: "0", - head: "someHead", - target: "someTarget", - source: "-1", - inclusion_delay: "0", - inactivity: "0" - } - } - ]; - - const startEpoch = 1; - const endEpoch = 4; // Total opportunities: 3 (1, 2, 3) - - const successRate = getAttestationSuccessRate({ - validatorData, - startEpoch, - endEpoch - }); - - expect(successRate).to.equal(67); // 2 successful attestations out of 3 opportunities - }); - - it("should return 0 if the total attestation opportunities are less than or equal to 0", () => { - const validatorData: ValidatorPerformance[] = []; - const startEpoch = 3; - const endEpoch = 3; // Total opportunities: 0 - - const successRate = getAttestationSuccessRate({ - validatorData, - startEpoch, - endEpoch - }); - - expect(successRate).to.equal(0); - }); - - it("should correctly handle edge case with no successful attestations", () => { - const validatorData: ValidatorPerformance[] = [ - { - validatorIndex: 0, - epoch: 1, - executionClient: ExecutionClient.Geth, - consensusClient: ConsensusClient.Lighthouse, - blockProposalStatus: BlockProposalStatus.Proposed, - attestationsTotalRewards: { - validator_index: "0", - head: "someHead", - target: "someTarget", - source: "-1", // Unsuccessful - inclusion_delay: "0", - inactivity: "0" - } - } - ]; - - const startEpoch = 1; - const endEpoch = 2; // Total opportunities: 1 - - const successRate = getAttestationSuccessRate({ - validatorData, - startEpoch, - endEpoch - }); - - expect(successRate).to.equal(0); // No successful attestations - }); -}); diff --git a/packages/brain/test/unit/modules/validatorsDataIngest/getAttestationSuccessRatePerClients.unit.test.ts b/packages/brain/test/unit/modules/validatorsDataIngest/getAttestationSuccessRatePerClients.unit.test.ts deleted file mode 100644 index 5ed167a0..00000000 --- a/packages/brain/test/unit/modules/validatorsDataIngest/getAttestationSuccessRatePerClients.unit.test.ts +++ /dev/null @@ -1,177 +0,0 @@ -import { expect } from "chai"; -import { getAttestationSuccessRatePerClients } from "../../../../src/modules/validatorsDataIngest/getAttestationSuccessRatePerClients.js"; -import { ExecutionClient, ConsensusClient } from "@stakingbrain/common"; -import { ValidatorPerformance, BlockProposalStatus } from "../../../../src/modules/apiClients/postgres/types.js"; - -describe("validatorsDataIngest - getAttestationSuccessRatePerClients", () => { - // Sample validator data - const validatorData: ValidatorPerformance[] = [ - { - validatorIndex: 1, - epoch: 0, - executionClient: ExecutionClient.Geth, - consensusClient: ConsensusClient.Teku, - blockProposalStatus: BlockProposalStatus.Proposed, - attestationsTotalRewards: { - validator_index: "1", - head: "head1", - target: "target1", - source: "0", // Successful attestation - inclusion_delay: "0", - inactivity: "0" - } - }, - { - validatorIndex: 1, - epoch: 1, - executionClient: ExecutionClient.Geth, - consensusClient: ConsensusClient.Teku, - blockProposalStatus: BlockProposalStatus.Proposed, - attestationsTotalRewards: { - validator_index: "1", - head: "head2", - target: "target2", - source: "-1", // Failed attestation - inclusion_delay: "0", - inactivity: "0" - } - }, - { - validatorIndex: 1, - epoch: 2, - executionClient: ExecutionClient.Geth, - consensusClient: ConsensusClient.Teku, - blockProposalStatus: BlockProposalStatus.Proposed, - attestationsTotalRewards: { - validator_index: "1", - head: "head3", - target: "target3", - source: "1", // Successful attestation - inclusion_delay: "0", - inactivity: "0" - } - }, - { - validatorIndex: 2, - epoch: 0, - executionClient: ExecutionClient.Besu, - consensusClient: ConsensusClient.Prysm, - blockProposalStatus: BlockProposalStatus.Proposed, - attestationsTotalRewards: { - validator_index: "2", - head: "head1", - target: "target1", - source: "0", // Successful attestation - inclusion_delay: "0", - inactivity: "0" - } - }, - { - validatorIndex: 2, - epoch: 1, - executionClient: ExecutionClient.Besu, - consensusClient: ConsensusClient.Prysm, - blockProposalStatus: BlockProposalStatus.Proposed, - attestationsTotalRewards: { - validator_index: "2", - head: "head2", - target: "target2", - source: "0", // Successful attestation - inclusion_delay: "0", - inactivity: "0" - } - }, - { - validatorIndex: 2, - epoch: 2, - executionClient: ExecutionClient.Besu, - consensusClient: ConsensusClient.Prysm, - blockProposalStatus: BlockProposalStatus.Proposed, - attestationsTotalRewards: { - validator_index: "2", - head: "head3", - target: "target3", - source: "-1", // Failed attestation - inclusion_delay: "0", - inactivity: "0" - } - } - ]; - - it("should calculate the attestation success rate per clients correctly", () => { - const startEpoch = 0; - const endEpoch = 3; // Covering epochs 0, 1, and 2 - - const result = getAttestationSuccessRatePerClients({ - validatorData, - startEpoch, - endEpoch - }); - - // Check success rates for Geth-Teku - expect(result.get(`${ExecutionClient.Geth}-${ConsensusClient.Teku}`)).to.equal(67); // 2 successful out of 3 - - // Check success rates for Besu-Prysm - expect(result.get(`${ExecutionClient.Besu}-${ConsensusClient.Prysm}`)).to.equal(67); // 2 successful out of 3 - }); - - it("should return 0% success rate if there are no opportunities", () => { - const startEpoch = 3; - const endEpoch = 3; // No opportunities - - const result = getAttestationSuccessRatePerClients({ - validatorData, - startEpoch, - endEpoch - }); - - expect(result.get(`${ExecutionClient.Geth}-${ConsensusClient.Teku}`)).to.equal(0); // No opportunities - expect(result.get(`${ExecutionClient.Besu}-${ConsensusClient.Prysm}`)).to.equal(0); // No opportunities - }); - - it("should handle a scenario where there are no successful attestations", () => { - const validatorDataNoSuccess: ValidatorPerformance[] = [ - { - validatorIndex: 1, - epoch: 0, - executionClient: ExecutionClient.Geth, - consensusClient: ConsensusClient.Teku, - blockProposalStatus: BlockProposalStatus.Proposed, - attestationsTotalRewards: { - validator_index: "1", - head: "head1", - target: "target1", - source: "-1", // Failed attestation - inclusion_delay: "0", - inactivity: "0" - } - }, - { - validatorIndex: 1, - epoch: 1, - executionClient: ExecutionClient.Geth, - consensusClient: ConsensusClient.Teku, - blockProposalStatus: BlockProposalStatus.Proposed, - attestationsTotalRewards: { - validator_index: "1", - head: "head2", - target: "target2", - source: "-1", // Failed attestation - inclusion_delay: "0", - inactivity: "0" - } - } - ]; - - const startEpoch = 0; - const endEpoch = 2; // Covering epochs 0 and 1 - - const result = getAttestationSuccessRatePerClients({ - validatorData: validatorDataNoSuccess, - startEpoch, - endEpoch - }); - - expect(result.get(`${ExecutionClient.Geth}-${ConsensusClient.Teku}`)).to.equal(0); // No successful attestations - }); -}); diff --git a/packages/brain/test/unit/modules/validatorsDataIngest/getClientsUsedPerIntervalsMap.unit.test.ts b/packages/brain/test/unit/modules/validatorsDataIngest/getClientsUsedPerIntervalsMap.unit.test.ts deleted file mode 100644 index 22709a93..00000000 --- a/packages/brain/test/unit/modules/validatorsDataIngest/getClientsUsedPerIntervalsMap.unit.test.ts +++ /dev/null @@ -1,219 +0,0 @@ -import { expect } from "chai"; -import { getClientsUsedPerIntervalsMap } from "../../../../src/modules/validatorsDataIngest/getClientsUsedPerIntervalsMap.js"; -import { ExecutionClient, ConsensusClient } from "@stakingbrain/common"; -import { ValidatorPerformance, BlockProposalStatus } from "../../../../src/modules/apiClients/postgres/types.js"; - -describe("validatorsDataIngest - getClientsUsedPerIntervalsMap", () => { - const validatorData: ValidatorPerformance[] = [ - { - validatorIndex: 1, - epoch: 0, - executionClient: ExecutionClient.Geth, - consensusClient: ConsensusClient.Teku, - blockProposalStatus: BlockProposalStatus.Proposed, - attestationsTotalRewards: { - validator_index: "1", - head: "head1", - target: "target1", - source: "source1", - inclusion_delay: "0", - inactivity: "0" - } - }, - { - validatorIndex: 2, - epoch: 1, - executionClient: ExecutionClient.Besu, - consensusClient: ConsensusClient.Prysm, - blockProposalStatus: BlockProposalStatus.Missed, - attestationsTotalRewards: { - validator_index: "2", - head: "head2", - target: "target2", - source: "source2", - inclusion_delay: "1", - inactivity: "0" - } - }, - { - validatorIndex: 3, - epoch: 1, - executionClient: ExecutionClient.Geth, - consensusClient: ConsensusClient.Lighthouse, - blockProposalStatus: BlockProposalStatus.Unchosen, - attestationsTotalRewards: { - validator_index: "3", - head: "head3", - target: "target3", - source: "source3", - inclusion_delay: "0", - inactivity: "0" - } - }, - { - validatorIndex: 4, - epoch: 2, - executionClient: ExecutionClient.Erigon, - consensusClient: ConsensusClient.Nimbus, - blockProposalStatus: BlockProposalStatus.Error, - attestationsTotalRewards: { - validator_index: "4", - head: "head4", - target: "target4", - source: "source4", - inclusion_delay: "0", - inactivity: "0" - } - }, - { - validatorIndex: 5, - epoch: 1, - executionClient: ExecutionClient.Geth, - consensusClient: ConsensusClient.Teku, - blockProposalStatus: BlockProposalStatus.Proposed, - attestationsTotalRewards: { - validator_index: "5", - head: "head5", - target: "target5", - source: "source5", - inclusion_delay: "0", - inactivity: "0" - } - }, - { - validatorIndex: 6, - epoch: 2, - executionClient: ExecutionClient.Geth, - consensusClient: ConsensusClient.Teku, - blockProposalStatus: BlockProposalStatus.Proposed, - attestationsTotalRewards: { - validator_index: "6", - head: "head6", - target: "target6", - source: "source6", - inclusion_delay: "0", - inactivity: "0" - } - }, - { - validatorIndex: 7, - epoch: 2, - executionClient: ExecutionClient.Besu, - consensusClient: ConsensusClient.Prysm, - blockProposalStatus: BlockProposalStatus.Missed, - attestationsTotalRewards: { - validator_index: "7", - head: "head7", - target: "target7", - source: "source7", - inclusion_delay: "0", - inactivity: "0" - } - }, - { - validatorIndex: 8, - epoch: 3, - executionClient: ExecutionClient.Besu, - consensusClient: ConsensusClient.Lighthouse, - blockProposalStatus: BlockProposalStatus.Unchosen, - attestationsTotalRewards: { - validator_index: "8", - head: "head8", - target: "target8", - source: "source8", - inclusion_delay: "0", - inactivity: "0" - } - }, - { - validatorIndex: 9, - epoch: 3, - executionClient: ExecutionClient.Geth, - consensusClient: ConsensusClient.Nimbus, - blockProposalStatus: BlockProposalStatus.Error, - attestationsTotalRewards: { - validator_index: "9", - head: "head9", - target: "target9", - source: "source9", - inclusion_delay: "0", - inactivity: "0" - } - }, - { - validatorIndex: 10, - epoch: 4, - executionClient: ExecutionClient.Erigon, - consensusClient: ConsensusClient.Teku, - blockProposalStatus: BlockProposalStatus.Proposed, - attestationsTotalRewards: { - validator_index: "10", - head: "head10", - target: "target10", - source: "source10", - inclusion_delay: "0", - inactivity: "0" - } - }, - { - validatorIndex: 11, - epoch: 4, - executionClient: ExecutionClient.Besu, - consensusClient: ConsensusClient.Prysm, - blockProposalStatus: BlockProposalStatus.Missed, - attestationsTotalRewards: { - validator_index: "11", - head: "head11", - target: "target11", - source: "source11", - inclusion_delay: "0", - inactivity: "0" - } - }, - { - validatorIndex: 12, - epoch: 4, - executionClient: ExecutionClient.Geth, - consensusClient: ConsensusClient.Teku, - blockProposalStatus: BlockProposalStatus.Unchosen, - attestationsTotalRewards: { - validator_index: "12", - head: "head12", - target: "target12", - source: "source12", - inclusion_delay: "0", - inactivity: "0" - } - } - ]; - - it("should return correct counts for a given epoch range", () => { - const startEpoch = 1; - const endEpoch = 4; - - const result = getClientsUsedPerIntervalsMap({ validatorData, startEpoch, endEpoch }); - expect(result.size).to.equal(7); // Geth-Teku, Besu-Prysm, Geth-Lighthouse, Erigon-Teku - expect(result.get(`${ExecutionClient.Geth}-${ConsensusClient.Teku}`)).to.equal(2); - expect(result.get(`${ExecutionClient.Besu}-${ConsensusClient.Prysm}`)).to.equal(2); - expect(result.get(`${ExecutionClient.Geth}-${ConsensusClient.Lighthouse}`)).to.equal(1); - expect(result.get(`${ExecutionClient.Erigon}-${ConsensusClient.Nimbus}`)).to.equal(1); - expect(result.get(`${ExecutionClient.Geth}-${ConsensusClient.Nimbus}`)).to.equal(1); - expect(result.get(`${ExecutionClient.Erigon}-${ConsensusClient.Teku}`)).to.equal(0); - }); - - it("should return zero counts for an epoch range with no data", () => { - const startEpoch = 12; - const endEpoch = 15; - - const result = getClientsUsedPerIntervalsMap({ validatorData, startEpoch, endEpoch }); - for (const value of result.values()) expect(value).to.equal(0); - }); - - it("should handle cases where startEpoch is equal to endEpoch. Nothing should be displayed since the intervals takes the first epoch and not the last one", () => { - const startEpoch = 1; - const endEpoch = 1; - - const result = getClientsUsedPerIntervalsMap({ validatorData, startEpoch, endEpoch }); - for (const value of result.values()) expect(value).to.equal(0); - }); -}); diff --git a/packages/brain/test/unit/modules/validatorsDataIngest/getIntervalsEpochs.unit.test.ts b/packages/brain/test/unit/modules/validatorsDataIngest/getIntervalsEpochs.unit.test.ts deleted file mode 100644 index 858961b3..00000000 --- a/packages/brain/test/unit/modules/validatorsDataIngest/getIntervalsEpochs.unit.test.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { expect } from "chai"; -import { getIntervalsEpochs } from "../../../../src/modules/validatorsDataIngest/getIntervalsEpochs.js"; -import { Granularity } from "../../../../src/modules/validatorsDataIngest/types.js"; - -describe("validatorsDataIngest - getIntervalsEpochs", () => { - const minGenesisTime = 1695902100; // Min genesis time Holesky - const secondsPerSlot = 12; // Seconds per slot - - it("should return correct intervals for daily granularity", () => { - const startDate = new Date("2024-09-22T00:00:00Z"); - const endDate = new Date("2024-09-23T00:00:00Z"); - const granularity = Granularity.Daily; - - const intervals = getIntervalsEpochs({ - startDate, - endDate, - granularity, - minGenesisTime, - secondsPerSlot - }); - - expect(intervals.length).to.equal(1); - expect(intervals[0]).to.deep.equal({ - startEpoch: 80888, - endEpoch: 81113 - }); - }); - - it("should return correct intervals for hourly granularity", () => { - const startDate = new Date("2024-09-22T00:00:00Z"); - const endDate = new Date("2024-09-22T02:00:00Z"); - const granularity = Granularity.Hourly; - - const intervals = getIntervalsEpochs({ - startDate, - endDate, - granularity, - minGenesisTime, - secondsPerSlot - }); - - expect(intervals.length).to.equal(2); - expect(intervals[0]).to.deep.equal({ - startEpoch: 80888, - endEpoch: 80897 - }); - expect(intervals[1]).to.deep.equal({ - startEpoch: 80897, - endEpoch: 80907 - }); - }); - - it("should return correct intervals for weekly granularity", () => { - const startDate = new Date("2024-08-01T00:00:00Z"); - const endDate = new Date("2024-08-15T00:00:00Z"); - const granularity = Granularity.Weekly; - - const intervals = getIntervalsEpochs({ - startDate, - endDate, - granularity, - minGenesisTime, - secondsPerSlot - }); - - expect(intervals.length).to.equal(2); - expect(intervals[0]).to.deep.equal({ - startEpoch: 69188, - endEpoch: 70763 - }); - expect(intervals[1]).to.deep.equal({ - startEpoch: 70763, - endEpoch: 72338 - }); - }); - - it("should handle cases where endDate is the same as startDate", () => { - const startDate = new Date("2023-01-01T00:00:00Z"); - const endDate = new Date("2023-01-01T00:00:00Z"); - const granularity = Granularity.Hourly; - - const intervals = getIntervalsEpochs({ - startDate, - endDate, - granularity, - minGenesisTime, - secondsPerSlot - }); - - expect(intervals.length).to.equal(0); - }); - - it("should return an empty array for invalid date ranges", () => { - const startDate = new Date("2023-01-02T00:00:00Z"); - const endDate = new Date("2023-01-01T00:00:00Z"); - const granularity = Granularity.Hourly; - - const intervals = getIntervalsEpochs({ - startDate, - endDate, - granularity, - minGenesisTime, - secondsPerSlot - }); - - expect(intervals.length).to.equal(0); - }); -}); diff --git a/packages/brain/test/unit/modules/validatorsDataIngest/index.unit.test.ts b/packages/brain/test/unit/modules/validatorsDataIngest/index.unit.test.ts index ce211523..096cf55c 100644 --- a/packages/brain/test/unit/modules/validatorsDataIngest/index.unit.test.ts +++ b/packages/brain/test/unit/modules/validatorsDataIngest/index.unit.test.ts @@ -1,6 +1,5 @@ import { PostgresClient } from "../../../../src/modules/apiClients/index.js"; import { fetchAndProcessValidatorsData } from "../../../../src/modules/validatorsDataIngest/index.js"; -import { Granularity } from "../../../../src/modules/validatorsDataIngest/types.js"; // This test must be executed with a real database connection @@ -20,8 +19,7 @@ describe.skip("Validators data ingest", function () { postgresClient, minGenesisTime, secondsPerSlot, - numberOfDaysToQuery: 1, - granularity: Granularity.Hourly + numberOfDaysToQuery: 1 }); console.log(data);