mirror of
https://github.com/datahaven-xyz/datahaven
synced 2026-05-24 09:50:01 +00:00
## Summary - fixes the untrusted CI failure in `e2e-tests / E2E Tests with Kurtosis Ethereum Network` - keeps validator-set-submitter startup actionable by avoiding test-only contract config imports during container startup - improves submitter readiness diagnostics by capturing both stdout and stderr from container logs and making streamed log matching robust to chunked UTF-8 output - reduces validator-set-submitter Docker build time in CI by building from `test/` and adding a tight `test/.dockerignore` - makes local arm64 E2E runs use a native local Snowbridge relayer image instead of forcing `linux/amd64` emulation - auto-builds the local relayer image when needed for `:local` tags ## Why The original failing untrusted test started as a submitter container startup problem, but the branch now also addresses a second timeout path that showed up while debugging: - the submitter image was being built from the repository root with a large Docker context, which made the `validator-set-update` suite spend most of its hook timeout budget inside `docker build` - on Apple Silicon, forcing `datahavenxyz/snowbridge-relay:latest` through `linux/amd64` caused `generate-beacon-checkpoint` to segfault during local runs These changes make the submitter failure actionable, cut the CI Docker build context down substantially, and keep local E2E runs reliable on arm64. ## Validation - `cd test && bun fmt` - `cd test && bun x tsc --noEmit` - `bun test e2e/suites/validator-set-update.test.ts --timeout 900000` - `cd test && docker build -f tools/validator-set-submitter/Dockerfile -t datahavenxyz/validator-set-submitter:local .`
137 lines
4.7 KiB
TypeScript
137 lines
4.7 KiB
TypeScript
/**
|
|
* E2E test helper for managing the validator-set-submitter Docker container.
|
|
*
|
|
* The submitter daemon automates `sendNewValidatorSetForEra` calls on the
|
|
* ServiceManager contract. This module builds the image, launches the
|
|
* container on the shared Docker network, and tears it down after the test.
|
|
*/
|
|
|
|
import path from "node:path";
|
|
import { $ } from "bun";
|
|
import { ANVIL_FUNDED_ACCOUNTS, logger, waitForContainerToStart, waitForLog } from "utils";
|
|
import { RELAYER_CONFIG_DIR } from "../../launcher/relayers";
|
|
|
|
const SUBMITTER_IMAGE = "datahavenxyz/validator-set-submitter:local";
|
|
const SUBMITTER_READY_LOG = "Submitter started — watching session changes";
|
|
const SUBMITTER_READY_TIMEOUT_SECONDS = 30;
|
|
const SUBMITTER_LOG_TAIL_LINES = 200;
|
|
|
|
/**
|
|
* Builds the validator-set-submitter Docker image from the test directory.
|
|
*/
|
|
export async function buildSubmitterImage(): Promise<void> {
|
|
logger.debug("Building validator-set-submitter Docker image...");
|
|
const testRoot = path.resolve(import.meta.dir, "../..");
|
|
await $`docker build -f tools/validator-set-submitter/Dockerfile -t ${SUBMITTER_IMAGE} .`
|
|
.cwd(testRoot)
|
|
.quiet();
|
|
logger.debug("Validator-set-submitter image built successfully");
|
|
}
|
|
|
|
export interface LaunchSubmitterOptions {
|
|
/** Docker network name (from launchedNetwork.networkName) */
|
|
networkName: string;
|
|
/** Network ID for container naming */
|
|
networkId: string;
|
|
/** Host-facing Ethereum RPC URL (e.g. http://127.0.0.1:32000) */
|
|
ethereumRpcUrl: string;
|
|
/** DataHaven container name for inter-container networking */
|
|
datahavenContainerName: string;
|
|
/** ServiceManager contract address from deployments */
|
|
serviceManagerAddress: string;
|
|
}
|
|
|
|
/**
|
|
* Launches the validator-set-submitter as a Docker container.
|
|
*
|
|
* Generates a YAML config, mounts it into the container, and connects
|
|
* it to the same Docker network as the DH nodes and relayers.
|
|
*/
|
|
export async function launchSubmitter(options: LaunchSubmitterOptions): Promise<{
|
|
containerName: string;
|
|
cleanup: () => Promise<void>;
|
|
}> {
|
|
const { networkName, networkId, ethereumRpcUrl, datahavenContainerName, serviceManagerAddress } =
|
|
options;
|
|
|
|
const containerName = `submitter-${networkId}`;
|
|
|
|
// Extract port from host-facing URL and rewrite for Docker inter-container access
|
|
const ethUrl = new URL(ethereumRpcUrl);
|
|
const dockerEthRpcUrl = `http://host.docker.internal:${ethUrl.port}`;
|
|
const dockerDhWsUrl = `ws://${datahavenContainerName}:9944`;
|
|
|
|
// Generate YAML config
|
|
const configContent = [
|
|
`ethereum_rpc_url: "${dockerEthRpcUrl}"`,
|
|
`datahaven_ws_url: "${dockerDhWsUrl}"`,
|
|
`service_manager_address: "${serviceManagerAddress}"`,
|
|
`network_id: "anvil"`,
|
|
`execution_fee: "0.1"`,
|
|
`relayer_fee: "0.2"`
|
|
].join("\n");
|
|
|
|
const configFileName = `submitter-config-${networkId}.yml`;
|
|
await $`mkdir -p ${RELAYER_CONFIG_DIR}`.quiet();
|
|
const hostConfigPath = path.resolve(path.join(RELAYER_CONFIG_DIR, configFileName));
|
|
await Bun.write(hostConfigPath, configContent);
|
|
logger.debug(`Submitter config written to ${hostConfigPath}`);
|
|
|
|
// Remove any existing container with the same name
|
|
await $`docker rm -f ${containerName}`.quiet().nothrow();
|
|
|
|
// Launch the container
|
|
const args = [
|
|
"run",
|
|
"-d",
|
|
"--name",
|
|
containerName,
|
|
"--network",
|
|
networkName,
|
|
"--add-host",
|
|
"host.docker.internal:host-gateway",
|
|
"-v",
|
|
`${hostConfigPath}:/config/config.yml:ro`,
|
|
"-e",
|
|
`SUBMITTER_PRIVATE_KEY=${ANVIL_FUNDED_ACCOUNTS[6].privateKey}`,
|
|
SUBMITTER_IMAGE
|
|
];
|
|
|
|
await $`docker ${args}`.quiet();
|
|
await waitForContainerToStart(containerName);
|
|
try {
|
|
await waitForLog({
|
|
containerName,
|
|
search: SUBMITTER_READY_LOG,
|
|
timeoutSeconds: SUBMITTER_READY_TIMEOUT_SECONDS
|
|
});
|
|
} catch (error) {
|
|
const logResult = await $`docker logs --tail ${SUBMITTER_LOG_TAIL_LINES} ${containerName}`
|
|
.nothrow()
|
|
.quiet();
|
|
const logs =
|
|
`${logResult.stdout.toString()}${logResult.stderr.toString()}`.trim() || "<no logs captured>";
|
|
await stopSubmitter(containerName);
|
|
throw new Error(
|
|
`Submitter did not become ready. Expected log "${SUBMITTER_READY_LOG}". Last ${SUBMITTER_LOG_TAIL_LINES} log lines:\n${logs}`,
|
|
{ cause: error }
|
|
);
|
|
}
|
|
|
|
logger.debug(`Submitter container ${containerName} started`);
|
|
|
|
const cleanup = async () => {
|
|
await stopSubmitter(containerName);
|
|
};
|
|
|
|
return { containerName, cleanup };
|
|
}
|
|
|
|
/**
|
|
* Stops and removes the submitter container.
|
|
*/
|
|
export async function stopSubmitter(containerName: string): Promise<void> {
|
|
logger.debug(`Stopping submitter container ${containerName}...`);
|
|
await $`docker rm -f ${containerName}`.quiet().nothrow();
|
|
logger.debug(`Submitter container ${containerName} removed`);
|
|
}
|