diff --git a/packages/brain/src/calls/getStakerConfig.ts b/packages/brain/src/calls/getStakerConfig.ts index 46f6294b..8ed3f349 100644 --- a/packages/brain/src/calls/getStakerConfig.ts +++ b/packages/brain/src/calls/getStakerConfig.ts @@ -1,8 +1,8 @@ import { StakerConfig } from "@stakingbrain/common"; import { network, - executionClientSelected, - consensusClientSelected, + executionClient, + consensusClient, isMevBoostSet, executionClientUrl, validatorUrl, @@ -13,8 +13,8 @@ import { export async function getStakerConfig(): Promise { return { network, - executionClientSelected, - consensusClientSelected, + executionClient, + consensusClient, isMevBoostSet, executionClientUrl, validatorUrl, diff --git a/packages/brain/src/index.ts b/packages/brain/src/index.ts index 066e8ff7..77112b91 100644 --- a/packages/brain/src/index.ts +++ b/packages/brain/src/index.ts @@ -33,8 +33,8 @@ export const __dirname = process.cwd(); // Load staker config export const { network, - executionClientSelected, - consensusClientSelected, + executionClient, + consensusClient, isMevBoostSet, executionClientUrl, validatorUrl, @@ -53,7 +53,7 @@ export const { tlsCert } = brainConfig(); logger.debug( - `Loaded staker config:\n - Network: ${network}\n - Execution client: ${executionClientSelected}\n - Consensus client: ${consensusClientSelected}\n - Execution client url: ${executionClientUrl}\n - Validator url: ${validatorUrl}\n - Beaconcha url: ${blockExplorerUrl}\n - Beaconchain url: ${beaconchainUrl}\n - Signer url: ${signerUrl}\n - Token: ${token}\n - Host: ${host}}\n - Postgres url: ${postgresUrl}\n}` + `Loaded staker config:\n - Network: ${network}\n - Execution client: ${executionClient}\n - Consensus client: ${consensusClient}\n - Execution client url: ${executionClientUrl}\n - Validator url: ${validatorUrl}\n - Beaconcha url: ${blockExplorerUrl}\n - Beaconchain url: ${beaconchainUrl}\n - Signer url: ${signerUrl}\n - Token: ${token}\n - Host: ${host}}\n - Postgres url: ${postgresUrl}\n}` ); // Create API instances. Must preceed db initialization @@ -104,7 +104,15 @@ const proofOfValidationCron = new CronJob(shareCronInterval, () => proofOfValidationCron.start(); const trackValidatorsPerformanceCron = new CronJob(slotsPerEpoch * secondsPerSlot * 1000, () => // once every epoch - trackValidatorsPerformance({ brainDb, postgresClient, beaconchainApi, minGenesisTime, secondsPerSlot }) + trackValidatorsPerformance({ + brainDb, + postgresClient, + beaconchainApi, + minGenesisTime, + secondsPerSlot, + executionClient, + consensusClient + }) ); const secondsToNextEpoch = getSecondsToNextEpoch({ minGenesisTime, secondsPerSlot }); // start the cron within the first minute of an epoch diff --git a/packages/brain/src/modules/apiClients/postgres/index.ts b/packages/brain/src/modules/apiClients/postgres/index.ts index 47eb007f..b9e65502 100644 --- a/packages/brain/src/modules/apiClients/postgres/index.ts +++ b/packages/brain/src/modules/apiClients/postgres/index.ts @@ -2,6 +2,20 @@ import postgres from "postgres"; import logger from "../../logger/index.js"; import { BlockProposalStatus, ValidatorPerformance } from "./types.js"; import { PostgresApiError } from "./error.js"; +import { ConsensusClient, ExecutionClient } from "@stakingbrain/common"; + +enum Columns { + validatorIndex = "validator_index", + epoch = "epoch", + executionClient = "execution_client", + consensusClient = "consensus_client", + slot = "slot", + liveness = "liveness", + blockProposalStatus = "block_proposal_status", + syncCommitteeRewards = "sync_comittee_rewards", + attestationsRewards = "attestations_rewards", + error = "error" +} export class PostgresClient { private readonly tableName = "validators_performance"; @@ -51,25 +65,40 @@ SELECT pg_total_relation_size('${this.tableName}'); */ public async initialize() { const query = ` - DO $$ - BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'block_proposal_status') THEN - CREATE TYPE block_proposal_status AS ENUM('${BlockProposalStatus.Missed}', '${BlockProposalStatus.Proposed}', '${BlockProposalStatus.Unchosen}'); - END IF; - END $$; - - CREATE TABLE IF NOT EXISTS ${this.tableName} ( - validator_index BIGINT NOT NULL, - epoch BIGINT NOT NULL, - slot BIGINT, - liveness BOOLEAN, - block_proposal_status block_proposal_status, - sync_comittee_rewards BIGINT, - attestations_rewards JSONB, - error TEXT, - PRIMARY KEY (validator_index, epoch) - ); - `; +DO $$ +BEGIN + -- Check and create BLOCK_PROPOSAL_STATUS ENUM type if not exists + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'BLOCK_PROPOSAL_STATUS') THEN + CREATE TYPE BLOCK_PROPOSAL_STATUS AS ENUM('${BlockProposalStatus.Missed}', '${BlockProposalStatus.Proposed}', '${BlockProposalStatus.Unchosen}'); + END IF; + + -- Check and create EXECUTION_CLIENT ENUM type if not exists + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'EXECUTION_CLIENT') THEN + CREATE TYPE EXECUTION_CLIENT AS ENUM('${ExecutionClient.Besu}', '${ExecutionClient.Nethermind}', '${ExecutionClient.Geth}', '${ExecutionClient.Erigon}', '${ExecutionClient.Unknown}'); + END IF; + + -- Check and create CONSENSUS_CLIENT ENUM type if not exists + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'CONSENSUS_CLIENT') THEN + CREATE TYPE CONSENSUS_CLIENT AS ENUM('${ConsensusClient.Teku}', '${ConsensusClient.Prysm}', '${ConsensusClient.Lighthouse}', '${ConsensusClient.Nimbus}', '${ConsensusClient.Unknown}'); + END IF; +END $$; + +-- Create the table if not exists +CREATE TABLE IF NOT EXISTS ${this.tableName} ( + ${Columns.validatorIndex} BIGINT NOT NULL, + ${Columns.epoch} BIGINT NOT NULL, + ${Columns.executionClient} EXECUTION_CLIENT NOT NULL, + ${Columns.consensusClient} CONSENSUS_CLIENT NOT NULL, + ${Columns.slot} BIGINT, + ${Columns.liveness} BOOLEAN, + ${Columns.blockProposalStatus} BLOCK_PROPOSAL_STATUS, + ${Columns.syncCommitteeRewards} BIGINT, + ${Columns.attestationsRewards} JSONB, + ${Columns.error} TEXT, + PRIMARY KEY (${Columns.validatorIndex}, ${Columns.epoch}) +); +`; + try { await this.sql.unsafe(query); logger.info("Table created or already exists."); @@ -94,6 +123,23 @@ SELECT pg_total_relation_size('${this.tableName}'); } } + /** + * Delete enum types. + */ + public async deleteEnumTypes(): Promise { + const query = ` + DROP TYPE IF EXISTS BLOCK_PROPOSAL_STATUS; + DROP TYPE IF EXISTS EXECUTION_CLIENT; + DROP TYPE IF EXISTS CONSENSUS_CLIENT; + `; + try { + await this.sql.unsafe(query); + logger.info("Enum types deleted."); + } catch (err) { + logger.error("Error deleting enum types:", err); + } + } + /** * Inserts the given performance data into the database. * @@ -102,13 +148,15 @@ SELECT pg_total_relation_size('${this.tableName}'); */ public async insertPerformanceData(data: ValidatorPerformance): Promise { const query = ` -INSERT INTO ${this.tableName} (validator_index, epoch, slot, liveness, block_proposal_status, sync_comittee_rewards, attestations_rewards, error) +INSERT INTO ${this.tableName} (${Columns.validatorIndex}, ${Columns.epoch}, ${Columns.slot}, ${Columns.liveness}, ${Columns.blockProposalStatus}, ${Columns.syncCommitteeRewards}, ${Columns.attestationsRewards}, ${Columns.error}) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) `; try { await this.sql.unsafe(query, [ data.validatorIndex, data.epoch, + data.executionClient, + data.consensusClient, data.slot ?? null, data.liveness ?? null, data.blockProposalStatus ?? null, @@ -122,6 +170,39 @@ VALUES ($1, $2, $3, $4, $5, $6, $7, $8) } } + /** + * Get the validators data for the given validator indexes from all epochs. + * + * @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 { + const query = ` +SELECT * FROM ${this.tableName} +WHERE ${Columns.validatorIndex} = ANY($1) + `; + try { + const result = await this.sql.unsafe(query, [validatorIndexes]); + // TODO: add type for result + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return result.map((row: any) => ({ + 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, + attestationsRewards: row.attestations_rewards, + error: row.error + })); + } catch (err) { + logger.error("Error getting data:", err); + return []; + } + } + /** * Method to close the database connection. */ diff --git a/packages/brain/src/modules/apiClients/postgres/types.ts b/packages/brain/src/modules/apiClients/postgres/types.ts index 49a6a94f..f8653d9b 100644 --- a/packages/brain/src/modules/apiClients/postgres/types.ts +++ b/packages/brain/src/modules/apiClients/postgres/types.ts @@ -1,3 +1,5 @@ +import { ConsensusClient, ExecutionClient } from "@stakingbrain/common"; + export enum BlockProposalStatus { Missed = "Missed", Proposed = "Proposed", @@ -8,6 +10,8 @@ export enum BlockProposalStatus { export interface ValidatorPerformance { validatorIndex: number; epoch: number; + executionClient: ExecutionClient; + consensusClient: ConsensusClient; slot?: number; liveness?: boolean; blockProposalStatus?: BlockProposalStatus; diff --git a/packages/brain/src/modules/config/index.ts b/packages/brain/src/modules/config/index.ts index bf1a9abc..c2e61eca 100644 --- a/packages/brain/src/modules/config/index.ts +++ b/packages/brain/src/modules/config/index.ts @@ -10,19 +10,18 @@ import { } from "./networks/index.js"; export const brainConfig = (): BrainConfig => { - const { network, executionClientSelected, consensusClientSelected, isMevBoostSet, shareDataWithDappnode } = - loadEnvs(); + const { network, executionClient, consensusClient, isMevBoostSet, shareDataWithDappnode } = loadEnvs(); switch (network) { case Network.Holesky: - return holeskyBrainConfig(executionClientSelected, consensusClientSelected, isMevBoostSet, shareDataWithDappnode); + return holeskyBrainConfig(executionClient, consensusClient, isMevBoostSet, shareDataWithDappnode); case Network.Mainnet: - return mainnetBrainConfig(executionClientSelected, consensusClientSelected, isMevBoostSet, shareDataWithDappnode); + return mainnetBrainConfig(executionClient, consensusClient, isMevBoostSet, shareDataWithDappnode); case Network.Gnosis: - return gnosisBrainConfig(executionClientSelected, consensusClientSelected, isMevBoostSet, shareDataWithDappnode); + return gnosisBrainConfig(executionClient, consensusClient, isMevBoostSet, shareDataWithDappnode); case Network.Lukso: - return luksoBrainConfig(executionClientSelected, consensusClientSelected, isMevBoostSet, shareDataWithDappnode); + return luksoBrainConfig(executionClient, consensusClient, isMevBoostSet, shareDataWithDappnode); case Network.Prater: - return praterBrainConfig(executionClientSelected, consensusClientSelected, isMevBoostSet, shareDataWithDappnode); + return praterBrainConfig(executionClient, consensusClient, isMevBoostSet, shareDataWithDappnode); default: throw Error(`Network ${network} is not supported`); } diff --git a/packages/brain/src/modules/config/loadEnvs.ts b/packages/brain/src/modules/config/loadEnvs.ts index d041cbb8..12d0be86 100644 --- a/packages/brain/src/modules/config/loadEnvs.ts +++ b/packages/brain/src/modules/config/loadEnvs.ts @@ -1,31 +1,61 @@ -import { Network } from "@stakingbrain/common"; +import { ConsensusClient, ExecutionClient, Network } from "@stakingbrain/common"; export function loadEnvs(): { network: Network; - executionClientSelected: string; - consensusClientSelected: string; + executionClient: ExecutionClient; + consensusClient: ConsensusClient; isMevBoostSet: boolean; shareDataWithDappnode: boolean; } { - const network = process.env.NETWORK; - if (!network) throw Error("NETWORK env is required"); - if (!Object.values(Network).includes(network as Network)) - throw Error(`NETWORK env must be one of ${Object.values(Network).join(", ")}`); - - const executionClientSelected = process.env[`_DAPPNODE_GLOBAL_EXECUTION_CLIENT_${network.toUpperCase()}`]; - if (!executionClientSelected) - throw Error(`_DAPPNODE_GLOBAL_EXECUTION_CLIENT_${network.toUpperCase()} env is required`); - const consensusClientSelected = process.env[`_DAPPNODE_GLOBAL_CONSENSUS_CLIENT_${network.toUpperCase()}`]; - if (!consensusClientSelected) - throw Error(`_DAPPNODE_GLOBAL_CONSENSUS_CLIENT_${network.toUpperCase()} env is required`); + const network = getNetwork(); + + const executionClient = getExecutionClient(network); + const consensusClient = getConsensusClient(network); + const isMevBoostSet = process.env[`_DAPPNODE_GLOBAL_MEVBOOST_${network.toUpperCase()}`] === "true"; const shareDataWithDappnode = process.env.SHARE_DATA_WITH_DAPPNODE === "true"; return { network: network as Network, - executionClientSelected, - consensusClientSelected, + executionClient, + consensusClient, isMevBoostSet, shareDataWithDappnode }; } + +function getNetwork(): Network { + const network = process.env.NETWORK; + if (!network) throw Error("NETWORK env is required"); + + if (network === Network.Mainnet) return Network.Mainnet; + if (network === Network.Prater) return Network.Prater; + if (network === Network.Gnosis) return Network.Gnosis; + if (network === Network.Lukso) return Network.Lukso; + if (network === Network.Holesky) return Network.Holesky; + + throw Error(`NETWORK env must be one of ${Object.values(Network).join(", ")}`); +} + +function getExecutionClient(network: Network): ExecutionClient { + const executionClientStr = process.env[`_DAPPNODE_GLOBAL_EXECUTION_CLIENT_${network.toUpperCase()}`]; + if (!executionClientStr) throw Error(`_DAPPNODE_GLOBAL_EXECUTION_CLIENT_${network.toUpperCase()} env is required`); + + if (executionClientStr.includes(ExecutionClient.Geth)) return ExecutionClient.Geth; + if (executionClientStr.includes(ExecutionClient.Besu)) return ExecutionClient.Besu; + if (executionClientStr.includes(ExecutionClient.Nethermind)) return ExecutionClient.Nethermind; + if (executionClientStr.includes(ExecutionClient.Erigon)) return ExecutionClient.Erigon; + return ExecutionClient.Unknown; +} + +function getConsensusClient(network: Network): ConsensusClient { + const consensusClientStr = process.env[`_DAPPNODE_GLOBAL_CONSENSUS_CLIENT_${network.toUpperCase()}`]; + if (!consensusClientStr) throw Error(`_DAPPNODE_GLOBAL_CONSENSUS_CLIENT_${network.toUpperCase()} env is required`); + + if (consensusClientStr.includes(ConsensusClient.Teku)) return ConsensusClient.Teku; + if (consensusClientStr.includes(ConsensusClient.Prysm)) return ConsensusClient.Prysm; + if (consensusClientStr.includes(ConsensusClient.Lighthouse)) return ConsensusClient.Lighthouse; + if (consensusClientStr.includes(ConsensusClient.Nimbus)) return ConsensusClient.Nimbus; + if (consensusClientStr.includes(ConsensusClient.Lodestar)) return ConsensusClient.Lodestar; + return ConsensusClient.Unknown; +} diff --git a/packages/brain/src/modules/config/networks/gnosis.ts b/packages/brain/src/modules/config/networks/gnosis.ts index 00b8ab01..bc69db4e 100644 --- a/packages/brain/src/modules/config/networks/gnosis.ts +++ b/packages/brain/src/modules/config/networks/gnosis.ts @@ -1,25 +1,25 @@ -import { Network } from "@stakingbrain/common"; +import { ConsensusClient, ExecutionClient, Network } from "@stakingbrain/common"; import { BrainConfig } from "../types.js"; import { tlsCert } from "./tlsCert.js"; import { validatorToken } from "./validatorToken.js"; export const gnosisBrainConfig = ( - executionClientSelected: string, - consensusClientSelected: string, + executionClient: ExecutionClient, + consensusClient: ConsensusClient, isMevBoostSet: boolean, shareDataWithDappnode: boolean ): BrainConfig => { return { network: Network.Gnosis, - executionClientSelected, - consensusClientSelected, + executionClient, + consensusClient, isMevBoostSet, executionClientUrl: "http://execution.gnosis.dncore.dappnode:8545", validatorUrl: "http://validator.gnosis.dncore.dappnode:3500", beaconchainUrl: "http:/beacon-chain.gnosis.dncore.dappnode:3500", blockExplorerUrl: "https://gnosischa.in", signerUrl: "http://web3signer.web3signer-gnosis.dappnode:9000", - token: validatorToken(consensusClientSelected), + token: validatorToken(consensusClient), host: "brain.web3signer-gnosis.dappnode", shareDataWithDappnode, validatorsMonitorUrl: "https://validators-proofs.dappnode.io", @@ -28,6 +28,6 @@ export const gnosisBrainConfig = ( postgresUrl: "postgres://postgres:gnosis@postgres.web3signer-gnosis.dappnode:5432/web3signer-gnosis", secondsPerSlot: 5, slotsPerEpoch: 16, - tlsCert: tlsCert(consensusClientSelected) + tlsCert: tlsCert(consensusClient) }; }; diff --git a/packages/brain/src/modules/config/networks/holesky.ts b/packages/brain/src/modules/config/networks/holesky.ts index 29477923..15eb5a38 100644 --- a/packages/brain/src/modules/config/networks/holesky.ts +++ b/packages/brain/src/modules/config/networks/holesky.ts @@ -1,25 +1,25 @@ -import { Network } from "@stakingbrain/common"; +import { ConsensusClient, ExecutionClient, Network } from "@stakingbrain/common"; import { BrainConfig } from "../types.js"; import { tlsCert } from "./tlsCert.js"; import { validatorToken } from "./validatorToken.js"; export const holeskyBrainConfig = ( - executionClientSelected: string, - consensusClientSelected: string, + executionClient: ExecutionClient, + consensusClient: ConsensusClient, isMevBoostSet: boolean, shareDataWithDappnode: boolean ): BrainConfig => { return { network: Network.Holesky, - executionClientSelected, - consensusClientSelected, + executionClient, + consensusClient, isMevBoostSet, executionClientUrl: "http://execution.holesky.dncore.dappnode:8545", validatorUrl: "http://validator.holesky.dncore.dappnode:3500", beaconchainUrl: "http:/beacon-chain.holesky.dncore.dappnode:3500", blockExplorerUrl: "https://holesky.beaconcha.in", signerUrl: "http://web3signer.web3signer-holesky.dappnode:9000", - token: validatorToken(consensusClientSelected), + token: validatorToken(consensusClient), host: "brain.web3signer-holesky.dappnode", shareDataWithDappnode, validatorsMonitorUrl: "https://validators-proofs.dappnode.io", @@ -28,6 +28,6 @@ export const holeskyBrainConfig = ( postgresUrl: "postgres://postgres:password@postgres.web3signer-holesky.dappnode:5432/web3signer", secondsPerSlot: 12, slotsPerEpoch: 32, - tlsCert: tlsCert(consensusClientSelected) + tlsCert: tlsCert(consensusClient) }; }; diff --git a/packages/brain/src/modules/config/networks/lukso.ts b/packages/brain/src/modules/config/networks/lukso.ts index 091682e8..388e95d1 100644 --- a/packages/brain/src/modules/config/networks/lukso.ts +++ b/packages/brain/src/modules/config/networks/lukso.ts @@ -1,25 +1,25 @@ -import { Network } from "@stakingbrain/common"; +import { ConsensusClient, ExecutionClient, Network } from "@stakingbrain/common"; import { BrainConfig } from "../types.js"; import { tlsCert } from "./tlsCert.js"; import { validatorToken } from "./validatorToken.js"; export const luksoBrainConfig = ( - executionClientSelected: string, - consensusClientSelected: string, + executionClient: ExecutionClient, + consensusClient: ConsensusClient, isMevBoostSet: boolean, shareDataWithDappnode: boolean ): BrainConfig => { return { network: Network.Lukso, - executionClientSelected, - consensusClientSelected, + executionClient, + consensusClient, isMevBoostSet, executionClientUrl: "http://execution.lukso.dncore.dappnode:8545", validatorUrl: "http://validator.lukso.dncore.dappnode:3500", beaconchainUrl: "http:/beacon-chain.lukso.dncore.dappnode:3500", blockExplorerUrl: "https://explorer.consensus.mainnet.lukso.network/", signerUrl: "http://web3signer.web3signer-lukso.dappnode:9000", - token: validatorToken(consensusClientSelected), + token: validatorToken(consensusClient), host: "brain.web3signer-lukso.dappnode", shareDataWithDappnode, validatorsMonitorUrl: "https://validators-proofs.dappnode.io", @@ -28,6 +28,6 @@ export const luksoBrainConfig = ( postgresUrl: "postgres://postgres:password@postgres.web3signer-lukso.dappnode:5432/web3signer", secondsPerSlot: 12, slotsPerEpoch: 32, - tlsCert: tlsCert(consensusClientSelected) + tlsCert: tlsCert(consensusClient) }; }; diff --git a/packages/brain/src/modules/config/networks/mainnet.ts b/packages/brain/src/modules/config/networks/mainnet.ts index dcda4b91..d8e4776a 100644 --- a/packages/brain/src/modules/config/networks/mainnet.ts +++ b/packages/brain/src/modules/config/networks/mainnet.ts @@ -1,25 +1,25 @@ -import { Network } from "@stakingbrain/common"; +import { ConsensusClient, ExecutionClient, Network } from "@stakingbrain/common"; import { BrainConfig } from "../types.js"; import { tlsCert } from "./tlsCert.js"; import { validatorToken } from "./validatorToken.js"; export const mainnetBrainConfig = ( - executionClientSelected: string, - consensusClientSelected: string, + executionClient: ExecutionClient, + consensusClient: ConsensusClient, isMevBoostSet: boolean, shareDataWithDappnode: boolean ): BrainConfig => { return { network: Network.Mainnet, - executionClientSelected, - consensusClientSelected, + executionClient, + consensusClient, isMevBoostSet, executionClientUrl: "http://execution.mainnet.dncore.dappnode:8545", validatorUrl: "http://validator.mainnet.dncore.dappnode:3500", beaconchainUrl: "http:/beacon-chain.mainnet.dncore.dappnode:3500", blockExplorerUrl: "https://beaconcha.in", signerUrl: "http://web3signer.web3signer.dappnode:9000", - token: validatorToken(consensusClientSelected), + token: validatorToken(consensusClient), host: "brain.web3signer.dappnode", shareDataWithDappnode, validatorsMonitorUrl: "https://validators-proofs.dappnode.io", @@ -28,6 +28,6 @@ export const mainnetBrainConfig = ( postgresUrl: "postgres://postgres:mainnet@postgres.web3signer.dappnode:5432/web3signer-mainnet", secondsPerSlot: 12, slotsPerEpoch: 32, - tlsCert: tlsCert(consensusClientSelected) + tlsCert: tlsCert(consensusClient) }; }; diff --git a/packages/brain/src/modules/config/networks/prater.ts b/packages/brain/src/modules/config/networks/prater.ts index 4a325889..213bd088 100644 --- a/packages/brain/src/modules/config/networks/prater.ts +++ b/packages/brain/src/modules/config/networks/prater.ts @@ -1,25 +1,25 @@ -import { Network } from "@stakingbrain/common"; +import { ConsensusClient, ExecutionClient, Network } from "@stakingbrain/common"; import { BrainConfig } from "../types.js"; import { tlsCert } from "./tlsCert.js"; import { validatorToken } from "./validatorToken.js"; export const praterBrainConfig = ( - executionClientSelected: string, - consensusClientSelected: string, + executionClient: ExecutionClient, + consensusClient: ConsensusClient, isMevBoostSet: boolean, shareDataWithDappnode: boolean ): BrainConfig => { return { network: Network.Prater, - executionClientSelected, - consensusClientSelected, + executionClient, + consensusClient, isMevBoostSet, executionClientUrl: "http://execution.prater.dncore.dappnode:8545", validatorUrl: "http://validator.prater.dncore.dappnode:3500", beaconchainUrl: "http:/beacon-chain.prater.dncore.dappnode:3500", blockExplorerUrl: "https://prater.beaconcha.in", signerUrl: "http://web3signer.web3signer-prater.dappnode:9000", - token: validatorToken(consensusClientSelected), + token: validatorToken(consensusClient), host: "brain.web3signer-prater.dappnode", shareDataWithDappnode, validatorsMonitorUrl: "https://validators-proofs.dappnode.io", @@ -28,6 +28,6 @@ export const praterBrainConfig = ( postgresUrl: "postgres://postgres:password@postgres.web3signer-prater.dappnode:5432/web3signer", secondsPerSlot: 12, slotsPerEpoch: 32, - tlsCert: tlsCert(consensusClientSelected) + tlsCert: tlsCert(consensusClient) }; }; diff --git a/packages/brain/src/modules/config/networks/tlsCert.ts b/packages/brain/src/modules/config/networks/tlsCert.ts index f5223bed..0a0b099b 100644 --- a/packages/brain/src/modules/config/networks/tlsCert.ts +++ b/packages/brain/src/modules/config/networks/tlsCert.ts @@ -1,7 +1,8 @@ +import { ConsensusClient } from "@stakingbrain/common"; import fs from "fs"; import path from "path"; -export const tlsCert = (consensusClientSelected: string): Buffer | null => { - if (!consensusClientSelected.includes("teku")) return null; +export const tlsCert = (consensusClient: ConsensusClient): Buffer | null => { + if (consensusClient !== ConsensusClient.Teku) return null; return fs.readFileSync(path.join("tls", "mainnet", "teku_client_keystore.p12")); }; diff --git a/packages/brain/src/modules/config/networks/validatorToken.ts b/packages/brain/src/modules/config/networks/validatorToken.ts index 2a12f08c..4679c3bd 100644 --- a/packages/brain/src/modules/config/networks/validatorToken.ts +++ b/packages/brain/src/modules/config/networks/validatorToken.ts @@ -1,12 +1,14 @@ -export const validatorToken = (consensusClientSelected: string): string => { - if (consensusClientSelected.includes("teku")) return "cd4892ca35d2f5d3e2301a65fc7aa660"; - if (consensusClientSelected.includes("lighthouse")) +import { ConsensusClient } from "@stakingbrain/common"; + +export const validatorToken = (consensusClient: ConsensusClient): string => { + if (consensusClient === ConsensusClient.Teku) return "cd4892ca35d2f5d3e2301a65fc7aa660"; + if (consensusClient === ConsensusClient.Lighthouse) return "api-token-0x0200e6ce18e26fd38caca7ae1bfb9e2bba7efb20ed2746ad17f2f6dda44603152d"; - if (consensusClientSelected.includes("prysm")) + if (consensusClient === ConsensusClient.Prysm) return "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.MxwOozSH-TLbW_XKepjyYDHm2IT8Ki0tD3AHuajfNMg"; - if (consensusClientSelected.includes("nimbus")) + if (consensusClient === ConsensusClient.Nimbus) return "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.MxwOozSH-TLbW_XKepjyYDHm2IT8Ki0tD3AHuajfNMg"; - if (consensusClientSelected.includes("lodestar")) + if (consensusClient === ConsensusClient.Lodestar) return "api-token-0x7fd16fff6453982a5d8bf14617e7823b68cd18ade59985befe64e0a659300e7d"; - throw Error(`Unknown consensus client selected: ${consensusClientSelected}`); + throw Error(`Unknown consensus client selected: ${consensusClient}`); }; diff --git a/packages/brain/src/modules/config/types.ts b/packages/brain/src/modules/config/types.ts index 71dc28a9..c6ddd1e8 100644 --- a/packages/brain/src/modules/config/types.ts +++ b/packages/brain/src/modules/config/types.ts @@ -1,9 +1,9 @@ -import { Network } from "@stakingbrain/common"; +import { ConsensusClient, ExecutionClient, Network } from "@stakingbrain/common"; export interface BrainConfig { network: Network; - executionClientSelected: string; - consensusClientSelected: string; + executionClient: ExecutionClient; + consensusClient: ConsensusClient; isMevBoostSet: boolean; executionClientUrl: string; validatorUrl: string; diff --git a/packages/brain/src/modules/cron/trackValidatorsPerformance/getActiveValidatorsLoadedInBrain.ts b/packages/brain/src/modules/cron/trackValidatorsPerformance/getActiveValidatorsLoadedInBrain.ts index ac056c40..61120a58 100644 --- a/packages/brain/src/modules/cron/trackValidatorsPerformance/getActiveValidatorsLoadedInBrain.ts +++ b/packages/brain/src/modules/cron/trackValidatorsPerformance/getActiveValidatorsLoadedInBrain.ts @@ -16,14 +16,16 @@ import { logPrefix } from "./logPrefix.js"; */ export async function getActiveValidatorsLoadedInBrain({ beaconchainApi, - brainDb + brainDb, + activeValidatorsIndexes }: { beaconchainApi: BeaconchainApi; brainDb: BrainDataBase; -}): Promise { + activeValidatorsIndexes: string[]; +}): Promise { const validatorIndexes = await getValidatorIndexesAndSaveInDb({ beaconchainApi, brainDb }); - if (validatorIndexes.length === 0) return []; - return ( + if (validatorIndexes.length === 0) return; + ( await beaconchainApi.postStateValidators({ body: { ids: validatorIndexes, @@ -31,7 +33,7 @@ export async function getActiveValidatorsLoadedInBrain({ }, stateId: "finalized" }) - ).data.map((validator) => validator.index); + ).data.forEach((validator) => activeValidatorsIndexes.push(validator.index)); } /** diff --git a/packages/brain/src/modules/cron/trackValidatorsPerformance/getAttestationsTotalRewards.ts b/packages/brain/src/modules/cron/trackValidatorsPerformance/getAttestationsTotalRewards.ts index 2e7c1dc0..67299aa0 100644 --- a/packages/brain/src/modules/cron/trackValidatorsPerformance/getAttestationsTotalRewards.ts +++ b/packages/brain/src/modules/cron/trackValidatorsPerformance/getAttestationsTotalRewards.ts @@ -12,16 +12,18 @@ import { TotalRewards } from "../../apiClients/types.js"; export async function getAttestationsTotalRewards({ beaconchainApi, epoch, - validatorIndexes + validatorIndexes, + totalRewards }: { beaconchainApi: BeaconchainApi; epoch: string; validatorIndexes: string[]; -}): Promise { - return ( + totalRewards: TotalRewards[]; +}): Promise { + ( await beaconchainApi.getAttestationsRewards({ epoch, pubkeysOrIndexes: validatorIndexes }) - ).data.total_rewards; + ).data.total_rewards.forEach((reward) => totalRewards.push(reward)); } diff --git a/packages/brain/src/modules/cron/trackValidatorsPerformance/index.ts b/packages/brain/src/modules/cron/trackValidatorsPerformance/index.ts index 8c5435e8..e75ff06f 100644 --- a/packages/brain/src/modules/cron/trackValidatorsPerformance/index.ts +++ b/packages/brain/src/modules/cron/trackValidatorsPerformance/index.ts @@ -2,12 +2,15 @@ import { BeaconchainApi } from "../../apiClients/beaconchain/index.js"; import { PostgresClient } from "../../apiClients/postgres/index.js"; import logger from "../../logger/index.js"; import { BrainDataBase } from "../../db/index.js"; -import { insertPerformanceData } from "./insertPerformanceData.js"; +import { insertPerformanceDataNotThrow } from "./insertPerformanceData.js"; import { getAttestationsTotalRewards } from "./getAttestationsTotalRewards.js"; -import { getBlockProposalStatusMap } from "./getBlockProposalStatusMap.js"; +import { setBlockProposalStatusMap } from "./setBlockProposalStatusMap.js"; import { checkNodeHealth } from "./checkNodeHealth.js"; import { getActiveValidatorsLoadedInBrain } from "./getActiveValidatorsLoadedInBrain.js"; import { logPrefix } from "./logPrefix.js"; +import { TotalRewards } from "../../apiClients/types.js"; +import { BlockProposalStatus } from "../../apiClients/postgres/types.js"; +import { ConsensusClient, ExecutionClient } from "@stakingbrain/common"; const MINUTE_IN_SECONDS = 60; @@ -32,24 +35,32 @@ export async function trackValidatorsPerformance({ postgresClient, beaconchainApi, minGenesisTime, - secondsPerSlot + secondsPerSlot, + executionClient, + consensusClient }: { brainDb: BrainDataBase; postgresClient: PostgresClient; beaconchainApi: BeaconchainApi; minGenesisTime: number; secondsPerSlot: number; + executionClient: ExecutionClient; + consensusClient: ConsensusClient; }): Promise { try { const epochFinalized = await beaconchainApi.getEpochHeader({ blockId: "finalized" }); + let errorGettingValidatorData: Error | undefined; let newEpochFinalized = epochFinalized; + const activeValidatorsIndexes: string[] = []; + const validatorsAttestationsRewards: TotalRewards[] = []; + const validatorBlockStatusMap: Map = new Map(); - while (epochFinalized === newEpochFinalized) { + label: while (epochFinalized === newEpochFinalized) { try { logger.debug(`${logPrefix}Epoch finalized: ${epochFinalized}`); // active validators indexes - const activeValidatorsIndexes = await getActiveValidatorsLoadedInBrain({ beaconchainApi, brainDb }); + await getActiveValidatorsLoadedInBrain({ beaconchainApi, brainDb, activeValidatorsIndexes }); if (activeValidatorsIndexes.length === 0) { logger.info(`${logPrefix}No active validators found`); return; @@ -60,42 +71,39 @@ export async function trackValidatorsPerformance({ await checkNodeHealth({ beaconchainApi }); // get block attestations rewards - const validatorsAttestationsRewards = await getAttestationsTotalRewards({ + await getAttestationsTotalRewards({ beaconchainApi, epoch: epochFinalized.toString(), - validatorIndexes: activeValidatorsIndexes + validatorIndexes: activeValidatorsIndexes, + totalRewards: validatorsAttestationsRewards }); logger.debug(`${logPrefix}Attestations rewards: ${JSON.stringify(validatorsAttestationsRewards)}`); // get block proposal status - const validatorBlockStatus = await getBlockProposalStatusMap({ + await setBlockProposalStatusMap({ beaconchainApi, epoch: epochFinalized.toString(), - validatorIndexes: activeValidatorsIndexes - }); - logger.debug(`${logPrefix}Block proposal status map: ${JSON.stringify([...validatorBlockStatus])}`); - - // insert performance data - await insertPerformanceData({ - postgresClient, validatorIndexes: activeValidatorsIndexes, - epochFinalized, - validatorBlockStatus, - validatorsAttestationsRewards + validatorBlockStatusMap }); + logger.debug(`${logPrefix}Block proposal status map: ${JSON.stringify([...validatorBlockStatusMap])}`); - logger.debug(`${logPrefix}Performance data inserted for epoch ${epochFinalized}`); - return; + // update error to undefined if no error occurred in last iteration + errorGettingValidatorData = undefined; } catch (error) { logger.error(`${logPrefix}Error occurred: ${error}. Updating epoch finalized and retrying in 1 minute`); + // update error if an error occurred + errorGettingValidatorData = error; // skip if the seconds to the next epoch is less than 1 minute const secondsToNextEpoch = getSecondsToNextEpoch({ minGenesisTime, secondsPerSlot }); if (secondsToNextEpoch < MINUTE_IN_SECONDS) { logger.warn( - `${logPrefix}Seconds to the next epoch is less than 1 minute (${secondsToNextEpoch}). Skipping until next epoch` + `${logPrefix}Could not get validator data for epoch ${epochFinalized}. Writing error and skipping to next epoch.` ); - return; + // TODO: collect report of the staker setup status: el is offline, node is syncing, signer is not up and original error + // exit the while loop and write the error to the DB + break label; } // wait 1 minute without blocking the event loop and update epoch finalized newEpochFinalized = await new Promise((resolve) => @@ -106,10 +114,20 @@ export async function trackValidatorsPerformance({ ); } } + logger.debug(`${logPrefix}Epoch finalized changed: ${newEpochFinalized}`); - logger.debug( - `${logPrefix}Epoch finalized changed: ${newEpochFinalized}, finished tracking performance for epoch ${epochFinalized}` - ); + // insert performance data or each validator + await insertPerformanceDataNotThrow({ + postgresClient, + validatorIndexes: activeValidatorsIndexes, + epochFinalized, + validatorBlockStatus: validatorBlockStatusMap, + validatorsAttestationsRewards, + error: errorGettingValidatorData, + executionClient, + consensusClient + }); + logger.debug(`${logPrefix}Performance data inserted for epoch ${epochFinalized}`); } catch (e) { logger.error(`${logPrefix}Error in trackValidatorsPerformance: ${e}`); return; diff --git a/packages/brain/src/modules/cron/trackValidatorsPerformance/insertPerformanceData.ts b/packages/brain/src/modules/cron/trackValidatorsPerformance/insertPerformanceData.ts index 68480bdb..e31268da 100644 --- a/packages/brain/src/modules/cron/trackValidatorsPerformance/insertPerformanceData.ts +++ b/packages/brain/src/modules/cron/trackValidatorsPerformance/insertPerformanceData.ts @@ -1,3 +1,4 @@ +import { ConsensusClient, ExecutionClient } from "@stakingbrain/common"; import { PostgresClient } from "../../apiClients/index.js"; import { BlockProposalStatus } from "../../apiClients/postgres/types.js"; import { TotalRewards } from "../../apiClients/types.js"; @@ -15,18 +16,24 @@ import { logPrefix } from "./logPrefix.js"; * @param validatorBlockStatus - Map with the block proposal status of each validator. * @param validatorsAttestationsRewards - Array of total rewards for the validators. */ -export async function insertPerformanceData({ +export async function insertPerformanceDataNotThrow({ postgresClient, validatorIndexes, epochFinalized, validatorBlockStatus, - validatorsAttestationsRewards + validatorsAttestationsRewards, + executionClient, + consensusClient, + error }: { postgresClient: PostgresClient; validatorIndexes: string[]; epochFinalized: number; validatorBlockStatus: Map; validatorsAttestationsRewards: TotalRewards[]; + executionClient: ExecutionClient; + consensusClient: ConsensusClient; + error?: Error; }): Promise { for (const validatorIndex of validatorIndexes) { //const liveness = validatorsLiveness.find((liveness) => liveness.index === validatorIndex)?.is_live; @@ -53,8 +60,11 @@ export async function insertPerformanceData({ await postgresClient.insertPerformanceData({ validatorIndex: parseInt(validatorIndex), epoch: epochFinalized, - blockProposalStatus: blockProposalStatus, - attestationsRewards + blockProposalStatus, + attestationsRewards, + error: error?.message, + executionClient, + consensusClient }); } catch (e) { logger.error(`${logPrefix}Error inserting performance data for validator ${validatorIndex}: ${e}`); diff --git a/packages/brain/src/modules/cron/trackValidatorsPerformance/getBlockProposalStatusMap.ts b/packages/brain/src/modules/cron/trackValidatorsPerformance/setBlockProposalStatusMap.ts similarity index 80% rename from packages/brain/src/modules/cron/trackValidatorsPerformance/getBlockProposalStatusMap.ts rename to packages/brain/src/modules/cron/trackValidatorsPerformance/setBlockProposalStatusMap.ts index ad3c8ae7..d06aebb3 100644 --- a/packages/brain/src/modules/cron/trackValidatorsPerformance/getBlockProposalStatusMap.ts +++ b/packages/brain/src/modules/cron/trackValidatorsPerformance/setBlockProposalStatusMap.ts @@ -10,15 +10,17 @@ import { logPrefix } from "./logPrefix.js"; * @param {string} epoch - The epoch to get the block proposal duties. * @param {string[]} validatorIndexes - Array of validator indexes. */ -export async function getBlockProposalStatusMap({ +export async function setBlockProposalStatusMap({ beaconchainApi, epoch, - validatorIndexes + validatorIndexes, + validatorBlockStatusMap }: { beaconchainApi: BeaconchainApi; epoch: string; validatorIndexes: string[]; -}): Promise> { + validatorBlockStatusMap: Map; +}): Promise { // Get the block proposal duties for the given epoch. Which validators // are supposed to propose a block in which slot? const blockProposalsResponse = await beaconchainApi.getProposerDuties({ @@ -28,12 +30,9 @@ export async function getBlockProposalStatusMap({ // Utilize a Set for quick lookup. We assume that the validator indexes are unique. const validatorIndexesSet = new Set(validatorIndexes); - // Map to store the block proposal status of each validator - const validatorBlockStatus = new Map(); - // Initialize all validator's status to Unchosen. validatorIndexesSet.forEach((validatorIndex) => { - validatorBlockStatus.set(validatorIndex, BlockProposalStatus.Unchosen); + validatorBlockStatusMap.set(validatorIndex, BlockProposalStatus.Unchosen); }); // For each slot in the epoch, determine if the validator supposed to propose @@ -47,7 +46,7 @@ export async function getBlockProposalStatusMap({ // Get the block header for the slot. It has the proposer index. const blockHeader = await beaconchainApi.getBlockHeader({ blockId: slot }); // If the proposer index in the block header matches the validator index, the block was proposed correctly. - validatorBlockStatus.set( + validatorBlockStatusMap.set( validator_index, blockHeader.data.header.message.proposer_index === validator_index ? BlockProposalStatus.Proposed @@ -56,15 +55,13 @@ export async function getBlockProposalStatusMap({ } catch (error) { if (error.status === 404) { // If the block header is not found, the validator missed the block proposal - validatorBlockStatus.set(validator_index, BlockProposalStatus.Missed); + validatorBlockStatusMap.set(validator_index, BlockProposalStatus.Missed); } else { // If consensus client doesnt return 200 or 404, something went wrong logger.error(`${logPrefix}Error retrieving block header for slot ${slot}: ${error}`); - validatorBlockStatus.set(validator_index, BlockProposalStatus.Error); + validatorBlockStatusMap.set(validator_index, BlockProposalStatus.Error); } } } } - - return validatorBlockStatus; } diff --git a/packages/brain/src/modules/validatorsPerformance/index.ts b/packages/brain/src/modules/validatorsPerformance/index.ts new file mode 100644 index 00000000..7fd016a1 --- /dev/null +++ b/packages/brain/src/modules/validatorsPerformance/index.ts @@ -0,0 +1,63 @@ +import { PostgresClient } from "../apiClients/index.js"; + +// Module in charge of queriyng the validators attestation rewards, block proposals and sync committee rewards and +// processing the data to be displayed in the validators performance page. + +// FRONTEND + +// Will display the following data: +// - Attestation success rate (not chart until granularity) +// - Blocks proposed success rate (not chart until granularity) +// - Sync committee success rate (not chart until granularity) +// - Balance -> No chart +// - Means: mean attestation success rate, mean blocks proposed success rate, mean balance -> No chart + +// BACKEND + +// The frontend will call backend with arguments: +// - startDate and endDate -> backend will translate these dates to epochs. +// The backend will calculate ValidatorsPerformanceProcessed for the given dates +// If no arguments passeed to backend then the backend will use last 7 days epoch and latest epoch +// - Clients (execution and consensus) -> optional +// - Attestation/block success rate granularity (future): admit granularity of att success rate: by epoch, by day, by week, by month -> THIS enables chart visualization + +// Return also current balance for each validator + +// 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. + +// (%) = (Number of Successful Attestations + Number of Successful Proposals) / (Total Attestation Opportunities + Total Proposal Opportunities) * 100 +// Total Attestation Opportunities: is the number of epochs between the first and last epoch in the data set of a specific validator. +// Total Proposal Opportunities: + +// TODO: blocksProposedByEpochAndSlot + +export interface ValidatorsPerformanceProcessed { + mapValidatorPerformance: Map< + string, + { + attestationSuccessRate: number; + blocksProposedSuccessRate: number; + balance: number; + syncCommitteeSuccessRate?: number; + } + >; + meanAttestationSuccessRate: number; + meanBlocksProposedSuccessRate: number; + meanBalance: number; +} + +/** + * + */ +export async function processValidatorsData({ + validatorIndexes, + postgresClient +}: { + validatorIndexes: string[]; + postgresClient: PostgresClient; +}) { + console.log("Processing validators data"); + console.log("Validator indexes: ", validatorIndexes); + console.log("Postgres client: ", postgresClient); +} diff --git a/packages/brain/test/unit/modules/apiClients/postgresClient.unit.test.ts b/packages/brain/test/unit/modules/apiClients/postgresClient.unit.test.ts index 38c6033e..aa57bbd2 100644 --- a/packages/brain/test/unit/modules/apiClients/postgresClient.unit.test.ts +++ b/packages/brain/test/unit/modules/apiClients/postgresClient.unit.test.ts @@ -17,7 +17,14 @@ describe.skip("Postgres client", function () { console.log("Table size: ", tableSize); }); + it("should get validators data from the db", async () => { + const validatorIndexes = ["1802289", "1802258"]; + const data = await postgresClient.getValidatorsDataFromAllEpochs(validatorIndexes); + console.log("Validators data: ", data); + }); + it("should delete the table", async () => { + await postgresClient.deleteEnumTypes(); await postgresClient.deleteDatabaseTable(); }); }); diff --git a/packages/common/src/types/index.ts b/packages/common/src/types/index.ts index b8171cfe..5c665f57 100644 --- a/packages/common/src/types/index.ts +++ b/packages/common/src/types/index.ts @@ -6,10 +6,27 @@ export enum Network { Holesky = "holesky" } +export enum ExecutionClient { + Besu = "besu", + Nethermind = "nethermind", + Geth = "geth", + Erigon = "erigon", + Unknown = "unknown" +} + +export enum ConsensusClient { + Teku = "teku", + Prysm = "prysm", + Lighthouse = "lighthouse", + Nimbus = "nimbus", + Lodestar = "lodestar", + Unknown = "unknown" +} + export interface StakerConfig { network: Network; - executionClientSelected: string; - consensusClientSelected: string; + executionClient: ExecutionClient; + consensusClient: ConsensusClient; isMevBoostSet: boolean; executionClientUrl: string; validatorUrl: string; diff --git a/packages/ui/public/assets/besu-goerli.png b/packages/ui/public/assets/besu-goerli.png deleted file mode 100644 index 067291de..00000000 Binary files a/packages/ui/public/assets/besu-goerli.png and /dev/null differ diff --git a/packages/ui/public/assets/erigon-goerli.png b/packages/ui/public/assets/erigon-goerli.png deleted file mode 100644 index 83b2d73f..00000000 Binary files a/packages/ui/public/assets/erigon-goerli.png and /dev/null differ diff --git a/packages/ui/public/assets/geth-goerli.png b/packages/ui/public/assets/geth-goerli.png deleted file mode 100644 index 02df135d..00000000 Binary files a/packages/ui/public/assets/geth-goerli.png and /dev/null differ diff --git a/packages/ui/public/assets/gnosis-erigon.png b/packages/ui/public/assets/gnosis-erigon.png deleted file mode 100644 index c84a72a0..00000000 Binary files a/packages/ui/public/assets/gnosis-erigon.png and /dev/null differ diff --git a/packages/ui/public/assets/lighthouse-gnosis.png b/packages/ui/public/assets/lighthouse-gnosis.png deleted file mode 100644 index 598e0e6c..00000000 Binary files a/packages/ui/public/assets/lighthouse-gnosis.png and /dev/null differ diff --git a/packages/ui/public/assets/lighthouse-prater.png b/packages/ui/public/assets/lighthouse-prater.png deleted file mode 100644 index dd1221b6..00000000 Binary files a/packages/ui/public/assets/lighthouse-prater.png and /dev/null differ diff --git a/packages/ui/public/assets/lodestar-gnosis.png b/packages/ui/public/assets/lodestar-gnosis.png deleted file mode 100644 index ff26fd42..00000000 Binary files a/packages/ui/public/assets/lodestar-gnosis.png and /dev/null differ diff --git a/packages/ui/public/assets/lodestar-prater.png b/packages/ui/public/assets/lodestar-prater.png deleted file mode 100644 index dd124695..00000000 Binary files a/packages/ui/public/assets/lodestar-prater.png and /dev/null differ diff --git a/packages/ui/public/assets/nethermind-gnosis.png b/packages/ui/public/assets/nethermind-gnosis.png deleted file mode 100644 index 32620ff4..00000000 Binary files a/packages/ui/public/assets/nethermind-gnosis.png and /dev/null differ diff --git a/packages/ui/public/assets/nethermind-goerli.png b/packages/ui/public/assets/nethermind-goerli.png deleted file mode 100644 index 1ccbf792..00000000 Binary files a/packages/ui/public/assets/nethermind-goerli.png and /dev/null differ diff --git a/packages/ui/public/assets/nimbus-prater.png b/packages/ui/public/assets/nimbus-prater.png deleted file mode 100644 index f7f208cf..00000000 Binary files a/packages/ui/public/assets/nimbus-prater.png and /dev/null differ diff --git a/packages/ui/public/assets/prysm-gnosis.png b/packages/ui/public/assets/prysm-gnosis.png deleted file mode 100644 index ac2a01aa..00000000 Binary files a/packages/ui/public/assets/prysm-gnosis.png and /dev/null differ diff --git a/packages/ui/public/assets/prysm-prater.png b/packages/ui/public/assets/prysm-prater.png deleted file mode 100644 index 3da8f3bf..00000000 Binary files a/packages/ui/public/assets/prysm-prater.png and /dev/null differ diff --git a/packages/ui/public/assets/teku-gnosis.png b/packages/ui/public/assets/teku-gnosis.png deleted file mode 100644 index 9825d420..00000000 Binary files a/packages/ui/public/assets/teku-gnosis.png and /dev/null differ diff --git a/packages/ui/public/assets/teku-prater.png b/packages/ui/public/assets/teku-prater.png deleted file mode 100644 index 5c4f1d90..00000000 Binary files a/packages/ui/public/assets/teku-prater.png and /dev/null differ diff --git a/packages/ui/src/components/StakerConfig/StakerConfig.tsx b/packages/ui/src/components/StakerConfig/StakerConfig.tsx index 49128868..749cf00a 100644 --- a/packages/ui/src/components/StakerConfig/StakerConfig.tsx +++ b/packages/ui/src/components/StakerConfig/StakerConfig.tsx @@ -7,59 +7,15 @@ import { prettyClientDnpName } from "../../utils/dataUtils"; export default function StakerConfig({ stakerConfig }: { stakerConfig: StakerConfigType }): JSX.Element { const images: { [key: string]: string } = { - // Mainnet - "erigon.dnp.dappnode.eth": "/assets/erigon.png", - "geth.dnp.dappnode.eth": "/assets/geth.png", - "besu.public.dappnode.eth": "/assets/besu.png", - "nethermind.public.dappnode.eth": "/assets/nethermind.png", - "prysm.dnp.dappnode.eth": "/assets/prysm.png", - "lighthouse.dnp.dappnode.eth": "/assets/lighthouse.png", - "teku.dnp.dappnode.eth": "/assets/teku.png", - "nimbus.dnp.dappnode.eth": "/assets/nimbus.png", - "lodestar.dnp.dappnode.eth": "/assets/lodestar.png", - - // Goerli/Prater - "goerli-erigon.dnp.dappnode.eth": "/assets/erigon-goerli.png", - "goerli-geth.dnp.dappnode.eth": "/assets/geth-goerli.png", - "goerli-besu.dnp.dappnode.eth": "/assets/besu-goerli.png", - "goerli-nethermind.dnp.dappnode.eth": "/assets/nethermind-goerli.png", - "prysm-prater.dnp.dappnode.eth": "/assets/prysm-prater.png", - "lighthouse-prater.dnp.dappnode.eth": "/assets/lighthouse-prater.png", - "teku-prater.dnp.dappnode.eth": "/assets/teku-prater.png", - "nimbus-prater.dnp.dappnode.eth": "/assets/nimbus-prater.png", - "lodestar-prater.dnp.dappnode.eth": "/assets/lodestar-prater.png", - - // Gnosis - "nethermind-xdai.dnp.dappnode.eth": "/assets/nethermind-gnosis.png", - "gnosis-erigon.dnp.dappnode.eth": "/assets/gnosis-erigon.png", - "gnosis-beacon-chain-prysm.dnp.dappnode.eth": "/assets/prysm-gnosis.png", - "lighthouse-gnosis.dnp.dappnode.eth": "/assets/lighthouse-gnosis.png", - "teku-gnosis.dnp.dappnode.eth": "/assets/teku-gnosis.png", - // TODO: Add Nimbus Gnosis logo (now mainnet) - "nimbus-gnosis.dnp.dappnode.eth": "/assets/nimbus.png", - "lodestar-gnosis.dnp.dappnode.eth": "/assets/lodestar-gnosis.png", - - // Lukso --> // TODO: Add Lukso logos (now mainnet) - "lukso-geth.dnp.dappnode.eth": "/assets/geth.png", - "lukso-erigon.dnp.dappnode.eth": "/assets/erigon.png", - "lukso-besu.dnp.dappnode.eth": "/assets/besu.png", - "lukso-nethermind.dnp.dappnode.eth": "/assets/nethermind.png", - "prysm-lukso.dnp.dappnode.eth": "/assets/prysm.png", - "lighthouse-lukso.dnp.dappnode.eth": "/assets/lighthouse.png", - "teku-lukso.dnp.dappnode.eth": "/assets/teku.png", - "nimbus-lukso.dnp.dappnode.eth": "/assets/nimbus.png", - "lodestar-lukso.dnp.dappnode.eth": "/assets/lodestar.png", - - //Holesky --> // TODO: Add Holesky logos (now mainnet) - "holesky-geth.dnp.dappnode.eth": "/assets/geth.png", - "holesky-erigon.dnp.dappnode.eth": "/assets/erigon.png", - "holesky-besu.dnp.dappnode.eth": "/assets/besu.png", - "holesky-nethermind.dnp.dappnode.eth": "/assets/nethermind.png", - "prysm-holesky.dnp.dappnode.eth": "/assets/prysm.png", - "lighthouse-holesky.dnp.dappnode.eth": "/assets/lighthouse.png", - "teku-holesky.dnp.dappnode.eth": "/assets/teku.png", - "nimbus-holesky.dnp.dappnode.eth": "/assets/nimbus.png", - "lodestar-holesky.dnp.dappnode.eth": "/assets/lodestar.png", + erigon: "/assets/erigon.png", + geth: "/assets/geth.png", + besu: "/assets/besu.png", + nethermind: "/assets/nethermind.png", + prysm: "/assets/prysm.png", + lighthouse: "/assets/lighthouse.png", + teku: "/assets/teku.png", + nimbus: "/assets/nimbus.png", + lodestar: "/assets/lodestar.png", // Default logo until we have a package for them default: "/assets/dappnode_logo_clean.png" @@ -114,14 +70,14 @@ export default function StakerConfig({ stakerConfig }: { stakerConfig: StakerCon }} alt="erigon-goerli" src={ - Object.keys(images).includes(stakerConfig.executionClientSelected) - ? images[stakerConfig.executionClientSelected] + Object.keys(images).includes(stakerConfig.executionClient) + ? images[stakerConfig.executionClient] : images["default"] } /> - {prettyClientDnpName(stakerConfig.executionClientSelected)} + {prettyClientDnpName(stakerConfig.executionClient)} - {prettyClientDnpName(stakerConfig.consensusClientSelected)} + {prettyClientDnpName(stakerConfig.consensusClient)}