From e161accac22ecf57b0c7978da309662ea481fa2d Mon Sep 17 00:00:00 2001 From: Facundo Farall <37149322+ffarall@users.noreply.github.com> Date: Thu, 8 May 2025 09:42:45 -0300 Subject: [PATCH] =?UTF-8?q?fix:=20=F0=9F=A7=91=E2=80=8D=F0=9F=92=BB=20Fix?= =?UTF-8?q?=20and=20improve=20`bun=20cli`=20logging=20and=20functionalitie?= =?UTF-8?q?s=20(#60)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR: 1. Generally improves the logging of the testing CLI, making the logs more concise and easier to follow, with clearer sections and separations. 2. Launches DataHaven solochain nodes at the beginning not the end. 3. Prompts the user if they want to launch DataHaven nodes and Snowbridge Relayers. --------- Co-authored-by: Tim B <79199034+timbrinded@users.noreply.github.com> --- .github/workflows/task-e2e.yml | 8 ++ test/cli/handlers/launch/checks.ts | 5 +- test/cli/handlers/launch/datahaven.ts | 36 +++++++- test/cli/handlers/launch/index.ts | 53 +++--------- test/cli/handlers/launch/kurtosis.ts | 13 +-- test/cli/handlers/launch/launchedNetwork.ts | 2 +- test/cli/handlers/launch/relayer.ts | 37 ++++++-- test/cli/handlers/launch/summary.ts | 13 ++- test/cli/handlers/launch/validator.ts | 85 ++++++++++++------- test/cli/index.ts | 10 +-- test/configs/snowbridge/beacon-relay.json | 50 +++++------ test/package.json | 4 +- .../datahaven-integration-test-flow.md | 1 + test/scripts/deploy-contracts.ts | 12 ++- test/scripts/fund-validators.ts | 14 +-- test/scripts/send-txn.ts | 8 +- test/scripts/setup-validators.ts | 58 +++++-------- test/scripts/update-validator-set.ts | 6 +- test/utils/shell.ts | 18 ++-- 19 files changed, 253 insertions(+), 180 deletions(-) diff --git a/.github/workflows/task-e2e.yml b/.github/workflows/task-e2e.yml index a6a50279..ce0cbde1 100644 --- a/.github/workflows/task-e2e.yml +++ b/.github/workflows/task-e2e.yml @@ -71,6 +71,14 @@ jobs: with: name: datahaven-node-${{ inputs.binary-hash }} path: operator/target/release/ + + - name: Download snowbridge binary + run: | + docker create --name temp moonsonglabs/snowbridge-relayer:latest + mkdir -p tmp/bin + docker cp temp:/usr/local/bin/snowbridge-relay tmp/bin/ + chmod +x tmp/bin/snowbridge-relay + - run: tmp/bin/snowbridge-relay --help - run: chmod +x ../operator/target/release/datahaven-node - run: ../operator/target/release/datahaven-node --help diff --git a/test/cli/handlers/launch/checks.ts b/test/cli/handlers/launch/checks.ts index 8c1b8c0c..8b424f47 100644 --- a/test/cli/handlers/launch/checks.ts +++ b/test/cli/handlers/launch/checks.ts @@ -1,8 +1,10 @@ import { $ } from "bun"; -import { logger } from "utils"; +import { logger, printDivider, printHeader } from "utils"; // ===== Checks ===== export const checkDependencies = async (): Promise => { + printHeader("Environment Checks"); + if (!(await checkKurtosisInstalled())) { logger.error("Kurtosis CLI is required to be installed: https://docs.kurtosis.com/install"); throw Error("❌ Kurtosis CLI application not found."); @@ -23,6 +25,7 @@ export const checkDependencies = async (): Promise => { } logger.success("Forge is installed"); + printDivider(); }; const checkKurtosisInstalled = async (): Promise => { diff --git a/test/cli/handlers/launch/datahaven.ts b/test/cli/handlers/launch/datahaven.ts index 92421a11..2d068479 100644 --- a/test/cli/handlers/launch/datahaven.ts +++ b/test/cli/handlers/launch/datahaven.ts @@ -2,7 +2,7 @@ import fs from "node:fs"; import path from "node:path"; import { $ } from "bun"; import invariant from "tiny-invariant"; -import { logger, printHeader } from "utils"; +import { confirmWithTimeout, logger, printDivider, printHeader } from "utils"; import type { LaunchOptions } from "."; import type { LaunchedNetwork } from "./launchedNetwork"; @@ -21,11 +21,39 @@ const COMMON_LAUNCH_ARGS = [ const AUTHORITY_IDS = ["alice", "bob", "charlie", "dave", "eve"]; // TODO: This is very rough and will need something more substantial when we know what we want! -export const performDatahavenOperations = async ( +/** + * Launches a DataHaven solochain network for testing. + * + * @param options - Configuration options for launching the network. + * @param launchedNetwork - An instance of LaunchedNetwork to track the network's state. + */ +export const launchDataHavenSolochain = async ( options: LaunchOptions, launchedNetwork: LaunchedNetwork ) => { - printHeader("Starting Datahaven Network"); + printHeader("Starting DataHaven Network"); + + let shouldLaunchDataHaven = options.datahaven; + if (shouldLaunchDataHaven === undefined) { + shouldLaunchDataHaven = await confirmWithTimeout( + "Do you want to launch the DataHaven network?", + true, + 10 + ); + } else { + logger.info( + `Using flag option: ${shouldLaunchDataHaven ? "will launch" : "will not launch"} DataHaven network` + ); + } + + if (!shouldLaunchDataHaven) { + logger.info("Skipping DataHaven network launch. Done!"); + printDivider(); + return; + } + + // Kill any pre-existing datahaven processes if they exist + await $`pkill datahaven`.nothrow().quiet(); invariant(options.datahavenBinPath, "❌ Datahaven binary path not defined"); invariant( @@ -59,7 +87,7 @@ export const performDatahavenOperations = async ( let completed = false; const file = Bun.file(logFilePath); - for (let i = 0; i < 10; i++) { + for (let i = 0; i < 60; i++) { const pattern = "Running JSON-RPC server: addr=127.0.0.1:"; const blob = await file.text(); logger.debug(`Blob: ${blob}`); diff --git a/test/cli/handlers/launch/index.ts b/test/cli/handlers/launch/index.ts index f7e1267c..8d9f6852 100644 --- a/test/cli/handlers/launch/index.ts +++ b/test/cli/handlers/launch/index.ts @@ -2,18 +2,12 @@ import type { Command } from "@commander-js/extra-typings"; import { deployContracts } from "scripts/deploy-contracts"; import sendTxn from "scripts/send-txn"; import invariant from "tiny-invariant"; -import { - ANVIL_FUNDED_ACCOUNTS, - getPortFromKurtosis, - logger, - printDivider, - printHeader -} from "utils"; +import { ANVIL_FUNDED_ACCOUNTS, getPortFromKurtosis, logger } from "utils"; import { checkDependencies } from "./checks"; -import { performDatahavenOperations } from "./datahaven"; +import { launchDataHavenSolochain } from "./datahaven"; import { launchKurtosis } from "./kurtosis"; import { LaunchedNetwork } from "./launchedNetwork"; -import { performRelayerOperations } from "./relayer"; +import { launchRelayers } from "./relayer"; import { performSummaryOperations } from "./summary"; import { performValidatorOperations } from "./validator"; @@ -51,39 +45,26 @@ const launchFunction = async (options: LaunchOptions, launchedNetwork: LaunchedN const timeStart = performance.now(); - printHeader("Environment Checks"); - await checkDependencies(); - logger.trace("Launching Kurtosis enclave"); - await launchKurtosis(options); - logger.trace("Kurtosis enclave launched"); + await launchDataHavenSolochain(options, launchedNetwork); + + await launchKurtosis(options); - logger.trace("Send test transaction"); - printHeader("Setting Up Blockchain"); logger.debug(`Using account ${ANVIL_FUNDED_ACCOUNTS[1].publicKey}`); const privateKey = ANVIL_FUNDED_ACCOUNTS[1].privateKey; const rethPublicPort = await getPortFromKurtosis("el-1-reth-lighthouse", "rpc"); const networkRpcUrl = `http://127.0.0.1:${rethPublicPort}`; invariant(networkRpcUrl, "❌ Network RPC URL not found"); - logger.info("💸 Sending test transaction..."); await sendTxn(privateKey, networkRpcUrl); - printDivider(); - - logger.trace("Show completion information"); - const timeEnd = performance.now(); - const minutes = ((timeEnd - timeStart) / (1000 * 60)).toFixed(1); - - logger.success(`Kurtosis network started successfully in ${minutes} minutes`); - - logger.trace("Deploy contracts using the extracted function"); let blockscoutBackendUrl: string | undefined = undefined; if (options.blockscout === true) { const blockscoutPublicPort = await getPortFromKurtosis("blockscout", "http"); blockscoutBackendUrl = `http://127.0.0.1:${blockscoutPublicPort}`; + logger.trace("Blockscout backend URL:", blockscoutBackendUrl); } else if (options.verified) { logger.warn( "âš ī¸ Contract verification (--verified) requested, but Blockscout is disabled (--no-blockscout). Verification will be skipped." @@ -97,34 +78,20 @@ const launchFunction = async (options: LaunchOptions, launchedNetwork: LaunchedN deployContracts: options.deployContracts }); - if (contractsDeployed) { - await performValidatorOperations(options, networkRpcUrl); - } else if (options.setupValidators || options.fundValidators) { - logger.warn( - "âš ī¸ Validator operations requested but contracts were not deployed. Skipping validator operations." - ); - } - if (options.datahaven) { - await performDatahavenOperations(options, launchedNetwork); - } + await performValidatorOperations(options, networkRpcUrl, contractsDeployed); - if (options.relayer) { - await performRelayerOperations(options, launchedNetwork); - } - - printDivider(); + await launchRelayers(options, launchedNetwork); performSummaryOperations(options, launchedNetwork); const fullEnd = performance.now(); const fullMinutes = ((fullEnd - timeStart) / (1000 * 60)).toFixed(1); - logger.info(`Launch function completed successfully in ${fullMinutes} minutes`); + logger.success(`Launch function completed successfully in ${fullMinutes} minutes`); }; export const launch = async (options: LaunchOptions) => { const run = new LaunchedNetwork(); try { await launchFunction(options, run); - logger.success("Launch script completed successfully"); } finally { await run.cleanup(); } diff --git a/test/cli/handlers/launch/kurtosis.ts b/test/cli/handlers/launch/kurtosis.ts index 6eff88f7..5cd4525f 100644 --- a/test/cli/handlers/launch/kurtosis.ts +++ b/test/cli/handlers/launch/kurtosis.ts @@ -20,12 +20,15 @@ import { parse, stringify } from "yaml"; export const launchKurtosis = async ( options: LaunchOptions = {} ): Promise> => { + printHeader("Starting Kurtosis Network"); + if ((await checkKurtosisRunning()) && !options.alwaysClean) { logger.info("â„šī¸ Kurtosis network is already running."); logger.trace("Checking if launchKurtosis option was set via flags"); if (options.launchKurtosis === false) { - logger.info("Keeping existing Kurtosis enclave. Exiting..."); + logger.info("Keeping existing Kurtosis enclave."); + printDivider(); return getServicesFromKurtosis(); } @@ -40,7 +43,8 @@ export const launchKurtosis = async ( ); if (!shouldRelaunch) { - logger.info("Keeping existing Kurtosis enclave. Exiting..."); + logger.info("Keeping existing Kurtosis enclave."); + printDivider(); return getServicesFromKurtosis(); } @@ -48,8 +52,6 @@ export const launchKurtosis = async ( } } - printHeader("Starting Kurtosis Network"); - if (!options.skipCleaning) { logger.info("🧹 Cleaning up Docker and Kurtosis environments..."); logger.debug(await $`kurtosis enclave stop datahaven-ethereum`.nothrow().text()); @@ -69,7 +71,7 @@ export const launchKurtosis = async ( const configFile = await modifyConfig(options, "configs/kurtosis/minimal.yaml"); - logger.info(`Using Kurtosis config file: ${configFile}`); + logger.info(`âš™ī¸ Using Kurtosis config file: ${configFile}`); const { stderr, stdout, exitCode } = await $`kurtosis run github.com/ethpandaops/ethereum-package --args-file ${configFile} --enclave datahaven-ethereum` @@ -85,6 +87,7 @@ export const launchKurtosis = async ( logger.info("🔍 Gathering Kurtosis public ports..."); const services = await getServicesFromKurtosis(); + logger.success("Kurtosis network started successfully"); printDivider(); return services; diff --git a/test/cli/handlers/launch/launchedNetwork.ts b/test/cli/handlers/launch/launchedNetwork.ts index fb6aae48..ee3bc2be 100644 --- a/test/cli/handlers/launch/launchedNetwork.ts +++ b/test/cli/handlers/launch/launchedNetwork.ts @@ -43,7 +43,7 @@ export class LaunchedNetwork { async cleanup() { for (const process of this.processes) { - logger.info(`Process is still running: ${process.pid}`); + logger.debug(`Process is still running: ${process.pid}`); } for (const fd of this.fileDescriptors) { diff --git a/test/cli/handlers/launch/relayer.ts b/test/cli/handlers/launch/relayer.ts index ff875118..acd4f1b6 100644 --- a/test/cli/handlers/launch/relayer.ts +++ b/test/cli/handlers/launch/relayer.ts @@ -6,10 +6,12 @@ import { ANVIL_FUNDED_ACCOUNTS, type RelayerType, SUBSTRATE_FUNDED_ACCOUNTS, + confirmWithTimeout, getPortFromKurtosis, logger, parseDeploymentsFile, parseRelayConfig, + printDivider, printHeader } from "utils"; import type { LaunchOptions } from "."; @@ -22,12 +24,36 @@ type RelayerSpec = { pk: { type: "ethereum" | "substrate"; value: string }; }; -export const performRelayerOperations = async ( - options: LaunchOptions, - launchedNetwork: LaunchedNetwork -) => { +/** + * Launches Snowbridge relayers for the DataHaven network. + * + * @param options - Configuration options for launching the relayers. + * @param launchedNetwork - An instance of LaunchedNetwork to track the network's state. + */ +export const launchRelayers = async (options: LaunchOptions, launchedNetwork: LaunchedNetwork) => { printHeader("Starting Snowbridge Relayers"); - logger.info("Preparing to generate configs"); + + let shouldLaunchRelayers = options.relayer; + if (shouldLaunchRelayers === undefined) { + shouldLaunchRelayers = await confirmWithTimeout( + "Do you want to launch the Snowbridge relayers?", + true, + 10 + ); + } else { + logger.info( + `Using flag option: ${shouldLaunchRelayers ? "will launch" : "will not launch"} Snowbridge relayers` + ); + } + + if (!shouldLaunchRelayers) { + logger.info("Skipping Snowbridge relayers launch. Done!"); + printDivider(); + return; + } + + // Kill any pre-existing relayer processes if they exist + await $`pkill snowbridge-relay`.nothrow().quiet(); const anvilDeployments = await parseDeploymentsFile(); const beefyClientAddress = anvilDeployments.BeefyClient; @@ -155,4 +181,5 @@ export const performRelayerOperations = async ( } logger.success("Snowbridge relayers started"); + printDivider(); }; diff --git a/test/cli/handlers/launch/summary.ts b/test/cli/handlers/launch/summary.ts index 82edf164..4fbdddcd 100644 --- a/test/cli/handlers/launch/summary.ts +++ b/test/cli/handlers/launch/summary.ts @@ -7,23 +7,21 @@ export const performSummaryOperations = async ( options: LaunchOptions, launchedNetwork: LaunchedNetwork ) => { - logger.trace("Display service information in a clean table"); printHeader("Service Endpoints"); - logger.trace("Filter services to display based on blockscout option"); const servicesToDisplay = BASE_SERVICES; if (options.blockscout === true) { servicesToDisplay.push(...["blockscout", "blockscout-frontend"]); } - if (options.datahaven === true) { - const dhNodes = launchedNetwork.getDHNodes(); - for (const { id } of dhNodes) { - servicesToDisplay.push(`datahaven-${id}`); - } + const dhNodes = launchedNetwork.getDHNodes(); + for (const { id } of dhNodes) { + servicesToDisplay.push(`datahaven-${id}`); } + logger.trace("Services to display", servicesToDisplay); + const displayData: { service: string; ports: Record; url: string }[] = []; for (const service of servicesToDisplay) { logger.debug(`Checking service: ${service}`); @@ -106,5 +104,4 @@ export const performSummaryOperations = async ( } console.table(displayData); - logger.debug("Summary completed successfully"); }; diff --git a/test/cli/handlers/launch/validator.ts b/test/cli/handlers/launch/validator.ts index b1b87351..1c2015f6 100644 --- a/test/cli/handlers/launch/validator.ts +++ b/test/cli/handlers/launch/validator.ts @@ -1,16 +1,16 @@ import { fundValidators } from "scripts/fund-validators"; import { setupValidators } from "scripts/setup-validators"; import { updateValidatorSet } from "scripts/update-validator-set"; -import { confirmWithTimeout, logger } from "utils"; +import { confirmWithTimeout, logger, printDivider, printHeader } from "utils"; import type { LaunchOptions } from ".."; -export const performValidatorOperations = async (options: LaunchOptions, networkRpcUrl: string) => { - logger.trace("Set up validators using the extracted function"); +export const performValidatorOperations = async ( + options: LaunchOptions, + networkRpcUrl: string, + contractsDeployed: boolean +) => { + // If not specified, prompt for funding let shouldFundValidators = options.fundValidators; - let shouldSetupValidators = options.setupValidators; - let shouldUpdateValidatorSet = options.updateValidatorSet; - - logger.trace("If not specified, prompt for funding"); if (shouldFundValidators === undefined) { shouldFundValidators = await confirmWithTimeout( "Do you want to fund validators with tokens and ETH?", @@ -23,7 +23,23 @@ export const performValidatorOperations = async (options: LaunchOptions, network ); } - logger.trace("If not specified, prompt for setup"); + if (shouldFundValidators) { + if (!contractsDeployed) { + logger.warn( + "âš ī¸ Funding validators but contracts were not deployed in this CLI run. Could have unexpected results." + ); + } + + await fundValidators({ + rpcUrl: networkRpcUrl + }); + } else { + logger.debug("Skipping validator funding"); + printDivider(); + } + + // If not specified, prompt for setup + let shouldSetupValidators = options.setupValidators; if (shouldSetupValidators === undefined) { shouldSetupValidators = await confirmWithTimeout( "Do you want to register validators in EigenLayer?", @@ -36,40 +52,47 @@ export const performValidatorOperations = async (options: LaunchOptions, network ); } - logger.trace("If not specified, prompt for update"); - if (shouldUpdateValidatorSet === undefined) { - shouldUpdateValidatorSet = await confirmWithTimeout( - "Do you want to update the validator set on the substrate chain?", - true, - 10 - ); - } else { - logger.info( - `Using flag option: ${shouldUpdateValidatorSet ? "will update" : "will not update"} validator set` - ); - } - - if (shouldFundValidators) { - await fundValidators({ - rpcUrl: networkRpcUrl - }); - } else { - logger.info("Skipping validator funding"); - } - if (shouldSetupValidators) { + if (!contractsDeployed) { + logger.warn( + "âš ī¸ Setting up validators but contracts were not deployed in this CLI run. Could have unexpected results." + ); + } + await setupValidators({ rpcUrl: networkRpcUrl }); + // If not specified, prompt for update + let shouldUpdateValidatorSet = options.updateValidatorSet; + if (shouldUpdateValidatorSet === undefined) { + shouldUpdateValidatorSet = await confirmWithTimeout( + "Do you want to update the validator set on the substrate chain?", + true, + 10 + ); + } else { + logger.info( + `Using flag option: ${shouldUpdateValidatorSet ? "will update" : "will not update"} validator set` + ); + } + if (shouldUpdateValidatorSet) { + if (!contractsDeployed) { + logger.warn( + "âš ī¸ Updating validator set but contracts were not deployed in this CLI run. Could have unexpected results." + ); + } + await updateValidatorSet({ rpcUrl: networkRpcUrl }); } else { - logger.info("Skipping validator set update"); + logger.debug("Skipping validator set update"); + printDivider(); } } else { - logger.info("Skipping validator setup"); + logger.debug("Skipping validator setup"); + printDivider(); } }; diff --git a/test/cli/index.ts b/test/cli/index.ts index 0b9ae985..c7bc17ac 100644 --- a/test/cli/index.ts +++ b/test/cli/index.ts @@ -14,6 +14,7 @@ function parseIntValue(value: string): number { // So far we only have the launch command // we can expand this to more commands in the future const program = new Command() + .option("--datahaven", "Enable Datahaven network to be launched") .option("-l, --launch-kurtosis", "Launch Kurtosis") .option("-d, --deploy-contracts", "Deploy smart contracts") .option("-f, --fund-validators", "Fund validators") @@ -24,17 +25,16 @@ const program = new Command() .option("--no-update-validator-set", "Skip update validator set") .option("-b, --blockscout", "Enable Blockscout") .option("--slot-time ", "Set slot time in seconds", parseIntValue) - .option("--datahaven", "Enable Datahaven network to be launched") .option("--kurtosis-network-args ", "CustomKurtosis network args") + .option("-v, --verified", "Verify smart contracts with Blockscout") + .option("--always-clean", "Always clean Kurtosis", false) + .option("-q, --skip-cleaning", "Skip cleaning Kurtosis") + .option("-r, --relayer", "Enable Relayer") .option( "--datahaven-bin-path ", "Path to the datahaven binary", "../operator/target/release/datahaven-node" ) - .option("-v, --verified", "Verify smart contracts with Blockscout") - .option("--always-clean", "Always clean Kurtosis", false) - .option("-q, --skip-cleaning", "Skip cleaning Kurtosis") - .option("-r, --relayer", "Enable Relayer") .option( "-p, --relayer-bin-path ", "Path to the relayer binary", diff --git a/test/configs/snowbridge/beacon-relay.json b/test/configs/snowbridge/beacon-relay.json index 2ba9511a..eb7000a3 100644 --- a/test/configs/snowbridge/beacon-relay.json +++ b/test/configs/snowbridge/beacon-relay.json @@ -1,29 +1,29 @@ { - "source": { - "beacon": { - "endpoint": "http://127.0.0.1:32791", - "stateEndpoint": "http://127.0.0.1:32791", - "spec": { - "syncCommitteeSize": 512, - "slotsInEpoch": 32, - "epochsPerSyncCommitteePeriod": 256, - "forkVersions": { - "deneb": 0, - "electra": 0 - } - }, - "datastore": { - "location": "/home/timbo/workspace/moonsong/datahaven/test/tmp/facu-test", - "maxEntries": 100 - } + "source": { + "beacon": { + "endpoint": "http://127.0.0.1:33030", + "stateEndpoint": "http://127.0.0.1:33030", + "spec": { + "syncCommitteeSize": 512, + "slotsInEpoch": 32, + "epochsPerSyncCommitteePeriod": 4, + "forkVersions": { + "deneb": 0, + "electra": 18446744073709551615 } - }, - "sink": { - "parachain": { - "endpoint": "ws://127.0.0.1:9944", - "maxWatchedExtrinsics": 8, - "headerRedundancy": 20 - }, - "updateSlotInterval": 30 + }, + "datastore": { + "location": "/Users/facundofarall/Desktop/Moonsong/datahaven/test/tmp/facu-test", + "maxEntries": 100 + } } + }, + "sink": { + "parachain": { + "endpoint": "ws://127.0.0.1:9944", + "maxWatchedExtrinsics": 8, + "headerRedundancy": 20 + }, + "updateSlotInterval": 30 + } } diff --git a/test/package.json b/test/package.json index 33c4a7de..6ae2acae 100644 --- a/test/package.json +++ b/test/package.json @@ -11,9 +11,9 @@ "generate:wagmi": "wagmi generate", "generate:snowbridge-cfgs": "bun -e \"import {generateSnowbridgeConfigs} from './scripts/gen-snowbridge-cfgs.ts'; await generateSnowbridgeConfigs()\"", "start:e2e:verified": "bun cli --verified --blockscout --deploy-contracts --setup-validators --update-validator-set --fund-validators --slot-time 1", - "start:e2e:ci": "bun cli -d --setup-validators --update-validator-set --fund-validators --always-clean --slot-time 2", + "start:e2e:ci": "bun cli -d --setup-validators --update-validator-set --fund-validators --always-clean --slot-time 2 --datahaven --relayer", "start:e2e:minrelayer": "bun cli --relayer -d --no-setup-validators --no-update-validator-set --no-fund-validators --datahaven", - "stop:e2e": "pkill datahaven ; kurtosis enclave stop datahaven-ethereum && kurtosis clean && kurtosis engine stop && docker container prune -f", + "stop:e2e": "pkill datahaven ; pkill snowbridge-relay ; kurtosis enclave stop datahaven-ethereum && kurtosis clean && kurtosis engine stop && docker container prune -f", "stop:e2e:verified": "bun stop:e2e", "stop:e2e:minimal": "bun stop:e2e", "stop:e2e:quick": "kurtosis enclave stop datahaven-ethereum", diff --git a/test/resources/datahaven-integration-test-flow.md b/test/resources/datahaven-integration-test-flow.md index 8f1ecdf4..0ff952bc 100644 --- a/test/resources/datahaven-integration-test-flow.md +++ b/test/resources/datahaven-integration-test-flow.md @@ -93,6 +93,7 @@ In this phase, we register validators as operators in EigenLayer and sync the va - Configures validator addresses and permissions 3. **Sync Validator Set to DataHaven** + - Use `update-validator-set.ts` script to sync validators - Calls `sendNewValidatorSet` function in the DataHavenServiceManager contract - Sends validator set through Snowbridge Gateway to DataHaven solochain diff --git a/test/scripts/deploy-contracts.ts b/test/scripts/deploy-contracts.ts index 39dddc9d..4b0b836a 100644 --- a/test/scripts/deploy-contracts.ts +++ b/test/scripts/deploy-contracts.ts @@ -1,6 +1,12 @@ import { $ } from "bun"; import invariant from "tiny-invariant"; -import { confirmWithTimeout, logger, printHeader, runShellCommandWithLogger } from "utils"; +import { + confirmWithTimeout, + logger, + printDivider, + printHeader, + runShellCommandWithLogger +} from "utils"; interface DeployContractsOptions { rpcUrl: string; @@ -38,6 +44,8 @@ export const deployContracts = async (options: DeployContractsOptions): Promise< if (!shouldDeployContracts) { logger.info("Skipping contract deployment. Done!"); + printDivider(); + return false; } @@ -76,6 +84,8 @@ export const deployContracts = async (options: DeployContractsOptions): Promise< await runShellCommandWithLogger(deployCommand, { cwd: "../contracts" }); logger.success("Contracts deployed successfully"); + printDivider(); + return true; }; diff --git a/test/scripts/fund-validators.ts b/test/scripts/fund-validators.ts index 2f48a7f8..c2feef5e 100644 --- a/test/scripts/fund-validators.ts +++ b/test/scripts/fund-validators.ts @@ -3,7 +3,7 @@ import path from "node:path"; // Script to fund validators with tokens and ETH for local testing import { $ } from "bun"; import invariant from "tiny-invariant"; -import { logger, printHeader } from "../utils/index"; +import { logger, printDivider, printHeader } from "../utils/index"; interface FundValidatorsOptions { rpcUrl: string; @@ -167,8 +167,9 @@ export const fundValidators = async (options: FundValidatorsOptions): Promise => { - const { - rpcUrl, - validatorsConfig, - executeSignup, - networkName = "anvil", - deploymentPath - } = options; - - // Check if executeSignup option was set via flags, or prompt if not - let shouldExecuteSignup = executeSignup; - if (shouldExecuteSignup === undefined) { - shouldExecuteSignup = await confirmWithTimeout( - "Do you want to register validators in EigenLayer?", - true, - 10 - ); - } else { - logger.info( - `Using flag option: ${shouldExecuteSignup ? "will register" : "will not register"} validators` - ); - } - - if (!shouldExecuteSignup) { - logger.info("Skipping validator setup. Done!"); - return false; - } + const { rpcUrl, validatorsConfig, networkName = "anvil" } = options; printHeader("Setting Up DataHaven Validators"); @@ -130,11 +116,13 @@ export const setupValidators = async (options: SetupValidatorsOptions): Promise< const signupCommand = `forge script script/transact/SignUpValidator.s.sol --rpc-url ${rpcUrl} --broadcast --no-rpc-rate-limit --non-interactive`; logger.debug(`Running command: ${signupCommand}`); - await runShellCommandWithLogger(signupCommand, { env, cwd: "../contracts" }); + await runShellCommandWithLogger(signupCommand, { env, cwd: "../contracts", logLevel: "debug" }); logger.success(`Successfully registered validator ${validator.publicKey}`); } + printDivider(); + return true; }; diff --git a/test/scripts/update-validator-set.ts b/test/scripts/update-validator-set.ts index 01d04f7d..53d497da 100644 --- a/test/scripts/update-validator-set.ts +++ b/test/scripts/update-validator-set.ts @@ -3,7 +3,7 @@ import path from "node:path"; // Update validator set on DataHaven substrate chain import { $ } from "bun"; import invariant from "tiny-invariant"; -import { logger, printHeader } from "../utils/index"; +import { logger, printDivider, printHeader } from "../utils/index"; interface UpdateValidatorSetOptions { rpcUrl: string; @@ -56,7 +56,7 @@ export const updateValidatorSet = async (options: UpdateValidatorSetOptions): Pr logger.debug(`Running command: ${sendCommand}`); - const { exitCode, stderr } = await $`sh -c ${sendCommand}`.nothrow(); + const { exitCode, stderr } = await $`sh -c ${sendCommand}`.nothrow().quiet(); if (exitCode !== 0) { logger.error(`Failed to send validator set: ${stderr.toString()}`); @@ -83,6 +83,8 @@ export const updateValidatorSet = async (options: UpdateValidatorSetOptions): Pr } */ + printDivider(); + return true; }; diff --git a/test/utils/shell.ts b/test/utils/shell.ts index 646634ec..a379dc6b 100644 --- a/test/utils/shell.ts +++ b/test/utils/shell.ts @@ -2,11 +2,13 @@ import { existsSync } from "node:fs"; import { spawn } from "bun"; import { logger } from "./logger"; +export type LogLevel = "info" | "debug" | "error" | "warn"; + export const runShellCommandWithLogger = async ( command: string, - options?: { cwd?: string; env?: object } + options?: { cwd?: string; env?: object; logLevel?: LogLevel } ) => { - const { cwd = ".", env = {} } = options || {}; + const { cwd = ".", env = {}, logLevel = "info" as LogLevel } = options || {}; try { if (!existsSync(cwd)) { @@ -29,7 +31,8 @@ export const runShellCommandWithLogger = async ( const readStream = async ( reader: typeof stdoutReader | typeof stderrReader, - streamName: string + streamName: string, + logLevel: LogLevel ) => { try { while (true) { @@ -37,7 +40,9 @@ export const runShellCommandWithLogger = async ( if (done) break; const text = new TextDecoder().decode(value); const trimmedText = text.trim(); - logger.info(trimmedText.includes("\n") ? `\n${trimmedText}` : trimmedText); + if (trimmedText) { + logger[logLevel](trimmedText.includes("\n") ? `\n${trimmedText}` : trimmedText); + } } } catch (err) { logger.error(`Error reading from ${streamName} stream:`, err); @@ -46,7 +51,10 @@ export const runShellCommandWithLogger = async ( } }; - Promise.all([readStream(stdoutReader, "stdout"), readStream(stderrReader, "stderr")]); + Promise.all([ + readStream(stdoutReader, "stdout", logLevel), + readStream(stderrReader, "stderr", "error") + ]); await proc.exited; } catch (err) {