From 6cae5f7aebb7f554d581484cb3a35a790a07e8cb Mon Sep 17 00:00:00 2001 From: dappnodedev Date: Mon, 6 Nov 2023 12:44:49 +0100 Subject: [PATCH] Add holesky network --- README.md | 2 +- packages/brain/src/modules/envs/index.ts | 98 ++++++++++++++++++ .../fetchValidatorIndex.unit.test.ts | 20 +++- .../tls/holesky/teku_client_keystore.p12 | Bin 0 -> 2622 bytes .../tls/holesky/teku_keystore_password.txt | 1 + packages/common/src/types/network/types.ts | 30 +++++- packages/ui/src/ImportScreen.tsx | 6 ++ packages/ui/src/params.ts | 1 + packages/ui/src/utils/dataUtils.ts | 3 +- 9 files changed, 157 insertions(+), 4 deletions(-) create mode 100644 packages/brain/tls/holesky/teku_client_keystore.p12 create mode 100644 packages/brain/tls/holesky/teku_keystore_password.txt diff --git a/README.md b/README.md index d2fa4ab5..c01182ac 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Staking Brain is a critical logical component for Staking in DAppNode. It provides a user interface and a Launchpad API that allow manual and automatic keystore management. Designed to support not only solo stakers, but also DVT/LSD technologies, Staking Brain streamlines the staking process for all users. -Within the Dappnode environment, Staking Brain is incorporated into the Web3Signer packages (gnosis, mainnet, prater and lukso). It ensures that user configurations for validators are reliably maintained. Please note that Staking Brain does not store keystores itself, but ensures their storage in the web3signer. It also maintains consistency between the validator service and web3signer service, as the validator must recognize all the pubkeys of validators whose keystores have been imported into the signer. +Within the Dappnode environment, Staking Brain is incorporated into the Web3Signer packages (gnosis, mainnet, prater, lukso and holesky). It ensures that user configurations for validators are reliably maintained. Please note that Staking Brain does not store keystores itself, but ensures their storage in the web3signer. It also maintains consistency between the validator service and web3signer service, as the validator must recognize all the pubkeys of validators whose keystores have been imported into the signer. Each Web3Signer package includes four services: diff --git a/packages/brain/src/modules/envs/index.ts b/packages/brain/src/modules/envs/index.ts index b1182913..77a5403e 100644 --- a/packages/brain/src/modules/envs/index.ts +++ b/packages/brain/src/modules/envs/index.ts @@ -5,18 +5,22 @@ import { executionClientsPrater, executionClientsGnosis, executionClientsLukso, + executionClientsHolesky, ExecutionClientMainnet, consensusClientsMainnet, ConsensusClientMainnet, ExecutionClientGnosis, ExecutionClientLukso, + ExecutionClientHolesky, ExecutionClientPrater, consensusClientsGnosis, consensusClientsLukso, + consensusClientsHolesky, consensusClientsPrater, ConsensusClientPrater, ConsensusClientGnosis, ConsensusClientLukso, + ConsensusClientHolesky, ExecutionClient, ConsensusClient, } from "@stakingbrain/common"; @@ -342,6 +346,78 @@ export function loadStakerConfig(): { : undefined, tlsCert, }; + } else if (network === "holesky") { + const { executionClient, consensusClient, defaultFeeRecipient } = + loadEnvs("holesky"); + switch (executionClient) { + case "holesky-nethermind.dnp.dappnode.eth": + executionClientUrl = `http://holesky-nethermind.dappnode:8545`; + break; + case "holesky-besu.dnp.dappnode.eth": + executionClientUrl = `http://holesky-besu.dappnode:8545`; + break; + case "holesky-erigon.dnp.dappnode.eth": + executionClientUrl = `http://holesky-erigon.dappnode:8545`; + case "holesky-geth.dnp.dappnode.eth": + executionClientUrl = `http://holesky-geth.dappnode:8545`; + break; + default: + throw Error( + `Unknown execution client for network ${network}: ${executionClient}` + ); + } + switch (consensusClient) { + case "prysm-holesky.dnp.dappnode.eth": + token = `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.MxwOozSH-TLbW_XKepjyYDHm2IT8Ki0tD3AHuajfNMg`; + beaconchainUrl = `http://beacon-chain.prysm-holesky.dappnode:3500`; + validatorUrl = `http://validator.prysm-holesky.dappnode:3500`; + break; + case "teku-holesky.dnp.dappnode.eth": + token = `cd4892ca35d2f5d3e2301a65fc7aa660`; + beaconchainUrl = `http://beacon-chain.teku-holesky.dappnode:3500`; + validatorUrl = `https://validator.teku-holesky.dappnode:3500`; + tlsCert = fs.readFileSync( + path.join(certDir, "holesky", "teku_client_keystore.p12") + ); + break; + case "lighthouse-holesky.dnp.dappnode.eth": + token = `api-token-0x0200e6ce18e26fd38caca7ae1bfb9e2bba7efb20ed2746ad17f2f6dda44603152d`; + beaconchainUrl = `http://beacon-chain.lighthouse-holesky.dappnode:3500`; + validatorUrl = `http://validator.lighthouse-holesky.dappnode:3500`; + break; + case "lodestar-holesky.dnp.dappnode.eth": + token = `api-token-0x7fd16fff6453982a5d8bf14617e7823b68cd18ade59985befe64e0a659300e7d`; + beaconchainUrl = `http://beacon-chain.lodestar-holesky.dappnode:3500`; + validatorUrl = `http://validator.lodestar-holesky.dappnode:3500`; + break; + case "nimbus-holesky.dnp.dappnode.eth": + token = `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.MxwOozSH-TLbW_XKepjyYDHm2IT8Ki0tD3AHuajfNMg`; + beaconchainUrl = `http://beacon-validator.nimbus-holesky.dappnode:4500`; + validatorUrl = `http://beacon-validator.nimbus-holesky.dappnode:3500`; + break; + default: + throw Error( + `Unknown consensus client for network ${network}: ${consensusClient}` + ); + } + + return { + network, + executionClient, + consensusClient, + executionClientUrl, + validatorUrl, + beaconchainUrl, + beaconchaUrl: `https://holesky.beaconcha.in`, + signerUrl: `http://web3signer.web3signer-holesky.dappnode:9000`, + token, + host: `web3signer.web3signer-holesky.dappnode`, + defaultFeeRecipient: + defaultFeeRecipient && isValidEcdsaPubkey(defaultFeeRecipient) + ? defaultFeeRecipient + : undefined, + tlsCert, + }; } else { throw Error(`Unknown network ${network}`); } @@ -453,6 +529,28 @@ function loadEnvs( )}` ); break; + case "holesky": + if ( + !executionClientsHolesky.includes( + executionClient as ExecutionClientHolesky + ) + ) + errors.push( + `Execution client is not valid for network ${network}: ${executionClient}. Valid execution clients for ${network}: ${executionClientsHolesky.join( + ", " + )}` + ); + if ( + !consensusClientsHolesky.includes( + consensusClient as ConsensusClientHolesky + ) + ) + errors.push( + `Consensus client is not valid for network ${network}: ${consensusClient}. Valid consensus clients for ${network}: ${consensusClientsHolesky.join( + ", " + )}` + ); + break; default: errors.push( `NETWORK environment variable is not valid: ${network}. Valid NETWORK values: ${networks.join( diff --git a/packages/brain/tests/unit/modules/apiClients/fetchValidatorIndex.unit.test.ts b/packages/brain/tests/unit/modules/apiClients/fetchValidatorIndex.unit.test.ts index dd3fa7db..72fd1d6e 100644 --- a/packages/brain/tests/unit/modules/apiClients/fetchValidatorIndex.unit.test.ts +++ b/packages/brain/tests/unit/modules/apiClients/fetchValidatorIndex.unit.test.ts @@ -4,7 +4,7 @@ import { BeaconchaApi } from "../../../../src/modules/apiClients/beaconcha/index describe.skip("Test for fetching validator indexes in every available network", () => { it("should return data corresponding to every validator PK", async () => { - const networks = ["mainnet", "prater", "gnosis", "lukso"]; + const networks = ["mainnet", "prater", "gnosis", "lukso", "holesky"]; for (const network of networks) { console.log("NETWORK: ", network); @@ -74,6 +74,16 @@ const networkTestMap = new Map< indexes: [24693, 4272], }, ], + [ + "holesky", + { + pubkeys: [ + "0x800000b3884235f70b06fec68c19642fc9e81e34fbe7f1c0ae156b8b45860dfe5ac71037ae561c2a759ba83401488e18", + "0x800009f644592de8d2de0da0caca00f26fd6fb3d7f99f57101bbbfb45d4b166f8dbe5fd82b3611e6e90fe323de955bd2" + ], + indexes: [886680, 68945], + } + ], ]); // TODO: move below to common @@ -111,4 +121,12 @@ const beaconchaApiParamsMap = new Map([ apiPath: "/api/v1/", }, ], + [ + "holesky", + { + baseUrl: "https://holesky.beaconcha.in", + host: "brain.web3signer-holesky.dappnode", + apiPath: "/api/v1/", + }, + ], ]); diff --git a/packages/brain/tls/holesky/teku_client_keystore.p12 b/packages/brain/tls/holesky/teku_client_keystore.p12 new file mode 100644 index 0000000000000000000000000000000000000000..c319e7e8468405db561fdc357521c75990f44f1e GIT binary patch literal 2622 zcma)8S5y-U7ED6uC58}0q!$4}Or(V(O+<=F3%v;_(joLBE%YWr9z_t9CZP1*BoKlE zPpTlj3Ir7Cc~oK9vwM8IU%Mao%(*jn=5x+PVCm#QKnetwcA1LydW=TQ0V9wa_y$Wm z2FB73U(o#sEM?t)Nt8ukEM?vWoqI7asObOgVxR+p-e4(}FGvN1!*2&ABf<=U`g>(S zh=N%berfjwsMMm38}83u7Kjmj*Hy+30tsCL0eKJ%R5bre1kq9g5S&!B4`Vcdk14>w z>)=b(riON%ElOUIA+n1af^pGcEO-b`P3=rUIO|~K4De36{4fi%#K@taLh=y5r+tRu z!thV}0Ws9(M*EKCKxHYwPnppz6XKPD4*|SGjss9P73p@P9YNku(O6Od4u%AY+sU&J zewn2K)4Xn|@K8W0U^R92A(biDp4!=Gu)H7kAn`9d{o3h&n?^P%#x;6Iw$UBmF+@}=+s-AW#9nkHNJr&RdrBCxGTO#gcG-s7 z%K2cYe{1oPr0=@H#QwA(KbSo=>l8Ot;7;#0YDEZ6p{(f_pN_WCLw~zDbVO{oB`*tW?X@VO{z=D@Wp)ZBd4R zFAY(#>+b`2fSQ+Iy*wHgx*K6t${D3Gf2bVinMluuAp0vX5RoC(gNu=V1a_c@@0vmX z!i3tC38HN$I8wMO|J!Bd;Rv8s>Wi&7`M{Mk3N=$dhv zX2xHJUa!OJcPp*d_uB~9C9W>(ru&|_eXv?O0b;q;w3-sWs##OC3mFeKAF;KHI6c0q zu2}4yv;FdLJD2Y|zOyL=@ z&GoMgKfgaAEBKB$?Ls=aF|sjCa!DR*S>cUL-olRtu);Zdbi+9vOM-H5e(z1CJo0Vm zR7R>Jcz@R=Een!`iy7btum!jRoB)q5&;Z)HZjpp{t=y7UFR?xg$5MnJxuuuJ5Qtjx?`;?~%8>rs9%1Ozy_4j+A7<{(N$lb5aZtJZAZOv^LEBn!cfx|G-iy(1*u7sL&jtpP_c#rSwvxX@e%so>Ho=+dXQ=*?iiu zvW4B|eOq*~Sf9Nnck5aoDQExe`pPTg(Fr-v(?9vFI~X6j{Sc}F#_uG}Rgx^%G!K+H zgtaDa$f=fSrawL3bV^iiwAhato5uBZ@+ir^)z@1On+8?05}jINzPugE?QTTN92w~+&&mTvNnT?wDwNuAV)|l2%)0$H>-`>Yf2+)jE za$4@P%yhVw^^=%JJZ<*C3iDv0jUPh+?u8vxkibxSs>%nZ!})oudY_17QK#^pwpsS9 z$O(NvmWmG!bDbSI*MSG!MM>cwH1OQHle43jNb3oJ$UMZ*Rt5p`Mg*Ys$ZEx-Ili=r zssxjts`-DVps-a0_*`1-XRR^(vKRr=t{Y!G~Py1## z7A!V8BH+?o(Yezrcq7jQ$23OR$>t2Tgx&(u1v>2eRaIMeKgOak!>|d7y6Kk1HCEMN zXqty?+^fIk@JljXka7GO$uR^aIh-o>K+&P>tt)%&9p>+w9(-~)G3y8U{;KnZYsJ7O zOukW>!2xun91gkCe^BZSnM~9)3WcpmCGLH879XP7P}y6NHJV1X5q>-|6sCO62*=~v z_)ZrINkl{J6vgu<+IeoW%~iP=y-6tBQ9dE(hT5<2EBhru zuya)hr9MbWE&FP>+MO$Cft$_yx-lr4^A6)=gU~@B5sbfoDiDwo0OnC^$#zf69~O8h z6q;2*s&@|A>17M4(5+IwxBT2txqb!A62R|$)?{<#QT9S71hYw!fvsBF{j&o80hi>y Ang9R* literal 0 HcmV?d00001 diff --git a/packages/brain/tls/holesky/teku_keystore_password.txt b/packages/brain/tls/holesky/teku_keystore_password.txt new file mode 100644 index 00000000..a1a35a1b --- /dev/null +++ b/packages/brain/tls/holesky/teku_keystore_password.txt @@ -0,0 +1 @@ +dappnode \ No newline at end of file diff --git a/packages/common/src/types/network/types.ts b/packages/common/src/types/network/types.ts index 23c6cb95..3f28b70d 100644 --- a/packages/common/src/types/network/types.ts +++ b/packages/common/src/types/network/types.ts @@ -1,4 +1,4 @@ -export const networks = ["mainnet", "gnosis", "prater", "lukso"] as const; +export const networks = ["mainnet", "gnosis", "prater", "lukso", "holesky"] as const; export type Network = (typeof networks)[number]; @@ -21,6 +21,8 @@ export type ExecutionClient = T extends "mainnet" ? ExecutionClientPrater : T extends "lukso" ? ExecutionClientLukso + : T extends "holesky" + ? ExecutionClientHolesky : never; export type ConsensusClient = T extends "mainnet" @@ -31,6 +33,8 @@ export type ConsensusClient = T extends "mainnet" ? ConsensusClientPrater : T extends "lukso" ? ConsensusClientLukso + : T extends "holesky" + ? ConsensusClientHolesky : never; export type Signer = T extends "mainnet" @@ -41,6 +45,8 @@ export type Signer = T extends "mainnet" ? SignerPrater : T extends "lukso" ? SignerLukso + : T extends "holesky" + ? SignerHolesky : never; // Mainnet @@ -127,3 +133,25 @@ export const executionClientsLukso = [ "lukso-besu.dnp.dappnode.eth", ] as const; export type ExecutionClientLukso = (typeof executionClientsLukso)[number]; + +// Holesky + +export const signerHolesky = "web3signer-holesky.dnp.dappnode.eth"; +export type SignerHolesky = typeof signerHolesky; + +export const consensusClientsHolesky = [ + "prysm-holesky.dnp.dappnode.eth", + "lighthouse-holesky.dnp.dappnode.eth", + "teku-holesky.dnp.dappnode.eth", + "nimbus-holesky.dnp.dappnode.eth", + "lodestar-holesky.dnp.dappnode.eth", +] as const; +export type ConsensusClientHolesky = (typeof consensusClientsHolesky)[number]; + +export const executionClientsHolesky = [ + "holesky-geth.dnp.dappnode.eth", + "holesky-erigon.dnp.dappnode.eth", + "holesky-nethermind.dnp.dappnode.eth", + "holesky-besu.dnp.dappnode.eth", +] as const; +export type ExecutionClientHolesky = (typeof executionClientsHolesky)[number]; \ No newline at end of file diff --git a/packages/ui/src/ImportScreen.tsx b/packages/ui/src/ImportScreen.tsx index 3b5af54e..c7749950 100644 --- a/packages/ui/src/ImportScreen.tsx +++ b/packages/ui/src/ImportScreen.tsx @@ -154,6 +154,12 @@ export default function ImportScreen({ network ) ? [{ value: "solo", label: "Solo" }] + : ["holesky"].includes(network) + ? [ + { value: "solo", label: "Solo" }, + { value: "rocketpool", label: "Rocketpool" }, + { value: "stakehouse", label: "StakeHouse" }, + ] : [ { value: "solo", label: "Solo" }, { value: "rocketpool", label: "Rocketpool" }, diff --git a/packages/ui/src/params.ts b/packages/ui/src/params.ts index c932ff82..e3d4f33a 100644 --- a/packages/ui/src/params.ts +++ b/packages/ui/src/params.ts @@ -11,6 +11,7 @@ export const beaconchaApiParamsMap = new Map>([ apiPath: "/api/v1/", }, ], + ["holesky", { baseUrl: "https://holesky.beaconcha.in", apiPath: "/api/v1/" }], ]); export interface AppParams { diff --git a/packages/ui/src/utils/dataUtils.ts b/packages/ui/src/utils/dataUtils.ts index d693f07e..0042b7ff 100644 --- a/packages/ui/src/utils/dataUtils.ts +++ b/packages/ui/src/utils/dataUtils.ts @@ -27,7 +27,8 @@ export function prettyClientDnpName(dnpName: string): string { !name.includes("goerli") && !name.includes("prater") && !name.includes("gnosis") && - !name.includes("lukso") + !name.includes("lukso") && + !name.includes("holesky") ); if (!clientName) return dnpName;