From 10362d3361d9fdc9b345b1405ce96ef6b0e3e2dd Mon Sep 17 00:00:00 2001 From: Gonza Montiel Date: Mon, 21 Jul 2025 15:02:25 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20=F0=9F=94=8C=20CLI=20connection=20issues?= =?UTF-8?q?=20(#119)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Problem Introducing `--network` should make easy to container nodes to find each other. But this change was made half-way for the relayers, and it was using the external port to find the first datahaven node (usually Alice). So: - In cli launch, Alice node port mapping was left to random port `-p 9944` instead of `-p 9944:9944`. - Relayers couldn't connect to DataHaven nodes because they were using the external WS port (now random) instead of hitting the internal port (which for a cli launch we actually fix it to 9944). ### Solution - [x] **Fixed Docker port mapping**: Explicit `-p 9944:9944` for Alice node under network `cli-launch` - [x] **Enhanced container spec**: Added `internalPorts` tracking to `LaunchedNetwork` - [x] **Fixed relayer connections**: Use internal ports for container communication --- test/cli/handlers/deploy/datahaven.ts | 17 ++++++------- test/cli/handlers/deploy/parameters.ts | 4 +-- test/cli/handlers/launch/index.ts | 11 ++++++++ test/launcher/datahaven.ts | 35 +++++++++++++++++++++++--- test/launcher/relayers.ts | 12 ++++++--- test/launcher/types/launchedNetwork.ts | 14 ++++++++--- test/utils/constants.ts | 2 ++ 7 files changed, 74 insertions(+), 21 deletions(-) diff --git a/test/cli/handlers/deploy/datahaven.ts b/test/cli/handlers/deploy/datahaven.ts index 75dbfad9..9ce9e911 100644 --- a/test/cli/handlers/deploy/datahaven.ts +++ b/test/cli/handlers/deploy/datahaven.ts @@ -2,14 +2,13 @@ import path from "node:path"; import { $ } from "bun"; import invariant from "tiny-invariant"; import { logger, printDivider, printHeader } from "utils"; +import { DEFAULT_SUBSTRATE_WS_PORT } from "utils/constants"; import { waitFor } from "utils/waits"; import { isNetworkReady, setupDataHavenValidatorConfig } from "../../../launcher/datahaven"; import type { LaunchedNetwork } from "../../../launcher/types/launchedNetwork"; import { forwardPort } from "../common/kubernetes"; import type { DeployOptions } from "."; -const DEFAULT_PUBLIC_WS_PORT = 9944; - /** * Deploys a DataHaven solochain network in a Kubernetes namespace. * @@ -27,8 +26,8 @@ export const deployDataHavenSolochain = async ( // Forward port from validator to localhost, to interact with the network. const { cleanup: validatorPortForwardCleanup } = await forwardPort( "dh-validator-0", - DEFAULT_PUBLIC_WS_PORT, - DEFAULT_PUBLIC_WS_PORT, + DEFAULT_SUBSTRATE_WS_PORT, + DEFAULT_SUBSTRATE_WS_PORT, launchedNetwork ); @@ -92,8 +91,8 @@ export const deployDataHavenSolochain = async ( // Forward port from validator to localhost, to interact with the network. const { cleanup: validatorPortForwardCleanup } = await forwardPort( "dh-validator-0", - DEFAULT_PUBLIC_WS_PORT, - DEFAULT_PUBLIC_WS_PORT, + DEFAULT_SUBSTRATE_WS_PORT, + DEFAULT_SUBSTRATE_WS_PORT, launchedNetwork ); @@ -104,7 +103,7 @@ export const deployDataHavenSolochain = async ( await waitFor({ lambda: async () => { logger.info(`📡 Checking if DataHaven is ready (timeout: ${timeoutMs / 1000}s)...`); - const isReady = await isNetworkReady(DEFAULT_PUBLIC_WS_PORT, timeoutMs); + const isReady = await isNetworkReady(DEFAULT_SUBSTRATE_WS_PORT, timeoutMs); if (!isReady) { logger.info(`⌛️ Node not ready, waiting ${delayMs / 1000}s to check again...`); } @@ -116,7 +115,7 @@ export const deployDataHavenSolochain = async ( }); logger.success( - `DataHaven network started, primary node accessible on port ${DEFAULT_PUBLIC_WS_PORT}` + `DataHaven network started, primary node accessible on port ${DEFAULT_SUBSTRATE_WS_PORT}` ); await registerNodes(launchedNetwork); @@ -174,7 +173,7 @@ const checkOrCreateKubernetesNamespace = async (namespace: string) => { const registerNodes = async (launchedNetwork: LaunchedNetwork) => { // Register the validator node, using the standard host WS port that we just forwarded. launchedNetwork.addContainer("dh-validator-0", { - ws: DEFAULT_PUBLIC_WS_PORT + ws: DEFAULT_SUBSTRATE_WS_PORT }); logger.info("📝 Node dh-validator-0 successfully registered in launchedNetwork."); }; diff --git a/test/cli/handlers/deploy/parameters.ts b/test/cli/handlers/deploy/parameters.ts index a0aea647..8cf7fa05 100644 --- a/test/cli/handlers/deploy/parameters.ts +++ b/test/cli/handlers/deploy/parameters.ts @@ -1,10 +1,8 @@ import { setDataHavenParameters } from "scripts/set-datahaven-parameters"; import { logger, printDivider, printHeader } from "utils"; +import { DEFAULT_SUBSTRATE_WS_PORT } from "utils/constants"; import type { ParameterCollection } from "utils/parameters"; -// Standard ports for the substrate network -const DEFAULT_SUBSTRATE_WS_PORT = 9944; - /** * A helper function to set DataHaven parameters from a ParameterCollection * diff --git a/test/cli/handlers/launch/index.ts b/test/cli/handlers/launch/index.ts index 151ecfdf..9f57ba9b 100644 --- a/test/cli/handlers/launch/index.ts +++ b/test/cli/handlers/launch/index.ts @@ -1,5 +1,6 @@ import type { Command } from "@commander-js/extra-typings"; import { logger } from "utils"; +import { DEFAULT_SUBSTRATE_WS_PORT } from "utils/constants"; import { createParameterCollection } from "utils/parameters"; import { getBlockscoutUrl } from "../../../launcher/kurtosis"; import { LaunchedNetwork } from "../../../launcher/types/launchedNetwork"; @@ -14,6 +15,16 @@ import { performValidatorOperations, performValidatorSetUpdate } from "./validat export const NETWORK_ID = "cli-launch"; +export interface NetworkOptions { + networkId: string; + dhInternalPort?: number; +} + +export const CLI_NETWORK_OPTIONS: NetworkOptions = { + networkId: NETWORK_ID, + dhInternalPort: DEFAULT_SUBSTRATE_WS_PORT +}; + // Non-optional properties should have default values set by the CLI export interface LaunchOptions { all?: boolean; diff --git a/test/launcher/datahaven.ts b/test/launcher/datahaven.ts index a36c93f9..c7c6274d 100644 --- a/test/launcher/datahaven.ts +++ b/test/launcher/datahaven.ts @@ -12,6 +12,7 @@ import { logger, waitForContainerToStart } from "utils"; +import { DEFAULT_SUBSTRATE_WS_PORT } from "utils/constants"; import { waitFor } from "utils/waits"; import { type Hex, keccak256, toHex } from "viem"; import { publicKeyToAddress } from "viem/accounts"; @@ -29,6 +30,30 @@ export interface DataHavenOptions { datahavenBuildExtraArgs?: string; } +/** + * Determines the port mapping for a DataHaven node based on the network type. + * + * For CLI-launch networks (networkId === "cli-launch"), only the alice node gets + * a fixed port mapping (9944:9944). For other networks, only the internal port is exposed + * and Docker assigns a random external port. + * + * @param nodeId - The node identifier (e.g., "alice", "bob") + * @param networkId - The network identifier + * @returns Array of port mapping arguments for Docker run command + */ +export const getPortMappingForNode = (nodeId: string, networkId: string): string[] => { + const isCliLaunch = networkId === "cli-launch"; + + if (isCliLaunch && nodeId === "alice") { + // For CLI-launch networks, only alice gets the fixed port mapping + return ["-p", `${DEFAULT_SUBSTRATE_WS_PORT}:${DEFAULT_SUBSTRATE_WS_PORT}`]; + } + + // For other networks or non-alice nodes, only expose internal port + // Docker will assign a random external port + return ["-p", `${DEFAULT_SUBSTRATE_WS_PORT}`]; +}; + /** * Launches a local DataHaven solochain network for testing. * @@ -103,7 +128,7 @@ export const launchLocalDataHavenSolochain = async ( containerName, "--network", dockerNetworkName, - ...(id === "alice" ? ["-p", "9944"] : []), + ...getPortMappingForNode(id, options.networkId), options.datahavenImageTag, `--${id}`, ...COMMON_LAUNCH_ARGS @@ -485,11 +510,15 @@ export const registerNodes = async (networkId: string, launchedNetwork: Launched } // Query the dynamic port and register - const dynamicPort = await getPublicPort(targetContainerName, 9944); + const dynamicPort = await getPublicPort(targetContainerName, DEFAULT_SUBSTRATE_WS_PORT); logger.debug( `Docker container ${targetContainerName} is running. Registering with dynamic port ${dynamicPort}.` ); - launchedNetwork.addContainer(targetContainerName, { ws: dynamicPort }); + launchedNetwork.addContainer( + targetContainerName, + { ws: dynamicPort }, + { ws: DEFAULT_SUBSTRATE_WS_PORT } + ); logger.info( `📝 Node ${targetContainerName} successfully registered in ${networkId} as datahaven-alice` ); diff --git a/test/launcher/relayers.ts b/test/launcher/relayers.ts index 2536b140..8c92cba8 100644 --- a/test/launcher/relayers.ts +++ b/test/launcher/relayers.ts @@ -7,6 +7,7 @@ import { getWsProvider } from "polkadot-api/ws-provider/web"; import invariant from "tiny-invariant"; import { ANVIL_FUNDED_ACCOUNTS, + DEFAULT_SUBSTRATE_WS_PORT, getEvmEcdsaSigner, getPortFromKurtosis, killExistingContainers, @@ -409,17 +410,20 @@ export const launchRelayers = async ( container.name.includes("datahaven") ); let substrateWsPort: number; + let substrateWsInternalPort: number; let substrateNodeId: string; if (dhNodes.length === 0) { logger.warn( - "⚠️ No DataHaven nodes found in launchedNetwork. Assuming DataHaven is running and defaulting to port 9944 for relayers." + `⚠️ No DataHaven nodes found in launchedNetwork. Assuming DataHaven is running and defaulting to ${DEFAULT_SUBSTRATE_WS_PORT} for relayers.` ); - substrateWsPort = 9944; + substrateWsPort = DEFAULT_SUBSTRATE_WS_PORT; + substrateWsInternalPort = DEFAULT_SUBSTRATE_WS_PORT; substrateNodeId = "default (assumed)"; } else { const firstDhNode = dhNodes[0]; substrateWsPort = firstDhNode.publicPorts.ws; + substrateWsInternalPort = firstDhNode.internalPorts.ws; substrateNodeId = firstDhNode.name; logger.info( `🔌 Using DataHaven node ${substrateNodeId} on port ${substrateWsPort} for relayers and BEEFY check.` @@ -449,7 +453,9 @@ export const launchRelayers = async ( const ethElRpcEndpoint = `ws://host.docker.internal:${ethWsPort}`; const ethClEndpoint = `http://host.docker.internal:${ethHttpPort}`; - const substrateWsEndpoint = `ws://${substrateNodeId}:${substrateWsPort}`; + + const substrateWsEndpoint = `ws://${substrateNodeId}:${substrateWsInternalPort}`; + logger.info(`🔗 Substrate endpoint for relayers: ${substrateWsEndpoint}`); const relayersToStart: RelayerSpec[] = [ { diff --git a/test/launcher/types/launchedNetwork.ts b/test/launcher/types/launchedNetwork.ts index af32ee27..71398f16 100644 --- a/test/launcher/types/launchedNetwork.ts +++ b/test/launcher/types/launchedNetwork.ts @@ -1,7 +1,11 @@ import invariant from "tiny-invariant"; import { logger, type RelayerType } from "utils"; -type ContainerSpec = { name: string; publicPorts: Record }; +type ContainerSpec = { + name: string; + publicPorts: Record; + internalPorts: Record; +}; /** * Represents the state and associated resources of a launched network environment, @@ -63,8 +67,12 @@ export class LaunchedNetwork { return container.publicPorts.ws ?? -1; } - addContainer(containerName: string, publicPorts: Record = {}) { - this._containers.push({ name: containerName, publicPorts }); + addContainer( + containerName: string, + publicPorts: Record = {}, + internalPorts: Record = {} + ) { + this._containers.push({ name: containerName, publicPorts, internalPorts }); } public getPublicWsPort(): number { diff --git a/test/utils/constants.ts b/test/utils/constants.ts index 9d325a52..1dfb6daa 100644 --- a/test/utils/constants.ts +++ b/test/utils/constants.ts @@ -1,3 +1,5 @@ +export const DEFAULT_SUBSTRATE_WS_PORT = 9944; + export const ANVIL_FUNDED_ACCOUNTS = { 0: { publicKey: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",