mirror of
https://github.com/datahaven-xyz/datahaven
synced 2026-05-24 09:50:01 +00:00
## Summary - Replace the manual `sendNewValidatorSetForEra` contract call in the `validator-set-update` E2E test with the **validator-set-submitter daemon** running as a Docker container - Add `test/e2e/framework/submitter.ts` with helpers to build the image, launch the container on the shared Docker network, and clean up after the test - Remove unused imports (`getOwnerAccount`, `decodeEventLog`, `parseEther`, `gatewayAbi`) that were only needed for manual submission The submitter automatically detects the last session of an era and submits the validator set via Snowbridge, matching production behavior more closely than the previous manual call. ## Test plan - [x] `bun test e2e/suites/validator-set-update.test.ts --timeout 900000` passes (4/4 tests, 15 assertions) - [x] Verify submitter container starts and connects to both Ethereum and DataHaven - [x] Verify `ExternalValidatorsSet` event is observed on DataHaven - [x] Verify submitter container is cleaned up after the test - [x] Verify Charlie and Dave appear in the final validator set
135 lines
4.6 KiB
TypeScript
135 lines
4.6 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 repo root.
|
|
*/
|
|
export async function buildSubmitterImage(): Promise<void> {
|
|
logger.debug("Building validator-set-submitter Docker image...");
|
|
const repoRoot = path.resolve(import.meta.dir, "../../..");
|
|
await $`docker build -f test/tools/validator-set-submitter/Dockerfile -t ${SUBMITTER_IMAGE} .`
|
|
.cwd(repoRoot)
|
|
.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 logs =
|
|
(await $`docker logs --tail ${SUBMITTER_LOG_TAIL_LINES} ${containerName}`.nothrow().text()) ||
|
|
"<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`);
|
|
}
|