Skip to content
67 changes: 35 additions & 32 deletions cli/src/cmd/testnet/add_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use clap::Parser;
use color_eyre::eyre::{eyre, Context as _};
use color_eyre::Result;
use malachitebft_eth_types::Address;
use tracing::info;
use tracing::{debug, info, warn};

use super::reth::{self, RethProcess};
use super::types::RethNode;
Expand Down Expand Up @@ -46,16 +46,19 @@ pub struct TestnetAddNodeCmd {
impl TestnetAddNodeCmd {
/// Execute the add-node command
pub fn run(&self, home_dir: &Path) -> Result<()> {
println!("📝 Adding non-validator node to testnet...\n");
info!("Adding non-validator node to testnet");

// 1. Check if custom-reth is available
print!("Checking custom-reth installation... ");
debug!("Checking custom-reth installation");
match reth::check_installation(&self.custom_reth_bin) {
Ok(version) => {
println!("✓ {}", version.lines().next().unwrap_or(&version));
info!(
"Custom-reth installation verified: {}",
version.lines().next().unwrap_or(&version)
);
}
Err(e) => {
println!("");
warn!("Custom-reth installation check failed");
return Err(e.wrap_err(
"Custom reth is not available. Make sure custom-reth/ directory exists and contains a valid reth binary."
));
Expand All @@ -64,26 +67,26 @@ impl TestnetAddNodeCmd {

// 2. Determine the next node ID
let node_id = self.find_next_node_id(home_dir)?;
println!("\n📋 Next available node ID: {node_id}");
info!("Next available node ID: {node_id}");

// 3. Create node directories
println!("\n📁 Creating node directories...");
info!("Creating node directories");
let node_home = home_dir.join(node_id.to_string());
let config_dir = node_home.join("config");
let log_dir = node_home.join("logs");
fs::create_dir_all(&config_dir)?;
fs::create_dir_all(&log_dir)?;
println!("Node directories created");
info!("Node directories created");

// 4. Copy genesis file from existing testnet
println!("\n📋 Copying genesis file...");
info!("Copying genesis file");
self.copy_genesis(home_dir, node_id)?;
println!("Genesis file copied");
info!("Genesis file copied");

// 5. Generate Malachite config
println!("\n⚙️ Generating Malachite config...");
info!("Generating Malachite config");
self.generate_malachite_config(home_dir, node_id)?;
println!("Malachite config generated");
info!("Malachite config generated");

let fee_receiver = if let Some(fee_receiver_str) = &self.fee_receiver {
Address::from(AlloyAddress::from_str(fee_receiver_str)?)
Expand All @@ -92,23 +95,23 @@ impl TestnetAddNodeCmd {
};

// 6. Generate Emerald config
println!("\n⚙️ Generating Emerald config...");
info!("Generating Emerald config");
info!("Will use address `{fee_receiver}` as Fee Receiver address");
self.generate_emerald_config(home_dir, node_id, fee_receiver)?;
println!("Emerald config generated");
info!("Emerald config generated");

// 7. Generate private validator key
println!("\n🔑 Generating private validator key...");
info!("Generating private validator key");
self.generate_private_key(home_dir, node_id)?;
println!("Private validator key generated");
info!("Private validator key generated");

// 8. Spawn Reth process
println!("\n🔗 Starting Reth execution client...");
info!("Starting Reth execution client");
let reth_process = self.spawn_reth_node(home_dir, node_id)?;
println!("Reth node started (PID: {})", reth_process.pid);
info!("Reth node started (PID: {})", reth_process.pid);

// 9. Wait for Reth node to be ready
println!("\n⏳ Waiting for Reth node to initialize...");
info!("Waiting for Reth node to initialize");
let assets_dir = home_dir.join("assets");
let reth_node = RethNode::new(
node_id,
Expand All @@ -126,22 +129,22 @@ impl TestnetAddNodeCmd {
rpc.get_block_number()
},
)?;
println!("Reth node ready");
info!("Reth node ready");

// 10. Connect to existing peers
println!("\n🔗 Connecting to existing peers...");
info!("Connecting to existing peers");
self.connect_to_peers(home_dir, node_id)?;
println!("Connected to peers");
info!("Connected to peers");

// 11. Spawn Emerald process
println!("\n💎 Starting Emerald consensus node...");
info!("Starting Emerald consensus node");
let emerald_process = self.spawn_emerald_node(home_dir, node_id)?;
println!("Emerald node started (PID: {})", emerald_process.pid);
info!("Emerald node started (PID: {})", emerald_process.pid);

println!("\n✅ Non-validator node {node_id} added successfully!");
println!("\n📁 Logs:");
println!(" Reth: {}/{}/logs/reth.log", home_dir.display(), node_id);
println!(
info!("Non-validator node {node_id} added successfully!");
info!("Logs:");
info!(" Reth: {}/{}/logs/reth.log", home_dir.display(), node_id);
info!(
" Emerald: {}/{}/logs/emerald.log",
home_dir.display(),
node_id
Expand Down Expand Up @@ -414,18 +417,18 @@ fee_recipient = "{}"
);
// Try to get enode and connect
if let Ok(enode) = existing_node.get_enode() {
print!(" Connecting to node {id}... ");
debug!("Connecting to node {id}");
if new_node.add_peer(&enode).is_ok() {
println!("");
debug!("Connected to node {id}");
connected += 1;
} else {
println!(" (skipped)");
debug!("Failed to connect to node {id} (skipped)");
}
}
}

if connected == 0 {
println!(" ⚠️ No existing peers found to connect to");
warn!("No existing peers found to connect to");
}

Ok(())
Expand Down
24 changes: 11 additions & 13 deletions cli/src/cmd/testnet/destroy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::process::Command;
use clap::Parser;
use color_eyre::eyre::eyre;
use color_eyre::Result;
use tracing::{debug, info, warn};

#[derive(Parser, Debug, Clone, PartialEq)]
pub struct TestnetDestroyCmd {
Expand All @@ -19,19 +20,16 @@ impl TestnetDestroyCmd {
/// Execute the destroy command
pub fn run(&self, home_dir: &Path) -> Result<()> {
if !home_dir.exists() {
println!(
"⚠️ Testnet directory does not exist at {}",
home_dir.display()
);
warn!("Testnet directory does not exist at {}", home_dir.display());
return Ok(());
}

// Confirm with user unless --force is specified
if !self.force {
println!("⚠️ This will stop all nodes and permanently delete all testnet data at:");
println!(" {}", home_dir.display());
println!();
print!(" Are you sure? (y/N): ");
warn!("This will stop all nodes and permanently delete all testnet data at:");
info!(" {}", home_dir.display());
info!("");
info!(" Are you sure? (y/N): ");

use std::io::{self, Write};
io::stdout().flush()?;
Expand All @@ -41,21 +39,21 @@ impl TestnetDestroyCmd {

let input = input.trim().to_lowercase();
if input != "y" && input != "yes" {
println!("Cancelled.");
info!("Cancelled.");
return Ok(());
}
}

// First, stop all running processes
println!("🛑 Stopping all running nodes...");
info!("Stopping all running nodes");
self.stop_all_nodes(home_dir)?;

println!("\n🗑️ Removing testnet data...");
info!("Removing testnet data");

// Remove the entire directory
fs::remove_dir_all(home_dir).map_err(|e| eyre!("Failed to remove directory: {}", e))?;

println!("Testnet data removed successfully");
info!("Testnet data removed successfully");

Ok(())
}
Expand Down Expand Up @@ -105,7 +103,7 @@ impl TestnetDestroyCmd {
}

if stopped_count > 0 {
println!(" Stopped {stopped_count} process(es)");
debug!("Stopped {stopped_count} process(es)");
}

Ok(())
Expand Down
13 changes: 7 additions & 6 deletions cli/src/cmd/testnet/reth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::process::Command;

use color_eyre::eyre::{eyre, Context as _};
use color_eyre::Result;
use tracing::info;

use super::rpc::RpcClient;
use super::types::{ProcessHandle, RethNode};
Expand Down Expand Up @@ -109,12 +110,12 @@ impl RethNode {

let args = self.build_args();

println!("Starting Reth node {} on ports:", self.node_id);
println!(" HTTP: {}", self.ports.http);
println!(" AuthRPC: {}", self.ports.authrpc);
println!(" Metrics: {}", self.ports.metrics);
println!(" P2P: {}", self.ports.p2p);
println!(" Logs: {}", log_file_path.display());
info!("Starting Reth node {} on ports:", self.node_id);
info!(" HTTP: {}", self.ports.http);
info!(" AuthRPC: {}", self.ports.authrpc);
info!(" Metrics: {}", self.ports.metrics);
info!(" P2P: {}", self.ports.p2p);
info!(" Logs: {}", log_file_path.display());

let pid_file = self
.home_dir
Expand Down
Loading