openmina_node_testing/
main.rs1use clap::Parser;
2
3use node::p2p::webrtc::Host;
4use openmina_node_testing::{
5 cluster::{Cluster, ClusterConfig},
6 exit_with_error,
7 scenario::Scenario,
8 scenarios::Scenarios,
9 server, setup,
10};
11
12pub type CommandError = anyhow::Error;
13
14#[derive(Debug, clap::Parser)]
15#[command(name = "openmina-testing", about = "Openmina Testing Cli")]
16pub struct OpenminaTestingCli {
17 #[command(subcommand)]
18 pub command: Command,
19}
20
21#[derive(Debug, clap::Subcommand)]
22pub enum Command {
23 Server(CommandServer),
24
25 ScenariosGenerate(CommandScenariosGenerate),
26 ScenariosRun(CommandScenariosRun),
27}
28
29#[derive(Debug, clap::Args)]
30pub struct CommandServer {
31 #[arg(long, short, default_value = "127.0.0.1")]
32 pub host: Host,
33
34 #[arg(long, short, default_value = "11000")]
35 pub port: u16,
36 #[arg(long, short)]
37 pub ssl_port: Option<u16>,
38}
39
40#[derive(Debug, clap::Args)]
41pub struct CommandScenariosGenerate {
42 #[arg(long, short)]
43 pub name: Option<String>,
44 #[arg(long, short)]
45 pub use_debugger: bool,
46 #[arg(long, short)]
47 pub webrtc: bool,
48}
49
50#[derive(Debug, clap::Args)]
52pub struct CommandScenariosRun {
53 #[arg(long, short)]
57 pub name: String,
58}
59
60impl Command {
61 pub fn run(self) -> Result<(), crate::CommandError> {
62 let rt = setup();
63 let _rt_guard = rt.enter();
64
65 let (shutdown_tx, shutdown_rx) = openmina_core::channels::oneshot::channel();
66 let mut shutdown_tx = Some(shutdown_tx);
67
68 ctrlc::set_handler(move || match shutdown_tx.take() {
69 Some(tx) => {
70 let _ = tx.send(());
71 }
72 None => {
73 std::process::exit(1);
74 }
75 })
76 .expect("Error setting Ctrl-C handler");
77
78 match self {
79 Self::Server(args) => {
80 server(rt, args.host, args.port, args.ssl_port);
81 Ok(())
82 }
83 Self::ScenariosGenerate(cmd) => {
84 #[cfg(feature = "scenario-generators")]
85 {
86 let run_scenario = |scenario: Scenarios| -> Result<_, anyhow::Error> {
87 let mut config = scenario.default_cluster_config()?;
88 if cmd.use_debugger {
89 config.use_debugger();
90 }
91 if cmd.webrtc {
92 config.set_all_rust_to_rust_use_webrtc();
93 }
94 Ok(scenario.run_only_from_scratch(config))
95 };
96 let fut = async move {
97 if let Some(name) = cmd.name {
98 if let Some(scenario) = Scenarios::find_by_name(&name) {
99 run_scenario(scenario)?.await;
100 } else {
101 anyhow::bail!("no such scenario: \"{name}\"");
102 }
103 } else {
104 for scenario in Scenarios::iter() {
105 run_scenario(scenario)?.await;
106 }
107 }
108 Ok(())
109 };
110 rt.block_on(async {
111 tokio::select! {
112 res = fut => res,
113 _ = shutdown_rx => {
114 anyhow::bail!("Received ctrl-c signal! shutting down...");
115 }
116 }
117 })
118 }
119 #[cfg(not(feature = "scenario-generators"))]
120 Err("binary not compiled with `scenario-generators` feature"
121 .to_owned()
122 .into())
123 }
124 Self::ScenariosRun(cmd) => {
125 let mut config = ClusterConfig::new(None).map_err(|err| {
126 anyhow::anyhow!("failed to create cluster configuration: {err}")
127 })?;
128 config.set_replay();
129
130 let id = cmd.name.parse()?;
131 let fut = async move {
132 let mut cluster = Cluster::new(config);
133 cluster.start(Scenario::load(&id).await?).await?;
134 cluster.exec_to_end().await?;
135 for (node_id, node) in cluster.nodes_iter() {
136 let Some(best_tip) = node.state().transition_frontier.best_tip() else {
137 continue;
138 };
139
140 eprintln!(
141 "[node_status] node_{node_id} {} - {} [{}]",
142 best_tip.height(),
143 best_tip.hash(),
144 best_tip.producer()
145 );
146 }
147 Ok(())
148 };
149 rt.block_on(async {
150 tokio::select! {
151 res = fut => res,
152 _ = shutdown_rx => {
153 anyhow::bail!("Received ctrl-c signal! shutting down...");
154 }
155 }
156 })
157 }
158 }
159 }
160}
161
162pub fn main() {
163 match OpenminaTestingCli::parse().command.run() {
164 Ok(_) => {}
165 Err(err) => exit_with_error(err),
166 }
167}