From 961ac230a0b247297b1ad59a400c175586d125f3 Mon Sep 17 00:00:00 2001 From: tyshko5 <74514992+tyshko5@users.noreply.github.com> Date: Tue, 2 Aug 2022 10:57:17 +0200 Subject: [PATCH] Order-independent configuration for TOML serde (#1743) * most changes * fix header * lint fixes * fixes * renaming to client * fmt * clippy * fmt --- forest/src/cli/auth_cmd.rs | 4 +-- forest/src/cli/client.rs | 54 ++++++++++++++++++++++++++++ forest/src/cli/config.rs | 49 +++---------------------- forest/src/cli/fetch_params_cmd.rs | 2 +- forest/src/cli/mod.rs | 30 ++++++++-------- forest/src/daemon.rs | 57 ++++++++++++++++++------------ forest/tests/config_tests.rs | 23 +++++++----- 7 files changed, 127 insertions(+), 92 deletions(-) create mode 100644 forest/src/cli/client.rs diff --git a/forest/src/cli/auth_cmd.rs b/forest/src/cli/auth_cmd.rs index 536478cb7db3..b0cbf640cd08 100644 --- a/forest/src/cli/auth_cmd.rs +++ b/forest/src/cli/auth_cmd.rs @@ -56,8 +56,8 @@ impl AuthCommands { match auth_new((perms,)).await { Ok(token) => { let mut addr = Multiaddr::empty(); - addr.push(cfg.rpc_address.ip().into()); - addr.push(Protocol::Tcp(cfg.rpc_address.port())); + addr.push(cfg.client.rpc_address.ip().into()); + addr.push(Protocol::Tcp(cfg.client.rpc_address.port())); addr.push(Protocol::Http); println!( "FULLNODE_API_INFO=\"{}:{}\"", diff --git a/forest/src/cli/client.rs b/forest/src/cli/client.rs new file mode 100644 index 000000000000..d6f4a519ce93 --- /dev/null +++ b/forest/src/cli/client.rs @@ -0,0 +1,54 @@ +// Copyright 2019-2022 ChainSafe Systems +// SPDX-License-Identifier: Apache-2.0, MIT + +use directories::ProjectDirs; +use rpc_client::DEFAULT_PORT; +use serde::{Deserialize, Serialize}; +use std::{ + net::{IpAddr, Ipv4Addr, SocketAddr}, + path::PathBuf, + str::FromStr, +}; + +#[derive(Serialize, Deserialize, PartialEq)] +#[serde(default)] +pub struct Client { + pub data_dir: PathBuf, + pub genesis_file: Option, + pub enable_rpc: bool, + pub rpc_port: u16, + pub rpc_token: Option, + /// If this is true, then we do not validate the imported snapshot. + /// Otherwise, we validate and compute the states. + pub snapshot: bool, + pub snapshot_height: Option, + pub snapshot_path: Option, + /// Skips loading import CAR file and assumes it's already been loaded. + /// Will use the cids in the header of the file to index the chain. + pub skip_load: bool, + pub encrypt_keystore: bool, + /// Metrics bind, e.g. 127.0.0.1:6116 + pub metrics_address: SocketAddr, + /// RPC bind, e.g. 127.0.0.1:1234 + pub rpc_address: SocketAddr, +} + +impl Default for Client { + fn default() -> Self { + let dir = ProjectDirs::from("com", "ChainSafe", "Forest").expect("failed to find project directories, please set FOREST_CONFIG_PATH environment variable manually."); + Self { + data_dir: dir.data_dir().to_path_buf(), + genesis_file: None, + enable_rpc: true, + rpc_port: DEFAULT_PORT, + rpc_token: None, + snapshot_path: None, + snapshot: false, + snapshot_height: None, + skip_load: false, + encrypt_keystore: true, + metrics_address: FromStr::from_str("127.0.0.1:6116").unwrap(), + rpc_address: SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), DEFAULT_PORT), + } + } +} diff --git a/forest/src/cli/config.rs b/forest/src/cli/config.rs index 63c26dd02270..78af47c0d864 100644 --- a/forest/src/cli/config.rs +++ b/forest/src/cli/config.rs @@ -2,60 +2,19 @@ // SPDX-License-Identifier: Apache-2.0, MIT use chain_sync::SyncConfig; -use directories::ProjectDirs; use forest_libp2p::Libp2pConfig; use networks::ChainConfig; -use rpc_client::DEFAULT_PORT; use serde::{Deserialize, Serialize}; -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; -use std::path::PathBuf; use std::sync::Arc; -#[derive(Serialize, Deserialize, PartialEq)] +use super::client::Client; + +#[derive(Serialize, Deserialize, PartialEq, Default)] #[serde(default)] pub struct Config { - pub data_dir: PathBuf, - pub genesis_file: Option, - pub enable_rpc: bool, - pub rpc_token: Option, - /// If this is true, then we do not validate the imported snapshot. - /// Otherwise, we validate and compute the states. - pub snapshot: bool, - pub snapshot_height: Option, - pub snapshot_path: Option, - /// Skips loading import CAR file and assumes it's already been loaded. - /// Will use the cids in the header of the file to index the chain. - pub skip_load: bool, - pub encrypt_keystore: bool, - /// Metrics bind, e.g. 127.0.0.1:6116 - pub metrics_address: SocketAddr, - /// RPC bind, e.g. 127.0.0.1:1234 - pub rpc_address: SocketAddr, + pub client: Client, pub rocks_db: forest_db::rocks_config::RocksDbConfig, pub network: Libp2pConfig, pub sync: SyncConfig, pub chain: Arc, } - -impl Default for Config { - fn default() -> Self { - let dir = ProjectDirs::from("com", "ChainSafe", "Forest").expect("failed to find project directories, please set FOREST_CONFIG_PATH environment variable manually."); - Self { - network: Libp2pConfig::default(), - data_dir: dir.data_dir().to_path_buf(), - genesis_file: None, - enable_rpc: true, - rpc_token: None, - snapshot_path: None, - snapshot: false, - snapshot_height: None, - skip_load: false, - sync: SyncConfig::default(), - encrypt_keystore: true, - metrics_address: SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 6116), - rpc_address: SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), DEFAULT_PORT), - rocks_db: forest_db::rocks_config::RocksDbConfig::default(), - chain: Arc::default(), - } - } -} diff --git a/forest/src/cli/fetch_params_cmd.rs b/forest/src/cli/fetch_params_cmd.rs index ee0a9261e7a7..3deba6d2b0f5 100644 --- a/forest/src/cli/fetch_params_cmd.rs +++ b/forest/src/cli/fetch_params_cmd.rs @@ -33,7 +33,7 @@ impl FetchCommands { panic!("Sector size option must be chosen. Choose between --all, --keys, or "); }; - get_params_default(&config.data_dir, sizes, self.verbose) + get_params_default(&config.client.data_dir, sizes, self.verbose) .await .unwrap(); } diff --git a/forest/src/cli/mod.rs b/forest/src/cli/mod.rs index 04e3e28857fb..e5ebce73b34d 100644 --- a/forest/src/cli/mod.rs +++ b/forest/src/cli/mod.rs @@ -3,6 +3,7 @@ mod auth_cmd; mod chain_cmd; +mod client; mod config; mod config_cmd; mod fetch_params_cmd; @@ -15,6 +16,7 @@ mod wallet_cmd; pub(super) use self::auth_cmd::AuthCommands; pub(super) use self::chain_cmd::ChainCommands; +pub use self::client::Client; pub use self::config::Config; pub(super) use self::fetch_params_cmd::FetchCommands; pub(super) use self::genesis_cmd::GenesisCommands; @@ -186,37 +188,37 @@ impl CliOpts { } if let Some(genesis_file) = &self.genesis { - cfg.genesis_file = Some(genesis_file.to_owned()); + cfg.client.genesis_file = Some(genesis_file.to_owned()); } - if self.rpc.unwrap_or(cfg.enable_rpc) { - cfg.enable_rpc = true; + if self.rpc.unwrap_or(cfg.client.enable_rpc) { + cfg.client.enable_rpc = true; if let Some(rpc_address) = self.rpc_address { - cfg.rpc_address = rpc_address; + cfg.client.rpc_address = rpc_address; } if self.token.is_some() { - cfg.rpc_token = self.token.to_owned(); + cfg.client.rpc_token = self.token.to_owned(); } } else { - cfg.enable_rpc = false; + cfg.client.enable_rpc = false; } if let Some(metrics_address) = self.metrics_address { - cfg.metrics_address = metrics_address; + cfg.client.metrics_address = metrics_address; } if self.import_snapshot.is_some() && self.import_chain.is_some() { panic!("Can't set import_snapshot and import_chain at the same time!"); } else { if let Some(snapshot_path) = &self.import_snapshot { - cfg.snapshot_path = Some(snapshot_path.to_owned()); - cfg.snapshot = true; + cfg.client.snapshot_path = Some(snapshot_path.to_owned()); + cfg.client.snapshot = true; } if let Some(snapshot_path) = &self.import_chain { - cfg.snapshot_path = Some(snapshot_path.to_owned()); - cfg.snapshot = false; + cfg.client.snapshot_path = Some(snapshot_path.to_owned()); + cfg.client.snapshot = false; } - cfg.snapshot_height = self.height; + cfg.client.snapshot_height = self.height; - cfg.skip_load = self.skip_load; + cfg.client.skip_load = self.skip_load; } cfg.network.kademlia = self.kademlia.unwrap_or(cfg.network.kademlia); @@ -235,7 +237,7 @@ impl CliOpts { cfg.sync.tipset_sample_size = tipset_sample_size.into(); } if let Some(encrypt_keystore) = self.encrypt_keystore { - cfg.encrypt_keystore = encrypt_keystore; + cfg.client.encrypt_keystore = encrypt_keystore; } Ok(cfg) diff --git a/forest/src/daemon.rs b/forest/src/daemon.rs index 308626bd47a6..e9a8cd29a8f3 100644 --- a/forest/src/daemon.rs +++ b/forest/src/daemon.rs @@ -39,7 +39,7 @@ pub(super) async fn start(config: Config) { option_env!("FOREST_VERSION").unwrap_or(env!("CARGO_PKG_VERSION")) ); - let path: PathBuf = config.data_dir.join("libp2p"); + let path: PathBuf = config.client.data_dir.join("libp2p"); let net_keypair = get_keypair(&path.join("keypair")).unwrap_or_else(|| { // Keypair not found, generate and save generated keypair let gen_keypair = ed25519::Keypair::generate(); @@ -59,14 +59,14 @@ pub(super) async fn start(config: Config) { Keypair::Ed25519(gen_keypair) }); - let mut ks = if config.encrypt_keystore { + let mut ks = if config.client.encrypt_keystore { loop { print!("Enter the keystore passphrase: "); std::io::stdout().flush().unwrap(); let passphrase = read_password().expect("Error reading passphrase"); - let data_dir = PathBuf::from(&config.data_dir).join(ENCRYPTED_KEYSTORE_NAME); + let data_dir = PathBuf::from(&config.client.data_dir).join(ENCRYPTED_KEYSTORE_NAME); if !data_dir.exists() { print!("Confirm passphrase: "); std::io::stdout().flush().unwrap(); @@ -78,7 +78,7 @@ pub(super) async fn start(config: Config) { } let key_store_init_result = KeyStore::new(KeyStoreConfig::Encrypted( - PathBuf::from(&config.data_dir), + PathBuf::from(&config.client.data_dir), passphrase, )); @@ -91,8 +91,10 @@ pub(super) async fn start(config: Config) { } } else { warn!("Warning: Keystore encryption disabled!"); - KeyStore::new(KeyStoreConfig::Persistent(PathBuf::from(&config.data_dir))) - .expect("Error initializing keystore") + KeyStore::new(KeyStoreConfig::Persistent(PathBuf::from( + &config.client.data_dir, + ))) + .expect("Error initializing keystore") }; if ks.get(JWT_IDENTIFIER).is_err() { @@ -101,12 +103,18 @@ pub(super) async fn start(config: Config) { } // Start Prometheus server port - let prometheus_listener = TcpListener::bind(config.metrics_address) + let prometheus_listener = TcpListener::bind(config.client.metrics_address) .await .unwrap_or_else(|_| { - cli_error_and_die(format!("could not bind to {}", config.metrics_address), 1) + cli_error_and_die( + format!("could not bind to {}", config.client.metrics_address), + 1, + ) }); - info!("Prometheus server started at {}", config.metrics_address); + info!( + "Prometheus server started at {}", + config.client.metrics_address + ); let prometheus_server_task = task::spawn(metrics::init_prometheus( prometheus_listener, db_path(&config) @@ -135,7 +143,7 @@ pub(super) async fn start(config: Config) { // Read Genesis file // * When snapshot command implemented, this genesis does not need to be initialized let genesis = read_genesis_header( - config.genesis_file.as_ref(), + config.client.genesis_file.as_ref(), config.chain.genesis_bytes(), &chain_store, ) @@ -181,9 +189,9 @@ pub(super) async fn start(config: Config) { #[cfg(all(feature = "fil_cns", not(any(feature = "deleg_cns"))))] { use paramfetch::{get_params_default, set_proofs_parameter_cache_dir_env, SectorSizeOpt}; - set_proofs_parameter_cache_dir_env(&config.data_dir); + set_proofs_parameter_cache_dir_env(&config.client.data_dir); - get_params_default(&config.data_dir, SectorSizeOpt::Keys, false) + get_params_default(&config.client.data_dir, SectorSizeOpt::Keys, false) .await .unwrap(); } @@ -299,14 +307,14 @@ pub(super) async fn start(config: Config) { let p2p_task = task::spawn(async { p2p_service.run().await; }); - let rpc_task = if config.enable_rpc { + let rpc_task = if config.client.enable_rpc { let keystore_rpc = Arc::clone(&keystore); - let rpc_listen = TcpListener::bind(&config.rpc_address) + let rpc_listen = TcpListener::bind(&config.client.rpc_address) .await .unwrap_or_else(|_| cli_error_and_die("could not bind to {rpc_address}", 1)); Some(task::spawn(async move { - info!("JSON-RPC endpoint started at {}", config.rpc_address); + info!("JSON-RPC endpoint started at {}", config.client.rpc_address); start_rpc::<_, _, FullVerifier, FullConsensus>( Arc::new(RPCState { state_manager: Arc::clone(&state_manager), @@ -348,16 +356,21 @@ pub(super) async fn start(config: Config) { } async fn sync_from_snapshot(config: &Config, state_manager: &Arc>) { - if let Some(path) = &config.snapshot_path { + if let Some(path) = &config.client.snapshot_path { let stopwatch = time::Instant::now(); - let validate_height = if config.snapshot { - config.snapshot_height + let validate_height = if config.client.snapshot { + config.client.snapshot_height } else { Some(0) }; - import_chain::(state_manager, path, validate_height, config.skip_load) - .await - .expect("Failed miserably while importing chain from snapshot"); + import_chain::( + state_manager, + path, + validate_height, + config.client.skip_load, + ) + .await + .expect("Failed miserably while importing chain from snapshot"); debug!("Imported snapshot in: {}s", stopwatch.elapsed().as_secs()); } } @@ -373,7 +386,7 @@ fn db_path(config: &Config) -> PathBuf { } fn chain_path(config: &Config) -> PathBuf { - PathBuf::from(&config.data_dir).join(&config.chain.name) + PathBuf::from(&config.client.data_dir).join(&config.chain.name) } #[cfg(test)] diff --git a/forest/tests/config_tests.rs b/forest/tests/config_tests.rs index 871002e7f583..67fecee2c72c 100644 --- a/forest/tests/config_tests.rs +++ b/forest/tests/config_tests.rs @@ -1,7 +1,7 @@ // Copyright 2019-2022 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT use assert_cmd::Command; -use forest::cli::Config; +use forest::cli::{Client, Config}; use rand::Rng; use std::{io::Write, net::SocketAddr, path::PathBuf, str::FromStr}; @@ -45,7 +45,7 @@ fn test_overrides_are_reflected_in_configuration_dump() { .expect("Invalid configuration!"); assert_eq!( - config.metrics_address, + config.client.metrics_address, SocketAddr::from_str(&randomized_metrics_host).unwrap() ); } @@ -55,10 +55,14 @@ fn test_reading_configuration_from_file() { let mut rng = rand::thread_rng(); let expected_config = Config { - metrics_address: SocketAddr::from_str(&format!("127.0.0.1:{}", rng.gen::())).unwrap(), - rpc_token: Some("Azazello".into()), - genesis_file: Some("cthulhu".into()), - encrypt_keystore: false, + client: Client { + rpc_token: Some("Azazello".into()), + genesis_file: Some("cthulhu".into()), + encrypt_keystore: false, + metrics_address: SocketAddr::from_str(&format!("127.0.0.1:{}", rng.gen::())) + .unwrap(), + ..Client::default() + }, ..Config::default() }; @@ -90,8 +94,11 @@ fn test_reading_configuration_from_file() { #[test] fn test_config_env_var() { let expected_config = Config { - rpc_token: Some("some_rpc_token".into()), - data_dir: PathBuf::from("some_path_buf"), + client: Client { + rpc_token: Some("some_rpc_token".into()), + data_dir: PathBuf::from("some_path_buf"), + ..Client::default() + }, ..Config::default() };