1#![allow(clippy::too_many_arguments)]
10
11use super::get_bin_path;
12use crate::{
13 VerbosityLevel,
14 add_services::config::PortRange,
15 local::{LocalNetworkOptions, kill_network, run_network},
16 print_banner, status_report,
17};
18use ant_bootstrap::InitialPeersConfig;
19use ant_evm::{EvmNetwork, RewardsAddress};
20use ant_logging::LogFormat;
21use ant_releases::{AntReleaseRepoActions, ReleaseType};
22use ant_service_management::{
23 NodeRegistryManager, control::ServiceController, get_local_node_registry_path,
24};
25use color_eyre::{Help, Report, Result, eyre::eyre};
26use std::{
27 path::PathBuf,
28 process::{Command, Stdio},
29};
30use sysinfo::System;
31use tokio::time::{Duration, sleep};
32
33pub async fn join(
34 build: bool,
35 count: u16,
36 enable_metrics_server: bool,
37 interval: u64,
38 metrics_port: Option<PortRange>,
39 node_path: Option<PathBuf>,
40 node_port: Option<PortRange>,
41 node_version: Option<String>,
42 log_format: Option<LogFormat>,
43 _peers_args: InitialPeersConfig,
44 rpc_port: Option<PortRange>,
45 rewards_address: RewardsAddress,
46 evm_network: EvmNetwork,
47 skip_validation: bool,
48 verbosity: VerbosityLevel,
49) -> Result<(), Report> {
50 if verbosity != VerbosityLevel::Minimal {
51 print_banner("Joining Local Network");
52 }
53 info!("Joining local network");
54
55 if (enable_metrics_server || metrics_port.is_some()) && !cfg!(feature = "open-metrics") && build
56 {
57 return Err(eyre!(
58 "Metrics server is not available. Please enable the open-metrics feature flag. Run the command with the --features open-metrics"
59 ));
60 }
61
62 let local_node_reg_path = &get_local_node_registry_path()?;
63 let local_node_registry = NodeRegistryManager::load(local_node_reg_path).await?;
64
65 let release_repo = <dyn AntReleaseRepoActions>::default_config();
66
67 let antnode_bin_path = get_bin_path(
68 build,
69 node_path,
70 ReleaseType::AntNode,
71 node_version,
72 &*release_repo,
73 verbosity,
74 )
75 .await?;
76
77 let options = LocalNetworkOptions {
78 antnode_bin_path,
79 enable_metrics_server,
80 interval,
81 join: true,
82 metrics_port,
83 node_count: count,
84 node_port,
85 peers: None,
86 rpc_port,
87 skip_validation,
88 log_format,
89 rewards_address,
90 evm_network,
91 };
92
93 ensure_evm_testnet_running(build, verbosity).await?;
95
96 run_network(options, local_node_registry, &ServiceController {}).await?;
97 Ok(())
98}
99
100pub async fn kill(keep_directories: bool, verbosity: VerbosityLevel) -> Result<()> {
101 let local_reg_path = &get_local_node_registry_path()?;
102 let local_node_registry = NodeRegistryManager::load(local_reg_path).await?;
103 if local_node_registry.nodes.read().await.is_empty() {
104 info!("No local network is currently running, cannot kill it");
105 println!("No local network is currently running");
106 } else {
107 if verbosity != VerbosityLevel::Minimal {
108 print_banner("Killing Local Network");
109 }
110 info!("Kill local network");
111 kill_network(local_node_registry, keep_directories).await?;
112 std::fs::remove_file(local_reg_path)?;
113 }
114
115 Ok(())
116}
117
118pub async fn run(
119 build: bool,
120 clean: bool,
121 count: u16,
122 enable_metrics_server: bool,
123 interval: u64,
124 metrics_port: Option<PortRange>,
125 node_path: Option<PathBuf>,
126 node_port: Option<PortRange>,
127 node_version: Option<String>,
128 log_format: Option<LogFormat>,
129 rpc_port: Option<PortRange>,
130 rewards_address: RewardsAddress,
131 evm_network: EvmNetwork,
132 skip_validation: bool,
133 verbosity: VerbosityLevel,
134) -> Result<(), Report> {
135 if (enable_metrics_server || metrics_port.is_some()) && !cfg!(feature = "open-metrics") && build
136 {
137 return Err(eyre!(
138 "Metrics server is not available. Please enable the open-metrics feature flag. Run the command with the --features open-metrics"
139 ));
140 }
141
142 let local_node_reg_path = &get_local_node_registry_path()?;
145 let local_node_registry: NodeRegistryManager = if clean {
146 debug!(
147 "Clean set to true, removing client, node dir, local registry and killing the network."
148 );
149 let client_data_path = dirs_next::data_dir()
150 .ok_or_else(|| eyre!("Could not obtain user's data directory"))?
151 .join("autonomi")
152 .join("client");
153 if client_data_path.is_dir() {
154 std::fs::remove_dir_all(client_data_path)?;
155 }
156 if local_node_reg_path.exists() {
157 std::fs::remove_file(local_node_reg_path)?;
158 }
159 kill(false, verbosity).await?;
160 NodeRegistryManager::load(local_node_reg_path).await?
161 } else {
162 let local_node_registry = NodeRegistryManager::load(local_node_reg_path).await?;
163 if !local_node_registry.nodes.read().await.is_empty() {
164 error!("A local network is already running, cannot run a new one");
165 return Err(eyre!("A local network is already running")
166 .suggestion("Use the kill command to destroy the network then try again"));
167 }
168 local_node_registry
169 };
170
171 if verbosity != VerbosityLevel::Minimal {
172 print_banner("Launching Local Network");
173 }
174 info!("Launching local network");
175
176 let release_repo = <dyn AntReleaseRepoActions>::default_config();
177
178 let antnode_bin_path = get_bin_path(
179 build,
180 node_path,
181 ReleaseType::AntNode,
182 node_version,
183 &*release_repo,
184 verbosity,
185 )
186 .await?;
187
188 ensure_evm_testnet_running(build, verbosity).await?;
190
191 let options = LocalNetworkOptions {
192 antnode_bin_path,
193 enable_metrics_server,
194 join: false,
195 interval,
196 metrics_port,
197 node_port,
198 node_count: count,
199 peers: None,
200 rpc_port,
201 skip_validation,
202 log_format,
203 rewards_address,
204 evm_network,
205 };
206 run_network(options, local_node_registry.clone(), &ServiceController {}).await?;
207
208 local_node_registry.save().await?;
209 Ok(())
210}
211
212fn get_evm_testnet_bin_path(build: bool, verbosity: VerbosityLevel) -> Result<PathBuf> {
214 if build {
215 if verbosity != VerbosityLevel::Minimal {
217 print_banner("Building evm-testnet binary");
218 }
219
220 let mut cmd = Command::new("cargo");
221 cmd.args(["build", "--release", "--bin", "evm-testnet"])
222 .stdout(if verbosity == VerbosityLevel::Minimal {
223 Stdio::null()
224 } else {
225 Stdio::inherit()
226 })
227 .stderr(if verbosity == VerbosityLevel::Minimal {
228 Stdio::null()
229 } else {
230 Stdio::inherit()
231 });
232
233 let output = cmd.output()?;
234 if !output.status.success() {
235 return Err(eyre!("Failed to build evm-testnet binary"));
236 }
237
238 let target_dir = std::env::var("CARGO_TARGET_DIR").unwrap_or_else(|_| "target".to_string());
239 Ok(PathBuf::from(target_dir)
240 .join("release")
241 .join("evm-testnet"))
242 } else {
243 match which::which("evm-testnet") {
245 Ok(path) => Ok(path),
246 Err(_) => {
247 if verbosity != VerbosityLevel::Minimal {
249 println!("evm-testnet not found in PATH, building from source...");
250 }
251
252 let mut cmd = Command::new("cargo");
253 cmd.args(["build", "--release", "--bin", "evm-testnet"])
254 .stdout(if verbosity == VerbosityLevel::Minimal {
255 Stdio::null()
256 } else {
257 Stdio::inherit()
258 })
259 .stderr(if verbosity == VerbosityLevel::Minimal {
260 Stdio::null()
261 } else {
262 Stdio::inherit()
263 });
264
265 let output = cmd.output()?;
266 if !output.status.success() {
267 return Err(eyre!("Failed to build evm-testnet binary"));
268 }
269
270 let target_dir =
271 std::env::var("CARGO_TARGET_DIR").unwrap_or_else(|_| "target".to_string());
272 Ok(PathBuf::from(target_dir)
273 .join("release")
274 .join("evm-testnet"))
275 }
276 }
277 }
278}
279
280async fn spawn_evm_testnet(build: bool, verbosity: VerbosityLevel) -> Result<()> {
282 let evm_testnet_path = get_evm_testnet_bin_path(build, verbosity)?;
283
284 if verbosity != VerbosityLevel::Minimal {
285 print_banner("Starting EVM testnet");
286 }
287
288 let mut cmd = Command::new(&evm_testnet_path);
289 cmd.stdout(if verbosity == VerbosityLevel::Minimal {
290 Stdio::null()
291 } else {
292 Stdio::inherit()
293 })
294 .stderr(if verbosity == VerbosityLevel::Minimal {
295 Stdio::null()
296 } else {
297 Stdio::inherit()
298 });
299
300 let _ = cmd.spawn()?;
301
302 sleep(Duration::from_millis(2000)).await;
304
305 Ok(())
306}
307
308fn check_evm_testnet_running() -> bool {
310 let mut system = System::new_all();
311 system.refresh_all();
312
313 for process in system.processes().values() {
315 let process_name = process.name().to_lowercase();
316 if process_name.contains("evm-testnet") || process_name.contains("anvil") {
317 return true;
318 }
319 }
320 false
321}
322
323async fn ensure_evm_testnet_running(build: bool, verbosity: VerbosityLevel) -> Result<()> {
325 if check_evm_testnet_running() {
326 if verbosity != VerbosityLevel::Minimal {
327 println!("EVM testnet is already running");
328 }
329 return Ok(());
330 }
331
332 spawn_evm_testnet(build, verbosity).await?;
333
334 let mut attempts = 0;
336 while !check_evm_testnet_running() && attempts < 30 {
337 sleep(Duration::from_millis(1000)).await;
338 attempts += 1;
339 }
340
341 if !check_evm_testnet_running() {
342 return Err(eyre!(
343 "Failed to start EVM testnet - not responding after 30 seconds"
344 ));
345 }
346
347 if verbosity != VerbosityLevel::Minimal {
348 println!("EVM testnet started successfully");
349 }
350
351 Ok(())
352}
353
354pub async fn status(details: bool, fail: bool, json: bool) -> Result<()> {
355 let local_node_registry = NodeRegistryManager::load(&get_local_node_registry_path()?).await?;
356 if !json {
357 print_banner("Local Network");
358 }
359 status_report(
360 &local_node_registry,
361 &ServiceController {},
362 details,
363 json,
364 fail,
365 true,
366 )
367 .await?;
368 local_node_registry.save().await?;
369 Ok(())
370}