From 1b70ae48a7c78f71783df5b4427f5b8f33356d6c Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Mon, 12 Feb 2024 13:16:31 +0000 Subject: [PATCH 01/27] create nix scripts, create clap CLI --- m1/scripts/default.nix | 14 +++++++++++++ m1/scripts/simulator.sh | 45 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 m1/scripts/default.nix create mode 100755 m1/scripts/simulator.sh diff --git a/m1/scripts/default.nix b/m1/scripts/default.nix new file mode 100644 index 00000000..7b6bc356 --- /dev/null +++ b/m1/scripts/default.nix @@ -0,0 +1,14 @@ +{ pkgs ? import {} }: + +pkgs.stdenv.mkDerivation { + name = "simulator"; + buildInputs = [ + pkgs.rustc + pkgs.cargo + ]; + + shellHook = '' + echo "Welcome to the development environment for your-project-name" + # Any setup commands you need to run when entering the shell + ''; +} diff --git a/m1/scripts/simulator.sh b/m1/scripts/simulator.sh new file mode 100755 index 00000000..bdf8b942 --- /dev/null +++ b/m1/scripts/simulator.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env nix-shell +#!nix-shell -i bash + +# Dummy function placeholders for actual commands that would be implemented + +start_network() { + echo "Starting network with 5 validators..." + # Placeholder for command to start the network +} + +simulate_partition() { + echo "Simulating network partition..." + # Placeholder for command to partition the network +} + +reconnect_nodes() { + echo "Reconnecting partitioned nodes..." + # Placeholder for command to reconnect nodes +} + +observe_health() { + echo "Observing consensus health..." + # Placeholder for command to observe the network's consensus health +} + +# Script execution flow + +# Step 1: Start the network +start_network + +# Wait for the network to stabilize +sleep 5 + +# Step 2: Simulate network partition +simulate_partition + +# Simulate some duration of network partition +sleep 10 + +# Step 3: Reconnect the nodes +reconnect_nodes + +# Observe the network for a period to assess impact on consensus health +sleep 5 +observe_health From 1024138bb4af4cff7f52f5748dea01ddb3d3fe8f Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Mon, 12 Feb 2024 16:34:43 +0000 Subject: [PATCH 02/27] move resuable utils into simulator --- m1/Cargo.lock | 73 ++++++++- m1/Cargo.toml | 4 +- m1/simulator/Cargo.toml | 17 +++ m1/simulator/src/lib.rs | 272 ++++++++++++++++++++++++++++++++++ m1/simulator/src/main.rs | 103 +++++++++++++ m1/tests/e2e/Cargo.toml | 2 + m1/tests/e2e/src/lib.rs | 27 ---- m1/tests/e2e/src/tests/mod.rs | 232 +---------------------------- 8 files changed, 472 insertions(+), 258 deletions(-) create mode 100644 m1/simulator/Cargo.toml create mode 100644 m1/simulator/src/lib.rs create mode 100644 m1/simulator/src/main.rs diff --git a/m1/Cargo.lock b/m1/Cargo.lock index eb9d3fc7..084d2fc5 100644 --- a/m1/Cargo.lock +++ b/m1/Cargo.lock @@ -3177,6 +3177,23 @@ dependencies = [ "vec_map", ] +[[package]] +name = "clap" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +dependencies = [ + "atty", + "bitflags 1.3.2", + "clap_derive 3.2.25", + "clap_lex 0.2.4", + "indexmap 1.9.3", + "once_cell", + "strsim 0.10.0", + "termcolor", + "textwrap 0.16.0", +] + [[package]] name = "clap" version = "4.4.11" @@ -3184,7 +3201,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" dependencies = [ "clap_builder", - "clap_derive", + "clap_derive 4.4.7", ] [[package]] @@ -3195,10 +3212,23 @@ checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" dependencies = [ "anstream", "anstyle", - "clap_lex", + "clap_lex 0.6.0", "strsim 0.10.0", ] +[[package]] +name = "clap_derive" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" +dependencies = [ + "heck 0.4.1", + "proc-macro-error", + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 1.0.109", +] + [[package]] name = "clap_derive" version = "4.4.7" @@ -3211,6 +3241,15 @@ dependencies = [ "syn 2.0.41", ] +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "clap_lex" version = "0.6.0" @@ -3961,6 +4000,7 @@ dependencies = [ "log", "random-manager", "serde_json", + "simulator", "subnet", "tempfile", "tokio", @@ -6820,6 +6860,12 @@ dependencies = [ "num-traits 0.2.17", ] +[[package]] +name = "os_str_bytes" +version = "6.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" + [[package]] name = "ouroboros" version = "0.9.5" @@ -8941,6 +8987,23 @@ dependencies = [ "termcolor", ] +[[package]] +name = "simulator" +version = "0.1.0" +dependencies = [ + "anyhow", + "aptos-sdk", + "avalanche-installer", + "avalanche-network-runner-sdk", + "avalanche-types", + "clap 3.2.25", + "env_logger", + "once_cell", + "rand 0.7.3", + "tokio", + "url 2.5.0", +] + [[package]] name = "siphasher" version = "0.3.11" @@ -9384,6 +9447,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "textwrap" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" + [[package]] name = "thiserror" version = "1.0.50" diff --git a/m1/Cargo.toml b/m1/Cargo.toml index 13858016..4d65489b 100644 --- a/m1/Cargo.toml +++ b/m1/Cargo.toml @@ -3,7 +3,8 @@ resolver = "2" members = [ "subnet", "tests/e2e", - "e2e-benchmark" + "e2e-benchmark", + "simulator", ] [workspace.package] @@ -35,6 +36,7 @@ codespan-reporting = "0.11.1" criterion = "0.3.5" criterion-cpu-time = "0.1.0" dirs = "4.0.0" +env_logger = "0.10.1" hex = "0.4.3" hkdf = "0.10.0" hostname = "0.3.1" diff --git a/m1/simulator/Cargo.toml b/m1/simulator/Cargo.toml new file mode 100644 index 00000000..72e459c7 --- /dev/null +++ b/m1/simulator/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "simulator" +version = "0.1.0" +edition = "2021" + +[dependencies] +avalanche-installer = "0.0.77" +avalanche-network-runner-sdk = "0.3.3" # https://crates.io/crates/avalanche-network-runner-sdk +avalanche-types = { workspace = true } # https://crates.io/crates/avalanche-types +aptos-sdk = {workspace = true } +anyhow = { workspace = true } +env_logger = { workspace = true } +url = { workspace = true } +tokio = { workspace = true } +once_cell = { workspace = true } +rand = { workspace = true } +clap = { workspace = true } \ No newline at end of file diff --git a/m1/simulator/src/lib.rs b/m1/simulator/src/lib.rs new file mode 100644 index 00000000..38adad47 --- /dev/null +++ b/m1/simulator/src/lib.rs @@ -0,0 +1,272 @@ +use core::time; +use std::{ + io, + fs::{self, File}, + path::Path, + str::FromStr, + thread, + time::{Duration, Instant}, io::Write, +}; + +use avalanche_network_runner_sdk::{BlockchainSpec, Client, GlobalConfig, StartRequest}; +use avalanche_types::{ids, jsonrpc::client::info as avalanche_sdk_info, subnet}; + +#[must_use] +pub fn get_network_runner_grpc_endpoint() -> (String, bool) { + match std::env::var("NETWORK_RUNNER_GRPC_ENDPOINT") { + Ok(s) => (s, true), + _ => (String::new(), false), + } +} + +#[must_use] +pub fn get_network_runner_enable_shutdown() -> bool { + matches!(std::env::var("NETWORK_RUNNER_ENABLE_SHUTDOWN"), Ok(_)) +} + +#[must_use] +pub fn get_avalanchego_path() -> (String, bool) { + match std::env::var("AVALANCHEGO_PATH") { + Ok(s) => (s, true), + _ => (String::new(), false), + } +} + +#[must_use] +pub fn get_vm_plugin_path() -> (String, bool) { + match std::env::var("VM_PLUGIN_PATH") { + Ok(s) => (s, true), + _ => (String::new(), false), + } +} + +const AVALANCHEGO_VERSION: &str = "v1.10.9"; + +// todo: extracted from genesis method +// todo: really we should use a genesis once more +pub fn sync_genesis(byte_string : &str, file_path: &str) -> io::Result<()> { + log::info!("syncing genesis to '{}'", file_path); + + let path = Path::new(file_path); + let parent_dir = path.parent().expect("Invalid path"); + fs::create_dir_all(parent_dir)?; + + let d = byte_string.as_bytes(); + + let mut f = File::create(file_path)?; + f.write_all(&d)?; + + Ok(()) +} + +pub async fn init_m1_network() { + let _ = env_logger::builder() + .filter_level(log::LevelFilter::Info) + .is_test(true) + .try_init(); + + let (ep, is_set) = get_network_runner_grpc_endpoint(); + assert!(is_set); + + let cli = Client::new(&ep).await; + + log::info!("ping..."); + let resp = cli.ping().await.expect("failed ping"); + log::info!("network-runner is running (ping response {:?})", resp); + + let (vm_plugin_path, exists) = get_vm_plugin_path(); + log::info!("Vm Plugin path: {vm_plugin_path}"); + assert!(exists); + assert!(Path::new(&vm_plugin_path).exists()); + + let vm_id = Path::new(&vm_plugin_path) + .file_stem() + .unwrap() + .to_str() + .unwrap() + .to_string(); + // ! for now, we hardcode the id to be subnet for orchestration + let vm_id = subnet::vm_name_to_id("subnet").unwrap(); + + let (mut avalanchego_exec_path, _) = get_avalanchego_path(); + let plugins_dir = if !avalanchego_exec_path.is_empty() { + let parent_dir = Path::new(&avalanchego_exec_path) + .parent() + .expect("unexpected None parent"); + parent_dir + .join("plugins") + .as_os_str() + .to_str() + .unwrap() + .to_string() + } else { + let exec_path = avalanche_installer::avalanchego::github::download( + None, + None, + Some(AVALANCHEGO_VERSION.to_string()), + ) + .await + .unwrap(); + avalanchego_exec_path = exec_path; + avalanche_installer::avalanchego::get_plugin_dir(&avalanchego_exec_path) + }; + + log::info!( + "copying vm plugin {} to {}/{}", + vm_plugin_path, + plugins_dir, + vm_id + ); + + fs::create_dir(&plugins_dir).unwrap(); + fs::copy( + &vm_plugin_path, + Path::new(&plugins_dir).join(vm_id.to_string()), + ) + .unwrap(); + + // write some random genesis file + let genesis = random_manager::secure_string(10); + + let genesis_file_path = random_manager::tmp_path(10, None).unwrap(); + sync_genesis(genesis.as_ref(), &genesis_file_path).unwrap(); + + log::info!( + "starting {} with avalanchego {}, genesis file path {}", + vm_id, + &avalanchego_exec_path, + genesis_file_path, + ); + let resp = cli + .start(StartRequest { + exec_path: avalanchego_exec_path, + num_nodes: Some(5), + plugin_dir: plugins_dir, + global_node_config: Some( + serde_json::to_string(&GlobalConfig { + log_level: String::from("info"), + }) + .unwrap(), + ), + blockchain_specs: vec![BlockchainSpec { + vm_name: String::from("subnet"), + genesis: genesis_file_path.to_string(), + // blockchain_alias : String::from("subnet"), // todo: this doesn't always work oddly enough, need to debug + ..Default::default() + }], + ..Default::default() + }) + .await + .expect("failed start"); + log::info!( + "started avalanchego cluster with network-runner: {:?}", + resp + ); + + // enough time for network-runner to get ready + thread::sleep(Duration::from_secs(20)); + + log::info!("checking cluster healthiness..."); + let mut ready = false; + + let timeout = Duration::from_secs(300); + let interval = Duration::from_secs(15); + let start = Instant::now(); + let mut cnt: u128 = 0; + loop { + let elapsed = start.elapsed(); + if elapsed.gt(&timeout) { + break; + } + + let itv = { + if cnt == 0 { + // first poll with no wait + Duration::from_secs(1) + } else { + interval + } + }; + thread::sleep(itv); + + ready = { + match cli.health().await { + Ok(_) => { + log::info!("healthy now!"); + true + } + Err(e) => { + log::warn!("not healthy yet {}", e); + false + } + } + }; + if ready { + break; + } + + cnt += 1; + } + assert!(ready); + + log::info!("checking status..."); + let mut status = cli.status().await.expect("failed status"); + loop { + let elapsed = start.elapsed(); + if elapsed.gt(&timeout) { + break; + } + + if let Some(ci) = &status.cluster_info { + if !ci.custom_chains.is_empty() { + break; + } + } + + log::info!("retrying checking status..."); + thread::sleep(interval); + status = cli.status().await.expect("failed status"); + } + + assert!(status.cluster_info.is_some()); + let cluster_info = status.cluster_info.unwrap(); + let mut rpc_eps: Vec = Vec::new(); + for (node_name, iv) in cluster_info.node_infos.into_iter() { + log::info!("{}: {}", node_name, iv.uri); + rpc_eps.push(iv.uri.clone()); + } + let mut blockchain_id = ids::Id::empty(); + for (k, v) in cluster_info.custom_chains.iter() { + log::info!("custom chain info: {}={:?}", k, v); + if v.chain_name == "subnet" { + blockchain_id = ids::Id::from_str(&v.chain_id).unwrap(); + break; + } + } + log::info!("avalanchego RPC endpoints: {:?}", rpc_eps); + + let resp = avalanche_sdk_info::get_network_id(&rpc_eps[0]) + .await + .unwrap(); + let network_id = resp.result.unwrap().network_id; + log::info!("network Id: {}", network_id); + + // keep alive by sleeping for duration provided by SUBNET_TIMEOUT environment variable + // use sensible default + + let val = std::env::var("SUBNET_TIMEOUT") + .unwrap_or_else(|_| "0".to_string()) + .parse::() + .unwrap(); + + log::info!("sleeping for {} seconds", timeout.as_secs()); + if val < 0 { + // run forever + loop { + thread::sleep(Duration::from_secs(1000)); + } + } else { + let timeout = Duration::from_secs(val as u64); + thread::sleep(timeout); + } +} \ No newline at end of file diff --git a/m1/simulator/src/main.rs b/m1/simulator/src/main.rs new file mode 100644 index 00000000..d5a22f5a --- /dev/null +++ b/m1/simulator/src/main.rs @@ -0,0 +1,103 @@ +use clap::{Parser, Subcommand}; + +type NodeId = u64; + +#[derive(Debug, Parser)] +#[clap(name = "forc index", about = "M1 network simulator", version = "0.1")] +pub struct Cli { + /// The command to run + #[clap(subcommand)] + pub command: SubCommands, +} + +/// Start the simulator +#[derive(Debug, Parser)] +pub struct StartCommand { + /// The number of validators for the network + #[clap(long, default_value = "5", help = "The number of validators for the network.")] + pub validators: u64, + + /// Sets if the validators join the network at once, or in a staggered way + #[clap(long, default_value = "false", help = "Sets if the validators join the network at once, or in a staggered way.")] + pub staggered: bool, + + /// Verbose output + #[clap(short, long, help = "Verbose output.")] + pub verbose: bool +} + +/// Partition the network +#[derive(Debug, Parser)] +pub struct PartitionCommand { + /// The percentage of validators that will be partitioned + #[clap(long, default_value = "5", help = "The percentage of validators that will be in a partitioned state")] + pub amount: u8, + + /// Sets if the validators become paritioned at once or in a staggered way + #[clap(long, default_value = "false", help = "Sets if the validators become partitioned at once or in a staggered way.")] + pub staggered: bool, + + /// Verbose output + #[clap(short, long, help = "Verbose output.")] + pub verbose: bool +} + +#[derive(Debug, Parser)] +pub struct ReconnectCommand { + /// The nodes to reconnect by `NodeId` + pub nodes: Vec, + + /// Sets if the validators rejoin the network together or in a staggered way + #[clap(long, default_value = "false")] + pub staggered: bool, + + /// Verbose output + #[clap(short, long, help = "Verbose output.")] + pub verbose: bool +} + +#[derive(Debug, Parser)] +pub struct HealthCommand { + /// Verbose ouput + #[clap(short, long, help = "Verbose output.")] + pub verbose: bool +} + +#[derive(Debug, Subcommand)] +pub enum SubCommands{ + /// Starts the network with a number of validators + Start(StartCommand), + /// Simulates a network partition. + Partition(PartitionCommand), + /// Reconnects the validators after they have become partitioned + Reconnect(ReconnectCommand), + /// Output the overall network and consensus health + Health(HealthCommand), +} + +fn main() { + let cli = Cli::parse(); + match cli.command { + SubCommands::Start(opts) => start_network(opts), + SubCommands::Partition(opts) => partition_network(opts), + SubCommands::Reconnect(opts) => reconnect_validators(opts), + SubCommands::Health(opts) => network_health(opts), + } +} + +fn start_network(opts: StartCommand) { + simulator::init_m1_network() + +} + +fn partition_network(opts: PartitionCommand) { + +} + +fn reconnect_validators(opts: ReconnectCommand) { + +} + +fn network_health(opts: HealthCommand) { + +} \ No newline at end of file diff --git a/m1/tests/e2e/Cargo.toml b/m1/tests/e2e/Cargo.toml index 69c36c12..8c6fb749 100644 --- a/m1/tests/e2e/Cargo.toml +++ b/m1/tests/e2e/Cargo.toml @@ -9,6 +9,8 @@ license = "BSD-3-Clause" homepage = "https://avax.network" [dependencies] +tokio = { workspace = true } +simulator = { path = "../../simulator" } [dev-dependencies] avalanche-installer = "0.0.77" diff --git a/m1/tests/e2e/src/lib.rs b/m1/tests/e2e/src/lib.rs index 6e11c1a5..318e31bc 100644 --- a/m1/tests/e2e/src/lib.rs +++ b/m1/tests/e2e/src/lib.rs @@ -1,31 +1,4 @@ #[cfg(test)] mod tests; -#[must_use] -pub fn get_network_runner_grpc_endpoint() -> (String, bool) { - match std::env::var("NETWORK_RUNNER_GRPC_ENDPOINT") { - Ok(s) => (s, true), - _ => (String::new(), false), - } -} -#[must_use] -pub fn get_network_runner_enable_shutdown() -> bool { - matches!(std::env::var("NETWORK_RUNNER_ENABLE_SHUTDOWN"), Ok(_)) -} - -#[must_use] -pub fn get_avalanchego_path() -> (String, bool) { - match std::env::var("AVALANCHEGO_PATH") { - Ok(s) => (s, true), - _ => (String::new(), false), - } -} - -#[must_use] -pub fn get_vm_plugin_path() -> (String, bool) { - match std::env::var("VM_PLUGIN_PATH") { - Ok(s) => (s, true), - _ => (String::new(), false), - } -} diff --git a/m1/tests/e2e/src/tests/mod.rs b/m1/tests/e2e/src/tests/mod.rs index 6382b619..930619d1 100644 --- a/m1/tests/e2e/src/tests/mod.rs +++ b/m1/tests/e2e/src/tests/mod.rs @@ -10,236 +10,12 @@ use std::{ use avalanche_network_runner_sdk::{BlockchainSpec, Client, GlobalConfig, StartRequest}; use avalanche_types::{ids, jsonrpc::client::info as avalanche_sdk_info, subnet}; +use simulator::{get_network_runner_grpc_endpoint, get_network_runner_enable_shutdown, +get_avalanchego_path, get_vm_plugin_path, init_m1_network}; const AVALANCHEGO_VERSION: &str = "v1.10.9"; -// todo: extracted from genesis method -// todo: really we should use a genesis once more -pub fn sync_genesis(byte_string : &str, file_path: &str) -> io::Result<()> { - log::info!("syncing genesis to '{}'", file_path); - - let path = Path::new(file_path); - let parent_dir = path.parent().expect("Invalid path"); - fs::create_dir_all(parent_dir)?; - - let d = byte_string.as_bytes(); - - let mut f = File::create(file_path)?; - f.write_all(&d)?; - - Ok(()) -} - -#[tokio::test] +#[tokio::test] async fn e2e() { - let _ = env_logger::builder() - .filter_level(log::LevelFilter::Info) - .is_test(true) - .try_init(); - - let (ep, is_set) = crate::get_network_runner_grpc_endpoint(); - assert!(is_set); - - let cli = Client::new(&ep).await; - - log::info!("ping..."); - let resp = cli.ping().await.expect("failed ping"); - log::info!("network-runner is running (ping response {:?})", resp); - - let (vm_plugin_path, exists) = crate::get_vm_plugin_path(); - log::info!("Vm Plugin path: {vm_plugin_path}"); - assert!(exists); - assert!(Path::new(&vm_plugin_path).exists()); - - let vm_id = Path::new(&vm_plugin_path) - .file_stem() - .unwrap() - .to_str() - .unwrap() - .to_string(); - // ! for now, we hardcode the id to be subnet for orchestration - let vm_id = subnet::vm_name_to_id("subnet").unwrap(); - - let (mut avalanchego_exec_path, _) = crate::get_avalanchego_path(); - let plugins_dir = if !avalanchego_exec_path.is_empty() { - let parent_dir = Path::new(&avalanchego_exec_path) - .parent() - .expect("unexpected None parent"); - parent_dir - .join("plugins") - .as_os_str() - .to_str() - .unwrap() - .to_string() - } else { - let exec_path = avalanche_installer::avalanchego::github::download( - None, - None, - Some(AVALANCHEGO_VERSION.to_string()), - ) - .await - .unwrap(); - avalanchego_exec_path = exec_path; - avalanche_installer::avalanchego::get_plugin_dir(&avalanchego_exec_path) - }; - - log::info!( - "copying vm plugin {} to {}/{}", - vm_plugin_path, - plugins_dir, - vm_id - ); - - fs::create_dir(&plugins_dir).unwrap(); - fs::copy( - &vm_plugin_path, - Path::new(&plugins_dir).join(vm_id.to_string()), - ) - .unwrap(); - - // write some random genesis file - let genesis = random_manager::secure_string(10); - - let genesis_file_path = random_manager::tmp_path(10, None).unwrap(); - sync_genesis(genesis.as_ref(), &genesis_file_path).unwrap(); - - log::info!( - "starting {} with avalanchego {}, genesis file path {}", - vm_id, - &avalanchego_exec_path, - genesis_file_path, - ); - let resp = cli - .start(StartRequest { - exec_path: avalanchego_exec_path, - num_nodes: Some(5), - plugin_dir: plugins_dir, - global_node_config: Some( - serde_json::to_string(&GlobalConfig { - log_level: String::from("info"), - }) - .unwrap(), - ), - blockchain_specs: vec![BlockchainSpec { - vm_name: String::from("subnet"), - genesis: genesis_file_path.to_string(), - // blockchain_alias : String::from("subnet"), // todo: this doesn't always work oddly enough, need to debug - ..Default::default() - }], - ..Default::default() - }) - .await - .expect("failed start"); - log::info!( - "started avalanchego cluster with network-runner: {:?}", - resp - ); - - // enough time for network-runner to get ready - thread::sleep(Duration::from_secs(20)); - - log::info!("checking cluster healthiness..."); - let mut ready = false; - - let timeout = Duration::from_secs(300); - let interval = Duration::from_secs(15); - let start = Instant::now(); - let mut cnt: u128 = 0; - loop { - let elapsed = start.elapsed(); - if elapsed.gt(&timeout) { - break; - } - - let itv = { - if cnt == 0 { - // first poll with no wait - Duration::from_secs(1) - } else { - interval - } - }; - thread::sleep(itv); - - ready = { - match cli.health().await { - Ok(_) => { - log::info!("healthy now!"); - true - } - Err(e) => { - log::warn!("not healthy yet {}", e); - false - } - } - }; - if ready { - break; - } - - cnt += 1; - } - assert!(ready); - - log::info!("checking status..."); - let mut status = cli.status().await.expect("failed status"); - loop { - let elapsed = start.elapsed(); - if elapsed.gt(&timeout) { - break; - } - - if let Some(ci) = &status.cluster_info { - if !ci.custom_chains.is_empty() { - break; - } - } - - log::info!("retrying checking status..."); - thread::sleep(interval); - status = cli.status().await.expect("failed status"); - } - - assert!(status.cluster_info.is_some()); - let cluster_info = status.cluster_info.unwrap(); - let mut rpc_eps: Vec = Vec::new(); - for (node_name, iv) in cluster_info.node_infos.into_iter() { - log::info!("{}: {}", node_name, iv.uri); - rpc_eps.push(iv.uri.clone()); - } - let mut blockchain_id = ids::Id::empty(); - for (k, v) in cluster_info.custom_chains.iter() { - log::info!("custom chain info: {}={:?}", k, v); - if v.chain_name == "subnet" { - blockchain_id = ids::Id::from_str(&v.chain_id).unwrap(); - break; - } - } - log::info!("avalanchego RPC endpoints: {:?}", rpc_eps); - - let resp = avalanche_sdk_info::get_network_id(&rpc_eps[0]) - .await - .unwrap(); - let network_id = resp.result.unwrap().network_id; - log::info!("network Id: {}", network_id); - - // keep alive by sleeping for duration provided by SUBNET_TIMEOUT environment variable - // use sensible default - - let val = std::env::var("SUBNET_TIMEOUT") - .unwrap_or_else(|_| "0".to_string()) - .parse::() - .unwrap(); - - log::info!("sleeping for {} seconds", timeout.as_secs()); - if val < 0 { - // run forever - loop { - thread::sleep(Duration::from_secs(1000)); - } - } else { - let timeout = Duration::from_secs(val as u64); - thread::sleep(timeout); - } - + init_m1_network() } From a5809945e3b70d8d877ea8fd41d09ede99574684 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Mon, 12 Feb 2024 17:02:11 +0000 Subject: [PATCH 03/27] fix bug in build.debug for protoc v comparison --- m1/scripts/build.debug.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/m1/scripts/build.debug.sh b/m1/scripts/build.debug.sh index d4b63c7c..c9289a61 100755 --- a/m1/scripts/build.debug.sh +++ b/m1/scripts/build.debug.sh @@ -1,4 +1,5 @@ #!/usr/bin/env bash +#!/usr/bin/env bash set -xue if ! [[ "$0" =~ scripts/build.debug.sh ]]; then @@ -7,8 +8,10 @@ if ! [[ "$0" =~ scripts/build.debug.sh ]]; then fi PROTOC_VERSION=$(protoc --version | cut -f2 -d' ') -if [[ "${PROTOC_VERSION}" == "" ]] || [[ "${PROTOC_VERSION}" < 3.15.0 ]]; then - echo "protoc must be installed and the version must be greater than 3.15.0" +MIN_VERSION="3.15" + +if ! printf "%s\n%s" "$PROTOC_VERSION" "$MIN_VERSION" | sort -V | tail -n 1 | grep -q "$PROTOC_VERSION"; then + echo "protoc must be installed and the version must be greater than 3.15" exit 255 fi @@ -18,4 +21,4 @@ cargo build -p subnet --bin subnet ./target/debug/subnet --help ./target/debug/subnet genesis "hello world" -./target/debug/subnet vm-id subnet \ No newline at end of file +./target/debug/subnet vm-id subnet From a473901371b5e4aada682e58e97de450e905cae9 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Mon, 12 Feb 2024 18:27:43 +0000 Subject: [PATCH 04/27] debug --- m1/Cargo.lock | 3 +++ m1/simulator/Cargo.toml | 3 +++ m1/simulator/src/main.rs | 26 ++++++++++++++------------ m1/tests/e2e/src/tests/mod.rs | 2 +- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/m1/Cargo.lock b/m1/Cargo.lock index 084d2fc5..3504bede 100644 --- a/m1/Cargo.lock +++ b/m1/Cargo.lock @@ -8998,8 +8998,11 @@ dependencies = [ "avalanche-types", "clap 3.2.25", "env_logger", + "log", "once_cell", "rand 0.7.3", + "random-manager", + "serde_json", "tokio", "url 2.5.0", ] diff --git a/m1/simulator/Cargo.toml b/m1/simulator/Cargo.toml index 72e459c7..3f0a1392 100644 --- a/m1/simulator/Cargo.toml +++ b/m1/simulator/Cargo.toml @@ -6,6 +6,9 @@ edition = "2021" [dependencies] avalanche-installer = "0.0.77" avalanche-network-runner-sdk = "0.3.3" # https://crates.io/crates/avalanche-network-runner-sdk +log = "0.4.19" +random-manager = "0.0.5" +serde_json = "1.0.108" # https://github.com/serde-rs/json/releases avalanche-types = { workspace = true } # https://crates.io/crates/avalanche-types aptos-sdk = {workspace = true } anyhow = { workspace = true } diff --git a/m1/simulator/src/main.rs b/m1/simulator/src/main.rs index d5a22f5a..42a4f942 100644 --- a/m1/simulator/src/main.rs +++ b/m1/simulator/src/main.rs @@ -75,29 +75,31 @@ pub enum SubCommands{ Health(HealthCommand), } -fn main() { +#[tokio::main] +async fn main() { let cli = Cli::parse(); match cli.command { - SubCommands::Start(opts) => start_network(opts), - SubCommands::Partition(opts) => partition_network(opts), - SubCommands::Reconnect(opts) => reconnect_validators(opts), - SubCommands::Health(opts) => network_health(opts), + SubCommands::Start(opts) => start_network(opts).await, + SubCommands::Partition(opts) => partition_network(opts).await, + SubCommands::Reconnect(opts) => reconnect_validators(opts).await, + SubCommands::Health(opts) => network_health(opts).await, } } -fn start_network(opts: StartCommand) { - simulator::init_m1_network() +async fn start_network(opts: StartCommand) { + simulator::init_m1_network().await; } -fn partition_network(opts: PartitionCommand) { - +async fn partition_network(opts: PartitionCommand) { + todo!() } -fn reconnect_validators(opts: ReconnectCommand) { +async fn reconnect_validators(opts: ReconnectCommand) { + todo!() } -fn network_health(opts: HealthCommand) { - +async fn network_health(opts: HealthCommand) { + todo!() } \ No newline at end of file diff --git a/m1/tests/e2e/src/tests/mod.rs b/m1/tests/e2e/src/tests/mod.rs index 930619d1..d65889ff 100644 --- a/m1/tests/e2e/src/tests/mod.rs +++ b/m1/tests/e2e/src/tests/mod.rs @@ -17,5 +17,5 @@ const AVALANCHEGO_VERSION: &str = "v1.10.9"; #[tokio::test] async fn e2e() { - init_m1_network() + init_m1_network().await } From 61695f50cc811ef6689552eb6ced5a8859292a34 Mon Sep 17 00:00:00 2001 From: elodie chiper Date: Tue, 13 Feb 2024 16:06:18 +0000 Subject: [PATCH 05/27] update nix deps for macOS target --- m1/scripts/default.nix | 35 ++++++++++++++++++++++++++++------- m1/scripts/rust-toolchain | 1 + m1/scripts/simulator.sh | 17 ++++++++++++----- m1/simulator/src/main.rs | 1 - 4 files changed, 41 insertions(+), 13 deletions(-) create mode 100644 m1/scripts/rust-toolchain diff --git a/m1/scripts/default.nix b/m1/scripts/default.nix index 7b6bc356..02177a58 100644 --- a/m1/scripts/default.nix +++ b/m1/scripts/default.nix @@ -1,14 +1,35 @@ -{ pkgs ? import {} }: +{ pkgs ? import {}, darwin ? pkgs.darwin }: -pkgs.stdenv.mkDerivation { +pkgs.mkShell rec { name = "simulator"; - buildInputs = [ - pkgs.rustc - pkgs.cargo + buildInputs = with pkgs; [ + clang + libiconv + rustup + zlib + ] ++ lib.optionals stdenv.isDarwin [ + darwin.apple_sdk.frameworks.IOKit + darwin.apple_sdk.frameworks.SystemConfiguration + darwin.apple_sdk.frameworks.AppKit + libcxx ]; + RUSTC_VERSION = builtins.readFile ./rust-toolchain; + shellHook = '' - echo "Welcome to the development environment for your-project-name" - # Any setup commands you need to run when entering the shell + export MACOSX_DEPLOYMENT_TARGET=10.13 + export CC="$(which clang)" + export CXX="$(which clang++)" + export RUSTFLAGS="-C link-arg=-stdlib=libc++ -C link-arg=-lc++" + export LDFLAGS="-stdlib=libc++ -lc++" + export LDFLAGS="$LDFLAGS -v" + + # Configure rustup to use the specified Rust version + rustup override set $RUSTC_VERSION + + echo "Welcome to the movement simulator for the M1 network" + + # Run 'env' to validate the environment variables setup + env ''; } diff --git a/m1/scripts/rust-toolchain b/m1/scripts/rust-toolchain new file mode 100644 index 00000000..5e3a4256 --- /dev/null +++ b/m1/scripts/rust-toolchain @@ -0,0 +1 @@ +1.73.0 diff --git a/m1/scripts/simulator.sh b/m1/scripts/simulator.sh index bdf8b942..f736df0b 100755 --- a/m1/scripts/simulator.sh +++ b/m1/scripts/simulator.sh @@ -3,9 +3,15 @@ # Dummy function placeholders for actual commands that would be implemented +init_env() { + echo "Initializing Environment" + ./scripts/build.debug.sh \ + && VM_PLUGIN_PATH=$(pwd)/target/debug/subnet +} + start_network() { echo "Starting network with 5 validators..." - # Placeholder for command to start the network + ./target/debug/simulator start } simulate_partition() { @@ -24,6 +30,7 @@ observe_health() { } # Script execution flow +init_env # Step 1: Start the network start_network @@ -32,14 +39,14 @@ start_network sleep 5 # Step 2: Simulate network partition -simulate_partition +# simulate_partition # Simulate some duration of network partition -sleep 10 +# sleep 10 # Step 3: Reconnect the nodes -reconnect_nodes +# reconnect_nodes # Observe the network for a period to assess impact on consensus health sleep 5 -observe_health +# observe_health diff --git a/m1/simulator/src/main.rs b/m1/simulator/src/main.rs index 42a4f942..161df6a2 100644 --- a/m1/simulator/src/main.rs +++ b/m1/simulator/src/main.rs @@ -88,7 +88,6 @@ async fn main() { async fn start_network(opts: StartCommand) { simulator::init_m1_network().await; - } async fn partition_network(opts: PartitionCommand) { From 1d3d0b1c168692444b461e96cbd162e7d69b0c96 Mon Sep 17 00:00:00 2001 From: elodie chiper Date: Tue, 13 Feb 2024 19:40:07 +0000 Subject: [PATCH 06/27] add nix.flake --- m1/scripts/default.nix | 35 ----------------------------------- m1/scripts/flake.nix | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 35 deletions(-) delete mode 100644 m1/scripts/default.nix create mode 100644 m1/scripts/flake.nix diff --git a/m1/scripts/default.nix b/m1/scripts/default.nix deleted file mode 100644 index 02177a58..00000000 --- a/m1/scripts/default.nix +++ /dev/null @@ -1,35 +0,0 @@ -{ pkgs ? import {}, darwin ? pkgs.darwin }: - -pkgs.mkShell rec { - name = "simulator"; - buildInputs = with pkgs; [ - clang - libiconv - rustup - zlib - ] ++ lib.optionals stdenv.isDarwin [ - darwin.apple_sdk.frameworks.IOKit - darwin.apple_sdk.frameworks.SystemConfiguration - darwin.apple_sdk.frameworks.AppKit - libcxx - ]; - - RUSTC_VERSION = builtins.readFile ./rust-toolchain; - - shellHook = '' - export MACOSX_DEPLOYMENT_TARGET=10.13 - export CC="$(which clang)" - export CXX="$(which clang++)" - export RUSTFLAGS="-C link-arg=-stdlib=libc++ -C link-arg=-lc++" - export LDFLAGS="-stdlib=libc++ -lc++" - export LDFLAGS="$LDFLAGS -v" - - # Configure rustup to use the specified Rust version - rustup override set $RUSTC_VERSION - - echo "Welcome to the movement simulator for the M1 network" - - # Run 'env' to validate the environment variables setup - env - ''; -} diff --git a/m1/scripts/flake.nix b/m1/scripts/flake.nix new file mode 100644 index 00000000..9dde836b --- /dev/null +++ b/m1/scripts/flake.nix @@ -0,0 +1,36 @@ +{ + description = "A devShell example"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + rust-overlay.url = "github:oxalica/rust-overlay"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, rust-overlay, flake-utils, ... }: + flake-utils.lib.eachDefaultSystem (system: + let + overlays = [ (import rust-overlay) ]; + pkgs = import nixpkgs { + inherit system overlays; + }; + in + with pkgs; + { + devShells.default = mkShell { + buildInputs = [ + openssl + pkg-config + eza + fd + rust-bin.beta.latest.default + ]; + + shellHook = '' + alias ls=eza + alias find=fd + ''; + }; + } + ); +} From 51bf9f908e817f3ec7b5e0389fa9086c18d04016 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Tue, 13 Feb 2024 20:07:24 +0000 Subject: [PATCH 07/27] mv flake.nix --- m1/.cargo/config.toml | 33 +--------- m1/flake.lock | 130 +++++++++++++++++++++++++++++++++++++ m1/{scripts => }/flake.nix | 11 ++-- m1/scripts/rust-toolchain | 1 - m1/scripts/simulator.sh | 5 +- 5 files changed, 139 insertions(+), 41 deletions(-) create mode 100644 m1/flake.lock rename m1/{scripts => }/flake.nix (64%) delete mode 100644 m1/scripts/rust-toolchain diff --git a/m1/.cargo/config.toml b/m1/.cargo/config.toml index f93b5556..bff29e6e 100644 --- a/m1/.cargo/config.toml +++ b/m1/.cargo/config.toml @@ -1,33 +1,2 @@ -[alias] -xclippy = [ - "clippy", - "--workspace", - "--all-targets", - "--", - "-Dwarnings", - "-Wclippy::all", - "-Aclippy::upper_case_acronyms", - "-Aclippy::enum-variant-names", - "-Aclippy::result-large-err", - "-Aclippy::mutable-key-type", -] - [build] -rustflags = ["--cfg", "tokio_unstable", "-C", "force-frame-pointers=yes", "-C", "force-unwind-tables=yes"] - -# TODO(grao): Figure out whether we should enable other cpu features, and whether we should use a different way to configure them rather than list every single one here. -[target.x86_64-unknown-linux-gnu] -rustflags = ["--cfg", "tokio_unstable", "-C", "link-arg=-fuse-ld=lld", "-C", "force-frame-pointers=yes", "-C", "force-unwind-tables=yes", "-C", "target-feature=+sse4.2"] - -# 64 bit MSVC -[target.x86_64-pc-windows-msvc] -rustflags = [ - "--cfg", - "tokio_unstable", - "-C", - "force-frame-pointers=yes", - "-C", - "force-unwind-tables=yes", - "-C", - "link-arg=/STACK:8000000" # Set stack to 8 MB -] +rustflags = ["--cfg", "tokio_unstable"] diff --git a/m1/flake.lock b/m1/flake.lock new file mode 100644 index 00000000..420f3ea0 --- /dev/null +++ b/m1/flake.lock @@ -0,0 +1,130 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1705309234, + "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1705309234, + "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1707689078, + "narHash": "sha256-UUGmRa84ZJHpGZ1WZEBEUOzaPOWG8LZ0yPg1pdDF/yM=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "f9d39fb9aff0efee4a3d5f4a6d7c17701d38a1d8", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1706487304, + "narHash": "sha256-LE8lVX28MV2jWJsidW13D2qrHU/RUUONendL2Q/WlJg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "90f456026d284c22b3e3497be980b2e47d0b28ac", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "flake-utils": "flake-utils_2", + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1707790272, + "narHash": "sha256-KQXPNl3BLdRbz7xx+mwIq/017fxLRk6JhXHxVWCKsTU=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "8dfbe2dffc28c1a18a29ffa34d5d0b269622b158", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/m1/scripts/flake.nix b/m1/flake.nix similarity index 64% rename from m1/scripts/flake.nix rename to m1/flake.nix index 9dde836b..b0bcee47 100644 --- a/m1/scripts/flake.nix +++ b/m1/flake.nix @@ -2,18 +2,21 @@ description = "A devShell example"; inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; rust-overlay.url = "github:oxalica/rust-overlay"; - flake-utils.url = "github:numtide/flake-utils"; + flake-utils.url = "github:numtide/flake-utils"; }; - outputs = { self, nixpkgs, rust-overlay, flake-utils, ... }: + outputs = { self, nixpkgs, rust-overlay, flake-utils, ... }@inputs: flake-utils.lib.eachDefaultSystem (system: let overlays = [ (import rust-overlay) ]; pkgs = import nixpkgs { inherit system overlays; }; + darwinFrameworks = if system == "x86_64-darwin" then with pkgs.darwin.apple_sdk.frameworks; [ + IOKit + ] else []; in with pkgs; { @@ -24,7 +27,7 @@ eza fd rust-bin.beta.latest.default - ]; + ] ++ darwinFrameworks; shellHook = '' alias ls=eza diff --git a/m1/scripts/rust-toolchain b/m1/scripts/rust-toolchain deleted file mode 100644 index 5e3a4256..00000000 --- a/m1/scripts/rust-toolchain +++ /dev/null @@ -1 +0,0 @@ -1.73.0 diff --git a/m1/scripts/simulator.sh b/m1/scripts/simulator.sh index f736df0b..3444010e 100755 --- a/m1/scripts/simulator.sh +++ b/m1/scripts/simulator.sh @@ -1,7 +1,4 @@ -#!/usr/bin/env nix-shell -#!nix-shell -i bash - -# Dummy function placeholders for actual commands that would be implemented +# run nix develop then call this script init_env() { echo "Initializing Environment" From 4fd4d350039a4ff3f764c4cd3909f6d3f100be44 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Tue, 13 Feb 2024 23:42:51 +0000 Subject: [PATCH 08/27] resolve mac OS --- m1/flake.nix | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/m1/flake.nix b/m1/flake.nix index b0bcee47..95ba29b1 100644 --- a/m1/flake.nix +++ b/m1/flake.nix @@ -16,12 +16,21 @@ }; darwinFrameworks = if system == "x86_64-darwin" then with pkgs.darwin.apple_sdk.frameworks; [ IOKit + SystemConfiguration + AppKit ] else []; in with pkgs; { devShells.default = mkShell { buildInputs = [ + pkgs.clang_14 + zlib + bzip2 + lz4 + snappy + zstd + rocksdb openssl pkg-config eza @@ -32,6 +41,14 @@ shellHook = '' alias ls=eza alias find=fd + + echo "> Entered Nix-powered M1 simulator environment" + export OLD_PS1="$PS1" + PS1="(M1-nix) $PS1" + + # Set MACOSX_DEPLOYMENT_TARGET for compatibility + export MACOSX_DEPLOYMENT_TARGET="10.13" + echo "MACOSX_DEPLOYMENT_TARGET set to: $MACOSX_DEPLOYMENT_TARGET" ''; }; } From 5fc8fbf7163ce9c6cad6496d30ca038e9293cc95 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Wed, 14 Feb 2024 13:08:56 +0000 Subject: [PATCH 09/27] add libcxx and link --- m1/flake.nix | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/m1/flake.nix b/m1/flake.nix index 95ba29b1..fd5325a7 100644 --- a/m1/flake.nix +++ b/m1/flake.nix @@ -24,7 +24,8 @@ { devShells.default = mkShell { buildInputs = [ - pkgs.clang_14 + clang + libcxx zlib bzip2 lz4 @@ -35,7 +36,7 @@ pkg-config eza fd - rust-bin.beta.latest.default + rust-bin.stable.latest.default ] ++ darwinFrameworks; shellHook = '' @@ -49,6 +50,8 @@ # Set MACOSX_DEPLOYMENT_TARGET for compatibility export MACOSX_DEPLOYMENT_TARGET="10.13" echo "MACOSX_DEPLOYMENT_TARGET set to: $MACOSX_DEPLOYMENT_TARGET" + + export NIX_LDFLAGS="$NIX_LDFLAGS -L${pkgs.libcxx}/lib -lc++" ''; }; } From 38b87be1dbd2ca46960a5224c77de14c91a54d24 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Wed, 14 Feb 2024 15:33:24 +0000 Subject: [PATCH 10/27] aarch64 --- m1/flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/m1/flake.nix b/m1/flake.nix index fd5325a7..5f767ce0 100644 --- a/m1/flake.nix +++ b/m1/flake.nix @@ -14,7 +14,7 @@ pkgs = import nixpkgs { inherit system overlays; }; - darwinFrameworks = if system == "x86_64-darwin" then with pkgs.darwin.apple_sdk.frameworks; [ + darwinFrameworks = if system == "x86_64-darwin" || system == "aarch64-darwin" then with pkgs.darwin.apple_sdk.frameworks; [ IOKit SystemConfiguration AppKit From 48a5be21c67cdc291cea9045a904c541debccd9f Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Wed, 14 Feb 2024 16:06:47 +0000 Subject: [PATCH 11/27] remove pkgs already used by rocksdb --- m1/flake.nix | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/m1/flake.nix b/m1/flake.nix index 5f767ce0..02d320bb 100644 --- a/m1/flake.nix +++ b/m1/flake.nix @@ -14,7 +14,7 @@ pkgs = import nixpkgs { inherit system overlays; }; - darwinFrameworks = if system == "x86_64-darwin" || system == "aarch64-darwin" then with pkgs.darwin.apple_sdk.frameworks; [ + darwinFrameworks = if system == "x86_64-darwin" then with pkgs.darwin.apple_sdk.frameworks; [ IOKit SystemConfiguration AppKit @@ -24,13 +24,8 @@ { devShells.default = mkShell { buildInputs = [ - clang - libcxx - zlib - bzip2 - lz4 - snappy - zstd + llvmPackages_13.stdenv + llvmPackages_13.libcxxStdenv rocksdb openssl pkg-config From dc9e67d53d30b68d19c79c5bff1cd0bf7b58696b Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Wed, 14 Feb 2024 21:59:29 +0000 Subject: [PATCH 12/27] new flake --- m1/flake.lock | 103 +++++++++++++++++++++---------------------------- m1/flake.nix | 100 +++++++++++++++++++++++++++++------------------ m1/justfile | 2 + m1/scratch.nix | 64 ++++++++++++++++++++++++++++++ 4 files changed, 173 insertions(+), 96 deletions(-) create mode 100644 m1/justfile create mode 100644 m1/scratch.nix diff --git a/m1/flake.lock b/m1/flake.lock index 420f3ea0..527157fe 100644 --- a/m1/flake.lock +++ b/m1/flake.lock @@ -1,51 +1,51 @@ { "nodes": { - "flake-utils": { + "flake-parts": { "inputs": { - "systems": "systems" + "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1705309234, - "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", + "lastModified": 1706830856, + "narHash": "sha256-a0NYyp+h9hlb7ddVz4LUn1vT/PLwqfrWYcHMvFB1xYg=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "b253292d9c0a5ead9bc98c4e9a26c6312e27d69f", "type": "github" }, "original": { - "owner": "numtide", - "repo": "flake-utils", + "owner": "hercules-ci", + "repo": "flake-parts", "type": "github" } }, - "flake-utils_2": { - "inputs": { - "systems": "systems_2" - }, + "nixpkgs": { "locked": { - "lastModified": 1705309234, - "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", + "lastModified": 1707689078, + "narHash": "sha256-UUGmRa84ZJHpGZ1WZEBEUOzaPOWG8LZ0yPg1pdDF/yM=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "f9d39fb9aff0efee4a3d5f4a6d7c17701d38a1d8", "type": "github" }, "original": { - "owner": "numtide", - "repo": "flake-utils", + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", "type": "github" } }, - "nixpkgs": { + "nixpkgs-lib": { "locked": { - "lastModified": 1707689078, - "narHash": "sha256-UUGmRa84ZJHpGZ1WZEBEUOzaPOWG8LZ0yPg1pdDF/yM=", + "dir": "lib", + "lastModified": 1706550542, + "narHash": "sha256-UcsnCG6wx++23yeER4Hg18CXWbgNpqNXcHIo5/1Y+hc=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "f9d39fb9aff0efee4a3d5f4a6d7c17701d38a1d8", + "rev": "97b17f32362e475016f942bbdfda4a4a72a8a652", "type": "github" }, "original": { + "dir": "lib", "owner": "NixOS", "ref": "nixos-unstable", "repo": "nixpkgs", @@ -54,44 +54,26 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1706487304, - "narHash": "sha256-LE8lVX28MV2jWJsidW13D2qrHU/RUUONendL2Q/WlJg=", - "owner": "NixOS", + "lastModified": 1705856552, + "narHash": "sha256-JXfnuEf5Yd6bhMs/uvM67/joxYKoysyE3M2k6T3eWbg=", + "owner": "nixos", "repo": "nixpkgs", - "rev": "90f456026d284c22b3e3497be980b2e47d0b28ac", + "rev": "612f97239e2cc474c13c9dafa0df378058c5ad8d", "type": "github" }, "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", + "owner": "nixos", + "ref": "nixos-unstable", "repo": "nixpkgs", "type": "github" } }, "root": { "inputs": { - "flake-utils": "flake-utils", + "flake-parts": "flake-parts", "nixpkgs": "nixpkgs", - "rust-overlay": "rust-overlay" - } - }, - "rust-overlay": { - "inputs": { - "flake-utils": "flake-utils_2", - "nixpkgs": "nixpkgs_2" - }, - "locked": { - "lastModified": 1707790272, - "narHash": "sha256-KQXPNl3BLdRbz7xx+mwIq/017fxLRk6JhXHxVWCKsTU=", - "owner": "oxalica", - "repo": "rust-overlay", - "rev": "8dfbe2dffc28c1a18a29ffa34d5d0b269622b158", - "type": "github" - }, - "original": { - "owner": "oxalica", - "repo": "rust-overlay", - "type": "github" + "systems": "systems", + "treefmt-nix": "treefmt-nix" } }, "systems": { @@ -109,18 +91,21 @@ "type": "github" } }, - "systems_2": { + "treefmt-nix": { + "inputs": { + "nixpkgs": "nixpkgs_2" + }, "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "lastModified": 1707300477, + "narHash": "sha256-qQF0fEkHlnxHcrKIMRzOETnRBksUK048MXkX0SOmxvA=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "ac599dab59a66304eb511af07b3883114f061b9d", "type": "github" }, "original": { - "owner": "nix-systems", - "repo": "default", + "owner": "numtide", + "repo": "treefmt-nix", "type": "github" } } diff --git a/m1/flake.nix b/m1/flake.nix index 02d320bb..cfeece6c 100644 --- a/m1/flake.nix +++ b/m1/flake.nix @@ -1,54 +1,80 @@ { - description = "A devShell example"; - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - rust-overlay.url = "github:oxalica/rust-overlay"; - flake-utils.url = "github:numtide/flake-utils"; + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + flake-parts.url = "github:hercules-ci/flake-parts"; + systems.url = "github:nix-systems/default"; + + # Dev tools + treefmt-nix.url = "github:numtide/treefmt-nix"; }; - outputs = { self, nixpkgs, rust-overlay, flake-utils, ... }@inputs: - flake-utils.lib.eachDefaultSystem (system: + outputs = inputs: + inputs.flake-parts.lib.mkFlake { inherit inputs; } { + systems = import inputs.systems; + imports = [ + inputs.treefmt-nix.flakeModule + ]; + perSystem = { config, self', pkgs, lib, system, ... }: let - overlays = [ (import rust-overlay) ]; - pkgs = import nixpkgs { - inherit system overlays; + cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml); + nonRustDeps = [ + pkgs.libiconv + pkgs.zlib + pkgs.darwin.apple_sdk.frameworks.IOKit + pkgs.darwin.apple_sdk.frameworks.SystemConfiguration + pkgs.darwin.apple_sdk.frameworks.AppKit + ]; + rust-toolchain = pkgs.symlinkJoin { + name = "rust-toolchain"; + paths = [ pkgs.rustc pkgs.cargo pkgs.cargo-watch pkgs.rust-analyzer pkgs.rustPlatform.rustcSrc ]; }; - darwinFrameworks = if system == "x86_64-darwin" then with pkgs.darwin.apple_sdk.frameworks; [ - IOKit - SystemConfiguration - AppKit - ] else []; in - with pkgs; { - devShells.default = mkShell { - buildInputs = [ - llvmPackages_13.stdenv - llvmPackages_13.libcxxStdenv - rocksdb - openssl - pkg-config - eza - fd - rust-bin.stable.latest.default - ] ++ darwinFrameworks; + # Rust package + packages.default = pkgs.rustPlatform.buildRustPackage { + inherit (cargoToml.package) name version; + src = ./.; + cargoLock.lockFile = ./Cargo.lock; + buildInputs = lib.optional (lib.strings.hasPrefix "darwin" system) pkgs.darwin.apple_sdk.frameworks.IOKit; + }; + # Rust dev environment + devShells.default = pkgs.mkShell { + inputsFrom = [ + config.treefmt.build.devShell + ]; shellHook = '' - alias ls=eza - alias find=fd - - echo "> Entered Nix-powered M1 simulator environment" - export OLD_PS1="$PS1" - PS1="(M1-nix) $PS1" + # For rust-analyzer 'hover' tooltips to work. + export RUST_SRC_PATH=${pkgs.rustPlatform.rustLibSrc} # Set MACOSX_DEPLOYMENT_TARGET for compatibility export MACOSX_DEPLOYMENT_TARGET="10.13" echo "MACOSX_DEPLOYMENT_TARGET set to: $MACOSX_DEPLOYMENT_TARGET" - export NIX_LDFLAGS="$NIX_LDFLAGS -L${pkgs.libcxx}/lib -lc++" + export OLD_PS1="$PS1" + PS1="(M1-nix) $PS1" + + echo + echo "🍎🍎 Run 'just ' to get started" ''; + buildInputs = nonRustDeps; + nativeBuildInputs = with pkgs; [ + just + rust-toolchain + ] ++ lib.optional (lib.strings.hasPrefix "darwin" system) pkgs.darwin.apple_sdk.frameworks.IOKit; + RUST_BACKTRACE = 1; }; - } - ); -} + + # Add your auto-formatters here. + # cf. https://numtide.github.io/treefmt/ + treefmt.config = { + projectRootFile = "flake.nix"; + programs = { + nixpkgs-fmt.enable = true; + rustfmt.enable = true; + }; + }; + }; + }; + +} \ No newline at end of file diff --git a/m1/justfile b/m1/justfile new file mode 100644 index 00000000..979ce5b9 --- /dev/null +++ b/m1/justfile @@ -0,0 +1,2 @@ +build: + ./scripts/build.debug.sh && cargo build -p simuatlor --bin simulator \ No newline at end of file diff --git a/m1/scratch.nix b/m1/scratch.nix new file mode 100644 index 00000000..ebc30fa9 --- /dev/null +++ b/m1/scratch.nix @@ -0,0 +1,64 @@ +{ + description = "A devShell example"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + rust-overlay.url = "github:oxalica/rust-overlay"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, rust-overlay, flake-utils, ... }@inputs: + flake-utils.lib.eachDefaultSystem (system: + let + overlays = [ (import rust-overlay) ]; + pkgs = import nixpkgs { + inherit system overlays; + }; + darwinFrameworks = if system == "x86_64-darwin" then with pkgs.darwin.apple_sdk.frameworks; [ + IOKit + SystemConfiguration + AppKit + ] else []; + in + with pkgs; + { + devShells.default = mkShell { + buildInputs = [ + clang + libcxx + rocksdb + openssl + pkg-config + eza + fd + rust-bin.stable.latest.default + ] ++ darwinFrameworks; + + shellHook = '' + alias ls=eza + alias find=fd + + echo "> Entered Nix-powered M1 simulator environment" + export OLD_PS1="$PS1" + PS1="(M1-nix) $PS1" + + # Set MACOSX_DEPLOYMENT_TARGET for compatibility + export MACOSX_DEPLOYMENT_TARGET="10.13" + echo "MACOSX_DEPLOYMENT_TARGET set to: $MACOSX_DEPLOYMENT_TARGET" + + # Explicitly set the C++ compiler to use clang++, ensuring it's the Nix-provided one + export CXX="${clang}/bin/clang++" + + # Adding -v for verbose output during compilation to help diagnose linker issues + export NIX_CFLAGS_COMPILE="-v $NIX_CFLAGS_COMPILE" + + # Ensure linker flags include the path to libc++ and enable verbose output + export NIX_LDFLAGS="$NIX_LDFLAGS -L${libcxx}/lib -lc++ -v" + + # Set LDFLAGS for manual make/cmake builds + export LDFLAGS="-L${libcxx}/lib -lc++ -v" + ''; + }; + } + ); +} \ No newline at end of file From 9b0a2333262982fefa948f78e68149a7e35775e3 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Fri, 16 Feb 2024 16:24:26 +0000 Subject: [PATCH 13/27] update network init, refactors --- m1/simulator/src/lib.rs | 485 ++++++++++++++++++++++----------------- m1/simulator/src/main.rs | 89 ++++--- 2 files changed, 330 insertions(+), 244 deletions(-) diff --git a/m1/simulator/src/lib.rs b/m1/simulator/src/lib.rs index 38adad47..4b8de4c5 100644 --- a/m1/simulator/src/lib.rs +++ b/m1/simulator/src/lib.rs @@ -1,15 +1,278 @@ use core::time; use std::{ - io, fs::{self, File}, + io, + io::Write, path::Path, str::FromStr, thread, - time::{Duration, Instant}, io::Write, + time::{Duration, Instant}, }; +use anyhow::{anyhow, Result}; + use avalanche_network_runner_sdk::{BlockchainSpec, Client, GlobalConfig, StartRequest}; -use avalanche_types::{ids, jsonrpc::client::info as avalanche_sdk_info, subnet}; +use avalanche_types::{ + ids::{self, Id as VmId}, + jsonrpc::client::info as avalanche_sdk_info, + subnet::{self, vm_name_to_id}, +}; + +const LOCAL_GRPC_ENDPOINT: &str = "http://127.0.0.1:12342"; +const VM_NAME: &str = "subnet"; + +/// Network configuration +pub struct Network { + /// The GRPC endpoint of the network runner to connect to + pub grpc_endpoint: Option, + /// Sets if the validators join the network at once, or in a staggered way + pub enable_shutdown: bool, + /// The path to the avalanchego binary + pub avalanchego_path: String, + /// The path to the VM plugin + pub vm_plugin_path: String, + /// VM name, this is hardcoded for now + pub vm_name: VmId, +} + +impl Network { + /// Create a new network configuration + + pub fn new( + is_local: bool, + grpc_endpoint: Option, + enable_shutdown: bool, + avalanchego_path: String, + vm_plugin_path: String, + ) -> Result { + let grpc_endpoint = match is_local { + true => Some(LOCAL_GRPC_ENDPOINT.to_string()), + false => { + if let Some(endpoint) = grpc_endpoint { + Some(endpoint) + } else { + return Err(anyhow!("GRPC endpoint not provided")); + } + } + }; + Ok(Network { + grpc_endpoint, + enable_shutdown, + avalanchego_path, + vm_plugin_path, + vm_name: subnet::vm_name_to_id(VM_NAME)?, + }) + } + + pub async fn init_m1_network(&self) -> Result<(), anyhow::Error> { + let grpc = match self.grpc_endpoint.clone() { + Some(grpc) => grpc, + None => { + return Err(anyhow!("GRPC endpoint not provided")); + } + }; + let _ = env_logger::builder() + .filter_level(log::LevelFilter::Info) + .is_test(true) + .try_init(); + + let cli = Client::new(&grpc).await; + + log::info!("ping..."); + let resp = cli.ping().await.expect("failed ping"); + log::info!("network-runner is running (ping response {:?})", resp); + + let vm_id = Path::new(&self.vm_plugin_path) + .file_stem() + .unwrap() + .to_str() + .unwrap() + .to_string(); + + let (mut avalanchego_exec_path, _) = get_avalanchego_path(); + let plugins_dir = if !avalanchego_exec_path.is_empty() { + let parent_dir = Path::new(&avalanchego_exec_path) + .parent() + .expect("unexpected None parent"); + parent_dir + .join("plugins") + .as_os_str() + .to_str() + .unwrap() + .to_string() + } else { + let exec_path = avalanche_installer::avalanchego::github::download( + None, + None, + Some(AVALANCHEGO_VERSION.to_string()), + ) + .await + .unwrap(); + avalanchego_exec_path = exec_path; + avalanche_installer::avalanchego::get_plugin_dir(&avalanchego_exec_path) + }; + + log::info!( + "copying vm plugin {} to {}/{}", + self.vm_plugin_path, + plugins_dir, + vm_id + ); + + fs::create_dir(&plugins_dir).unwrap(); + fs::copy( + &self.vm_plugin_path, + Path::new(&plugins_dir).join(vm_id.to_string()), + ) + .unwrap(); + + // write some random genesis file + let genesis = random_manager::secure_string(10); + + let genesis_file_path = random_manager::tmp_path(10, None).unwrap(); + sync_genesis(genesis.as_ref(), &genesis_file_path).unwrap(); + + log::info!( + "starting {} with avalanchego {}, genesis file path {}", + vm_id, + &avalanchego_exec_path, + genesis_file_path, + ); + let resp = cli + .start(StartRequest { + exec_path: avalanchego_exec_path, + num_nodes: Some(5), + plugin_dir: plugins_dir, + global_node_config: Some( + serde_json::to_string(&GlobalConfig { + log_level: String::from("info"), + }) + .unwrap(), + ), + blockchain_specs: vec![BlockchainSpec { + vm_name: String::from(VM_NAME), + genesis: genesis_file_path.to_string(), + // blockchain_alias : String::from("subnet"), // todo: this doesn't always work oddly enough, need to debug + ..Default::default() + }], + ..Default::default() + }) + .await + .expect("failed start"); + log::info!( + "started avalanchego cluster with network-runner: {:?}", + resp + ); + + // enough time for network-runner to get ready + thread::sleep(Duration::from_secs(20)); + + log::info!("checking cluster healthiness..."); + let mut ready = false; + + let timeout = Duration::from_secs(300); + let interval = Duration::from_secs(15); + let start = Instant::now(); + let mut cnt: u128 = 0; + loop { + let elapsed = start.elapsed(); + if elapsed.gt(&timeout) { + break; + } + + let itv = { + if cnt == 0 { + // first poll with no wait + Duration::from_secs(1) + } else { + interval + } + }; + thread::sleep(itv); + + ready = { + match cli.health().await { + Ok(_) => { + log::info!("healthy now!"); + true + } + Err(e) => { + log::warn!("not healthy yet {}", e); + false + } + } + }; + if ready { + break; + } + + cnt += 1; + } + assert!(ready); + + log::info!("checking status..."); + let mut status = cli.status().await.expect("failed status"); + loop { + let elapsed = start.elapsed(); + if elapsed.gt(&timeout) { + break; + } + + if let Some(ci) = &status.cluster_info { + if !ci.custom_chains.is_empty() { + break; + } + } + + log::info!("retrying checking status..."); + thread::sleep(interval); + status = cli.status().await.expect("failed status"); + } + + assert!(status.cluster_info.is_some()); + let cluster_info = status.cluster_info.unwrap(); + let mut rpc_eps: Vec = Vec::new(); + for (node_name, iv) in cluster_info.node_infos.into_iter() { + log::info!("{}: {}", node_name, iv.uri); + rpc_eps.push(iv.uri.clone()); + } + let mut blockchain_id = ids::Id::empty(); + for (k, v) in cluster_info.custom_chains.iter() { + log::info!("custom chain info: {}={:?}", k, v); + if v.chain_name == "subnet" { + blockchain_id = ids::Id::from_str(&v.chain_id).unwrap(); + break; + } + } + log::info!("avalanchego RPC endpoints: {:?}", rpc_eps); + + let resp = avalanche_sdk_info::get_network_id(&rpc_eps[0]) + .await + .unwrap(); + let network_id = resp.result.unwrap().network_id; + log::info!("network Id: {}", network_id); + + // keep alive by sleeping for duration provided by SUBNET_TIMEOUT environment variable + // use sensible default + + let val = std::env::var("SUBNET_TIMEOUT") + .unwrap_or_else(|_| "0".to_string()) + .parse::() + .unwrap(); + + log::info!("sleeping for {} seconds", timeout.as_secs()); + if val < 0 { + // run forever + loop { + thread::sleep(Duration::from_secs(1000)); + } + } else { + let timeout = Duration::from_secs(val as u64); + thread::sleep(timeout); + } + Ok(()) + } +} #[must_use] pub fn get_network_runner_grpc_endpoint() -> (String, bool) { @@ -43,8 +306,8 @@ pub fn get_vm_plugin_path() -> (String, bool) { const AVALANCHEGO_VERSION: &str = "v1.10.9"; // todo: extracted from genesis method -// todo: really we should use a genesis once more -pub fn sync_genesis(byte_string : &str, file_path: &str) -> io::Result<()> { +// todo: really we should use a genesis once more +pub fn sync_genesis(byte_string: &str, file_path: &str) -> io::Result<()> { log::info!("syncing genesis to '{}'", file_path); let path = Path::new(file_path); @@ -59,214 +322,4 @@ pub fn sync_genesis(byte_string : &str, file_path: &str) -> io::Result<()> { Ok(()) } -pub async fn init_m1_network() { - let _ = env_logger::builder() - .filter_level(log::LevelFilter::Info) - .is_test(true) - .try_init(); - - let (ep, is_set) = get_network_runner_grpc_endpoint(); - assert!(is_set); - - let cli = Client::new(&ep).await; - - log::info!("ping..."); - let resp = cli.ping().await.expect("failed ping"); - log::info!("network-runner is running (ping response {:?})", resp); - - let (vm_plugin_path, exists) = get_vm_plugin_path(); - log::info!("Vm Plugin path: {vm_plugin_path}"); - assert!(exists); - assert!(Path::new(&vm_plugin_path).exists()); - - let vm_id = Path::new(&vm_plugin_path) - .file_stem() - .unwrap() - .to_str() - .unwrap() - .to_string(); - // ! for now, we hardcode the id to be subnet for orchestration - let vm_id = subnet::vm_name_to_id("subnet").unwrap(); - - let (mut avalanchego_exec_path, _) = get_avalanchego_path(); - let plugins_dir = if !avalanchego_exec_path.is_empty() { - let parent_dir = Path::new(&avalanchego_exec_path) - .parent() - .expect("unexpected None parent"); - parent_dir - .join("plugins") - .as_os_str() - .to_str() - .unwrap() - .to_string() - } else { - let exec_path = avalanche_installer::avalanchego::github::download( - None, - None, - Some(AVALANCHEGO_VERSION.to_string()), - ) - .await - .unwrap(); - avalanchego_exec_path = exec_path; - avalanche_installer::avalanchego::get_plugin_dir(&avalanchego_exec_path) - }; - - log::info!( - "copying vm plugin {} to {}/{}", - vm_plugin_path, - plugins_dir, - vm_id - ); - - fs::create_dir(&plugins_dir).unwrap(); - fs::copy( - &vm_plugin_path, - Path::new(&plugins_dir).join(vm_id.to_string()), - ) - .unwrap(); - - // write some random genesis file - let genesis = random_manager::secure_string(10); - - let genesis_file_path = random_manager::tmp_path(10, None).unwrap(); - sync_genesis(genesis.as_ref(), &genesis_file_path).unwrap(); - - log::info!( - "starting {} with avalanchego {}, genesis file path {}", - vm_id, - &avalanchego_exec_path, - genesis_file_path, - ); - let resp = cli - .start(StartRequest { - exec_path: avalanchego_exec_path, - num_nodes: Some(5), - plugin_dir: plugins_dir, - global_node_config: Some( - serde_json::to_string(&GlobalConfig { - log_level: String::from("info"), - }) - .unwrap(), - ), - blockchain_specs: vec![BlockchainSpec { - vm_name: String::from("subnet"), - genesis: genesis_file_path.to_string(), - // blockchain_alias : String::from("subnet"), // todo: this doesn't always work oddly enough, need to debug - ..Default::default() - }], - ..Default::default() - }) - .await - .expect("failed start"); - log::info!( - "started avalanchego cluster with network-runner: {:?}", - resp - ); - - // enough time for network-runner to get ready - thread::sleep(Duration::from_secs(20)); - - log::info!("checking cluster healthiness..."); - let mut ready = false; - - let timeout = Duration::from_secs(300); - let interval = Duration::from_secs(15); - let start = Instant::now(); - let mut cnt: u128 = 0; - loop { - let elapsed = start.elapsed(); - if elapsed.gt(&timeout) { - break; - } - - let itv = { - if cnt == 0 { - // first poll with no wait - Duration::from_secs(1) - } else { - interval - } - }; - thread::sleep(itv); - - ready = { - match cli.health().await { - Ok(_) => { - log::info!("healthy now!"); - true - } - Err(e) => { - log::warn!("not healthy yet {}", e); - false - } - } - }; - if ready { - break; - } - - cnt += 1; - } - assert!(ready); - - log::info!("checking status..."); - let mut status = cli.status().await.expect("failed status"); - loop { - let elapsed = start.elapsed(); - if elapsed.gt(&timeout) { - break; - } - - if let Some(ci) = &status.cluster_info { - if !ci.custom_chains.is_empty() { - break; - } - } - - log::info!("retrying checking status..."); - thread::sleep(interval); - status = cli.status().await.expect("failed status"); - } - - assert!(status.cluster_info.is_some()); - let cluster_info = status.cluster_info.unwrap(); - let mut rpc_eps: Vec = Vec::new(); - for (node_name, iv) in cluster_info.node_infos.into_iter() { - log::info!("{}: {}", node_name, iv.uri); - rpc_eps.push(iv.uri.clone()); - } - let mut blockchain_id = ids::Id::empty(); - for (k, v) in cluster_info.custom_chains.iter() { - log::info!("custom chain info: {}={:?}", k, v); - if v.chain_name == "subnet" { - blockchain_id = ids::Id::from_str(&v.chain_id).unwrap(); - break; - } - } - log::info!("avalanchego RPC endpoints: {:?}", rpc_eps); - - let resp = avalanche_sdk_info::get_network_id(&rpc_eps[0]) - .await - .unwrap(); - let network_id = resp.result.unwrap().network_id; - log::info!("network Id: {}", network_id); - - // keep alive by sleeping for duration provided by SUBNET_TIMEOUT environment variable - // use sensible default - - let val = std::env::var("SUBNET_TIMEOUT") - .unwrap_or_else(|_| "0".to_string()) - .parse::() - .unwrap(); - - log::info!("sleeping for {} seconds", timeout.as_secs()); - if val < 0 { - // run forever - loop { - thread::sleep(Duration::from_secs(1000)); - } - } else { - let timeout = Duration::from_secs(val as u64); - thread::sleep(timeout); - } -} \ No newline at end of file +pub async fn init_m1_network() {} diff --git a/m1/simulator/src/main.rs b/m1/simulator/src/main.rs index 161df6a2..1ad2f828 100644 --- a/m1/simulator/src/main.rs +++ b/m1/simulator/src/main.rs @@ -1,4 +1,5 @@ use clap::{Parser, Subcommand}; +use simulator::Network; type NodeId = u64; @@ -10,36 +11,60 @@ pub struct Cli { pub command: SubCommands, } -/// Start the simulator +/// Start the simulator #[derive(Debug, Parser)] pub struct StartCommand { /// The number of validators for the network - #[clap(long, default_value = "5", help = "The number of validators for the network.")] + #[clap( + long, + default_value = "5", + help = "The number of validators for the network." + )] pub validators: u64, /// Sets if the validators join the network at once, or in a staggered way - #[clap(long, default_value = "false", help = "Sets if the validators join the network at once, or in a staggered way.")] + #[clap( + long, + default_value = "false", + help = "Sets if the validators join the network at once, or in a staggered way." + )] pub staggered: bool, /// Verbose output #[clap(short, long, help = "Verbose output.")] - pub verbose: bool + pub verbose: bool, + + /// Run simuilator against the local network + #[clap(long, help = "Run simuilator against the local network.")] + pub local: bool, + + /// The GRPC endpoint of the network runner to connect to + #[clap(long, help = "The GRPC endpoint of the network runner to connect to.")] + pub grpc_endpoint: Option, } /// Partition the network #[derive(Debug, Parser)] pub struct PartitionCommand { /// The percentage of validators that will be partitioned - #[clap(long, default_value = "5", help = "The percentage of validators that will be in a partitioned state")] - pub amount: u8, + #[clap( + long, + default_value = "5", + help = "The percentage of validators that will be in a partitioned state" + )] + pub amount: u8, /// Sets if the validators become paritioned at once or in a staggered way - #[clap(long, default_value = "false", help = "Sets if the validators become partitioned at once or in a staggered way.")] + #[clap( + long, + default_value = "false", + help = "Sets if the validators become partitioned at once or in a staggered way." + )] pub staggered: bool, /// Verbose output #[clap(short, long, help = "Verbose output.")] - pub verbose: bool + pub verbose: bool, } #[derive(Debug, Parser)] @@ -53,21 +78,21 @@ pub struct ReconnectCommand { /// Verbose output #[clap(short, long, help = "Verbose output.")] - pub verbose: bool + pub verbose: bool, } #[derive(Debug, Parser)] pub struct HealthCommand { /// Verbose ouput - #[clap(short, long, help = "Verbose output.")] - pub verbose: bool + #[clap(short, long, help = "Verbose output.")] + pub verbose: bool, } #[derive(Debug, Subcommand)] -pub enum SubCommands{ +pub enum SubCommands { /// Starts the network with a number of validators Start(StartCommand), - /// Simulates a network partition. + /// Simulates a network partition. Partition(PartitionCommand), /// Reconnects the validators after they have become partitioned Reconnect(ReconnectCommand), @@ -76,29 +101,37 @@ pub enum SubCommands{ } #[tokio::main] -async fn main() { +async fn main() -> Result<(), anyhow::Error> { let cli = Cli::parse(); match cli.command { - SubCommands::Start(opts) => start_network(opts).await, - SubCommands::Partition(opts) => partition_network(opts).await, - SubCommands::Reconnect(opts) => reconnect_validators(opts).await, - SubCommands::Health(opts) => network_health(opts).await, + SubCommands::Start(opts) => start_network(opts).await?, + SubCommands::Partition(opts) => partition_network(opts).await?, + SubCommands::Reconnect(opts) => reconnect_validators(opts).await?, + SubCommands::Health(opts) => network_health(opts).await?, } + Ok(()) } -async fn start_network(opts: StartCommand) { - simulator::init_m1_network().await; +async fn start_network(opts: StartCommand) -> Result<(), anyhow::Error> { + let net = Network::new( + opts.local, + opts.grpc_endpoint, + opts.staggered, + "avalanchego_path".to_string(), + "vm_plugin_path".to_string(), + )?; + net.init_m1_network().await?; + Ok(()) } -async fn partition_network(opts: PartitionCommand) { - todo!() +async fn partition_network(opts: PartitionCommand) -> Result<(), anyhow::Error> { + Ok(()) } -async fn reconnect_validators(opts: ReconnectCommand) { - todo!() - +async fn reconnect_validators(opts: ReconnectCommand) -> Result<(), anyhow::Error> { + Ok(()) } -async fn network_health(opts: HealthCommand) { - todo!() -} \ No newline at end of file +async fn network_health(opts: HealthCommand) -> Result<(), anyhow::Error> { + Ok(()) +} From ad691847408f62050e2de288576aed0e349a3fe7 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Sat, 17 Feb 2024 15:33:43 +0000 Subject: [PATCH 14/27] get ava go paths, improve error handling, add log debugs --- m1/simulator/src/lib.rs | 69 +++++++++++++++++++++++++++++----------- m1/simulator/src/main.rs | 20 +++++++++--- 2 files changed, 67 insertions(+), 22 deletions(-) diff --git a/m1/simulator/src/lib.rs b/m1/simulator/src/lib.rs index 4b8de4c5..60ad2649 100644 --- a/m1/simulator/src/lib.rs +++ b/m1/simulator/src/lib.rs @@ -1,15 +1,15 @@ use core::time; use std::{ + env, fs::{self, File}, - io, - io::Write, + io::{self, Write}, path::Path, str::FromStr, thread, time::{Duration, Instant}, }; -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, Context, Result}; use avalanche_network_runner_sdk::{BlockchainSpec, Client, GlobalConfig, StartRequest}; use avalanche_types::{ @@ -64,7 +64,7 @@ impl Network { }) } - pub async fn init_m1_network(&self) -> Result<(), anyhow::Error> { + pub async fn init_m1_network(&mut self) -> Result<(), anyhow::Error> { let grpc = match self.grpc_endpoint.clone() { Some(grpc) => grpc, None => { @@ -89,9 +89,8 @@ impl Network { .unwrap() .to_string(); - let (mut avalanchego_exec_path, _) = get_avalanchego_path(); - let plugins_dir = if !avalanchego_exec_path.is_empty() { - let parent_dir = Path::new(&avalanchego_exec_path) + let plugins_dir = if !&self.avalanchego_path.is_empty() { + let parent_dir = Path::new(&self.avalanchego_path) .parent() .expect("unexpected None parent"); parent_dir @@ -108,8 +107,8 @@ impl Network { ) .await .unwrap(); - avalanchego_exec_path = exec_path; - avalanche_installer::avalanchego::get_plugin_dir(&avalanchego_exec_path) + self.avalanchego_path = exec_path; + avalanche_installer::avalanchego::get_plugin_dir(&self.avalanchego_path) }; log::info!( @@ -135,12 +134,12 @@ impl Network { log::info!( "starting {} with avalanchego {}, genesis file path {}", vm_id, - &avalanchego_exec_path, + &self.avalanchego_path, genesis_file_path, ); let resp = cli .start(StartRequest { - exec_path: avalanchego_exec_path, + exec_path: self.avalanchego_path.clone(), num_nodes: Some(5), plugin_dir: plugins_dir, global_node_config: Some( @@ -288,18 +287,52 @@ pub fn get_network_runner_enable_shutdown() -> bool { } #[must_use] -pub fn get_avalanchego_path() -> (String, bool) { - match std::env::var("AVALANCHEGO_PATH") { - Ok(s) => (s, true), - _ => (String::new(), false), +pub fn get_avalanchego_path(is_local: bool) -> Result { + match is_local { + true => { + let manifest_dir = env::var("CARGO_MANIFEST_DIR").context("No manifest dir found")?; + let manifest_path = Path::new(&manifest_dir); + + //Navigate two levels up from the Cargo manifest directory ../../ + let root = manifest_path + .parent() + .context("No parent dirctory found")? + .parent() + .context("No parent directory found")? + .parent() + .context("No parent directory found")? + .parent() + .context("No parent directory found")?; + + let avalanchego_path = root.join(".avalanchego"); + if !avalanchego_path.exists() { + log::debug!("avalanchego path: {:?}", avalanchego_path); + return Err(anyhow!( + " + avalanchego binary not in expected path. + Install the binary at the expected path {:?}", + avalanchego_path + )); + } + + let path_buf = avalanchego_path + .to_str() + .context("Failed to convert path to string")?; + log::debug!("avalanchego path: {}", path_buf); + Ok(path_buf.to_string()) + } + false => match std::env::var("AVALANCHEGO_PATH") { + Ok(s) => Ok(s), + _ => Err(anyhow!("AVALANCHEGO_PATH not provided")), + }, } } #[must_use] -pub fn get_vm_plugin_path() -> (String, bool) { +pub fn get_vm_plugin_path() -> Result { match std::env::var("VM_PLUGIN_PATH") { - Ok(s) => (s, true), - _ => (String::new(), false), + Ok(s) => Ok(s), + _ => Err(anyhow!("VM_PLUGIN_PATH not provided")), } } diff --git a/m1/simulator/src/main.rs b/m1/simulator/src/main.rs index 1ad2f828..3fa3624d 100644 --- a/m1/simulator/src/main.rs +++ b/m1/simulator/src/main.rs @@ -1,5 +1,6 @@ use clap::{Parser, Subcommand}; -use simulator::Network; +use env_logger::{Builder, Env}; +use simulator::{get_avalanchego_path, get_vm_plugin_path, Network}; type NodeId = u64; @@ -113,13 +114,24 @@ async fn main() -> Result<(), anyhow::Error> { } async fn start_network(opts: StartCommand) -> Result<(), anyhow::Error> { - let net = Network::new( + // Set log level based on verbosity + Builder::from_env(Env::default().default_filter_or(if opts.verbose { + "debug" + } else { + "info" + })) + .init(); + + let avalanche_path = get_avalanchego_path(opts.local)?; + let vm_path = get_vm_plugin_path()?; + let mut net = Network::new( opts.local, opts.grpc_endpoint, opts.staggered, - "avalanchego_path".to_string(), - "vm_plugin_path".to_string(), + avalanche_path, + vm_path, )?; + net.init_m1_network().await?; Ok(()) } From 196fb54488351ea31a3a733b2d1ce43ad05c8b0a Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Sat, 17 Feb 2024 19:36:53 +0000 Subject: [PATCH 15/27] update init logic --- m1/simulator/src/lib.rs | 65 ++++++++++++++++++++++++++++++---------- m1/simulator/src/main.rs | 3 +- 2 files changed, 50 insertions(+), 18 deletions(-) diff --git a/m1/simulator/src/lib.rs b/m1/simulator/src/lib.rs index 60ad2649..34a38c07 100644 --- a/m1/simulator/src/lib.rs +++ b/m1/simulator/src/lib.rs @@ -71,10 +71,6 @@ impl Network { return Err(anyhow!("GRPC endpoint not provided")); } }; - let _ = env_logger::builder() - .filter_level(log::LevelFilter::Info) - .is_test(true) - .try_init(); let cli = Client::new(&grpc).await; @@ -90,16 +86,18 @@ impl Network { .to_string(); let plugins_dir = if !&self.avalanchego_path.is_empty() { - let parent_dir = Path::new(&self.avalanchego_path) + let manifest_dir = env::var("CARGO_MANIFEST_DIR").context("No manifest dir found")?; + let workspace_dir = Path::new(&manifest_dir) .parent() - .expect("unexpected None parent"); - parent_dir + .context("No parent dir found")?; + workspace_dir .join("plugins") .as_os_str() .to_str() .unwrap() .to_string() } else { + // Don't think this block will ever get hit in the current state let exec_path = avalanche_installer::avalanchego::github::download( None, None, @@ -118,12 +116,11 @@ impl Network { vm_id ); - fs::create_dir(&plugins_dir).unwrap(); + //fs::create_dir(&plugins_dir)?; fs::copy( &self.vm_plugin_path, Path::new(&plugins_dir).join(vm_id.to_string()), - ) - .unwrap(); + )?; // write some random genesis file let genesis = random_manager::secure_string(10); @@ -137,6 +134,13 @@ impl Network { &self.avalanchego_path, genesis_file_path, ); + log::debug!( + "plugins dir: {}, global node config: {:?}", + plugins_dir, + serde_json::to_string(&GlobalConfig { + log_level: String::from("info"), + }) + ); let resp = cli .start(StartRequest { exec_path: self.avalanchego_path.clone(), @@ -156,8 +160,8 @@ impl Network { }], ..Default::default() }) - .await - .expect("failed start"); + .await?; + log::info!( "started avalanchego cluster with network-runner: {:?}", resp @@ -329,10 +333,39 @@ pub fn get_avalanchego_path(is_local: bool) -> Result { } #[must_use] -pub fn get_vm_plugin_path() -> Result { - match std::env::var("VM_PLUGIN_PATH") { - Ok(s) => Ok(s), - _ => Err(anyhow!("VM_PLUGIN_PATH not provided")), +pub fn get_vm_plugin_path(is_local: bool) -> Result { + match is_local { + true => { + let manifest_dir = env::var("CARGO_MANIFEST_DIR").context("No manifest dir found")?; + let manifest_path = Path::new(&manifest_dir); + + // Construct the path to the binary with ./target/debug/subnet + let subnet_path = manifest_path + .parent() + .context("Could not find the parent dir")? + .join("target") + .join("debug") + .join("subnet"); + if !subnet_path.exists() { + log::debug!("vm plugin path: {:?}", subnet_path); + return Err(anyhow!( + " + vm plugin not in expected path. + Install the plugin at the expected path {:?}", + subnet_path + )); + } + + let path_buf = subnet_path + .to_str() + .context("Failed to convert path to string")?; + log::debug!("vm plugin path: {}", path_buf); + Ok(path_buf.to_string()) + } + false => match std::env::var("VM_PLUGIN_PATH") { + Ok(s) => Ok(s), + _ => Err(anyhow!("VM_PLUGIN_PATH not provided")), + }, } } diff --git a/m1/simulator/src/main.rs b/m1/simulator/src/main.rs index 3fa3624d..64c4efa9 100644 --- a/m1/simulator/src/main.rs +++ b/m1/simulator/src/main.rs @@ -123,7 +123,7 @@ async fn start_network(opts: StartCommand) -> Result<(), anyhow::Error> { .init(); let avalanche_path = get_avalanchego_path(opts.local)?; - let vm_path = get_vm_plugin_path()?; + let vm_path = get_vm_plugin_path(opts.local)?; let mut net = Network::new( opts.local, opts.grpc_endpoint, @@ -131,7 +131,6 @@ async fn start_network(opts: StartCommand) -> Result<(), anyhow::Error> { avalanche_path, vm_path, )?; - net.init_m1_network().await?; Ok(()) } From 59ebfa9c839d554d5656cbbefc51f25810c27d9d Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Sun, 18 Feb 2024 17:21:11 +0000 Subject: [PATCH 16/27] solve avalanchego bin exec bug --- m1/simulator/src/lib.rs | 12 +++++++----- m1/simulator/src/main.rs | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/m1/simulator/src/lib.rs b/m1/simulator/src/lib.rs index 34a38c07..0ccfb4bb 100644 --- a/m1/simulator/src/lib.rs +++ b/m1/simulator/src/lib.rs @@ -85,6 +85,8 @@ impl Network { .unwrap() .to_string(); + let vm_id = subnet::vm_name_to_id("subnet").unwrap(); + let plugins_dir = if !&self.avalanchego_path.is_empty() { let manifest_dir = env::var("CARGO_MANIFEST_DIR").context("No manifest dir found")?; let workspace_dir = Path::new(&manifest_dir) @@ -105,7 +107,7 @@ impl Network { ) .await .unwrap(); - self.avalanchego_path = exec_path; + self.avalanchego_path = exec_path.clone(); avalanche_installer::avalanchego::get_plugin_dir(&self.avalanchego_path) }; @@ -298,17 +300,17 @@ pub fn get_avalanchego_path(is_local: bool) -> Result { let manifest_path = Path::new(&manifest_dir); //Navigate two levels up from the Cargo manifest directory ../../ - let root = manifest_path + let avalanchego_path = manifest_path .parent() .context("No parent dirctory found")? .parent() .context("No parent directory found")? .parent() .context("No parent directory found")? - .parent() - .context("No parent directory found")?; + .join("avalanchego") + .join("build") + .join("avalanchego"); - let avalanchego_path = root.join(".avalanchego"); if !avalanchego_path.exists() { log::debug!("avalanchego path: {:?}", avalanchego_path); return Err(anyhow!( diff --git a/m1/simulator/src/main.rs b/m1/simulator/src/main.rs index 64c4efa9..60189822 100644 --- a/m1/simulator/src/main.rs +++ b/m1/simulator/src/main.rs @@ -127,7 +127,7 @@ async fn start_network(opts: StartCommand) -> Result<(), anyhow::Error> { let mut net = Network::new( opts.local, opts.grpc_endpoint, - opts.staggered, + false, avalanche_path, vm_path, )?; From 0bce357be95b9c30e23fc3332473f26848eeebee Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Sun, 18 Feb 2024 18:37:47 +0000 Subject: [PATCH 17/27] revert config.toml --- m1/.cargo/config.toml | 33 ++++++++++++++++++++++++++++++++- m1/scripts/subnet-cli-setup.sh | 25 +++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100755 m1/scripts/subnet-cli-setup.sh diff --git a/m1/.cargo/config.toml b/m1/.cargo/config.toml index bff29e6e..769caad3 100644 --- a/m1/.cargo/config.toml +++ b/m1/.cargo/config.toml @@ -1,2 +1,33 @@ +[alias] +xclippy = [ + "clippy", + "--workspace", + "--all-targets", + "--", + "-Dwarnings", + "-Wclippy::all", + "-Aclippy::upper_case_acronyms", + "-Aclippy::enum-variant-names", + "-Aclippy::result-large-err", + "-Aclippy::mutable-key-type", +] + [build] -rustflags = ["--cfg", "tokio_unstable"] +rustflags = ["--cfg", "tokio_unstable", "-C", "force-frame-pointers=yes", "-C", "force-unwind-tables=yes"] + +# TODO(grao): Figure out whether we should enable other cpu features, and whether we should use a different way to configure them rather than list every single one here. +[target.x86_64-unknown-linux-gnu] +rustflags = ["--cfg", "tokio_unstable", "-C", "link-arg=-fuse-ld=lld", "-C", "force-frame-pointers=yes", "-C", "force-unwind-tables=yes", "-C", "target-feature=+sse4.2"] + +# 64 bit MSVC +[target.x86_64-pc-windows-msvc] +rustflags = [ + "--cfg", + "tokio_unstable", + "-C", + "force-frame-pointers=yes", + "-C", + "force-unwind-tables=yes", + "-C", + "link-arg=/STACK:8000000" # Set stack to 8 MB +] \ No newline at end of file diff --git a/m1/scripts/subnet-cli-setup.sh b/m1/scripts/subnet-cli-setup.sh new file mode 100755 index 00000000..1c0cc2ad --- /dev/null +++ b/m1/scripts/subnet-cli-setup.sh @@ -0,0 +1,25 @@ +#!/bin/bash -e + +# Install subnet-cli +VERSION=0.0.4 # Populate latest here + +GOARCH=$(go env GOARCH) +GOOS=$(go env GOOS) +DOWNLOAD_PATH=/tmp/subnet-cli.tar.gz +DOWNLOAD_URL=https://github.com/ava-labs/subnet-cli/releases/download/v${VERSION}/subnet-cli_${VERSION}_linux_${GOARCH}.tar.gz +if [[ ${GOOS} == "darwin" ]]; then + DOWNLOAD_URL=https://github.com/ava-labs/subnet-cli/releases/download/v${VERSION}/subnet-cli_${VERSION}_darwin_${GOARCH}.tar.gz +fi + +rm -f ${DOWNLOAD_PATH} +rm -f /tmp/subnet-cli + +echo "downloading subnet-cli ${VERSION} at ${DOWNLOAD_URL}" +curl -L ${DOWNLOAD_URL} -o ${DOWNLOAD_PATH} + +echo "extracting downloaded subnet-cli" +tar xzvf ${DOWNLOAD_PATH} -C /tmp + +/tmp/subnet-cli -h + +cp /tmp/subnet-cli $HOME/bin/subnet-cli \ No newline at end of file From 6ce4cc2dd7908f4ad61c56efb295b6c4d1859023 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Mon, 19 Feb 2024 13:26:49 +0000 Subject: [PATCH 18/27] implement Simulator, refactor, dispatch --- m1/simulator/Cargo.toml | 2 + m1/simulator/src/commands.rs | 103 +++++++++++++++ m1/simulator/src/lib.rs | 238 +++++++++++++++++------------------ m1/simulator/src/main.rs | 146 +-------------------- 4 files changed, 223 insertions(+), 266 deletions(-) create mode 100644 m1/simulator/src/commands.rs diff --git a/m1/simulator/Cargo.toml b/m1/simulator/Cargo.toml index 3f0a1392..55d7df72 100644 --- a/m1/simulator/Cargo.toml +++ b/m1/simulator/Cargo.toml @@ -15,6 +15,8 @@ anyhow = { workspace = true } env_logger = { workspace = true } url = { workspace = true } tokio = { workspace = true } +tonic = "0.9.2" once_cell = { workspace = true } rand = { workspace = true } +reqwest = "0.11.24" clap = { workspace = true } \ No newline at end of file diff --git a/m1/simulator/src/commands.rs b/m1/simulator/src/commands.rs new file mode 100644 index 00000000..7a5236a7 --- /dev/null +++ b/m1/simulator/src/commands.rs @@ -0,0 +1,103 @@ +use clap::{Parser, Subcommand}; + +#[derive(Debug, Parser, Clone)] +#[clap(name = "forc index", about = "M1 network simulator", version = "0.1")] +pub struct Cli { + /// The command to run + #[clap(subcommand)] + pub command: SubCommands, +} + +/// Start the simulator +#[derive(Debug, Parser, Clone)] +pub struct StartCommand { + /// The number of validators for the network + #[clap( + long, + default_value = "5", + help = "The number of validators for the network." + )] + pub validators: u64, + + /// Sets if the validators join the network at once, or in a staggered way + #[clap( + long, + default_value = "false", + help = "Sets if the validators join the network at once, or in a staggered way." + )] + pub staggered: bool, + + /// Verbose output + #[clap(short, long, help = "Verbose output.")] + pub verbose: bool, + + /// The GRPC endpoint of the network runner to connect to + #[clap(long, help = "The GRPC endpoint of the network runner to connect to.")] + pub grpc_endpoint: Option, +} + +/// Partition the network +#[derive(Debug, Parser, Clone)] +pub struct PartitionCommand { + /// The percentage of validators that will be partitioned + #[clap( + long, + default_value = "5", + help = "The percentage of validators that will be in a partitioned state" + )] + pub amount: u8, + + /// Sets if the validators become paritioned at once or in a staggered way + #[clap( + long, + default_value = "false", + help = "Sets if the validators become partitioned at once or in a staggered way." + )] + pub staggered: bool, + + /// Verbose output + #[clap(short, long, help = "Verbose output.")] + pub verbose: bool, +} + +#[derive(Debug, Parser, Clone)] +pub struct ReconnectCommand { + /// The nodes to reconnect by `NodeId` + pub nodes: Vec, + + /// Sets if the validators rejoin the network together or in a staggered way + #[clap(long, default_value = "false")] + pub staggered: bool, + + /// Verbose output + #[clap(short, long, help = "Verbose output.")] + pub verbose: bool, +} + +#[derive(Debug, Parser, Clone)] +pub struct AddNodeCommand { + /// Verbose output + #[clap(short, long, help = "Verbose output.")] + pub verbose: bool, +} + +#[derive(Debug, Parser, Clone)] +pub struct HealthCommand { + /// Verbose ouput + #[clap(short, long, help = "Verbose output.")] + pub verbose: bool, +} + +#[derive(Debug, Subcommand, Clone)] +pub enum SubCommands { + /// Starts the network with a number of validators + Start(StartCommand), + /// Adds a node to the network + AddNode(AddNodeCommand), + /// Simulates a network partition. + Partition(PartitionCommand), + /// Reconnects the validators after they have become partitioned + Reconnect(ReconnectCommand), + /// Output the overall network and consensus health + Health(HealthCommand), +} diff --git a/m1/simulator/src/lib.rs b/m1/simulator/src/lib.rs index 0ccfb4bb..50180bd4 100644 --- a/m1/simulator/src/lib.rs +++ b/m1/simulator/src/lib.rs @@ -17,66 +17,57 @@ use avalanche_types::{ jsonrpc::client::info as avalanche_sdk_info, subnet::{self, vm_name_to_id}, }; +use commands::*; +use tonic::transport::Channel; -const LOCAL_GRPC_ENDPOINT: &str = "http://127.0.0.1:12342"; +pub mod commands; + +const AVALANCHEGO_VERSION: &str = "v1.10.9"; +pub const LOCAL_GRPC_ENDPOINT: &str = "http://127.0.0.1:12342"; const VM_NAME: &str = "subnet"; -/// Network configuration -pub struct Network { - /// The GRPC endpoint of the network runner to connect to - pub grpc_endpoint: Option, - /// Sets if the validators join the network at once, or in a staggered way - pub enable_shutdown: bool, - /// The path to the avalanchego binary +pub struct Simulator { + pub cli: Client, + pub command: SubCommands, pub avalanchego_path: String, - /// The path to the VM plugin pub vm_plugin_path: String, - /// VM name, this is hardcoded for now - pub vm_name: VmId, } -impl Network { - /// Create a new network configuration - - pub fn new( - is_local: bool, - grpc_endpoint: Option, - enable_shutdown: bool, - avalanchego_path: String, - vm_plugin_path: String, - ) -> Result { - let grpc_endpoint = match is_local { - true => Some(LOCAL_GRPC_ENDPOINT.to_string()), - false => { - if let Some(endpoint) = grpc_endpoint { - Some(endpoint) - } else { - return Err(anyhow!("GRPC endpoint not provided")); - } - } - }; - Ok(Network { - grpc_endpoint, - enable_shutdown, - avalanchego_path, - vm_plugin_path, - vm_name: subnet::vm_name_to_id(VM_NAME)?, +impl Simulator { + pub async fn new(command: SubCommands) -> Result { + let cli = Client::new(LOCAL_GRPC_ENDPOINT).await; + log::info!("ping..."); + let resp = cli.ping().await.expect("failed ping"); + log::info!("network-runner is running (ping response {:?})", resp); + Ok(Self { + cli, + command, + avalanchego_path: get_avalanchego_path()?, + vm_plugin_path: get_vm_plugin_path()?, }) } - pub async fn init_m1_network(&mut self) -> Result<(), anyhow::Error> { - let grpc = match self.grpc_endpoint.clone() { - Some(grpc) => grpc, - None => { - return Err(anyhow!("GRPC endpoint not provided")); - } - }; - - let cli = Client::new(&grpc).await; + pub async fn dispatch(&mut self) -> Result<()> { + match &self.command { + SubCommands::Start(cmd) => self.start_network(cmd.clone()).await?, + SubCommands::Partition(cmd) => self.partition_network(cmd.clone()).await?, + SubCommands::Reconnect(cmd) => self.reconnect_validators(cmd.clone()).await?, + SubCommands::Health(cmd) => self.network_health(cmd.clone()).await?, + SubCommands::AddNode(cmd) => self.add_node(cmd.clone()).await?, + } + Ok(()) + } - log::info!("ping..."); - let resp = cli.ping().await.expect("failed ping"); - log::info!("network-runner is running (ping response {:?})", resp); + pub async fn start_network(&mut self, cmd: StartCommand) -> Result<()> { + // Set log level based on verbosity + env_logger::Builder::from_env( + env_logger::Env::default().default_filter_or(if cmd.verbose { + "debug" + } else { + "info" + }), + ) + .init(); let vm_id = Path::new(&self.vm_plugin_path) .file_stem() @@ -143,7 +134,8 @@ impl Network { log_level: String::from("info"), }) ); - let resp = cli + let resp = self + .cli .start(StartRequest { exec_path: self.avalanchego_path.clone(), num_nodes: Some(5), @@ -196,7 +188,7 @@ impl Network { thread::sleep(itv); ready = { - match cli.health().await { + match self.cli.health().await { Ok(_) => { log::info!("healthy now!"); true @@ -216,7 +208,7 @@ impl Network { assert!(ready); log::info!("checking status..."); - let mut status = cli.status().await.expect("failed status"); + let mut status = self.cli.status().await.expect("failed status"); loop { let elapsed = start.elapsed(); if elapsed.gt(&timeout) { @@ -231,7 +223,7 @@ impl Network { log::info!("retrying checking status..."); thread::sleep(interval); - status = cli.status().await.expect("failed status"); + status = self.cli.status().await.expect("failed status"); } assert!(status.cluster_info.is_some()); @@ -277,6 +269,24 @@ impl Network { } Ok(()) } + + pub async fn partition_network(&self, cmd: PartitionCommand) -> Result<()> { + Ok(()) + } + + pub async fn reconnect_validators(&self, cmd: ReconnectCommand) -> Result<()> { + Ok(()) + } + + pub async fn network_health(&self, cmd: HealthCommand) -> Result<()> { + let resp = self.cli.health().await?; + log::info!("network health: {:?}", resp); + Ok(()) + } + + pub async fn add_node(&self, cmd: AddNodeCommand) -> Result<()> { + Ok(()) + } } #[must_use] @@ -293,85 +303,67 @@ pub fn get_network_runner_enable_shutdown() -> bool { } #[must_use] -pub fn get_avalanchego_path(is_local: bool) -> Result { - match is_local { - true => { - let manifest_dir = env::var("CARGO_MANIFEST_DIR").context("No manifest dir found")?; - let manifest_path = Path::new(&manifest_dir); - - //Navigate two levels up from the Cargo manifest directory ../../ - let avalanchego_path = manifest_path - .parent() - .context("No parent dirctory found")? - .parent() - .context("No parent directory found")? - .parent() - .context("No parent directory found")? - .join("avalanchego") - .join("build") - .join("avalanchego"); - - if !avalanchego_path.exists() { - log::debug!("avalanchego path: {:?}", avalanchego_path); - return Err(anyhow!( - " +pub fn get_avalanchego_path() -> Result { + let manifest_dir = env::var("CARGO_MANIFEST_DIR").context("No manifest dir found")?; + let manifest_path = Path::new(&manifest_dir); + + //Navigate two levels up from the Cargo manifest directory ../../ + let avalanchego_path = manifest_path + .parent() + .context("No parent dirctory found")? + .parent() + .context("No parent directory found")? + .parent() + .context("No parent directory found")? + .join("avalanchego") + .join("build") + .join("avalanchego"); + + if !avalanchego_path.exists() { + log::debug!("avalanchego path: {:?}", avalanchego_path); + return Err(anyhow!( + " avalanchego binary not in expected path. Install the binary at the expected path {:?}", - avalanchego_path - )); - } - - let path_buf = avalanchego_path - .to_str() - .context("Failed to convert path to string")?; - log::debug!("avalanchego path: {}", path_buf); - Ok(path_buf.to_string()) - } - false => match std::env::var("AVALANCHEGO_PATH") { - Ok(s) => Ok(s), - _ => Err(anyhow!("AVALANCHEGO_PATH not provided")), - }, + avalanchego_path + )); } + + let path_buf = avalanchego_path + .to_str() + .context("Failed to convert path to string")?; + log::debug!("avalanchego path: {}", path_buf); + Ok(path_buf.to_string()) } #[must_use] -pub fn get_vm_plugin_path(is_local: bool) -> Result { - match is_local { - true => { - let manifest_dir = env::var("CARGO_MANIFEST_DIR").context("No manifest dir found")?; - let manifest_path = Path::new(&manifest_dir); - - // Construct the path to the binary with ./target/debug/subnet - let subnet_path = manifest_path - .parent() - .context("Could not find the parent dir")? - .join("target") - .join("debug") - .join("subnet"); - if !subnet_path.exists() { - log::debug!("vm plugin path: {:?}", subnet_path); - return Err(anyhow!( - " +pub fn get_vm_plugin_path() -> Result { + let manifest_dir = env::var("CARGO_MANIFEST_DIR").context("No manifest dir found")?; + let manifest_path = Path::new(&manifest_dir); + + // Construct the path to the binary with ./target/debug/subnet + let subnet_path = manifest_path + .parent() + .context("Could not find the parent dir")? + .join("target") + .join("debug") + .join("subnet"); + if !subnet_path.exists() { + log::debug!("vm plugin path: {:?}", subnet_path); + return Err(anyhow!( + " vm plugin not in expected path. Install the plugin at the expected path {:?}", - subnet_path - )); - } - - let path_buf = subnet_path - .to_str() - .context("Failed to convert path to string")?; - log::debug!("vm plugin path: {}", path_buf); - Ok(path_buf.to_string()) - } - false => match std::env::var("VM_PLUGIN_PATH") { - Ok(s) => Ok(s), - _ => Err(anyhow!("VM_PLUGIN_PATH not provided")), - }, + subnet_path + )); } -} -const AVALANCHEGO_VERSION: &str = "v1.10.9"; + let path_buf = subnet_path + .to_str() + .context("Failed to convert path to string")?; + log::debug!("vm plugin path: {}", path_buf); + Ok(path_buf.to_string()) +} // todo: extracted from genesis method // todo: really we should use a genesis once more @@ -389,5 +381,3 @@ pub fn sync_genesis(byte_string: &str, file_path: &str) -> io::Result<()> { Ok(()) } - -pub async fn init_m1_network() {} diff --git a/m1/simulator/src/main.rs b/m1/simulator/src/main.rs index 60189822..8aba9ca0 100644 --- a/m1/simulator/src/main.rs +++ b/m1/simulator/src/main.rs @@ -1,148 +1,10 @@ -use clap::{Parser, Subcommand}; -use env_logger::{Builder, Env}; -use simulator::{get_avalanchego_path, get_vm_plugin_path, Network}; - -type NodeId = u64; - -#[derive(Debug, Parser)] -#[clap(name = "forc index", about = "M1 network simulator", version = "0.1")] -pub struct Cli { - /// The command to run - #[clap(subcommand)] - pub command: SubCommands, -} - -/// Start the simulator -#[derive(Debug, Parser)] -pub struct StartCommand { - /// The number of validators for the network - #[clap( - long, - default_value = "5", - help = "The number of validators for the network." - )] - pub validators: u64, - - /// Sets if the validators join the network at once, or in a staggered way - #[clap( - long, - default_value = "false", - help = "Sets if the validators join the network at once, or in a staggered way." - )] - pub staggered: bool, - - /// Verbose output - #[clap(short, long, help = "Verbose output.")] - pub verbose: bool, - - /// Run simuilator against the local network - #[clap(long, help = "Run simuilator against the local network.")] - pub local: bool, - - /// The GRPC endpoint of the network runner to connect to - #[clap(long, help = "The GRPC endpoint of the network runner to connect to.")] - pub grpc_endpoint: Option, -} - -/// Partition the network -#[derive(Debug, Parser)] -pub struct PartitionCommand { - /// The percentage of validators that will be partitioned - #[clap( - long, - default_value = "5", - help = "The percentage of validators that will be in a partitioned state" - )] - pub amount: u8, - - /// Sets if the validators become paritioned at once or in a staggered way - #[clap( - long, - default_value = "false", - help = "Sets if the validators become partitioned at once or in a staggered way." - )] - pub staggered: bool, - - /// Verbose output - #[clap(short, long, help = "Verbose output.")] - pub verbose: bool, -} - -#[derive(Debug, Parser)] -pub struct ReconnectCommand { - /// The nodes to reconnect by `NodeId` - pub nodes: Vec, - - /// Sets if the validators rejoin the network together or in a staggered way - #[clap(long, default_value = "false")] - pub staggered: bool, - - /// Verbose output - #[clap(short, long, help = "Verbose output.")] - pub verbose: bool, -} - -#[derive(Debug, Parser)] -pub struct HealthCommand { - /// Verbose ouput - #[clap(short, long, help = "Verbose output.")] - pub verbose: bool, -} - -#[derive(Debug, Subcommand)] -pub enum SubCommands { - /// Starts the network with a number of validators - Start(StartCommand), - /// Simulates a network partition. - Partition(PartitionCommand), - /// Reconnects the validators after they have become partitioned - Reconnect(ReconnectCommand), - /// Output the overall network and consensus health - Health(HealthCommand), -} +use clap::Parser; +use simulator::{commands::Cli, Simulator}; #[tokio::main] async fn main() -> Result<(), anyhow::Error> { let cli = Cli::parse(); - match cli.command { - SubCommands::Start(opts) => start_network(opts).await?, - SubCommands::Partition(opts) => partition_network(opts).await?, - SubCommands::Reconnect(opts) => reconnect_validators(opts).await?, - SubCommands::Health(opts) => network_health(opts).await?, - } - Ok(()) -} - -async fn start_network(opts: StartCommand) -> Result<(), anyhow::Error> { - // Set log level based on verbosity - Builder::from_env(Env::default().default_filter_or(if opts.verbose { - "debug" - } else { - "info" - })) - .init(); - - let avalanche_path = get_avalanchego_path(opts.local)?; - let vm_path = get_vm_plugin_path(opts.local)?; - let mut net = Network::new( - opts.local, - opts.grpc_endpoint, - false, - avalanche_path, - vm_path, - )?; - net.init_m1_network().await?; - Ok(()) -} - -async fn partition_network(opts: PartitionCommand) -> Result<(), anyhow::Error> { - Ok(()) -} - -async fn reconnect_validators(opts: ReconnectCommand) -> Result<(), anyhow::Error> { - Ok(()) -} - -async fn network_health(opts: HealthCommand) -> Result<(), anyhow::Error> { + let mut simulator = Simulator::new(cli.command).await?; + simulator.dispatch().await?; Ok(()) } From aee52e7ed6862b0099e856d87883c1025ed02818 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Mon, 19 Feb 2024 16:40:53 +0000 Subject: [PATCH 19/27] updates --- m1/simulator/Cargo.toml | 3 +- m1/simulator/src/lib.rs | 66 +++++++++++++++++++++++++++++------------ 2 files changed, 49 insertions(+), 20 deletions(-) diff --git a/m1/simulator/Cargo.toml b/m1/simulator/Cargo.toml index 55d7df72..1b4c68d1 100644 --- a/m1/simulator/Cargo.toml +++ b/m1/simulator/Cargo.toml @@ -5,9 +5,10 @@ edition = "2021" [dependencies] avalanche-installer = "0.0.77" -avalanche-network-runner-sdk = "0.3.3" # https://crates.io/crates/avalanche-network-runner-sdk +avalanche-network-runner-sdk = { git = "https://github.com/0xmovses/avalanche-network-runner-sdk-rs", branch = "main" } log = "0.4.19" random-manager = "0.0.5" +serde = { workspace = true } serde_json = "1.0.108" # https://github.com/serde-rs/json/releases avalanche-types = { workspace = true } # https://crates.io/crates/avalanche-types aptos-sdk = {workspace = true } diff --git a/m1/simulator/src/lib.rs b/m1/simulator/src/lib.rs index 50180bd4..1ef13f58 100644 --- a/m1/simulator/src/lib.rs +++ b/m1/simulator/src/lib.rs @@ -1,17 +1,20 @@ +use anyhow::{anyhow, Context, Result}; use core::time; +use serde::{Deserialize, Serialize}; use std::{ env, fs::{self, File}, io::{self, Write}, path::Path, str::FromStr, + sync::Arc, thread, time::{Duration, Instant}, }; -use anyhow::{anyhow, Context, Result}; - -use avalanche_network_runner_sdk::{BlockchainSpec, Client, GlobalConfig, StartRequest}; +use avalanche_network_runner_sdk::{ + AddNodeRequest, BlockchainSpec, Client, GlobalConfig, StartRequest, +}; use avalanche_types::{ ids::{self, Id as VmId}, jsonrpc::client::info as avalanche_sdk_info, @@ -27,7 +30,7 @@ pub const LOCAL_GRPC_ENDPOINT: &str = "http://127.0.0.1:12342"; const VM_NAME: &str = "subnet"; pub struct Simulator { - pub cli: Client, + pub cli: Arc>, pub command: SubCommands, pub avalanchego_path: String, pub vm_plugin_path: String, @@ -36,11 +39,8 @@ pub struct Simulator { impl Simulator { pub async fn new(command: SubCommands) -> Result { let cli = Client::new(LOCAL_GRPC_ENDPOINT).await; - log::info!("ping..."); - let resp = cli.ping().await.expect("failed ping"); - log::info!("network-runner is running (ping response {:?})", resp); Ok(Self { - cli, + cli: Arc::new(cli), command, avalanchego_path: get_avalanchego_path()?, vm_plugin_path: get_vm_plugin_path()?, @@ -59,7 +59,6 @@ impl Simulator { } pub async fn start_network(&mut self, cmd: StartCommand) -> Result<()> { - // Set log level based on verbosity env_logger::Builder::from_env( env_logger::Env::default().default_filter_or(if cmd.verbose { "debug" @@ -68,6 +67,7 @@ impl Simulator { }), ) .init(); + log::debug!("Running command: {:?}", cmd); let vm_id = Path::new(&self.vm_plugin_path) .file_stem() @@ -257,16 +257,16 @@ impl Simulator { .parse::() .unwrap(); - log::info!("sleeping for {} seconds", timeout.as_secs()); - if val < 0 { - // run forever - loop { - thread::sleep(Duration::from_secs(1000)); - } - } else { - let timeout = Duration::from_secs(val as u64); - thread::sleep(timeout); - } + // log::info!("sleeping for {} seconds", timeout.as_secs()); + // if val < 0 { + // // run forever + // loop { + // thread::sleep(Duration::from_secs(1000)); + // } + // } else { + // let timeout = Duration::from_secs(val as u64); + // thread::sleep(timeout); + // } Ok(()) } @@ -279,12 +279,40 @@ impl Simulator { } pub async fn network_health(&self, cmd: HealthCommand) -> Result<()> { + env_logger::Builder::from_env( + env_logger::Env::default().default_filter_or(if cmd.verbose { + "debug" + } else { + "info" + }), + ) + .init(); + log::debug!("Running command: {:?}", cmd); let resp = self.cli.health().await?; log::info!("network health: {:?}", resp); Ok(()) } pub async fn add_node(&self, cmd: AddNodeCommand) -> Result<()> { + env_logger::Builder::from_env( + env_logger::Env::default().default_filter_or(if cmd.verbose { + "debug" + } else { + "info" + }), + ) + .init(); + log::debug!("Running command: {:?}", cmd); + let resp = self + .cli + .add_node(AddNodeRequest { + name: format!("node-simulator-{}", random_manager::secure_string(5)), + exec_path: self.avalanchego_path.clone(), + node_config: None, + ..Default::default() + }) + .await?; + log::info!("added node: {:?}", resp); Ok(()) } } From 31a5d54ec7b65f949a9e6e53a5229935de3021e6 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Mon, 19 Feb 2024 16:41:12 +0000 Subject: [PATCH 20/27] cargo.lock --- m1/Cargo.lock | 66 ++++++++++++++++++++++++++++++++----------------- m1/rustfmt.toml | 5 ++++ 2 files changed, 48 insertions(+), 23 deletions(-) create mode 100644 m1/rustfmt.toml diff --git a/m1/Cargo.lock b/m1/Cargo.lock index 3504bede..990e30b6 100644 --- a/m1/Cargo.lock +++ b/m1/Cargo.lock @@ -2579,6 +2579,22 @@ dependencies = [ "tonic-build", ] +[[package]] +name = "avalanche-network-runner-sdk" +version = "0.3.3" +source = "git+https://github.com/0xmovses/avalanche-network-runner-sdk-rs?branch=main#7b9a0ac30987325c5c098d9aa0af3031e835bf94" +dependencies = [ + "log", + "prost", + "prost-types", + "serde 1.0.193", + "serde_json", + "tokio", + "tokio-stream", + "tonic 0.9.2", + "tonic-build", +] + [[package]] name = "avalanche-types" version = "0.1.4" @@ -3443,14 +3459,25 @@ dependencies = [ "version_check", ] +[[package]] +name = "cookie" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24" +dependencies = [ + "percent-encoding 2.3.1", + "time", + "version_check", +] + [[package]] name = "cookie_store" -version = "0.16.2" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d606d0fba62e13cf04db20536c05cb7f13673c161cb47a47a82b9b9e7d3f1daa" +checksum = "387461abbc748185c3a6e1673d826918b450b87ff22639429c694619a83b6cf6" dependencies = [ - "cookie", - "idna 0.2.3", + "cookie 0.17.0", + "idna 0.3.0", "log", "publicsuffix", "serde 1.0.193", @@ -3994,7 +4021,7 @@ name = "e2e" version = "0.0.0" dependencies = [ "avalanche-installer", - "avalanche-network-runner-sdk", + "avalanche-network-runner-sdk 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "avalanche-types", "env_logger", "log", @@ -5115,17 +5142,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "idna" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "0.3.0" @@ -7310,7 +7326,7 @@ dependencies = [ "async-trait", "bytes", "chrono", - "cookie", + "cookie 0.16.2", "futures-util", "headers", "http", @@ -8066,13 +8082,13 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.11.22" +version = "0.11.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" +checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251" dependencies = [ "base64 0.21.5", "bytes", - "cookie", + "cookie 0.17.0", "cookie_store", "encoding_rs", "futures-core", @@ -8097,6 +8113,7 @@ dependencies = [ "serde 1.0.193", "serde_json", "serde_urlencoded", + "sync_wrapper", "system-configuration", "tokio", "tokio-native-tls", @@ -8994,7 +9011,7 @@ dependencies = [ "anyhow", "aptos-sdk", "avalanche-installer", - "avalanche-network-runner-sdk", + "avalanche-network-runner-sdk 0.3.3 (git+https://github.com/0xmovses/avalanche-network-runner-sdk-rs?branch=main)", "avalanche-types", "clap 3.2.25", "env_logger", @@ -9002,8 +9019,11 @@ dependencies = [ "once_cell", "rand 0.7.3", "random-manager", + "reqwest", + "serde 1.0.193", "serde_json", "tokio", + "tonic 0.9.2", "url 2.5.0", ] @@ -10522,9 +10542,9 @@ checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" [[package]] name = "wasm-streams" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" dependencies = [ "futures-util", "js-sys", diff --git a/m1/rustfmt.toml b/m1/rustfmt.toml new file mode 100644 index 00000000..055778f5 --- /dev/null +++ b/m1/rustfmt.toml @@ -0,0 +1,5 @@ +comment_width = 100 +format_code_in_doc_comments = true +imports_granularity = "Crate" +imports_layout = "Vertical" +wrap_comments = true \ No newline at end of file From e681064433893380fd708ec73cd151ffb07665fd Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Tue, 20 Feb 2024 15:06:45 +0000 Subject: [PATCH 21/27] add validators --- m1/simulator/src/commands.rs | 44 +++++++++++ m1/simulator/src/lib.rs | 140 ++++++++++++++++++++++++++++------- 2 files changed, 158 insertions(+), 26 deletions(-) diff --git a/m1/simulator/src/commands.rs b/m1/simulator/src/commands.rs index 7a5236a7..e1f98fc6 100644 --- a/m1/simulator/src/commands.rs +++ b/m1/simulator/src/commands.rs @@ -74,11 +74,49 @@ pub struct ReconnectCommand { pub verbose: bool, } +/// Add a node to the network #[derive(Debug, Parser, Clone)] pub struct AddNodeCommand { /// Verbose output #[clap(short, long, help = "Verbose output.")] pub verbose: bool, + + /// The name of the node to add + #[clap(long, help = "The name of the node to add.")] + pub name: Option, +} + +#[derive(Debug, Parser, Clone)] +pub struct RemoveNodeCommand { + /// Verbose output + #[clap(short, long, help = "Verbose output.")] + pub verbose: bool, + + /// The name of the node to remove + #[clap(long, help = "The name of the node to remove.")] + pub name: String, +} + +#[derive(Debug, Parser, Clone)] +pub struct AddValidatorCommand { + /// Verbose output + #[clap(short, long, help = "Verbose output.")] + pub verbose: bool, + + /// The name of the validator to add + #[clap(long, help = "The name of the validator to add.")] + pub name: String, +} + +#[derive(Debug, Parser, Clone)] +pub struct RemoveValidatorCommand { + /// Verbose output + #[clap(short, long, help = "Verbose output.")] + pub verbose: bool, + + /// The name of the validator to remove + #[clap(long, help = "The name of the validator to remove.")] + pub name: String, } #[derive(Debug, Parser, Clone)] @@ -94,6 +132,12 @@ pub enum SubCommands { Start(StartCommand), /// Adds a node to the network AddNode(AddNodeCommand), + /// Removes a node from the network + RemoveNode(RemoveNodeCommand), + /// Adds a validator to the network + AddValidator(AddValidatorCommand), + /// Removes a validator from the network + RemoveValidator(RemoveValidatorCommand), /// Simulates a network partition. Partition(PartitionCommand), /// Reconnects the validators after they have become partitioned diff --git a/m1/simulator/src/lib.rs b/m1/simulator/src/lib.rs index 1ef13f58..38978e64 100644 --- a/m1/simulator/src/lib.rs +++ b/m1/simulator/src/lib.rs @@ -2,6 +2,7 @@ use anyhow::{anyhow, Context, Result}; use core::time; use serde::{Deserialize, Serialize}; use std::{ + collections::HashMap, env, fs::{self, File}, io::{self, Write}, @@ -13,7 +14,11 @@ use std::{ }; use avalanche_network_runner_sdk::{ - AddNodeRequest, BlockchainSpec, Client, GlobalConfig, StartRequest, + rpcpb::{ + AddPermissionlessValidatorRequest, CustomChainInfo, PermissionlessStakerSpec, + RemoveSubnetValidatorRequest, VmidRequest, VmidResponse, + }, + AddNodeRequest, BlockchainSpec, Client, GlobalConfig, RemoveNodeRequest, StartRequest, }; use avalanche_types::{ ids::{self, Id as VmId}, @@ -25,15 +30,25 @@ use tonic::transport::Channel; pub mod commands; -const AVALANCHEGO_VERSION: &str = "v1.10.9"; +const AVALANCHEGO_VERSION: &str = "v1.10.12"; pub const LOCAL_GRPC_ENDPOINT: &str = "http://127.0.0.1:12342"; const VM_NAME: &str = "subnet"; +/// The Simulator is used to run commands on the avalanche-go-network-runner. +/// It can be used across multiple threads and is thread safe. pub struct Simulator { + /// The network runner client pub cli: Arc>, + /// The command to run pub command: SubCommands, + /// The path to the avalanchego binary, must be version no higher than 1.10.12 + /// higher versions use a VM plugin version higher that `28`, which is used by `subnet` pub avalanchego_path: String, + /// The path to the VM plugin pub vm_plugin_path: String, + /// The subnet ID created by the network runner, + /// this is not the same value as the VM ID. + pub subnet_id: Option, } impl Simulator { @@ -44,16 +59,20 @@ impl Simulator { command, avalanchego_path: get_avalanchego_path()?, vm_plugin_path: get_vm_plugin_path()?, + subnet_id: None, }) } pub async fn dispatch(&mut self) -> Result<()> { match &self.command { SubCommands::Start(cmd) => self.start_network(cmd.clone()).await?, + SubCommands::AddNode(cmd) => self.add_node(cmd.clone()).await?, + SubCommands::RemoveNode(cmd) => self.remove_node(cmd.clone()).await?, + SubCommands::AddValidator(cmd) => self.add_validator(cmd.clone()).await?, + SubCommands::RemoveValidator(cmd) => self.remove_validator(cmd.clone()).await?, SubCommands::Partition(cmd) => self.partition_network(cmd.clone()).await?, SubCommands::Reconnect(cmd) => self.reconnect_validators(cmd.clone()).await?, SubCommands::Health(cmd) => self.network_health(cmd.clone()).await?, - SubCommands::AddNode(cmd) => self.add_node(cmd.clone()).await?, } Ok(()) } @@ -149,17 +168,17 @@ impl Simulator { blockchain_specs: vec![BlockchainSpec { vm_name: String::from(VM_NAME), genesis: genesis_file_path.to_string(), - // blockchain_alias : String::from("subnet"), // todo: this doesn't always work oddly enough, need to debug + //blockchain_alias : String::from("subnet"), // todo: this doesn't always work oddly enough, need to debug ..Default::default() }], ..Default::default() }) .await?; - log::info!( "started avalanchego cluster with network-runner: {:?}", resp ); + // enough time for network-runner to get ready thread::sleep(Duration::from_secs(20)); @@ -242,31 +261,12 @@ impl Simulator { } } log::info!("avalanchego RPC endpoints: {:?}", rpc_eps); - let resp = avalanche_sdk_info::get_network_id(&rpc_eps[0]) .await .unwrap(); let network_id = resp.result.unwrap().network_id; log::info!("network Id: {}", network_id); - // keep alive by sleeping for duration provided by SUBNET_TIMEOUT environment variable - // use sensible default - - let val = std::env::var("SUBNET_TIMEOUT") - .unwrap_or_else(|_| "0".to_string()) - .parse::() - .unwrap(); - - // log::info!("sleeping for {} seconds", timeout.as_secs()); - // if val < 0 { - // // run forever - // loop { - // thread::sleep(Duration::from_secs(1000)); - // } - // } else { - // let timeout = Duration::from_secs(val as u64); - // thread::sleep(timeout); - // } Ok(()) } @@ -302,11 +302,15 @@ impl Simulator { }), ) .init(); - log::debug!("Running command: {:?}", cmd); + log::debug!("Running command: {:?}", cmd.clone()); + let name = match cmd.name { + Some(n) => format!("node-{}", n), + None => format!("node-{}", random_manager::secure_string(5)), + }; let resp = self .cli .add_node(AddNodeRequest { - name: format!("node-simulator-{}", random_manager::secure_string(5)), + name, exec_path: self.avalanchego_path.clone(), node_config: None, ..Default::default() @@ -315,6 +319,90 @@ impl Simulator { log::info!("added node: {:?}", resp); Ok(()) } + + pub async fn remove_node(&self, cmd: RemoveNodeCommand) -> Result<()> { + env_logger::Builder::from_env( + env_logger::Env::default().default_filter_or(if cmd.verbose { + "debug" + } else { + "info" + }), + ) + .init(); + log::debug!("Running command: {:?}", cmd); + let resp = self + .cli + .remove_node(RemoveNodeRequest { + name: cmd.name, + ..Default::default() + }) + .await?; + log::info!("removed node: {:?}", resp); + Ok(()) + } + + pub async fn add_validator(&self, cmd: AddValidatorCommand) -> Result<()> { + env_logger::Builder::from_env( + env_logger::Env::default().default_filter_or(if cmd.verbose { + "debug" + } else { + "info" + }), + ) + .init(); + + let resp = self.cli.health().await?; + let custom_chains: HashMap = resp + .cluster_info + .expect("no cluster info found") + .custom_chains + .clone(); + + let mut subnet_id = String::new(); + + // This is a bit brittle, but there is only one custom chain in the HashMap: + // the subnet. If more subets wanted to be tested on the simulator this wouldn't work. + // Create tracking issue for this. The problem is I can't get the blockchain ID as the key + // lookup for custom_chains. + for (i, chain) in custom_chains.into_iter().enumerate() { + if i == 0 { + subnet_id = chain.1.subnet_id; + } + } + + let resp = self + .cli + .add_validator(AddPermissionlessValidatorRequest { + validator_spec: vec![PermissionlessStakerSpec { + subnet_id, + node_name: cmd.name, + ..Default::default() + }], + }) + .await?; + log::info!("added validator: {:?}", resp); + Ok(()) + } + + pub async fn remove_validator(&self, cmd: RemoveValidatorCommand) -> Result<()> { + env_logger::Builder::from_env( + env_logger::Env::default().default_filter_or(if cmd.verbose { + "debug" + } else { + "info" + }), + ) + .init(); + log::debug!("Running command: {:?}", cmd); + let resp = self + .cli + .remove_validator(RemoveSubnetValidatorRequest { + ..Default::default() + }) + .await?; + log::info!("removed validator: {:?}", resp); + Ok(()) + } } #[must_use] From cbc005b20f530768c2fff628afbb34bf8d6d84f1 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Tue, 20 Feb 2024 15:06:59 +0000 Subject: [PATCH 22/27] add validators --- m1/Cargo.lock | 23 +++-------------------- m1/tests/e2e/Cargo.toml | 2 +- m1/tests/e2e/src/tests/mod.rs | 2 +- 3 files changed, 5 insertions(+), 22 deletions(-) diff --git a/m1/Cargo.lock b/m1/Cargo.lock index 990e30b6..54a388d1 100644 --- a/m1/Cargo.lock +++ b/m1/Cargo.lock @@ -2565,24 +2565,7 @@ dependencies = [ [[package]] name = "avalanche-network-runner-sdk" version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "355b909145de091c1c55b9aab6c507d77da853104c32ede00d3968998dfc4c18" -dependencies = [ - "log", - "prost", - "prost-types", - "serde 1.0.193", - "serde_json", - "tokio", - "tokio-stream", - "tonic 0.9.2", - "tonic-build", -] - -[[package]] -name = "avalanche-network-runner-sdk" -version = "0.3.3" -source = "git+https://github.com/0xmovses/avalanche-network-runner-sdk-rs?branch=main#7b9a0ac30987325c5c098d9aa0af3031e835bf94" +source = "git+https://github.com/0xmovses/avalanche-network-runner-sdk-rs?branch=main#a9117cd0ceec6d0db40ddc16da0dcdc750567667" dependencies = [ "log", "prost", @@ -4021,7 +4004,7 @@ name = "e2e" version = "0.0.0" dependencies = [ "avalanche-installer", - "avalanche-network-runner-sdk 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "avalanche-network-runner-sdk", "avalanche-types", "env_logger", "log", @@ -9011,7 +8994,7 @@ dependencies = [ "anyhow", "aptos-sdk", "avalanche-installer", - "avalanche-network-runner-sdk 0.3.3 (git+https://github.com/0xmovses/avalanche-network-runner-sdk-rs?branch=main)", + "avalanche-network-runner-sdk", "avalanche-types", "clap 3.2.25", "env_logger", diff --git a/m1/tests/e2e/Cargo.toml b/m1/tests/e2e/Cargo.toml index 8c6fb749..6b7acf3a 100644 --- a/m1/tests/e2e/Cargo.toml +++ b/m1/tests/e2e/Cargo.toml @@ -14,7 +14,7 @@ simulator = { path = "../../simulator" } [dev-dependencies] avalanche-installer = "0.0.77" -avalanche-network-runner-sdk = "0.3.3" # https://crates.io/crates/avalanche-network-runner-sdk +avalanche-network-runner-sdk = { git = "https://github.com/0xmovses/avalanche-network-runner-sdk-rs", branch = "main" } avalanche-types = { workspace = true } # https://crates.io/crates/avalanche-types env_logger = "0.10.1" log = "0.4.19" diff --git a/m1/tests/e2e/src/tests/mod.rs b/m1/tests/e2e/src/tests/mod.rs index d65889ff..81782cf7 100644 --- a/m1/tests/e2e/src/tests/mod.rs +++ b/m1/tests/e2e/src/tests/mod.rs @@ -13,7 +13,7 @@ use avalanche_types::{ids, jsonrpc::client::info as avalanche_sdk_info, subnet}; use simulator::{get_network_runner_grpc_endpoint, get_network_runner_enable_shutdown, get_avalanchego_path, get_vm_plugin_path, init_m1_network}; -const AVALANCHEGO_VERSION: &str = "v1.10.9"; +const AVALANCHEGO_VERSION: &str = "v1.3.7"; #[tokio::test] async fn e2e() { From 90b881a1357d288167f42d9029e7d5fc259dd6a6 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Tue, 20 Feb 2024 16:54:58 +0000 Subject: [PATCH 23/27] fix add validator --- m1/simulator/src/lib.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/m1/simulator/src/lib.rs b/m1/simulator/src/lib.rs index 38978e64..275f78c6 100644 --- a/m1/simulator/src/lib.rs +++ b/m1/simulator/src/lib.rs @@ -15,8 +15,7 @@ use std::{ use avalanche_network_runner_sdk::{ rpcpb::{ - AddPermissionlessValidatorRequest, CustomChainInfo, PermissionlessStakerSpec, - RemoveSubnetValidatorRequest, VmidRequest, VmidResponse, + AddPermissionlessValidatorRequest, AddSubnetValidatorsRequest, CustomChainInfo, PermissionlessStakerSpec, RemoveSubnetValidatorsRequest, SubnetValidatorsSpec, VmidRequest, VmidResponse }, AddNodeRequest, BlockchainSpec, Client, GlobalConfig, RemoveNodeRequest, StartRequest, }; @@ -178,7 +177,6 @@ impl Simulator { "started avalanchego cluster with network-runner: {:?}", resp ); - // enough time for network-runner to get ready thread::sleep(Duration::from_secs(20)); @@ -369,14 +367,15 @@ impl Simulator { subnet_id = chain.1.subnet_id; } } - + let resp = self .cli - .add_validator(AddPermissionlessValidatorRequest { - validator_spec: vec![PermissionlessStakerSpec { - subnet_id, - node_name: cmd.name, - ..Default::default() + .add_validator(AddSubnetValidatorsRequest { + validator_spec: vec![{ + SubnetValidatorsSpec { + subnet_id, + node_names: vec![cmd.name], + } }], }) .await?; @@ -396,7 +395,7 @@ impl Simulator { log::debug!("Running command: {:?}", cmd); let resp = self .cli - .remove_validator(RemoveSubnetValidatorRequest { + .remove_validator(RemoveSubnetValidatorsRequest { ..Default::default() }) .await?; From 21c49179b78ee27eefb5b929cfc3b176e6b414b4 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Tue, 20 Feb 2024 18:50:00 +0000 Subject: [PATCH 24/27] cleanup --- m1/Cargo.lock | 2 +- m1/flake.lock | 115 ---------------------------------- m1/flake.nix | 80 ----------------------- m1/simulator/src/commands.rs | 2 +- m1/simulator/src/lib.rs | 98 +++++++++++++++-------------- m1/simulator/src/main.rs | 2 +- m1/tests/e2e/src/tests/mod.rs | 29 ++++----- 7 files changed, 70 insertions(+), 258 deletions(-) delete mode 100644 m1/flake.lock delete mode 100644 m1/flake.nix diff --git a/m1/Cargo.lock b/m1/Cargo.lock index 54a388d1..74a996ee 100644 --- a/m1/Cargo.lock +++ b/m1/Cargo.lock @@ -2565,7 +2565,7 @@ dependencies = [ [[package]] name = "avalanche-network-runner-sdk" version = "0.3.3" -source = "git+https://github.com/0xmovses/avalanche-network-runner-sdk-rs?branch=main#a9117cd0ceec6d0db40ddc16da0dcdc750567667" +source = "git+https://github.com/0xmovses/avalanche-network-runner-sdk-rs?branch=main#c7bd446cb58931b33648810e6f0618231b27ae89" dependencies = [ "log", "prost", diff --git a/m1/flake.lock b/m1/flake.lock deleted file mode 100644 index 527157fe..00000000 --- a/m1/flake.lock +++ /dev/null @@ -1,115 +0,0 @@ -{ - "nodes": { - "flake-parts": { - "inputs": { - "nixpkgs-lib": "nixpkgs-lib" - }, - "locked": { - "lastModified": 1706830856, - "narHash": "sha256-a0NYyp+h9hlb7ddVz4LUn1vT/PLwqfrWYcHMvFB1xYg=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "b253292d9c0a5ead9bc98c4e9a26c6312e27d69f", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "flake-parts", - "type": "github" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1707689078, - "narHash": "sha256-UUGmRa84ZJHpGZ1WZEBEUOzaPOWG8LZ0yPg1pdDF/yM=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "f9d39fb9aff0efee4a3d5f4a6d7c17701d38a1d8", - "type": "github" - }, - "original": { - "owner": "nixos", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs-lib": { - "locked": { - "dir": "lib", - "lastModified": 1706550542, - "narHash": "sha256-UcsnCG6wx++23yeER4Hg18CXWbgNpqNXcHIo5/1Y+hc=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "97b17f32362e475016f942bbdfda4a4a72a8a652", - "type": "github" - }, - "original": { - "dir": "lib", - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_2": { - "locked": { - "lastModified": 1705856552, - "narHash": "sha256-JXfnuEf5Yd6bhMs/uvM67/joxYKoysyE3M2k6T3eWbg=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "612f97239e2cc474c13c9dafa0df378058c5ad8d", - "type": "github" - }, - "original": { - "owner": "nixos", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "flake-parts": "flake-parts", - "nixpkgs": "nixpkgs", - "systems": "systems", - "treefmt-nix": "treefmt-nix" - } - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "treefmt-nix": { - "inputs": { - "nixpkgs": "nixpkgs_2" - }, - "locked": { - "lastModified": 1707300477, - "narHash": "sha256-qQF0fEkHlnxHcrKIMRzOETnRBksUK048MXkX0SOmxvA=", - "owner": "numtide", - "repo": "treefmt-nix", - "rev": "ac599dab59a66304eb511af07b3883114f061b9d", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "treefmt-nix", - "type": "github" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/m1/flake.nix b/m1/flake.nix deleted file mode 100644 index cfeece6c..00000000 --- a/m1/flake.nix +++ /dev/null @@ -1,80 +0,0 @@ -{ - inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - flake-parts.url = "github:hercules-ci/flake-parts"; - systems.url = "github:nix-systems/default"; - - # Dev tools - treefmt-nix.url = "github:numtide/treefmt-nix"; - }; - - outputs = inputs: - inputs.flake-parts.lib.mkFlake { inherit inputs; } { - systems = import inputs.systems; - imports = [ - inputs.treefmt-nix.flakeModule - ]; - perSystem = { config, self', pkgs, lib, system, ... }: - let - cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml); - nonRustDeps = [ - pkgs.libiconv - pkgs.zlib - pkgs.darwin.apple_sdk.frameworks.IOKit - pkgs.darwin.apple_sdk.frameworks.SystemConfiguration - pkgs.darwin.apple_sdk.frameworks.AppKit - ]; - rust-toolchain = pkgs.symlinkJoin { - name = "rust-toolchain"; - paths = [ pkgs.rustc pkgs.cargo pkgs.cargo-watch pkgs.rust-analyzer pkgs.rustPlatform.rustcSrc ]; - }; - in - { - # Rust package - packages.default = pkgs.rustPlatform.buildRustPackage { - inherit (cargoToml.package) name version; - src = ./.; - cargoLock.lockFile = ./Cargo.lock; - buildInputs = lib.optional (lib.strings.hasPrefix "darwin" system) pkgs.darwin.apple_sdk.frameworks.IOKit; - }; - - # Rust dev environment - devShells.default = pkgs.mkShell { - inputsFrom = [ - config.treefmt.build.devShell - ]; - shellHook = '' - # For rust-analyzer 'hover' tooltips to work. - export RUST_SRC_PATH=${pkgs.rustPlatform.rustLibSrc} - - # Set MACOSX_DEPLOYMENT_TARGET for compatibility - export MACOSX_DEPLOYMENT_TARGET="10.13" - echo "MACOSX_DEPLOYMENT_TARGET set to: $MACOSX_DEPLOYMENT_TARGET" - - export OLD_PS1="$PS1" - PS1="(M1-nix) $PS1" - - echo - echo "🍎🍎 Run 'just ' to get started" - ''; - buildInputs = nonRustDeps; - nativeBuildInputs = with pkgs; [ - just - rust-toolchain - ] ++ lib.optional (lib.strings.hasPrefix "darwin" system) pkgs.darwin.apple_sdk.frameworks.IOKit; - RUST_BACKTRACE = 1; - }; - - # Add your auto-formatters here. - # cf. https://numtide.github.io/treefmt/ - treefmt.config = { - projectRootFile = "flake.nix"; - programs = { - nixpkgs-fmt.enable = true; - rustfmt.enable = true; - }; - }; - }; - }; - -} \ No newline at end of file diff --git a/m1/simulator/src/commands.rs b/m1/simulator/src/commands.rs index e1f98fc6..c2e8f88a 100644 --- a/m1/simulator/src/commands.rs +++ b/m1/simulator/src/commands.rs @@ -17,7 +17,7 @@ pub struct StartCommand { default_value = "5", help = "The number of validators for the network." )] - pub validators: u64, + pub nodes: u64, /// Sets if the validators join the network at once, or in a staggered way #[clap( diff --git a/m1/simulator/src/lib.rs b/m1/simulator/src/lib.rs index 275f78c6..60694cff 100644 --- a/m1/simulator/src/lib.rs +++ b/m1/simulator/src/lib.rs @@ -1,6 +1,4 @@ use anyhow::{anyhow, Context, Result}; -use core::time; -use serde::{Deserialize, Serialize}; use std::{ collections::HashMap, env, @@ -15,14 +13,15 @@ use std::{ use avalanche_network_runner_sdk::{ rpcpb::{ - AddPermissionlessValidatorRequest, AddSubnetValidatorsRequest, CustomChainInfo, PermissionlessStakerSpec, RemoveSubnetValidatorsRequest, SubnetValidatorsSpec, VmidRequest, VmidResponse + AddSubnetValidatorsRequest, CustomChainInfo, RemoveSubnetValidatorsRequest, + RemoveSubnetValidatorsSpec, SubnetValidatorsSpec, }, AddNodeRequest, BlockchainSpec, Client, GlobalConfig, RemoveNodeRequest, StartRequest, }; use avalanche_types::{ - ids::{self, Id as VmId}, + ids::{self}, jsonrpc::client::info as avalanche_sdk_info, - subnet::{self, vm_name_to_id}, + subnet::{self}, }; use commands::*; use tonic::transport::Channel; @@ -48,6 +47,10 @@ pub struct Simulator { /// The subnet ID created by the network runner, /// this is not the same value as the VM ID. pub subnet_id: Option, + /// The blockchain ID + pub blockchain_id: Option, + /// The network ID + pub network_id: Option, } impl Simulator { @@ -59,10 +62,12 @@ impl Simulator { avalanchego_path: get_avalanchego_path()?, vm_plugin_path: get_vm_plugin_path()?, subnet_id: None, + blockchain_id: None, + network_id: None, }) } - pub async fn dispatch(&mut self) -> Result<()> { + pub async fn exec(&mut self) -> Result<()> { match &self.command { SubCommands::Start(cmd) => self.start_network(cmd.clone()).await?, SubCommands::AddNode(cmd) => self.add_node(cmd.clone()).await?, @@ -76,7 +81,7 @@ impl Simulator { Ok(()) } - pub async fn start_network(&mut self, cmd: StartCommand) -> Result<()> { + async fn start_network(&mut self, cmd: StartCommand) -> Result<()> { env_logger::Builder::from_env( env_logger::Env::default().default_filter_or(if cmd.verbose { "debug" @@ -87,13 +92,6 @@ impl Simulator { .init(); log::debug!("Running command: {:?}", cmd); - let vm_id = Path::new(&self.vm_plugin_path) - .file_stem() - .unwrap() - .to_str() - .unwrap() - .to_string(); - let vm_id = subnet::vm_name_to_id("subnet").unwrap(); let plugins_dir = if !&self.avalanchego_path.is_empty() { @@ -127,7 +125,6 @@ impl Simulator { vm_id ); - //fs::create_dir(&plugins_dir)?; fs::copy( &self.vm_plugin_path, Path::new(&plugins_dir).join(vm_id.to_string()), @@ -156,7 +153,7 @@ impl Simulator { .cli .start(StartRequest { exec_path: self.avalanchego_path.clone(), - num_nodes: Some(5), + num_nodes: Some(cmd.nodes as u32), plugin_dir: plugins_dir, global_node_config: Some( serde_json::to_string(&GlobalConfig { @@ -250,33 +247,34 @@ impl Simulator { log::info!("{}: {}", node_name, iv.uri); rpc_eps.push(iv.uri.clone()); } - let mut blockchain_id = ids::Id::empty(); for (k, v) in cluster_info.custom_chains.iter() { log::info!("custom chain info: {}={:?}", k, v); if v.chain_name == "subnet" { - blockchain_id = ids::Id::from_str(&v.chain_id).unwrap(); + self.blockchain_id = Some(ids::Id::from_str(&v.chain_id)?); break; } } log::info!("avalanchego RPC endpoints: {:?}", rpc_eps); + let resp = avalanche_sdk_info::get_network_id(&rpc_eps[0]) .await .unwrap(); let network_id = resp.result.unwrap().network_id; log::info!("network Id: {}", network_id); + self.network_id = Some(network_id); Ok(()) } - pub async fn partition_network(&self, cmd: PartitionCommand) -> Result<()> { + async fn partition_network(&self, _: PartitionCommand) -> Result<()> { Ok(()) } - pub async fn reconnect_validators(&self, cmd: ReconnectCommand) -> Result<()> { + async fn reconnect_validators(&self, _: ReconnectCommand) -> Result<()> { Ok(()) } - pub async fn network_health(&self, cmd: HealthCommand) -> Result<()> { + async fn network_health(&self, cmd: HealthCommand) -> Result<()> { env_logger::Builder::from_env( env_logger::Env::default().default_filter_or(if cmd.verbose { "debug" @@ -291,7 +289,7 @@ impl Simulator { Ok(()) } - pub async fn add_node(&self, cmd: AddNodeCommand) -> Result<()> { + async fn add_node(&self, cmd: AddNodeCommand) -> Result<()> { env_logger::Builder::from_env( env_logger::Env::default().default_filter_or(if cmd.verbose { "debug" @@ -318,7 +316,7 @@ impl Simulator { Ok(()) } - pub async fn remove_node(&self, cmd: RemoveNodeCommand) -> Result<()> { + async fn remove_node(&self, cmd: RemoveNodeCommand) -> Result<()> { env_logger::Builder::from_env( env_logger::Env::default().default_filter_or(if cmd.verbose { "debug" @@ -339,7 +337,7 @@ impl Simulator { Ok(()) } - pub async fn add_validator(&self, cmd: AddValidatorCommand) -> Result<()> { + async fn add_validator(&self, cmd: AddValidatorCommand) -> Result<()> { env_logger::Builder::from_env( env_logger::Env::default().default_filter_or(if cmd.verbose { "debug" @@ -349,31 +347,12 @@ impl Simulator { ) .init(); - let resp = self.cli.health().await?; - let custom_chains: HashMap = resp - .cluster_info - .expect("no cluster info found") - .custom_chains - .clone(); - - let mut subnet_id = String::new(); - - // This is a bit brittle, but there is only one custom chain in the HashMap: - // the subnet. If more subets wanted to be tested on the simulator this wouldn't work. - // Create tracking issue for this. The problem is I can't get the blockchain ID as the key - // lookup for custom_chains. - for (i, chain) in custom_chains.into_iter().enumerate() { - if i == 0 { - subnet_id = chain.1.subnet_id; - } - } - let resp = self .cli .add_validator(AddSubnetValidatorsRequest { validator_spec: vec![{ SubnetValidatorsSpec { - subnet_id, + subnet_id: self.subnet_id().await?, node_names: vec![cmd.name], } }], @@ -383,7 +362,7 @@ impl Simulator { Ok(()) } - pub async fn remove_validator(&self, cmd: RemoveValidatorCommand) -> Result<()> { + async fn remove_validator(&self, cmd: RemoveValidatorCommand) -> Result<()> { env_logger::Builder::from_env( env_logger::Env::default().default_filter_or(if cmd.verbose { "debug" @@ -396,12 +375,39 @@ impl Simulator { let resp = self .cli .remove_validator(RemoveSubnetValidatorsRequest { - ..Default::default() + validator_spec: vec![{ + RemoveSubnetValidatorsSpec { + subnet_id: self.subnet_id().await?, + node_names: vec![cmd.name], + } + }], }) .await?; log::info!("removed validator: {:?}", resp); Ok(()) } + + async fn subnet_id(&self) -> Result { + let resp = self.cli.health().await?; + let custom_chains: HashMap = resp + .cluster_info + .expect("no cluster info found") + .custom_chains + .clone(); + + let mut subnet_id = String::new(); + + // This is a bit brittle, but there is only one custom chain in the HashMap: + // the subnet. If more subets wanted to be tested on the simulator this wouldn't work. + // Create tracking issue for this. The problem is I can't get the blockchain ID as the key + // lookup for custom_chains. + for (i, chain) in custom_chains.into_iter().enumerate() { + if i == 0 { + subnet_id = chain.1.subnet_id; + } + } + Ok(subnet_id) + } } #[must_use] diff --git a/m1/simulator/src/main.rs b/m1/simulator/src/main.rs index 8aba9ca0..a6cec417 100644 --- a/m1/simulator/src/main.rs +++ b/m1/simulator/src/main.rs @@ -5,6 +5,6 @@ use simulator::{commands::Cli, Simulator}; async fn main() -> Result<(), anyhow::Error> { let cli = Cli::parse(); let mut simulator = Simulator::new(cli.command).await?; - simulator.dispatch().await?; + simulator.exec().await?; Ok(()) } diff --git a/m1/tests/e2e/src/tests/mod.rs b/m1/tests/e2e/src/tests/mod.rs index 81782cf7..c4e93fed 100644 --- a/m1/tests/e2e/src/tests/mod.rs +++ b/m1/tests/e2e/src/tests/mod.rs @@ -1,21 +1,22 @@ -use core::time; -use std::{ - io, - fs::{self, File}, - path::Path, - str::FromStr, - thread, - time::{Duration, Instant}, io::Write, -}; - use avalanche_network_runner_sdk::{BlockchainSpec, Client, GlobalConfig, StartRequest}; use avalanche_types::{ids, jsonrpc::client::info as avalanche_sdk_info, subnet}; -use simulator::{get_network_runner_grpc_endpoint, get_network_runner_enable_shutdown, -get_avalanchego_path, get_vm_plugin_path, init_m1_network}; +use simulator::{ + commands::{StartCommand, SubCommands}, + Simulator, +}; const AVALANCHEGO_VERSION: &str = "v1.3.7"; -#[tokio::test] +#[tokio::test] async fn e2e() { - init_m1_network().await + let cmd = StartCommand { + nodes: 5, + staggered: false, + verbose: false, + grpc_endpoint: None, + }; + let mut simulator = Simulator::new(SubCommands::Start(cmd)) + .await + .expect("Failed to create simulator"); + simulator.exec().await.expect("Failed to execute simulator"); } From 1f3daf6a6728f746d5f867a0db61146f35bbe2bd Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Tue, 20 Feb 2024 19:03:07 +0000 Subject: [PATCH 25/27] remove unused --- m1/scratch.nix | 64 ----------------------------------------- m1/scripts/simulator.sh | 49 ------------------------------- 2 files changed, 113 deletions(-) delete mode 100644 m1/scratch.nix delete mode 100755 m1/scripts/simulator.sh diff --git a/m1/scratch.nix b/m1/scratch.nix deleted file mode 100644 index ebc30fa9..00000000 --- a/m1/scratch.nix +++ /dev/null @@ -1,64 +0,0 @@ -{ - description = "A devShell example"; - - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - rust-overlay.url = "github:oxalica/rust-overlay"; - flake-utils.url = "github:numtide/flake-utils"; - }; - - outputs = { self, nixpkgs, rust-overlay, flake-utils, ... }@inputs: - flake-utils.lib.eachDefaultSystem (system: - let - overlays = [ (import rust-overlay) ]; - pkgs = import nixpkgs { - inherit system overlays; - }; - darwinFrameworks = if system == "x86_64-darwin" then with pkgs.darwin.apple_sdk.frameworks; [ - IOKit - SystemConfiguration - AppKit - ] else []; - in - with pkgs; - { - devShells.default = mkShell { - buildInputs = [ - clang - libcxx - rocksdb - openssl - pkg-config - eza - fd - rust-bin.stable.latest.default - ] ++ darwinFrameworks; - - shellHook = '' - alias ls=eza - alias find=fd - - echo "> Entered Nix-powered M1 simulator environment" - export OLD_PS1="$PS1" - PS1="(M1-nix) $PS1" - - # Set MACOSX_DEPLOYMENT_TARGET for compatibility - export MACOSX_DEPLOYMENT_TARGET="10.13" - echo "MACOSX_DEPLOYMENT_TARGET set to: $MACOSX_DEPLOYMENT_TARGET" - - # Explicitly set the C++ compiler to use clang++, ensuring it's the Nix-provided one - export CXX="${clang}/bin/clang++" - - # Adding -v for verbose output during compilation to help diagnose linker issues - export NIX_CFLAGS_COMPILE="-v $NIX_CFLAGS_COMPILE" - - # Ensure linker flags include the path to libc++ and enable verbose output - export NIX_LDFLAGS="$NIX_LDFLAGS -L${libcxx}/lib -lc++ -v" - - # Set LDFLAGS for manual make/cmake builds - export LDFLAGS="-L${libcxx}/lib -lc++ -v" - ''; - }; - } - ); -} \ No newline at end of file diff --git a/m1/scripts/simulator.sh b/m1/scripts/simulator.sh deleted file mode 100755 index 3444010e..00000000 --- a/m1/scripts/simulator.sh +++ /dev/null @@ -1,49 +0,0 @@ -# run nix develop then call this script - -init_env() { - echo "Initializing Environment" - ./scripts/build.debug.sh \ - && VM_PLUGIN_PATH=$(pwd)/target/debug/subnet -} - -start_network() { - echo "Starting network with 5 validators..." - ./target/debug/simulator start -} - -simulate_partition() { - echo "Simulating network partition..." - # Placeholder for command to partition the network -} - -reconnect_nodes() { - echo "Reconnecting partitioned nodes..." - # Placeholder for command to reconnect nodes -} - -observe_health() { - echo "Observing consensus health..." - # Placeholder for command to observe the network's consensus health -} - -# Script execution flow -init_env - -# Step 1: Start the network -start_network - -# Wait for the network to stabilize -sleep 5 - -# Step 2: Simulate network partition -# simulate_partition - -# Simulate some duration of network partition -# sleep 10 - -# Step 3: Reconnect the nodes -# reconnect_nodes - -# Observe the network for a period to assess impact on consensus health -sleep 5 -# observe_health From dde6bef9c550eb31908d8de28b561c654a6efbf0 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Tue, 20 Feb 2024 19:21:15 +0000 Subject: [PATCH 26/27] remove unused --- m1/tests/e2e/src/tests/mod.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/m1/tests/e2e/src/tests/mod.rs b/m1/tests/e2e/src/tests/mod.rs index c4e93fed..76a9edb7 100644 --- a/m1/tests/e2e/src/tests/mod.rs +++ b/m1/tests/e2e/src/tests/mod.rs @@ -1,12 +1,8 @@ -use avalanche_network_runner_sdk::{BlockchainSpec, Client, GlobalConfig, StartRequest}; -use avalanche_types::{ids, jsonrpc::client::info as avalanche_sdk_info, subnet}; use simulator::{ commands::{StartCommand, SubCommands}, Simulator, }; -const AVALANCHEGO_VERSION: &str = "v1.3.7"; - #[tokio::test] async fn e2e() { let cmd = StartCommand { From 9cd0cdd6c7eebd532cc599af75525afd38d40857 Mon Sep 17 00:00:00 2001 From: Richard Melkonian Date: Wed, 21 Feb 2024 12:39:49 +0000 Subject: [PATCH 27/27] improve get subnet_id & init_log --- m1/simulator/src/commands.rs | 3 + m1/simulator/src/lib.rs | 107 +++++++++++----------------------- m1/simulator/src/main.rs | 2 +- m1/tests/e2e/src/tests/mod.rs | 5 +- 4 files changed, 42 insertions(+), 75 deletions(-) diff --git a/m1/simulator/src/commands.rs b/m1/simulator/src/commands.rs index c2e8f88a..d81f7333 100644 --- a/m1/simulator/src/commands.rs +++ b/m1/simulator/src/commands.rs @@ -6,6 +6,9 @@ pub struct Cli { /// The command to run #[clap(subcommand)] pub command: SubCommands, + /// Verbose output + #[clap(short, long, help = "Verbose output.")] + pub verbose: bool, } /// Start the simulator diff --git a/m1/simulator/src/lib.rs b/m1/simulator/src/lib.rs index 60694cff..0d631eae 100644 --- a/m1/simulator/src/lib.rs +++ b/m1/simulator/src/lib.rs @@ -1,4 +1,4 @@ -use anyhow::{anyhow, Context, Result}; +use anyhow::{anyhow, Context, Error, Result}; use std::{ collections::HashMap, env, @@ -19,9 +19,9 @@ use avalanche_network_runner_sdk::{ AddNodeRequest, BlockchainSpec, Client, GlobalConfig, RemoveNodeRequest, StartRequest, }; use avalanche_types::{ - ids::{self}, + ids, jsonrpc::client::info as avalanche_sdk_info, - subnet::{self}, + subnet::{self, rpc::snowman::block}, }; use commands::*; use tonic::transport::Channel; @@ -47,8 +47,6 @@ pub struct Simulator { /// The subnet ID created by the network runner, /// this is not the same value as the VM ID. pub subnet_id: Option, - /// The blockchain ID - pub blockchain_id: Option, /// The network ID pub network_id: Option, } @@ -62,12 +60,12 @@ impl Simulator { avalanchego_path: get_avalanchego_path()?, vm_plugin_path: get_vm_plugin_path()?, subnet_id: None, - blockchain_id: None, network_id: None, }) } - pub async fn exec(&mut self) -> Result<()> { + pub async fn exec(&mut self, verbose: bool) -> Result<()> { + self.init_logger(verbose); match &self.command { SubCommands::Start(cmd) => self.start_network(cmd.clone()).await?, SubCommands::AddNode(cmd) => self.add_node(cmd.clone()).await?, @@ -82,14 +80,6 @@ impl Simulator { } async fn start_network(&mut self, cmd: StartCommand) -> Result<()> { - env_logger::Builder::from_env( - env_logger::Env::default().default_filter_or(if cmd.verbose { - "debug" - } else { - "info" - }), - ) - .init(); log::debug!("Running command: {:?}", cmd); let vm_id = subnet::vm_name_to_id("subnet").unwrap(); @@ -249,10 +239,6 @@ impl Simulator { } for (k, v) in cluster_info.custom_chains.iter() { log::info!("custom chain info: {}={:?}", k, v); - if v.chain_name == "subnet" { - self.blockchain_id = Some(ids::Id::from_str(&v.chain_id)?); - break; - } } log::info!("avalanchego RPC endpoints: {:?}", rpc_eps); @@ -275,14 +261,6 @@ impl Simulator { } async fn network_health(&self, cmd: HealthCommand) -> Result<()> { - env_logger::Builder::from_env( - env_logger::Env::default().default_filter_or(if cmd.verbose { - "debug" - } else { - "info" - }), - ) - .init(); log::debug!("Running command: {:?}", cmd); let resp = self.cli.health().await?; log::info!("network health: {:?}", resp); @@ -290,14 +268,6 @@ impl Simulator { } async fn add_node(&self, cmd: AddNodeCommand) -> Result<()> { - env_logger::Builder::from_env( - env_logger::Env::default().default_filter_or(if cmd.verbose { - "debug" - } else { - "info" - }), - ) - .init(); log::debug!("Running command: {:?}", cmd.clone()); let name = match cmd.name { Some(n) => format!("node-{}", n), @@ -317,14 +287,6 @@ impl Simulator { } async fn remove_node(&self, cmd: RemoveNodeCommand) -> Result<()> { - env_logger::Builder::from_env( - env_logger::Env::default().default_filter_or(if cmd.verbose { - "debug" - } else { - "info" - }), - ) - .init(); log::debug!("Running command: {:?}", cmd); let resp = self .cli @@ -338,15 +300,6 @@ impl Simulator { } async fn add_validator(&self, cmd: AddValidatorCommand) -> Result<()> { - env_logger::Builder::from_env( - env_logger::Env::default().default_filter_or(if cmd.verbose { - "debug" - } else { - "info" - }), - ) - .init(); - let resp = self .cli .add_validator(AddSubnetValidatorsRequest { @@ -363,14 +316,6 @@ impl Simulator { } async fn remove_validator(&self, cmd: RemoveValidatorCommand) -> Result<()> { - env_logger::Builder::from_env( - env_logger::Env::default().default_filter_or(if cmd.verbose { - "debug" - } else { - "info" - }), - ) - .init(); log::debug!("Running command: {:?}", cmd); let resp = self .cli @@ -388,25 +333,41 @@ impl Simulator { } async fn subnet_id(&self) -> Result { + let blockchain_id = self.blockchain_id().await?; let resp = self.cli.health().await?; - let custom_chains: HashMap = resp - .cluster_info - .expect("no cluster info found") + let cluster_info = resp.cluster_info.context("no cluster info found")?; + let subnet_id = &cluster_info .custom_chains - .clone(); + .get(&blockchain_id.to_string()) + .context("no custom chains found")? + .subnet_id; + return Ok(subnet_id.to_owned()); + } - let mut subnet_id = String::new(); + async fn blockchain_id(&self) -> Result { + log::info!("checking status..."); + let status = self.cli.status().await?; + + let mut blockchain_id = ids::Id::empty(); - // This is a bit brittle, but there is only one custom chain in the HashMap: - // the subnet. If more subets wanted to be tested on the simulator this wouldn't work. - // Create tracking issue for this. The problem is I can't get the blockchain ID as the key - // lookup for custom_chains. - for (i, chain) in custom_chains.into_iter().enumerate() { - if i == 0 { - subnet_id = chain.1.subnet_id; + let cluster_info = status.cluster_info.context("no cluster info found")?; + for (k, v) in cluster_info.custom_chains.iter() { + log::info!("custom chain info: {}={:?}", k, v); + if v.chain_name == VM_NAME { + blockchain_id = ids::Id::from_str(&v.chain_id)?; + break; } } - Ok(subnet_id) + Ok(blockchain_id) + } + + fn init_logger(&self, verbose: bool) { + env_logger::Builder::from_env(env_logger::Env::default().default_filter_or(if verbose { + "debug" + } else { + "info" + })) + .init(); } } diff --git a/m1/simulator/src/main.rs b/m1/simulator/src/main.rs index a6cec417..4c989bd9 100644 --- a/m1/simulator/src/main.rs +++ b/m1/simulator/src/main.rs @@ -5,6 +5,6 @@ use simulator::{commands::Cli, Simulator}; async fn main() -> Result<(), anyhow::Error> { let cli = Cli::parse(); let mut simulator = Simulator::new(cli.command).await?; - simulator.exec().await?; + simulator.exec(cli.verbose).await?; Ok(()) } diff --git a/m1/tests/e2e/src/tests/mod.rs b/m1/tests/e2e/src/tests/mod.rs index 76a9edb7..1cfaefe4 100644 --- a/m1/tests/e2e/src/tests/mod.rs +++ b/m1/tests/e2e/src/tests/mod.rs @@ -14,5 +14,8 @@ async fn e2e() { let mut simulator = Simulator::new(SubCommands::Start(cmd)) .await .expect("Failed to create simulator"); - simulator.exec().await.expect("Failed to execute simulator"); + simulator + .exec(cmd.verbose) + .await + .expect("Failed to execute simulator"); }