diff --git a/src/commands/githubActions/endToEndTest/ensureDappnodeEnvironment.ts b/src/commands/githubActions/endToEndTest/ensureDappnodeEnvironment.ts index d45ff7e7..d24f25ee 100644 --- a/src/commands/githubActions/endToEndTest/ensureDappnodeEnvironment.ts +++ b/src/commands/githubActions/endToEndTest/ensureDappnodeEnvironment.ts @@ -2,30 +2,30 @@ import chalk from "chalk"; import { DappmanagerTestApi } from "./dappmanagerTestApi.js"; import { nonStakerPackagesSetup, - stakerGnosisConfig, - stakerMainnetConfig, - stakerPraterConfig, + getStakerConfigByNetwork, packagesToKeep } from "./params.js"; -import { IpfsClientTarget } from "./types.js"; +import { IpfsClientTarget, Network } from "./types.js"; import got from "got"; /** * Ensure that the DAppNode environment is ready to run the integration tests */ export async function ensureDappnodeEnvironment({ - dappmanagerTestApi + dappmanagerTestApi, + network }: { dappmanagerTestApi: DappmanagerTestApi; + network?: Network; }): Promise { // Check the Bind container IP address is in the /etc/resolv.conf file await ensureDockerAliasesResolveFromHost(); // Check dappmanager is running await dappmanagerTestApi.healthCheck(); // Make sure extra pkgs are removed - await ensureOnlyDefaultPkgsInstalled(dappmanagerTestApi); + await ensureOnlyDefaultPkgsInstalled(dappmanagerTestApi, network); // Ensure that the Staker configurations are persisted - await persistStakerConfigs(dappmanagerTestApi); + if (network) await persistStakerConfigs(dappmanagerTestApi, network); // Ensure that the Staker packages are installed await ensureNonStakerPkgsAreInstalled(dappmanagerTestApi); // Ensure IPFS is running and IPFS repository is in local mode @@ -50,23 +50,33 @@ async function ensureDockerAliasesResolveFromHost(): Promise { * Ensure that the Staker configurations are persisted */ async function persistStakerConfigs( - dappmanagerTestApi: DappmanagerTestApi + dappmanagerTestApi: DappmanagerTestApi, + network: Network ): Promise { - //await dappmanagerTestApi.stakerConfigSet(stakerMainnetConfig); - //await dappmanagerTestApi.stakerConfigSet(stakerGnosisConfig); - await dappmanagerTestApi.stakerConfigSet(stakerPraterConfig); + const stakerConfig = getStakerConfigByNetwork(network); + if (network === "prater") { + console.log("persisting prater staker configuration"); + await dappmanagerTestApi.stakerConfigSet(stakerConfig); + } else if (network === "mainnet") { + console.log("persisting mainnet staker configuration"); + await dappmanagerTestApi.stakerConfigSet(stakerConfig); + } else if (network === "gnosis") { + console.log("persisting gnosis staker configuration"); + await dappmanagerTestApi.stakerConfigSet(stakerConfig); + } } /** * Ensure only required packages are installed (Staker configs from prater mainnet and gnosis) */ async function ensureOnlyDefaultPkgsInstalled( - dappmanagerTestApi: DappmanagerTestApi + dappmanagerTestApi: DappmanagerTestApi, + network?: Network ): Promise { const installedPackages = await dappmanagerTestApi.packagesGet(); for (const installedPackage of installedPackages) { - if (!packagesToKeep.includes(installedPackage.dnpName)) { + if (!packagesToKeep(network).includes(installedPackage.dnpName)) { console.log( chalk.dim( ` - Removing package ${installedPackage.dnpName} from the DAppNode environment` diff --git a/src/commands/githubActions/endToEndTest/executeTests.ts b/src/commands/githubActions/endToEndTest/executeTests.ts index 001cd027..17a87873 100644 --- a/src/commands/githubActions/endToEndTest/executeTests.ts +++ b/src/commands/githubActions/endToEndTest/executeTests.ts @@ -5,13 +5,23 @@ import { Manifest } from "../../../types.js"; import { DappmanagerTestApi } from "./dappmanagerTestApi.js"; import Docker from "dockerode"; import chalk from "chalk"; +import { getStakerConfigByNetwork } from "./params.js"; +import { getIsStakerPkg } from "./utils.js"; import { - stakerPkgsToKeep, - stakerGnosisConfig, - stakerMainnetConfig, - stakerPraterConfig -} from "./params.js"; -import { getStakerPkgNetwork } from "./utils.js"; + ConsensusClientGnosis, + ConsensusClientMainnet, + ConsensusClientPrater, + ExecutionClientGnosis, + ExecutionClientMainnet, + ExecutionClientPrater, + Network, + consensusClientsGnosis, + consensusClientsMainnet, + consensusClientsPrater, + executionClientsGnosis, + executionClientsMainnet, + executionClientsPrater +} from "./types.js"; /** * Execute the tests for the integration test workflow. These tests require @@ -28,7 +38,8 @@ export async function executePackageInstallAndUpdateTest({ compose, errorLogsTimeout, healthCheckUrl, - environmentByService + environmentByService, + network }: { dappmanagerTestApi: DappmanagerTestApi; releaseMultiHash: string; @@ -37,26 +48,25 @@ export async function executePackageInstallAndUpdateTest({ errorLogsTimeout: number; healthCheckUrl?: string; environmentByService?: Record; + network?: Network; }): Promise { - // Test Install package from scratch - console.log(chalk.dim("\nTEST: Installing pkg from scratch...")); + const { name } = manifest; + const isStakerPkg = getIsStakerPkg(name); + + // TEST: Install package from scratch + console.log(chalk.dim("\nTEST: Installing pkg from scratch")); + // Remove package, if not found it will throw an error but it's ok await dappmanagerTestApi - .packageInstall({ - dnpName: manifest.name, - version: releaseMultiHash, - userSettings: { environment: environmentByService || {} } - }) - .then(() => { - // If its a staker pkg then the stakerConfigSet must be called - if (stakerPkgsToKeep.includes(manifest.name)) { - const network = getStakerPkgNetwork(manifest.name); - network === "mainnet" - ? dappmanagerTestApi.stakerConfigSet(stakerMainnetConfig) - : network === "gnosis" - ? dappmanagerTestApi.stakerConfigSet(stakerGnosisConfig) - : dappmanagerTestApi.stakerConfigSet(stakerPraterConfig); - } - }); + .packageRemove({ dnpName: name }) + .catch(() => console.log("Package already removed")); + await dappmanagerTestApi.packageInstall({ + dnpName: name, + version: releaseMultiHash, + userSettings: { environment: environmentByService || {} } + }); + // Set staker config if staker package + if (isStakerPkg && network) + await setStakerConfig(name, dappmanagerTestApi, network); await executeTestCheckers({ dnpName: manifest.name, compose, @@ -67,11 +77,10 @@ export async function executePackageInstallAndUpdateTest({ // Skip update test if running in test environment, dappnodesdk package name is not published if (process.env.ENVIRONMENT === "TEST") return; - // Remove package - await dappmanagerTestApi.packageRemove({ dnpName: manifest.name }); - // Update package to the given hash console.log(chalk.dim("\nTEST: Install production pkg and update")); + // Remove package, if not found it will throw an error but it's ok + await dappmanagerTestApi.packageRemove({ dnpName: manifest.name }); await dappmanagerTestApi.packageInstall({ dnpName: manifest.name, version: "latest", // Install production version @@ -79,9 +88,12 @@ export async function executePackageInstallAndUpdateTest({ }); await dappmanagerTestApi.packageInstall({ dnpName: manifest.name, - version: releaseMultiHash, + version: releaseMultiHash, // Install test version userSettings: { environment: environmentByService } }); + // Set staker config if staker package + if (isStakerPkg && network) + await setStakerConfig(name, dappmanagerTestApi, network); await executeTestCheckers({ dnpName: manifest.name, compose, @@ -94,12 +106,14 @@ async function executeTestCheckers({ dnpName, compose, errorLogsTimeout, - healthCheckUrl + healthCheckUrl, + network }: { dnpName: string; compose: Compose; errorLogsTimeout: number; healthCheckUrl?: string; + network?: Network; }): Promise { const docker = new Docker(); for (const service of Object.keys(compose.services)) { @@ -119,6 +133,10 @@ async function executeTestCheckers({ await ensureHealthCheck(healthCheckUrl).then(() => console.log(chalk.green(` ✓ Healthcheck endpoint returned 200`)) ); + if (network) + await attestanceProof(network).then(() => + console.log(chalk.green(` ✓ Attestation proof`)) + ); } async function ensureContainerStatus( @@ -176,3 +194,47 @@ async function ensureNoErrorLogs( throw Error(errorMessage); } } + +/** + * Test that the validators are attesting after a peri + */ +async function attestanceProof(network: Network): Promise { + if (network === "mainnet") return; + // TODO + return; +} + +async function setStakerConfig( + dnpName: string, + dappmanagerTestApi: DappmanagerTestApi, + network: Network +): Promise { + const stakerConfig = getStakerConfigByNetwork(network); + switch (network) { + case "mainnet": + if (executionClientsMainnet.includes(dnpName as any)) + stakerConfig.executionClient.dnpName = dnpName as ExecutionClientMainnet; + else if (consensusClientsMainnet.includes(dnpName as any)) + stakerConfig.consensusClient.dnpName = dnpName as ConsensusClientMainnet; + + break; + case "gnosis": + if (executionClientsGnosis.includes(dnpName as any)) + stakerConfig.executionClient.dnpName = dnpName as ExecutionClientGnosis; + else if (consensusClientsGnosis.includes(dnpName as any)) + stakerConfig.consensusClient.dnpName = dnpName as ConsensusClientGnosis; + await dappmanagerTestApi.stakerConfigSet(stakerConfig); + break; + case "prater": + if (executionClientsPrater.includes(dnpName as any)) + stakerConfig.executionClient.dnpName = dnpName as ExecutionClientPrater; + else if (consensusClientsPrater.includes(dnpName as any)) + stakerConfig.consensusClient.dnpName = dnpName as ConsensusClientPrater; + await dappmanagerTestApi.stakerConfigSet(stakerConfig); + break; + + default: + throw Error("unknown network"); + } + await dappmanagerTestApi.stakerConfigSet(stakerConfig); +} diff --git a/src/commands/githubActions/endToEndTest/index.ts b/src/commands/githubActions/endToEndTest/index.ts index ca92eb4b..591e5862 100644 --- a/src/commands/githubActions/endToEndTest/index.ts +++ b/src/commands/githubActions/endToEndTest/index.ts @@ -7,11 +7,13 @@ import { executePackageInstallAndUpdateTest } from "./executeTests.js"; import { DappmanagerTestApi } from "./dappmanagerTestApi.js"; import { localDappmanagerTestApiUrl, localIpfsApiUrl } from "./params.js"; import chalk from "chalk"; +import { Network } from "./types.js"; interface CliCommandOptions extends CliGlobalOptions { healthCheckUrl?: string; errorLogsTimeout: number; environmentByService?: string; + network?: string; } export const endToEndTest: CommandModule< @@ -32,6 +34,11 @@ export const endToEndTest: CommandModule< type: "number", default: 30 }, + network: { + describe: + "Network to use for the test if any. Available values are mainnet, prater, gnosis", + type: "string" + }, environmentByService: { describe: "Environments by service to install the package with. JSON format", @@ -47,8 +54,18 @@ export async function gaTestEndToEndHandler({ dir, healthCheckUrl, errorLogsTimeout, - environmentByService + environmentByService, + network }: CliCommandOptions): Promise { + if ( + network && + network !== "mainnet" && + network !== "prater" && + network !== "gnosis" + ) + throw Error( + `Invalid network ${network}. Available values are mainnet, prater, gnosis` + ); const dappmanagerTestApi = new DappmanagerTestApi(localDappmanagerTestApiUrl); const compose = readCompose({ dir }); const { manifest } = readManifest({ dir }); @@ -72,7 +89,8 @@ export async function gaTestEndToEndHandler({ chalk.dim("\nCleaning test-integration environment before starting") ); await ensureDappnodeEnvironment({ - dappmanagerTestApi + dappmanagerTestApi, + network: network as Network }); await executePackageInstallAndUpdateTest({ @@ -82,7 +100,8 @@ export async function gaTestEndToEndHandler({ compose, healthCheckUrl, errorLogsTimeout, - environmentByService: environmentByServiceParsed + environmentByService: environmentByServiceParsed, + network: network as Network }); } catch (e) { throw Error(`Error on test-integration: ${e}`); diff --git a/src/commands/githubActions/endToEndTest/params.ts b/src/commands/githubActions/endToEndTest/params.ts index 429fea21..90bbf2cf 100644 --- a/src/commands/githubActions/endToEndTest/params.ts +++ b/src/commands/githubActions/endToEndTest/params.ts @@ -1,9 +1,19 @@ -import { StakerConfigSet } from "./types.js"; +import { Network, StakerConfigSet } from "./types.js"; export const localIpfsApiUrl = `http://172.33.1.5:5001`; export const localDappmanagerTestApiUrl = `http://172.33.1.7:7000`; -export const stakerMainnetConfig: StakerConfigSet<"mainnet"> = { +export const getStakerConfigByNetwork = ( + network: Network +): StakerConfigSet => { + return network === "mainnet" + ? stakerMainnetConfig + : network === "gnosis" + ? stakerGnosisConfig + : stakerPraterConfig; +}; + +const stakerMainnetConfig: StakerConfigSet<"mainnet"> = { network: "mainnet", feeRecipient: "0x0000000000000000000000000000000000000001", executionClient: { @@ -48,7 +58,7 @@ export const stakerMainnetConfig: StakerConfigSet<"mainnet"> = { } }; -export const stakerPraterConfig: StakerConfigSet<"prater"> = { +const stakerPraterConfig: StakerConfigSet<"prater"> = { network: "prater", feeRecipient: "0x0000000000000000000000000000000000000001", executionClient: { @@ -89,7 +99,7 @@ export const stakerPraterConfig: StakerConfigSet<"prater"> = { } }; -export const stakerGnosisConfig: StakerConfigSet<"gnosis"> = { +const stakerGnosisConfig: StakerConfigSet<"gnosis"> = { network: "gnosis", feeRecipient: "0x0000000000000000000000000000000000000001", executionClient: { @@ -130,22 +140,29 @@ export const nonStakerPackagesSetup = [ "dappnode-exporter.dnp.dappnode.eth" ]; -export const stakerPkgsToKeep = [ - stakerMainnetConfig.executionClient.dnpName, - stakerGnosisConfig.executionClient.dnpName, - stakerPraterConfig.executionClient.dnpName, - stakerMainnetConfig.consensusClient.dnpName, - stakerGnosisConfig.consensusClient.dnpName, - stakerPraterConfig.consensusClient.dnpName, - "mev-boost.dnp.dappnode.eth", - "mev-boost-goerli.dnp.dappnode.eth", - "web3signer.dnp.dappnode.eth", - "web3signer-gnosis.dnp.dappnode.eth", - "web3signer-prater.dnp.dappnode.eth" -]; +export const stakerPkgsToKeep = (network: Network): string[] => { + return network === "mainnet" + ? [ + stakerMainnetConfig.executionClient.dnpName, + stakerMainnetConfig.consensusClient.dnpName, + "mev-boost.dnp.dappnode.eth", + "web3signer.dnp.dappnode.eth" + ] + : network === "gnosis" + ? [ + stakerGnosisConfig.executionClient.dnpName, + stakerGnosisConfig.consensusClient.dnpName, + "web3signer-gnosis.dnp.dappnode.eth" + ] + : [ + stakerPraterConfig.executionClient.dnpName, + stakerPraterConfig.consensusClient.dnpName, + "mev-boost-goerli.dnp.dappnode.eth", + "web3signer-prater.dnp.dappnode.eth" + ]; +}; -export const packagesToKeep = [ - ...corePackages, - ...stakerPkgsToKeep, - ...nonStakerPackagesSetup -]; +export const packagesToKeep = (network: Network | undefined): string[] => + network + ? [...corePackages, ...nonStakerPackagesSetup, ...stakerPkgsToKeep(network)] + : [...corePackages, ...nonStakerPackagesSetup]; diff --git a/src/commands/githubActions/endToEndTest/types.ts b/src/commands/githubActions/endToEndTest/types.ts index b92404f6..c655375e 100644 --- a/src/commands/githubActions/endToEndTest/types.ts +++ b/src/commands/githubActions/endToEndTest/types.ts @@ -13,50 +13,96 @@ import { // STAKERS export type Network = "mainnet" | "prater" | "gnosis"; -export declare const consensusClientsMainnet: readonly [ + +// MAINNET +export const consensusClientsMainnet = [ + "lodestar.dnp.dappnode.eth", "prysm.dnp.dappnode.eth", "lighthouse.dnp.dappnode.eth", "teku.dnp.dappnode.eth", "nimbus.dnp.dappnode.eth", "lodestar.dnp.dappnode.eth", "" -]; +] as const; export type ConsensusClientMainnet = typeof consensusClientsMainnet[number]; -export declare const executionClientsMainnet: readonly [ +export const executionClientsMainnet = [ "geth.dnp.dappnode.eth", "besu.public.dappnode.eth", "erigon.dnp.dappnode.eth", "nethermind.public.dappnode.eth", "" -]; +] as const; export type ExecutionClientMainnet = typeof executionClientsMainnet[number]; -export type SignerMainnet = "web3signer.dnp.dappnode.eth" | ""; -export type MevBoostMainnet = "mev-boost.dnp.dappnode.eth" | ""; -export type ConsensusClientPrater = - | "prysm-prater.dnp.dappnode.eth" - | "lighthouse-prater.dnp.dappnode.eth" - | "teku-prater.dnp.dappnode.eth" - | "nimbus-prater.dnp.dappnode.eth" - | "lodestar-prater.dnp.dappnode.eth" - | ""; -export type ExecutionClientPrater = - | "goerli-geth.dnp.dappnode.eth" - | "goerli-erigon.dnp.dappnode.eth" - | "goerli-nethermind.dnp.dappnode.eth" - | "goerli-besu.dnp.dappnode.eth" - | ""; -export type SignerPrater = "web3signer-prater.dnp.dappnode.eth" | ""; -export type MevBoostPrater = "mev-boost-goerli.dnp.dappnode.eth" | ""; -export type ConsensusClientGnosis = - | "gnosis-beacon-chain-prysm.dnp.dappnode.eth" - | "lighthouse-gnosis.dnp.dappnode.eth" - | "teku-gnosis.dnp.dappnode.eth" - | "nimbus-gnosis.dnp.dappnode.eth" - | "lodestar-gnosis.dnp.dappnode.eth" - | ""; -export type ExecutionClientGnosis = "nethermind-xdai.dnp.dappnode.eth" | ""; -export type SignerGnosis = "web3signer-gnosis.dnp.dappnode.eth"; -export type MevBoostGnosis = "mev-boost-gnosis.dnp.dappnode.eth" | ""; +export const signerMainnet = ["web3signer.dnp.dappnode.eth", ""] as const; +export type SignerMainnet = typeof signerMainnet[number]; +export const mevBoostMainnet = ["mev-boost.dnp.dappnode.eth", ""] as const; +export type MevBoostMainnet = typeof mevBoostMainnet[number]; + +// PRATER +export const consensusClientsPrater = [ + "prysm-prater.dnp.dappnode.eth", + "lighthouse-prater.dnp.dappnode.eth", + "teku-prater.dnp.dappnode.eth", + "nimbus-prater.dnp.dappnode.eth", + "lodestar-prater.dnp.dappnode.eth", + "" +] as const; +export type ConsensusClientPrater = typeof consensusClientsPrater[number]; +export const executionClientsPrater = [ + "goerli-geth.dnp.dappnode.eth", + "goerli-erigon.dnp.dappnode.eth", + "goerli-nethermind.dnp.dappnode.eth", + "goerli-besu.dnp.dappnode.eth", + "" +] as const; +export type ExecutionClientPrater = typeof executionClientsPrater[number]; +export const signerPrater = ["web3signer-prater.dnp.dappnode.eth", ""] as const; +export type SignerPrater = typeof signerPrater[number]; +export const mevBoostPrater = [ + "mev-boost-goerli.dnp.dappnode.eth", + "" +] as const; +export type MevBoostPrater = typeof mevBoostPrater[number]; + +// GNOSIS +export const consensusClientsGnosis = [ + "gnosis-beacon-chain-prysm.dnp.dappnode.eth", + "lighthouse-gnosis.dnp.dappnode.eth", + "teku-gnosis.dnp.dappnode.eth", + "nimbus-gnosis.dnp.dappnode.eth", + "lodestar-gnosis.dnp.dappnode.eth", + "" +] as const; +export type ConsensusClientGnosis = typeof consensusClientsGnosis[number]; +export const executionClientsGnosis = [ + "nethermind-xdai.dnp.dappnode.eth", + "" +] as const; +export type ExecutionClientGnosis = typeof executionClientsGnosis[number]; +export const signerGnosis = ["web3signer-Gnosis.dnp.dappnode.eth", ""] as const; +export type SignerGnosis = typeof signerGnosis[number]; +export const mevBoostGnosis = [ + "mev-boost-gnosis.dnp.dappnode.eth", + "" +] as const; +export type MevBoostGnosis = typeof mevBoostGnosis[number]; + +export const stakerPkgs = [ + ...executionClientsMainnet, + ...consensusClientsMainnet, + ...signerMainnet, + ...mevBoostMainnet, + ...executionClientsPrater, + ...consensusClientsPrater, + ...signerPrater, + ...mevBoostPrater, + ...executionClientsGnosis, + ...consensusClientsGnosis, + ...signerGnosis, + ...mevBoostGnosis +]; + +// stakers items export type StakerType = "execution" | "consensus" | "signer" | "mev-boost"; export type ExecutionClient = T extends "mainnet" ? ExecutionClientMainnet diff --git a/src/commands/githubActions/endToEndTest/utils.ts b/src/commands/githubActions/endToEndTest/utils.ts index c9111fe7..711c890b 100644 --- a/src/commands/githubActions/endToEndTest/utils.ts +++ b/src/commands/githubActions/endToEndTest/utils.ts @@ -1,10 +1,7 @@ -import { stakerPkgsToKeep } from "./params.js"; -import { Network } from "./types.js"; +import { stakerPkgs } from "./types.js"; -export function getStakerPkgNetwork(dnpName: string): Network { - if (!stakerPkgsToKeep.includes(dnpName)) - throw Error(`Not a staker package: ${dnpName}`); - if (dnpName.includes("prater" || "goerli")) return "prater"; - if (dnpName.includes("gnosis" || "xdai")) return "gnosis"; - return "mainnet"; +export function getIsStakerPkg(dnpName: string): boolean { + if (!dnpName) throw Error("dnpName must be defined: "); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return stakerPkgs.includes(dnpName as any); }