2025-04-22 19:49:51 +00:00
|
|
|
import fs from "node:fs";
|
|
|
|
|
import path from "node:path";
|
|
|
|
|
import invariant from "tiny-invariant";
|
test: Update validator set e2e test (#126)
## Add E2E validator-set update flow
- feat: `test/utils/validators.ts` for on-demand validator
orchestration.
- feat: `test/suites/validator-set-update.test.ts` covering allowlist →
register → update.
- some minor launcher updates: avoid docker cache, add `--platform` when
building datahaven image, avoid sending validator-set update on launch.
- Helpers: ABI shortcut in `test/utils/contracts.ts`; config tweaks in
`test/configs/validator-set.json`.
- Minor cleanup/formatting across `test/launcher/*`,
`test/scripts/setup-validators.ts`, and related tests.
- added `keepAlive` flag to `BaseTestSuite`, in order to avoid tearing
down the network while debugging. Defaults, obviously, to false.
- added a `failOnTomeout` option on to waitForDataHavenEvents() so the
test fails of the timeout is reached and no event was captured.
### Coverage
- The test simulates an scenario in which we have two active authorities
(alice and bob), which are running, and registered as operators, which
is the normal state after the chain launches. Then:
- It launches two more nodes (charlie and dave)
- It add the nodes to allowlist and register them as operators
- It sends the validator set update message
- Checks that the validator update message was propagated through the
gateway and arrived the external-validators pallet
- Checks that the chain continues producing blocks
### Notes
The last test case has a timeout of 10 minutes. This is to respect
propagation times of the message through the relayers. We are testing
that the external validators pallet actually updated the validator set.
Locally, I could expect 5~6 minutes, I just wanted to be on the safe
side. CI is passing showing that this was enough indeed.
---------
Co-authored-by: Steve Degosserie <723552+stiiifff@users.noreply.github.com>
Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com>
2025-10-02 11:23:40 +00:00
|
|
|
import {
|
|
|
|
|
getValidatorInfoByName,
|
|
|
|
|
logger,
|
|
|
|
|
runShellCommandWithLogger,
|
|
|
|
|
TestAccounts
|
|
|
|
|
} from "../utils/index";
|
2025-04-22 19:49:51 +00:00
|
|
|
|
|
|
|
|
interface SetupValidatorsOptions {
|
|
|
|
|
rpcUrl: string;
|
|
|
|
|
validatorsConfig?: string; // Path to JSON config file with validator addresses
|
|
|
|
|
executeSignup?: boolean;
|
|
|
|
|
networkName?: string; // Network name for default deployment path
|
|
|
|
|
deploymentPath?: string; // Optional custom deployment path
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* JSON structure for validator configuration
|
|
|
|
|
*/
|
|
|
|
|
interface ValidatorConfig {
|
|
|
|
|
validators: {
|
|
|
|
|
publicKey: string;
|
|
|
|
|
privateKey: string;
|
test: Update validator set e2e test (#126)
## Add E2E validator-set update flow
- feat: `test/utils/validators.ts` for on-demand validator
orchestration.
- feat: `test/suites/validator-set-update.test.ts` covering allowlist →
register → update.
- some minor launcher updates: avoid docker cache, add `--platform` when
building datahaven image, avoid sending validator-set update on launch.
- Helpers: ABI shortcut in `test/utils/contracts.ts`; config tweaks in
`test/configs/validator-set.json`.
- Minor cleanup/formatting across `test/launcher/*`,
`test/scripts/setup-validators.ts`, and related tests.
- added `keepAlive` flag to `BaseTestSuite`, in order to avoid tearing
down the network while debugging. Defaults, obviously, to false.
- added a `failOnTomeout` option on to waitForDataHavenEvents() so the
test fails of the timeout is reached and no event was captured.
### Coverage
- The test simulates an scenario in which we have two active authorities
(alice and bob), which are running, and registered as operators, which
is the normal state after the chain launches. Then:
- It launches two more nodes (charlie and dave)
- It add the nodes to allowlist and register them as operators
- It sends the validator set update message
- Checks that the validator update message was propagated through the
gateway and arrived the external-validators pallet
- Checks that the chain continues producing blocks
### Notes
The last test case has a timeout of 10 minutes. This is to respect
propagation times of the message through the relayers. We are testing
that the external validators pallet actually updated the validator set.
Locally, I could expect 5~6 minutes, I just wanted to be on the safe
side. CI is passing showing that this was enough indeed.
---------
Co-authored-by: Steve Degosserie <723552+stiiifff@users.noreply.github.com>
Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com>
2025-10-02 11:23:40 +00:00
|
|
|
solochainAddress?: string;
|
|
|
|
|
solochainPrivateKey?: string;
|
|
|
|
|
solochainAuthorityName: string;
|
2025-04-22 19:49:51 +00:00
|
|
|
}[];
|
|
|
|
|
notes?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2025-05-08 12:42:45 +00:00
|
|
|
* Registers validators in EigenLayer based on a configuration file.
|
|
|
|
|
* This function reads validator details (public/private keys, optional solochain addresses)
|
|
|
|
|
* from a JSON file. If `executeSignup` is true (or confirmed by user prompt),
|
|
|
|
|
* it iterates through the configured validators and runs the
|
|
|
|
|
* `script/transact/SignUpValidator.s.sol` forge script for each to register them.
|
|
|
|
|
* Environment variables `OPERATOR_PRIVATE_KEY`, `OPERATOR_SOLOCHAIN_ADDRESS`, and `NETWORK`
|
|
|
|
|
* are set for the forge script execution.
|
2025-04-22 19:49:51 +00:00
|
|
|
*
|
2025-05-08 12:42:45 +00:00
|
|
|
* @param options - Configuration options for the validator setup process.
|
|
|
|
|
* @param options.rpcUrl - The RPC URL for the Ethereum network to interact with.
|
|
|
|
|
* @param options.validatorsConfig - Optional path to the JSON file containing validator configurations.
|
|
|
|
|
* Defaults to `../configs/validator-set.json` relative to this script.
|
|
|
|
|
* @param options.executeSignup - Optional. If true, proceeds with registration. If false, skips.
|
|
|
|
|
* If undefined, the user is prompted to confirm registration.
|
|
|
|
|
* @param options.networkName - Optional network name used when executing underlying scripts (e.g., for setting the `NETWORK` environment variable).
|
|
|
|
|
* Defaults to "anvil".
|
|
|
|
|
* @returns A Promise resolving to `true` if the validator registration process was executed
|
|
|
|
|
* (for all configured validators), or `false` if the registration was skipped
|
|
|
|
|
* (either due to the `executeSignup` option or user declining the prompt).
|
2025-04-22 19:49:51 +00:00
|
|
|
*/
|
|
|
|
|
export const setupValidators = async (options: SetupValidatorsOptions): Promise<boolean> => {
|
2025-05-08 12:42:45 +00:00
|
|
|
const { rpcUrl, validatorsConfig, networkName = "anvil" } = options;
|
2025-04-22 19:49:51 +00:00
|
|
|
|
|
|
|
|
// Validate RPC URL
|
|
|
|
|
invariant(rpcUrl, "❌ RPC URL is required");
|
|
|
|
|
|
|
|
|
|
// Load validator configuration - use default path if not specified
|
|
|
|
|
const configPath = validatorsConfig || path.resolve(__dirname, "../configs/validator-set.json");
|
|
|
|
|
|
|
|
|
|
// Ensure the configuration file exists
|
|
|
|
|
if (!fs.existsSync(configPath)) {
|
|
|
|
|
logger.error(`Validator configuration file not found: ${configPath}`);
|
|
|
|
|
throw new Error("Validator configuration file is required");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Load and validate the validator configuration
|
|
|
|
|
logger.debug(`Loading validator configuration from ${configPath}`);
|
|
|
|
|
let config: ValidatorConfig;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const fileContent = fs.readFileSync(configPath, "utf8");
|
|
|
|
|
config = JSON.parse(fileContent);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.error(`Failed to parse validator config file: ${error}`);
|
|
|
|
|
throw new Error("Invalid JSON format in validator configuration file");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Validate the validators array
|
|
|
|
|
if (!config.validators || !Array.isArray(config.validators) || config.validators.length === 0) {
|
|
|
|
|
logger.error("Invalid validator configuration: 'validators' array is missing or empty");
|
|
|
|
|
throw new Error("Validator configuration must contain a non-empty 'validators' array");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Validate each validator entry
|
|
|
|
|
for (const [index, validator] of config.validators.entries()) {
|
|
|
|
|
if (!validator.publicKey) {
|
|
|
|
|
throw new Error(`Validator at index ${index} is missing 'publicKey'`);
|
|
|
|
|
}
|
|
|
|
|
if (!validator.privateKey) {
|
|
|
|
|
throw new Error(`Validator at index ${index} is missing 'privateKey'`);
|
|
|
|
|
}
|
|
|
|
|
if (!validator.publicKey.startsWith("0x")) {
|
|
|
|
|
throw new Error(`Validator publicKey at index ${index} must start with '0x'`);
|
|
|
|
|
}
|
|
|
|
|
if (!validator.privateKey.startsWith("0x")) {
|
|
|
|
|
throw new Error(`Validator privateKey at index ${index} must start with '0x'`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
test: Update validator set e2e test (#126)
## Add E2E validator-set update flow
- feat: `test/utils/validators.ts` for on-demand validator
orchestration.
- feat: `test/suites/validator-set-update.test.ts` covering allowlist →
register → update.
- some minor launcher updates: avoid docker cache, add `--platform` when
building datahaven image, avoid sending validator-set update on launch.
- Helpers: ABI shortcut in `test/utils/contracts.ts`; config tweaks in
`test/configs/validator-set.json`.
- Minor cleanup/formatting across `test/launcher/*`,
`test/scripts/setup-validators.ts`, and related tests.
- added `keepAlive` flag to `BaseTestSuite`, in order to avoid tearing
down the network while debugging. Defaults, obviously, to false.
- added a `failOnTomeout` option on to waitForDataHavenEvents() so the
test fails of the timeout is reached and no event was captured.
### Coverage
- The test simulates an scenario in which we have two active authorities
(alice and bob), which are running, and registered as operators, which
is the normal state after the chain launches. Then:
- It launches two more nodes (charlie and dave)
- It add the nodes to allowlist and register them as operators
- It sends the validator set update message
- Checks that the validator update message was propagated through the
gateway and arrived the external-validators pallet
- Checks that the chain continues producing blocks
### Notes
The last test case has a timeout of 10 minutes. This is to respect
propagation times of the message through the relayers. We are testing
that the external validators pallet actually updated the validator set.
Locally, I could expect 5~6 minutes, I just wanted to be on the safe
side. CI is passing showing that this was enough indeed.
---------
Co-authored-by: Steve Degosserie <723552+stiiifff@users.noreply.github.com>
Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com>
2025-10-02 11:23:40 +00:00
|
|
|
const validators = [
|
|
|
|
|
getValidatorInfoByName(config, TestAccounts.Alice),
|
|
|
|
|
getValidatorInfoByName(config, TestAccounts.Bob)
|
|
|
|
|
];
|
2025-04-22 19:49:51 +00:00
|
|
|
|
test: Update validator set e2e test (#126)
## Add E2E validator-set update flow
- feat: `test/utils/validators.ts` for on-demand validator
orchestration.
- feat: `test/suites/validator-set-update.test.ts` covering allowlist →
register → update.
- some minor launcher updates: avoid docker cache, add `--platform` when
building datahaven image, avoid sending validator-set update on launch.
- Helpers: ABI shortcut in `test/utils/contracts.ts`; config tweaks in
`test/configs/validator-set.json`.
- Minor cleanup/formatting across `test/launcher/*`,
`test/scripts/setup-validators.ts`, and related tests.
- added `keepAlive` flag to `BaseTestSuite`, in order to avoid tearing
down the network while debugging. Defaults, obviously, to false.
- added a `failOnTomeout` option on to waitForDataHavenEvents() so the
test fails of the timeout is reached and no event was captured.
### Coverage
- The test simulates an scenario in which we have two active authorities
(alice and bob), which are running, and registered as operators, which
is the normal state after the chain launches. Then:
- It launches two more nodes (charlie and dave)
- It add the nodes to allowlist and register them as operators
- It sends the validator set update message
- Checks that the validator update message was propagated through the
gateway and arrived the external-validators pallet
- Checks that the chain continues producing blocks
### Notes
The last test case has a timeout of 10 minutes. This is to respect
propagation times of the message through the relayers. We are testing
that the external validators pallet actually updated the validator set.
Locally, I could expect 5~6 minutes, I just wanted to be on the safe
side. CI is passing showing that this was enough indeed.
---------
Co-authored-by: Steve Degosserie <723552+stiiifff@users.noreply.github.com>
Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com>
2025-10-02 11:23:40 +00:00
|
|
|
logger.info(`🔎 Registering ${validators.length} validators`);
|
|
|
|
|
|
|
|
|
|
// Iterate through validators to register them
|
2025-04-22 19:49:51 +00:00
|
|
|
for (let i = 0; i < validators.length; i++) {
|
|
|
|
|
const validator = validators[i];
|
2025-05-18 23:31:46 +00:00
|
|
|
logger.info(`🔧 Setting up validator ${i} (${validator.publicKey})`);
|
2025-04-22 19:49:51 +00:00
|
|
|
|
|
|
|
|
const env = {
|
|
|
|
|
...process.env,
|
|
|
|
|
NETWORK: networkName,
|
|
|
|
|
// OPERATOR_PRIVATE_KEY is what the script reads to set the operator
|
|
|
|
|
OPERATOR_PRIVATE_KEY: validator.privateKey,
|
|
|
|
|
// OPERATOR_SOLOCHAIN_ADDRESS is the validator's address on the substrate chain
|
|
|
|
|
OPERATOR_SOLOCHAIN_ADDRESS: validator.solochainAddress || ""
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Prepare command to register validator
|
2025-05-06 20:20:02 +00:00
|
|
|
const signupCommand = `forge script script/transact/SignUpValidator.s.sol --rpc-url ${rpcUrl} --broadcast --no-rpc-rate-limit --non-interactive`;
|
2025-04-22 19:49:51 +00:00
|
|
|
logger.debug(`Running command: ${signupCommand}`);
|
|
|
|
|
|
2025-05-08 12:42:45 +00:00
|
|
|
await runShellCommandWithLogger(signupCommand, { env, cwd: "../contracts", logLevel: "debug" });
|
2025-04-22 19:49:51 +00:00
|
|
|
|
|
|
|
|
logger.success(`Successfully registered validator ${validator.publicKey}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Allow script to be run directly with CLI arguments
|
|
|
|
|
if (import.meta.main) {
|
|
|
|
|
const args = process.argv.slice(2);
|
|
|
|
|
const options: {
|
|
|
|
|
rpcUrl?: string;
|
|
|
|
|
validatorsConfig?: string;
|
|
|
|
|
executeSignup?: boolean;
|
|
|
|
|
networkName?: string;
|
|
|
|
|
deploymentPath?: string;
|
|
|
|
|
} = {
|
|
|
|
|
executeSignup: args.includes("--no-signup") ? false : undefined,
|
|
|
|
|
networkName: "anvil" // Default network name
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Extract RPC URL
|
|
|
|
|
const rpcUrlIndex = args.indexOf("--rpc-url");
|
|
|
|
|
if (rpcUrlIndex !== -1 && rpcUrlIndex + 1 < args.length) {
|
|
|
|
|
options.rpcUrl = args[rpcUrlIndex + 1];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Extract validators config path
|
|
|
|
|
const configIndex = args.indexOf("--config");
|
|
|
|
|
if (configIndex !== -1 && configIndex + 1 < args.length) {
|
|
|
|
|
options.validatorsConfig = args[configIndex + 1];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Extract network name
|
|
|
|
|
const networkIndex = args.indexOf("--network");
|
|
|
|
|
if (networkIndex !== -1 && networkIndex + 1 < args.length) {
|
|
|
|
|
options.networkName = args[networkIndex + 1];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Extract custom deployment path
|
|
|
|
|
const deploymentPathIndex = args.indexOf("--deployment-path");
|
|
|
|
|
if (deploymentPathIndex !== -1 && deploymentPathIndex + 1 < args.length) {
|
|
|
|
|
options.deploymentPath = args[deploymentPathIndex + 1];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Parse signup flag
|
|
|
|
|
if (args.includes("--signup")) {
|
|
|
|
|
options.executeSignup = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check required parameters
|
|
|
|
|
if (!options.rpcUrl) {
|
|
|
|
|
console.error("Error: --rpc-url parameter is required");
|
|
|
|
|
process.exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Run setup
|
|
|
|
|
setupValidators({
|
|
|
|
|
rpcUrl: options.rpcUrl,
|
|
|
|
|
validatorsConfig: options.validatorsConfig,
|
|
|
|
|
executeSignup: options.executeSignup,
|
|
|
|
|
networkName: options.networkName,
|
|
|
|
|
deploymentPath: options.deploymentPath
|
|
|
|
|
}).catch((error) => {
|
|
|
|
|
console.error("Validator setup failed:", error);
|
|
|
|
|
process.exit(1);
|
|
|
|
|
});
|
|
|
|
|
}
|