mirror of
https://github.com/datahaven-xyz/datahaven
synced 2026-05-24 09:50:01 +00:00
134 lines
4.6 KiB
TypeScript
134 lines
4.6 KiB
TypeScript
import fs from "node:fs";
|
|
import path from "node:path";
|
|
// Update validator set on DataHaven substrate chain
|
|
import { $ } from "bun";
|
|
import invariant from "tiny-invariant";
|
|
import { logger } from "../utils/index";
|
|
|
|
interface UpdateValidatorSetOptions {
|
|
rpcUrl: string;
|
|
targetEra?: bigint;
|
|
}
|
|
|
|
/**
|
|
* Sends the validator set to the DataHaven chain through Snowbridge
|
|
*
|
|
* @param options - Configuration options for update
|
|
* @param options.rpcUrl - The RPC URL to connect to
|
|
* @returns Promise resolving to true if validator set was sent successfully, false if skipped
|
|
*/
|
|
export const updateValidatorSet = async (options: UpdateValidatorSetOptions): Promise<boolean> => {
|
|
const { rpcUrl } = options;
|
|
|
|
// Validate RPC URL
|
|
invariant(rpcUrl, "❌ RPC URL is required");
|
|
|
|
// Get cast path for transactions
|
|
const { stdout: castPath } = await $`which cast`.quiet();
|
|
const castExecutable = castPath.toString().trim();
|
|
|
|
// Get the owner's private key for transaction signing from the .env
|
|
const ownerPrivateKey =
|
|
process.env.AVS_OWNER_PRIVATE_KEY ||
|
|
"0x92db14e403b83dfe3df233f83dfa3a0d7096f21ca9b0d6d6b8d88b2b4ec1564e"; // Sixth pre-funded account from Anvil
|
|
|
|
// Get deployed contract addresses from the deployments file
|
|
const deploymentPath = path.resolve("../contracts/deployments/anvil.json");
|
|
|
|
if (!fs.existsSync(deploymentPath)) {
|
|
logger.error(`Deployment file not found: ${deploymentPath}`);
|
|
return false;
|
|
}
|
|
|
|
const deployments = JSON.parse(fs.readFileSync(deploymentPath, "utf8"));
|
|
|
|
// Prepare command to send validator set
|
|
const serviceManagerAddress = deployments.ServiceManager;
|
|
invariant(serviceManagerAddress, "ServiceManager address not found in deployments");
|
|
|
|
// Security Note: Private key is passed via stdin with --interactive flag
|
|
// using printf to avoid command-line exposure in process lists (security best practice)
|
|
// Using cast to send the transaction
|
|
const executionFee = "100000000000000000"; // 0.1 ETH
|
|
const relayerFee = "200000000000000000"; // 0.2 ETH
|
|
const value = "300000000000000000"; // 0.3 ETH (sum of fees)
|
|
const targetEra = options.targetEra ?? 1n;
|
|
|
|
if (options.targetEra === undefined) {
|
|
logger.warn(
|
|
"No target era specified; defaulting to era 1. Use --target-era for already-running networks."
|
|
);
|
|
}
|
|
|
|
const sendCommand = `${castExecutable} send --private-key ${ownerPrivateKey} --value ${value} ${serviceManagerAddress} "sendNewValidatorSetForEra(uint64,uint128,uint128)" ${targetEra} ${executionFee} ${relayerFee} --rpc-url ${rpcUrl}`;
|
|
|
|
logger.debug(`Running command: ${sendCommand}`);
|
|
|
|
const { exitCode, stderr } = await $`sh -c ${sendCommand}`
|
|
.env({ ...process.env, PRIVATE_KEY: ownerPrivateKey })
|
|
.nothrow()
|
|
.quiet();
|
|
|
|
if (exitCode !== 0) {
|
|
logger.error(`Failed to send validator set: ${stderr.toString()}`);
|
|
return false;
|
|
}
|
|
|
|
logger.success("Validator set sent to Snowbridge Gateway");
|
|
|
|
// Check if the validator set has been queued on the substrate side (placeholder)
|
|
logger.debug("Checking validator set on substrate chain (not implemented)");
|
|
/*
|
|
// PLACEHOLDER: Code to check if validator set has been queued on substrate
|
|
// This requires a connection to the DataHaven substrate node which is not available yet
|
|
|
|
// Example of what this might look like:
|
|
const substrateApi = await ApiPromise.create({ provider: new WsProvider('ws://localhost:9944') });
|
|
const validatorSetModule = substrateApi.query.validatorSet;
|
|
const queuedValidators = await validatorSetModule.queuedValidators();
|
|
|
|
if (queuedValidators.length === validators.length) {
|
|
logger.success('Validator set successfully queued on substrate chain');
|
|
} else {
|
|
logger.warn('Validator set not properly queued on substrate chain');
|
|
}
|
|
*/
|
|
|
|
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;
|
|
targetEra?: bigint;
|
|
} = {};
|
|
|
|
// Extract RPC URL
|
|
const rpcUrlIndex = args.indexOf("--rpc-url");
|
|
if (rpcUrlIndex !== -1 && rpcUrlIndex + 1 < args.length) {
|
|
options.rpcUrl = args[rpcUrlIndex + 1];
|
|
}
|
|
|
|
// Extract target era
|
|
const targetEraIndex = args.indexOf("--target-era");
|
|
if (targetEraIndex !== -1 && targetEraIndex + 1 < args.length) {
|
|
options.targetEra = BigInt(args[targetEraIndex + 1]);
|
|
}
|
|
|
|
// Check required parameters
|
|
if (!options.rpcUrl) {
|
|
console.error("Error: --rpc-url parameter is required");
|
|
process.exit(1);
|
|
}
|
|
|
|
// Run update
|
|
updateValidatorSet({
|
|
rpcUrl: options.rpcUrl,
|
|
targetEra: options.targetEra
|
|
}).catch((error) => {
|
|
console.error("Validator set update failed:", error);
|
|
process.exit(1);
|
|
});
|
|
}
|