2025-12-15 23:13:38 +01:00
|
|
|
use std::{env, process, time::Duration};
|
2025-12-01 12:48:39 +01:00
|
|
|
|
2025-12-15 23:13:38 +01:00
|
|
|
use anyhow::{Context as _, Result};
|
|
|
|
|
use runner_examples::{ChaosBuilderExt as _, ScenarioBuilderExt as _, read_env_any};
|
2025-12-01 12:48:39 +01:00
|
|
|
use testing_framework_core::scenario::{Deployer as _, Runner, ScenarioBuilder};
|
|
|
|
|
use testing_framework_runner_compose::{ComposeDeployer, ComposeRunnerError};
|
|
|
|
|
use tracing::{info, warn};
|
|
|
|
|
|
|
|
|
|
const DEFAULT_VALIDATORS: usize = 1;
|
|
|
|
|
const DEFAULT_EXECUTORS: usize = 1;
|
|
|
|
|
const DEFAULT_RUN_SECS: u64 = 60;
|
|
|
|
|
const MIXED_TXS_PER_BLOCK: u64 = 5;
|
2025-12-13 05:59:28 +01:00
|
|
|
const TOTAL_WALLETS: usize = 1000;
|
|
|
|
|
const TRANSACTION_WALLETS: usize = 500;
|
2025-12-01 12:48:39 +01:00
|
|
|
|
2025-12-15 22:29:36 +01:00
|
|
|
// Chaos Testing Constants
|
|
|
|
|
const CHAOS_MIN_DELAY_SECS: u64 = 120;
|
|
|
|
|
const CHAOS_MAX_DELAY_SECS: u64 = 180;
|
|
|
|
|
const CHAOS_COOLDOWN_SECS: u64 = 240;
|
2025-12-15 23:13:38 +01:00
|
|
|
const CHAOS_DELAY_HEADROOM_SECS: u64 = 1;
|
2025-12-15 22:29:36 +01:00
|
|
|
|
|
|
|
|
// DA Testing Constants
|
|
|
|
|
const DA_CHANNEL_RATE: u64 = 1;
|
|
|
|
|
const DA_BLOB_RATE: u64 = 1;
|
|
|
|
|
|
2025-12-01 12:48:39 +01:00
|
|
|
#[tokio::main]
|
|
|
|
|
async fn main() {
|
2025-12-07 04:00:13 +01:00
|
|
|
// Compose containers mount KZG params at /kzgrs_test_params; ensure the
|
|
|
|
|
// generated configs point there unless the caller overrides explicitly.
|
2025-12-15 22:29:36 +01:00
|
|
|
if env::var("NOMOS_KZGRS_PARAMS_PATH").is_err() {
|
2025-12-07 04:00:13 +01:00
|
|
|
// Safe: setting a process-wide environment variable before any threads
|
|
|
|
|
// or async tasks are spawned.
|
|
|
|
|
unsafe {
|
2025-12-15 22:29:36 +01:00
|
|
|
env::set_var(
|
2025-12-08 06:19:45 +01:00
|
|
|
"NOMOS_KZGRS_PARAMS_PATH",
|
|
|
|
|
"/kzgrs_test_params/kzgrs_test_params",
|
|
|
|
|
);
|
2025-12-07 04:00:13 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-01 12:48:39 +01:00
|
|
|
tracing_subscriber::fmt::init();
|
|
|
|
|
|
2025-12-09 06:30:18 +01:00
|
|
|
let validators = read_env_any(
|
|
|
|
|
&["NOMOS_DEMO_VALIDATORS", "COMPOSE_DEMO_VALIDATORS"],
|
|
|
|
|
DEFAULT_VALIDATORS,
|
|
|
|
|
);
|
|
|
|
|
let executors = read_env_any(
|
|
|
|
|
&["NOMOS_DEMO_EXECUTORS", "COMPOSE_DEMO_EXECUTORS"],
|
|
|
|
|
DEFAULT_EXECUTORS,
|
|
|
|
|
);
|
|
|
|
|
let run_secs = read_env_any(
|
|
|
|
|
&["NOMOS_DEMO_RUN_SECS", "COMPOSE_DEMO_RUN_SECS"],
|
|
|
|
|
DEFAULT_RUN_SECS,
|
|
|
|
|
);
|
2025-12-01 12:48:39 +01:00
|
|
|
info!(
|
|
|
|
|
validators,
|
|
|
|
|
executors, run_secs, "starting compose runner demo"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if let Err(err) = run_compose_case(validators, executors, Duration::from_secs(run_secs)).await {
|
|
|
|
|
warn!("compose runner demo failed: {err}");
|
2025-12-15 22:29:36 +01:00
|
|
|
process::exit(1);
|
2025-12-01 12:48:39 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[rustfmt::skip]
|
|
|
|
|
async fn run_compose_case(
|
|
|
|
|
validators: usize,
|
|
|
|
|
executors: usize,
|
|
|
|
|
run_duration: Duration,
|
2025-12-15 23:13:38 +01:00
|
|
|
) -> Result<()> {
|
2025-12-01 12:48:39 +01:00
|
|
|
info!(
|
|
|
|
|
validators,
|
|
|
|
|
executors,
|
|
|
|
|
duration_secs = run_duration.as_secs(),
|
|
|
|
|
"building scenario plan"
|
|
|
|
|
);
|
|
|
|
|
|
2025-12-15 23:13:38 +01:00
|
|
|
let chaos_min_delay = Duration::from_secs(CHAOS_MIN_DELAY_SECS)
|
|
|
|
|
.max(run_duration + Duration::from_secs(CHAOS_DELAY_HEADROOM_SECS));
|
|
|
|
|
let chaos_max_delay =
|
|
|
|
|
Duration::from_secs(CHAOS_MAX_DELAY_SECS).max(chaos_min_delay);
|
|
|
|
|
|
2025-12-03 05:46:32 +01:00
|
|
|
let mut plan = ScenarioBuilder::topology_with(|t| {
|
|
|
|
|
t.network_star()
|
|
|
|
|
.validators(validators)
|
|
|
|
|
.executors(executors)
|
|
|
|
|
})
|
2025-12-01 12:48:39 +01:00
|
|
|
.enable_node_control()
|
2025-12-03 05:52:14 +01:00
|
|
|
.chaos_with(|c| {
|
|
|
|
|
c.restart()
|
|
|
|
|
// Keep chaos restarts outside the test run window to avoid crash loops on restart.
|
2025-12-15 23:13:38 +01:00
|
|
|
.min_delay(chaos_min_delay)
|
|
|
|
|
.max_delay(chaos_max_delay)
|
2025-12-15 22:29:36 +01:00
|
|
|
.target_cooldown(Duration::from_secs(CHAOS_COOLDOWN_SECS))
|
2025-12-03 05:52:14 +01:00
|
|
|
.apply()
|
|
|
|
|
})
|
2025-12-01 12:48:39 +01:00
|
|
|
.wallets(TOTAL_WALLETS)
|
2025-12-03 05:52:14 +01:00
|
|
|
.transactions_with(|txs| {
|
|
|
|
|
txs.rate(MIXED_TXS_PER_BLOCK)
|
|
|
|
|
.users(TRANSACTION_WALLETS)
|
|
|
|
|
})
|
|
|
|
|
.da_with(|da| {
|
2025-12-15 22:29:36 +01:00
|
|
|
da.channel_rate(DA_CHANNEL_RATE)
|
|
|
|
|
.blob_rate(DA_BLOB_RATE)
|
2025-12-03 05:52:14 +01:00
|
|
|
})
|
2025-12-01 12:48:39 +01:00
|
|
|
.with_run_duration(run_duration)
|
|
|
|
|
.expect_consensus_liveness()
|
|
|
|
|
.build();
|
|
|
|
|
|
|
|
|
|
let deployer = ComposeDeployer::new();
|
|
|
|
|
info!("deploying compose stack");
|
2025-12-15 20:43:25 +01:00
|
|
|
|
2025-12-01 12:48:39 +01:00
|
|
|
let runner: Runner = match deployer.deploy(&plan).await {
|
|
|
|
|
Ok(runner) => runner,
|
|
|
|
|
Err(ComposeRunnerError::DockerUnavailable) => {
|
|
|
|
|
warn!("Docker is unavailable; cannot run compose demo");
|
|
|
|
|
return Ok(());
|
|
|
|
|
}
|
2025-12-15 23:13:38 +01:00
|
|
|
Err(err) => return Err(anyhow::Error::new(err)).context("deploying compose stack failed"),
|
2025-12-01 12:48:39 +01:00
|
|
|
};
|
2025-12-15 22:29:36 +01:00
|
|
|
|
2025-12-01 12:48:39 +01:00
|
|
|
if !runner.context().telemetry().is_configured() {
|
|
|
|
|
warn!("compose runner should expose prometheus metrics");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
info!("running scenario");
|
2025-12-15 23:13:38 +01:00
|
|
|
runner
|
|
|
|
|
.run(&mut plan)
|
|
|
|
|
.await
|
|
|
|
|
.context("running compose scenario failed")?;
|
|
|
|
|
Ok(())
|
2025-12-01 12:48:39 +01:00
|
|
|
}
|