From 33efc05793347d369151e676abea06b9b93ef098 Mon Sep 17 00:00:00 2001 From: Sam Bukowski Date: Wed, 18 Sep 2024 16:30:21 -0600 Subject: [PATCH 1/3] read networks config --- Cargo.lock | 4 +++ crates/astria-cli/Cargo.toml | 4 +++ crates/astria-cli/src/cli/mod.rs | 4 +++ crates/astria-cli/src/config.rs | 50 ++++++++++++++++++++++++++++++++ crates/astria-cli/src/main.rs | 6 ++++ justfile | 4 +++ 6 files changed, 72 insertions(+) create mode 100644 crates/astria-cli/src/config.rs diff --git a/Cargo.lock b/Cargo.lock index f1cf124250..4a260d3d86 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -568,11 +568,14 @@ dependencies = [ "astria-sequencer-client", "clap", "color-eyre", + "env_logger", "ethers", "futures", "hex", + "home", "humantime", "ibc-types", + "log", "rand 0.8.5", "serde", "serde_json", @@ -580,6 +583,7 @@ dependencies = [ "sha2 0.10.8", "tendermint", "tokio", + "toml 0.7.8", "tracing", "tracing-subscriber 0.3.18", "tryhard", diff --git a/crates/astria-cli/Cargo.toml b/crates/astria-cli/Cargo.toml index 107b3f576e..a88e4d10ba 100644 --- a/crates/astria-cli/Cargo.toml +++ b/crates/astria-cli/Cargo.toml @@ -12,6 +12,10 @@ homepage = "https://astria.org" name = "astria-cli" [dependencies] +home = "0.5" +toml = "0.7" +env_logger = "0.10" +log = "0.4" color-eyre = "0.6" astria-bridge-contracts = { path = "../astria-bridge-contracts" } diff --git a/crates/astria-cli/src/cli/mod.rs b/crates/astria-cli/src/cli/mod.rs index 9b6af0119a..ad881ef96e 100644 --- a/crates/astria-cli/src/cli/mod.rs +++ b/crates/astria-cli/src/cli/mod.rs @@ -16,6 +16,10 @@ const DEFAULT_SEQUENCER_CHAIN_ID: &str = "astria-dusk-10"; #[derive(Debug, Parser)] #[command(name = "astria-cli", version)] pub struct Cli { + /// Sets the log level (e.g. error, warn, info, debug, trace) + #[arg(short, long, default_value = "info")] + pub(crate) log_level: String, + #[command(subcommand)] pub(crate) command: Option, } diff --git a/crates/astria-cli/src/config.rs b/crates/astria-cli/src/config.rs new file mode 100644 index 0000000000..3d214b1b73 --- /dev/null +++ b/crates/astria-cli/src/config.rs @@ -0,0 +1,50 @@ +use std::{ + collections::HashMap, + fs, +}; + +use color_eyre::eyre; +use home::home_dir; +use serde::{ + Deserialize, + Serialize, +}; +use toml; + +#[derive(Debug, Deserialize, Serialize)] +pub(crate) struct NetworkConfig { + sequencer_chain_id: String, + sequencer_url: String, + asset: String, + fee_asset: String, +} + +#[derive(Debug, Deserialize, Serialize)] +pub(crate) struct Config { + networks: HashMap, +} + +pub(crate) fn get_networks_config() -> eyre::Result { + // try to get the home directory and build the path to the config file + let mut path = home_dir().expect("Could not determine the home directory."); + path.push(".astria"); + path.push("sequencer-networks-config.toml"); + + // Read the TOML file + let toml_str = fs::read_to_string(path)?; + let config: Config = toml::from_str(&toml_str)?; + + for (network_name, network_config) in &config.networks { + println!("Network: {}", network_name); + println!( + " Sequencer Chain ID: {}", + network_config.sequencer_chain_id + ); + println!(" Sequencer URL: {}", network_config.sequencer_url); + println!(" Asset: {}", network_config.asset); + println!(" Fee Asset: {}", network_config.fee_asset); + println!(); + } + + Ok(config) +} diff --git a/crates/astria-cli/src/main.rs b/crates/astria-cli/src/main.rs index c587580795..2e0bc2148c 100644 --- a/crates/astria-cli/src/main.rs +++ b/crates/astria-cli/src/main.rs @@ -5,6 +5,8 @@ use astria_cli::{ commands, }; use color_eyre::eyre; +mod config; +use config::Config; #[tokio::main] async fn main() -> ExitCode { @@ -22,6 +24,10 @@ async fn main() -> ExitCode { } async fn run() -> eyre::Result<()> { + let _config: Config = config::get_networks_config()?; + + // Parse the TOML string into our Config struct let args = Cli::get_args()?; + commands::run(args).await } diff --git a/justfile b/justfile index ceeff571c7..c41bcd9957 100644 --- a/justfile +++ b/justfile @@ -87,3 +87,7 @@ _lint-proto: buf breaking proto/sequencerblockapis --against 'buf.build/astria/sequencerblock-apis' buf breaking proto/protocolapis --against 'buf.build/astria/protocol-apis' buf breaking proto/composerapis --against 'buf.build/astria/composer-apis' + +defaultargs := '' +cli *args=defaultargs: + cargo run -p astria-cli -- {{args}} From b1d862ea01f81ed19a98c58c53b0d763cdf679c1 Mon Sep 17 00:00:00 2001 From: Sam Bukowski Date: Fri, 20 Sep 2024 16:48:29 -0600 Subject: [PATCH 2/3] can use config file to override default args for nonce command --- crates/astria-cli/src/cli/config.rs | 86 +++++++++++++++++++++ crates/astria-cli/src/cli/mod.rs | 47 ++++++++++- crates/astria-cli/src/commands/mod.rs | 10 ++- crates/astria-cli/src/commands/sequencer.rs | 75 ++++++++++++++---- crates/astria-cli/src/config.rs | 50 ------------ crates/astria-cli/src/lib.rs | 3 + crates/astria-cli/src/main.rs | 20 ++++- 7 files changed, 219 insertions(+), 72 deletions(-) create mode 100644 crates/astria-cli/src/cli/config.rs delete mode 100644 crates/astria-cli/src/config.rs diff --git a/crates/astria-cli/src/cli/config.rs b/crates/astria-cli/src/cli/config.rs new file mode 100644 index 0000000000..2d7164b581 --- /dev/null +++ b/crates/astria-cli/src/cli/config.rs @@ -0,0 +1,86 @@ +use std::{ + collections::HashMap, + fs, +}; + +use color_eyre::eyre; +use home::home_dir; +use serde::{ + Deserialize, + Serialize, +}; +use toml; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct NetworkConfig { + pub sequencer_chain_id: String, + pub sequencer_url: String, + pub asset: String, + pub fee_asset: String, +} + +impl Default for NetworkConfig { + fn default() -> Self { + Self { + sequencer_chain_id: "astria-dusk-10".to_string(), + sequencer_url: "https://rpc.sequencer.dusk-10.devnet.astria.org".to_string(), + asset: "TIA".to_string(), + fee_asset: "TIA".to_string(), + } + } +} + +#[derive(Debug, Deserialize, Serialize)] +pub(crate) struct Config { + networks: HashMap, +} +impl Config { + /// Validate if the parsed input for choosing a network config exists and is valid + pub(crate) fn validate_network(&self, network: String) -> bool { + for (network_name, _) in &self.networks { + if *network_name == network { + return true; + } + } + false + } + + /// Get a list of valid network names from the config + pub(crate) fn get_valid_networks(&self) -> Vec { + let mut valid_networks = Vec::new(); + for (network_name, _) in &self.networks { + valid_networks.push(network_name.clone()); + } + valid_networks + } + + /// Get the network config for the selected network + pub(crate) fn get_network(&self, network: String) -> Option<&NetworkConfig> { + self.networks.get(&network) + } +} + +pub(crate) fn get_networks_config() -> eyre::Result { + // try to get the home directory and build the path to the config file + let mut path = home_dir().expect("Could not determine the home directory."); + path.push(".astria"); + path.push("sequencer-networks-config.toml"); + + // Read the TOML file + let toml_str = fs::read_to_string(path)?; + let config: Config = toml::from_str(&toml_str)?; + + // for (network_name, network_config) in &config.networks { + // println!("Network: {}", network_name); + // println!( + // " Sequencer Chain ID: {}", + // network_config.sequencer_chain_id + // ); + // println!(" Sequencer URL: {}", network_config.sequencer_url); + // println!(" Asset: {}", network_config.asset); + // println!(" Fee Asset: {}", network_config.fee_asset); + // println!(); + // } + + Ok(config) +} diff --git a/crates/astria-cli/src/cli/mod.rs b/crates/astria-cli/src/cli/mod.rs index ad881ef96e..3e9c3d867f 100644 --- a/crates/astria-cli/src/cli/mod.rs +++ b/crates/astria-cli/src/cli/mod.rs @@ -1,4 +1,5 @@ pub(crate) mod bridge; +pub(crate) mod config; pub(crate) mod sequencer; use clap::{ @@ -7,10 +8,17 @@ use clap::{ }; use color_eyre::eyre; -use crate::cli::sequencer::Command as SequencerCommand; +use super::*; +use crate::cli::{ + config::{ + Config, + NetworkConfig, + }, + sequencer::Command as SequencerCommand, +}; -const DEFAULT_SEQUENCER_RPC: &str = "https://rpc.sequencer.dusk-10.devnet.astria.org"; -const DEFAULT_SEQUENCER_CHAIN_ID: &str = "astria-dusk-10"; +// const DEFAULT_SEQUENCER_RPC: &str = "https://rpc.sequencer.dusk-10.devnet.astria.org"; +// const DEFAULT_SEQUENCER_CHAIN_ID: &str = "astria-dusk-10"; /// A CLI for deploying and managing Astria services and related infrastructure. #[derive(Debug, Parser)] @@ -20,8 +28,15 @@ pub struct Cli { #[arg(short, long, default_value = "info")] pub(crate) log_level: String, + /// Select the network you want to use + #[arg(short, long, default_value = "dawn")] + pub network: String, + #[command(subcommand)] pub(crate) command: Option, + + #[clap(skip)] + pub(crate) network_config: Option, } impl Cli { @@ -31,9 +46,33 @@ impl Cli { /// /// * If the arguments cannot be parsed pub fn get_args() -> eyre::Result { - let args = Self::parse(); + let mut args = Self::parse(); + // println!("") + + let config: Config = config::get_networks_config()?; + + // Validate the selected network name + if config.validate_network(args.network.clone()) { + println!("network: {:?}", args.network); + if let Some(network_config) = config.get_network(args.network.clone()) { + args.set_network_config(network_config.clone()); + } else { + println!("Network config not found"); + } + // args.set_network_config(config.get_network(args.network.clone())); + } else { + println!( + "Network is not valid. Expected one of: {:?}", + config.get_valid_networks() + ); + } + Ok(args) } + + pub fn set_network_config(&mut self, network_config: NetworkConfig) { + self.network_config = Some(network_config); + } } /// Commands that can be run diff --git a/crates/astria-cli/src/commands/mod.rs b/crates/astria-cli/src/commands/mod.rs index 436d5c74fc..8955b6c7db 100644 --- a/crates/astria-cli/src/commands/mod.rs +++ b/crates/astria-cli/src/commands/mod.rs @@ -7,6 +7,7 @@ use color_eyre::{ }; use crate::cli::{ + config::NetworkConfig, sequencer::{ AccountCommand, AddressCommand, @@ -48,7 +49,14 @@ pub async fn run(cli: Cli) -> eyre::Result<()> { } => match command { AccountCommand::Create => sequencer::create_account(), AccountCommand::Balance(args) => sequencer::get_balance(&args).await?, - AccountCommand::Nonce(args) => sequencer::get_nonce(&args).await?, + AccountCommand::Nonce(args) => { + if let Some(network) = &cli.network_config { + sequencer::get_nonce(&args, network.clone()).await? + } else { + let network = NetworkConfig::default(); + sequencer::get_nonce(&args, network).await? + } + } }, SequencerCommand::Address { command, diff --git a/crates/astria-cli/src/commands/sequencer.rs b/crates/astria-cli/src/commands/sequencer.rs index 46259e4763..1c950a221c 100644 --- a/crates/astria-cli/src/commands/sequencer.rs +++ b/crates/astria-cli/src/commands/sequencer.rs @@ -1,3 +1,5 @@ +use std::env; + use astria_core::{ crypto::SigningKey, primitive::v1::{ @@ -36,17 +38,24 @@ use color_eyre::{ }; use rand::rngs::OsRng; -use crate::cli::sequencer::{ - BasicAccountArgs, - Bech32mAddressArgs, - BlockHeightGetArgs, - BridgeLockArgs, - FeeAssetChangeArgs, - IbcRelayerChangeArgs, - InitBridgeAccountArgs, - SudoAddressChangeArgs, - TransferArgs, - ValidatorUpdateArgs, +// use serde::de; +use crate::{ + cli::{ + config::NetworkConfig, + sequencer::{ + BasicAccountArgs, + Bech32mAddressArgs, + BlockHeightGetArgs, + BridgeLockArgs, + FeeAssetChangeArgs, + IbcRelayerChangeArgs, + InitBridgeAccountArgs, + SudoAddressChangeArgs, + TransferArgs, + ValidatorUpdateArgs, + }, + }, + DEFAULT_SEQUENCER_RPC, }; /// Generate a new signing key (this is also called a secret key by other implementations) @@ -123,9 +132,47 @@ pub(crate) async fn get_balance(args: &BasicAccountArgs) -> eyre::Result<()> { /// /// * If the http client cannot be created /// * If the balance cannot be retrieved -pub(crate) async fn get_nonce(args: &BasicAccountArgs) -> eyre::Result<()> { - let sequencer_client = HttpClient::new(args.sequencer_url.as_str()) - .wrap_err("failed constructing http sequencer client")?; +pub(crate) async fn get_nonce(args: &BasicAccountArgs, network: NetworkConfig) -> eyre::Result<()> { + // update the sequencer_url if the config is in use + // Retrieve the default value manually + // let default_value = DEFAULT_SEQUENCER_RPC; + println!("Default value: {:?}", DEFAULT_SEQUENCER_RPC); + println!("Argument value: {:?}", network.sequencer_url); + + let mut seq_url = String::from(DEFAULT_SEQUENCER_RPC); + + let default = String::from(DEFAULT_SEQUENCER_RPC); + + // Check if the environment variable is set + let env_var = env::var("SEQUENCER_URL") + .ok() + .unwrap_or_else(|| default.clone()); + // let env_var = env_var_value.unwrap_or_else(default); + println!("Environment variable: {:?}", env_var); + + // Compare the final value with the default and environment variable + // this will fail if the user uses the default value as a flag argument + if args.sequencer_url != DEFAULT_SEQUENCER_RPC { + println!("Using command line value: {}", DEFAULT_SEQUENCER_RPC); + seq_url = args.sequencer_url.clone(); + } else if env_var != default.as_str() { + println!( + "Argument is using the value from the environment variable: {}", + args.sequencer_url + ); + seq_url = env_var; + } else if network.sequencer_url != DEFAULT_SEQUENCER_RPC { + println!("Using network config value: {}", network.sequencer_url); + seq_url = network.sequencer_url.clone(); + } else { + println!( + "Argument was explicitly set by the user: {}", + args.sequencer_url + ); + } + + let sequencer_client = + HttpClient::new(seq_url.as_str()).wrap_err("failed constructing http sequencer client")?; let res = sequencer_client .get_latest_nonce(args.address) diff --git a/crates/astria-cli/src/config.rs b/crates/astria-cli/src/config.rs deleted file mode 100644 index 3d214b1b73..0000000000 --- a/crates/astria-cli/src/config.rs +++ /dev/null @@ -1,50 +0,0 @@ -use std::{ - collections::HashMap, - fs, -}; - -use color_eyre::eyre; -use home::home_dir; -use serde::{ - Deserialize, - Serialize, -}; -use toml; - -#[derive(Debug, Deserialize, Serialize)] -pub(crate) struct NetworkConfig { - sequencer_chain_id: String, - sequencer_url: String, - asset: String, - fee_asset: String, -} - -#[derive(Debug, Deserialize, Serialize)] -pub(crate) struct Config { - networks: HashMap, -} - -pub(crate) fn get_networks_config() -> eyre::Result { - // try to get the home directory and build the path to the config file - let mut path = home_dir().expect("Could not determine the home directory."); - path.push(".astria"); - path.push("sequencer-networks-config.toml"); - - // Read the TOML file - let toml_str = fs::read_to_string(path)?; - let config: Config = toml::from_str(&toml_str)?; - - for (network_name, network_config) in &config.networks { - println!("Network: {}", network_name); - println!( - " Sequencer Chain ID: {}", - network_config.sequencer_chain_id - ); - println!(" Sequencer URL: {}", network_config.sequencer_url); - println!(" Asset: {}", network_config.asset); - println!(" Fee Asset: {}", network_config.fee_asset); - println!(); - } - - Ok(config) -} diff --git a/crates/astria-cli/src/lib.rs b/crates/astria-cli/src/lib.rs index 18a4aecb56..1872248a94 100644 --- a/crates/astria-cli/src/lib.rs +++ b/crates/astria-cli/src/lib.rs @@ -1,2 +1,5 @@ pub mod cli; pub mod commands; + +pub const DEFAULT_SEQUENCER_RPC: &str = "https://rpc.sequencer.dusk-10.devnet.astria.org"; +pub const DEFAULT_SEQUENCER_CHAIN_ID: &str = "astria-dusk-10"; diff --git a/crates/astria-cli/src/main.rs b/crates/astria-cli/src/main.rs index 2e0bc2148c..8190f204da 100644 --- a/crates/astria-cli/src/main.rs +++ b/crates/astria-cli/src/main.rs @@ -5,8 +5,6 @@ use astria_cli::{ commands, }; use color_eyre::eyre; -mod config; -use config::Config; #[tokio::main] async fn main() -> ExitCode { @@ -24,10 +22,26 @@ async fn main() -> ExitCode { } async fn run() -> eyre::Result<()> { - let _config: Config = config::get_networks_config()?; + // let config: Config = config::get_networks_config()?; // Parse the TOML string into our Config struct let args = Cli::get_args()?; + // // Validate the selected network name + // if config.validate_network(args.network.clone()) { + // println!("network: {:?}", args.network); + // if let Some(network_config) = config.get_network(args.network.clone()) { + // args.set_network_config(&network_config.clone()); + // } else { + // println!("Network config not found"); + // } + // // args.set_network_config(config.get_network(args.network.clone())); + // } else { + // println!( + // "Network is not valid. Expected one of: {:?}", + // config.get_valid_networks() + // ); + // } + commands::run(args).await } From bfedcdf9c35af6c069e291d3ad32971d3c9d57a2 Mon Sep 17 00:00:00 2001 From: Sam Bukowski Date: Fri, 20 Sep 2024 16:56:23 -0600 Subject: [PATCH 3/3] remove comments --- crates/astria-cli/src/cli/mod.rs | 5 ----- crates/astria-cli/src/commands/sequencer.rs | 6 +----- crates/astria-cli/src/main.rs | 18 ------------------ 3 files changed, 1 insertion(+), 28 deletions(-) diff --git a/crates/astria-cli/src/cli/mod.rs b/crates/astria-cli/src/cli/mod.rs index 3e9c3d867f..a4466291d1 100644 --- a/crates/astria-cli/src/cli/mod.rs +++ b/crates/astria-cli/src/cli/mod.rs @@ -17,9 +17,6 @@ use crate::cli::{ sequencer::Command as SequencerCommand, }; -// const DEFAULT_SEQUENCER_RPC: &str = "https://rpc.sequencer.dusk-10.devnet.astria.org"; -// const DEFAULT_SEQUENCER_CHAIN_ID: &str = "astria-dusk-10"; - /// A CLI for deploying and managing Astria services and related infrastructure. #[derive(Debug, Parser)] #[command(name = "astria-cli", version)] @@ -47,7 +44,6 @@ impl Cli { /// * If the arguments cannot be parsed pub fn get_args() -> eyre::Result { let mut args = Self::parse(); - // println!("") let config: Config = config::get_networks_config()?; @@ -59,7 +55,6 @@ impl Cli { } else { println!("Network config not found"); } - // args.set_network_config(config.get_network(args.network.clone())); } else { println!( "Network is not valid. Expected one of: {:?}", diff --git a/crates/astria-cli/src/commands/sequencer.rs b/crates/astria-cli/src/commands/sequencer.rs index 1c950a221c..54d8d1775f 100644 --- a/crates/astria-cli/src/commands/sequencer.rs +++ b/crates/astria-cli/src/commands/sequencer.rs @@ -133,9 +133,6 @@ pub(crate) async fn get_balance(args: &BasicAccountArgs) -> eyre::Result<()> { /// * If the http client cannot be created /// * If the balance cannot be retrieved pub(crate) async fn get_nonce(args: &BasicAccountArgs, network: NetworkConfig) -> eyre::Result<()> { - // update the sequencer_url if the config is in use - // Retrieve the default value manually - // let default_value = DEFAULT_SEQUENCER_RPC; println!("Default value: {:?}", DEFAULT_SEQUENCER_RPC); println!("Argument value: {:?}", network.sequencer_url); @@ -147,10 +144,9 @@ pub(crate) async fn get_nonce(args: &BasicAccountArgs, network: NetworkConfig) - let env_var = env::var("SEQUENCER_URL") .ok() .unwrap_or_else(|| default.clone()); - // let env_var = env_var_value.unwrap_or_else(default); println!("Environment variable: {:?}", env_var); - // Compare the final value with the default and environment variable + // Compare the input value with the default and environment variable // this will fail if the user uses the default value as a flag argument if args.sequencer_url != DEFAULT_SEQUENCER_RPC { println!("Using command line value: {}", DEFAULT_SEQUENCER_RPC); diff --git a/crates/astria-cli/src/main.rs b/crates/astria-cli/src/main.rs index 8190f204da..20119777dd 100644 --- a/crates/astria-cli/src/main.rs +++ b/crates/astria-cli/src/main.rs @@ -22,26 +22,8 @@ async fn main() -> ExitCode { } async fn run() -> eyre::Result<()> { - // let config: Config = config::get_networks_config()?; - // Parse the TOML string into our Config struct let args = Cli::get_args()?; - // // Validate the selected network name - // if config.validate_network(args.network.clone()) { - // println!("network: {:?}", args.network); - // if let Some(network_config) = config.get_network(args.network.clone()) { - // args.set_network_config(&network_config.clone()); - // } else { - // println!("Network config not found"); - // } - // // args.set_network_config(config.get_network(args.network.clone())); - // } else { - // println!( - // "Network is not valid. Expected one of: {:?}", - // config.get_valid_networks() - // ); - // } - commands::run(args).await }