diff --git a/Cargo.lock b/Cargo.lock index 388c0697fa..63ef8aef35 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1158,7 +1158,7 @@ checksum = "f769ab9e8c1652d78dd0b3ec59cdaa1e2bcb3b6b39f6681b256abcdbe101cc14" dependencies = [ "anyhow", "cargo_metadata", - "clap 4.4.6", + "clap 4.4.7", "concolor-control", "crates-index", "dirs-next", @@ -1393,9 +1393,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.6" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" +checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" dependencies = [ "clap_builder", "clap_derive", @@ -1403,13 +1403,13 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.6" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" +checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" dependencies = [ "anstream", "anstyle", - "clap_lex 0.5.1", + "clap_lex 0.6.0", "strsim", "terminal_size", ] @@ -1420,7 +1420,7 @@ version = "4.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3ae8ba90b9d8b007efe66e55e48fb936272f5ca00349b5b0e89877520d35ea7" dependencies = [ - "clap 4.4.6", + "clap 4.4.7", ] [[package]] @@ -1429,15 +1429,15 @@ version = "4.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29bdbe21a263b628f83fcbeac86a4416a1d588c7669dd41473bc4149e4e7d2f1" dependencies = [ - "clap 4.4.6", + "clap 4.4.7", "clap_complete", ] [[package]] name = "clap_derive" -version = "4.4.2" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" dependencies = [ "heck 0.4.1", "proc-macro2", @@ -1456,9 +1456,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "cloudabi" @@ -2871,7 +2871,7 @@ dependencies = [ "bytes", "cfg-if", "chrono", - "clap 4.4.6", + "clap 4.4.7", "config", "digest 0.10.7", "dirs 5.0.1", @@ -2941,7 +2941,7 @@ name = "explorer-api" version = "1.1.30" dependencies = [ "chrono", - "clap 4.4.6", + "clap 4.4.7", "dotenvy", "humantime-serde", "isocountry", @@ -5961,7 +5961,7 @@ dependencies = [ "bs58 0.4.0", "cfg-if", "chrono", - "clap 4.4.6", + "clap 4.4.7", "console-subscriber", "cosmwasm-std", "cw-utils", @@ -6066,7 +6066,7 @@ name = "nym-bin-common" version = "0.6.0" dependencies = [ "atty", - "clap 4.4.6", + "clap 4.4.7", "clap_complete", "clap_complete_fig", "log", @@ -6107,7 +6107,7 @@ dependencies = [ "base64 0.13.1", "bip39", "bs58 0.4.0", - "clap 4.4.6", + "clap 4.4.7", "clap_complete", "clap_complete_fig", "dotenvy", @@ -6132,7 +6132,7 @@ dependencies = [ "bip39", "bs58 0.4.0", "cfg-if", - "clap 4.4.6", + "clap 4.4.7", "comfy-table", "cosmrs", "cosmwasm-std", @@ -6176,7 +6176,7 @@ dependencies = [ name = "nym-client" version = "1.1.30" dependencies = [ - "clap 4.4.6", + "clap 4.4.7", "dirs 4.0.0", "futures", "lazy_static", @@ -6215,6 +6215,7 @@ dependencies = [ "async-trait", "base64 0.21.4", "cfg-if", + "clap 4.4.7", "dashmap", "dirs 4.0.0", "futures", @@ -6523,7 +6524,7 @@ dependencies = [ "atty", "bip39", "bs58 0.4.0", - "clap 4.4.6", + "clap 4.4.7", "colored", "dashmap", "dirs 4.0.0", @@ -6700,7 +6701,7 @@ dependencies = [ "axum", "bs58 0.4.0", "cfg-if", - "clap 4.4.6", + "clap 4.4.7", "colored", "cpu-cycles", "cupid", @@ -6820,7 +6821,7 @@ dependencies = [ "async-file-watcher", "async-trait", "bs58 0.4.0", - "clap 4.4.6", + "clap 4.4.7", "dirs 4.0.0", "futures", "humantime-serde", @@ -6982,7 +6983,7 @@ name = "nym-nr-query" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.4.6", + "clap 4.4.7", "log", "nym-bin-common", "nym-network-defaults", @@ -7107,7 +7108,7 @@ dependencies = [ name = "nym-socks5-client" version = "1.1.30" dependencies = [ - "clap 4.4.6", + "clap 4.4.7", "lazy_static", "log", "nym-bin-common", @@ -9534,7 +9535,7 @@ version = "0.1.0" dependencies = [ "anyhow", "cargo-edit", - "clap 4.4.6", + "clap 4.4.7", "semver 1.0.20", "serde", "serde_json", @@ -10296,7 +10297,7 @@ name = "ssl-inject" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.4.6", + "clap 4.4.7", "hex", "tokio", ] diff --git a/Cargo.toml b/Cargo.toml index 802b81f89e..d7771a2d17 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -138,6 +138,7 @@ axum = "0.6.20" base64 = "0.21.4" bip39 = { version = "2.0.0", features = ["zeroize"] } boringtun = { git = "https://github.com/cloudflare/boringtun", rev = "e1d6360d6ab4529fc942a078e4c54df107abe2ba" } +clap = "4.4.7" cfg-if = "1.0.0" cosmwasm-derive = "=1.3.0" cosmwasm-schema = "=1.3.0" diff --git a/clients/native/Cargo.toml b/clients/native/Cargo.toml index 57df3570b3..4f714aeaef 100644 --- a/clients/native/Cargo.toml +++ b/clients/native/Cargo.toml @@ -20,7 +20,7 @@ futures = { workspace = true } # bunch of futures stuff, however, now that I thi # and the single instance of abortable we have should really be refactored anyway url = { workspace = true } -clap = { version = "4.0", features = ["cargo", "derive"] } +clap = { workspace = true, features = ["cargo", "derive"] } dirs = "4.0" lazy_static = "1.4.0" log = { workspace = true } # self explanatory @@ -36,7 +36,7 @@ tokio-tungstenite = { workspace = true } ## internal nym-bandwidth-controller = { path = "../../common/bandwidth-controller" } nym-bin-common = { path = "../../common/bin-common", features = ["output_format"] } -nym-client-core = { path = "../../common/client-core", features = ["fs-surb-storage"] } +nym-client-core = { path = "../../common/client-core", features = ["fs-surb-storage", "cli"] } nym-coconut-interface = { path = "../../common/coconut-interface" } nym-config = { path = "../../common/config" } nym-credential-storage = { path = "../../common/credential-storage" } diff --git a/clients/native/src/client/config/mod.rs b/clients/native/src/client/config/mod.rs index 4f57387ceb..65185e579e 100644 --- a/clients/native/src/client/config/mod.rs +++ b/clients/native/src/client/config/mod.rs @@ -16,8 +16,10 @@ use std::net::{IpAddr, Ipv4Addr}; use std::path::{Path, PathBuf}; use std::str::FromStr; +use nym_client_core::config::disk_persistence::CommonClientPaths; pub use nym_client_core::config::Config as BaseClientConfig; pub use nym_client_core::config::{DebugConfig, GatewayEndpointConfig}; +use nym_client_core::init::client_init::ClientConfig; pub mod old_config_v1_1_13; pub mod old_config_v1_1_20; @@ -72,6 +74,24 @@ impl NymConfigTemplate for Config { } } +impl ClientConfig for Config { + fn common_paths(&self) -> &CommonClientPaths { + &self.storage_paths.common_paths + } + + fn core_config(&self) -> &BaseClientConfig { + &self.base + } + + fn default_store_location(&self) -> PathBuf { + self.default_location() + } + + fn save_to>(&self, path: P) -> io::Result<()> { + save_formatted_config_to_file(self, path) + } +} + impl Config { pub fn new>(id: S) -> Self { Config { diff --git a/clients/native/src/commands/init.rs b/clients/native/src/commands/init.rs index 0fc27590dd..8c9edeec2a 100644 --- a/clients/native/src/commands/init.rs +++ b/clients/native/src/commands/init.rs @@ -12,55 +12,49 @@ use crate::{ }; use clap::Args; use nym_bin_common::output_format::OutputFormat; -use nym_client_core::client::base_client::storage::gateway_details::OnDiskGatewayDetails; -use nym_client_core::client::key_manager::persistence::OnDiskKeys; -use nym_client_core::config::GatewayEndpointConfig; -use nym_client_core::error::ClientCoreError; -use nym_client_core::init::helpers::current_gateways; -use nym_client_core::init::types::{GatewayDetails, GatewaySelectionSpecification, GatewaySetup}; -use nym_crypto::asymmetric::identity; -use nym_sphinx::addressing::clients::Recipient; -use nym_topology::NymTopology; +use nym_client_core::init::client_init::{ + initialise_client, CommonClientInitArgs, InitResultsWithConfig, InitialisableClient, +}; use serde::Serialize; use std::fmt::Display; +use std::fs; use std::net::IpAddr; use std::path::PathBuf; -use std::{fs, io}; -use tap::TapFallible; + +struct NativeClientInit; + +impl InitialisableClient for NativeClientInit { + const NAME: &'static str = "native"; + type Error = ClientError; + type InitArgs = Init; + type Config = Config; + + fn try_upgrade_outdated_config(id: &str) -> Result<(), Self::Error> { + try_upgrade_config(id) + } + + fn initialise_storage_paths(id: &str) -> Result<(), Self::Error> { + fs::create_dir_all(default_data_directory(id))?; + fs::create_dir_all(default_config_directory(id))?; + Ok(()) + } + + fn default_config_path(id: &str) -> PathBuf { + default_config_filepath(id) + } + + fn construct_config(init_args: &Self::InitArgs) -> Self::Config { + override_config( + Config::new(&init_args.common_args.id), + OverrideConfig::from(init_args.clone()), + ) + } +} #[derive(Args, Clone)] pub(crate) struct Init { - /// Id of the nym-mixnet-client we want to create config for. - #[clap(long)] - id: String, - - /// Id of the gateway we are going to connect to. - #[clap(long)] - gateway: Option, - - /// Specifies whether the new gateway should be determined based by latency as opposed to being chosen - /// uniformly. - #[clap(long, conflicts_with = "gateway")] - latency_based_selection: bool, - - /// Force register gateway. WARNING: this will overwrite any existing keys for the given id, - /// potentially causing loss of access. - #[clap(long)] - force_register_gateway: bool, - - /// Comma separated list of rest endpoints of the nyxd validators - #[clap(long, alias = "nyxd_validators", value_delimiter = ',', hide = true)] - nyxd_urls: Option>, - - /// Comma separated list of rest endpoints of the API validators - #[clap( - long, - alias = "api_validators", - value_delimiter = ',', - group = "network" - )] - // the alias here is included for backwards compatibility (1.1.4 and before) - nym_apis: Option>, + #[command(flatten)] + common_args: CommonClientInitArgs, /// Whether to not start the websocket #[clap(long)] @@ -74,10 +68,6 @@ pub(crate) struct Init { #[clap(long)] host: Option, - /// Path to .json file containing custom network specification. - #[clap(long, group = "network", hide = true)] - custom_mixnet: Option, - /// Mostly debug-related option to increase default traffic rate so that you would not need to /// modify config post init #[clap(long, hide = true)] @@ -87,27 +77,28 @@ pub(crate) struct Init { #[clap(long, hide = true)] no_cover: bool, - /// Set this client to work in a enabled credentials mode that would attempt to use gateway - /// with bandwidth credential requirement. - #[clap(long, hide = true)] - enabled_credentials_mode: Option, - #[clap(short, long, default_value_t = OutputFormat::default())] output: OutputFormat, } +impl AsRef for Init { + fn as_ref(&self) -> &CommonClientInitArgs { + &self.common_args + } +} + impl From for OverrideConfig { fn from(init_config: Init) -> Self { OverrideConfig { - nym_apis: init_config.nym_apis, + nym_apis: init_config.common_args.nym_apis, disable_socket: init_config.disable_socket, port: init_config.port, host: init_config.host, fastmode: init_config.fastmode, no_cover: init_config.no_cover, - nyxd_urls: init_config.nyxd_urls, - enabled_credentials_mode: init_config.enabled_credentials_mode, + nyxd_urls: init_config.common_args.nyxd_urls, + enabled_credentials_mode: init_config.common_args.enabled_credentials_mode, } } } @@ -121,15 +112,11 @@ pub struct InitResults { } impl InitResults { - fn new(config: &Config, address: &Recipient, gateway: &GatewayEndpointConfig) -> Self { + fn new(res: InitResultsWithConfig) -> Self { Self { - client_core: nym_client_core::init::types::InitResults::new( - &config.base, - address, - gateway, - ), - client_listening_port: config.socket.listening_port, - client_address: address.to_string(), + client_address: res.init_results.address.to_string(), + client_core: res.init_results, + client_listening_port: res.config.socket.listening_port, } } } @@ -142,97 +129,14 @@ impl Display for InitResults { } } -fn init_paths(id: &str) -> io::Result<()> { - fs::create_dir_all(default_data_directory(id))?; - fs::create_dir_all(default_config_directory(id)) -} - pub(crate) async fn execute(args: Init) -> Result<(), ClientError> { eprintln!("Initialising client..."); - let id = &args.id; + let output = args.output; + let res = initialise_client::(args).await?; - let already_init = if default_config_filepath(id).exists() { - // in case we're using old config, try to upgrade it - // (if we're using the current version, it's a no-op) - try_upgrade_config(id)?; - eprintln!("Client \"{id}\" was already initialised before"); - true - } else { - init_paths(id)?; - false - }; - - // Usually you only register with the gateway on the first init, however you can force - // re-registering if wanted. - let user_wants_force_register = args.force_register_gateway; - if user_wants_force_register { - eprintln!("Instructed to force registering gateway. This will overwrite keys!"); - } - - // If the client was already initialized, don't generate new keys and don't re-register with - // the gateway (because this would create a new shared key). - // Unless the user really wants to. - let register_gateway = !already_init || user_wants_force_register; - - // Attempt to use a user-provided gateway, if possible - let user_chosen_gateway_id = args.gateway; - let selection_spec = GatewaySelectionSpecification::new( - user_chosen_gateway_id.map(|id| id.to_base58_string()), - Some(args.latency_based_selection), - false, - ); - - // Load and potentially override config - let config = override_config(Config::new(id), OverrideConfig::from(args.clone())); - - // Setup gateway by either registering a new one, or creating a new config from the selected - // one but with keys kept, or reusing the gateway configuration. - let key_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone()); - let details_store = - OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details); - - let available_gateways = if let Some(hardcoded_topology) = args - .custom_mixnet - .map(NymTopology::new_from_file) - .transpose()? - { - // hardcoded_topology - hardcoded_topology.get_gateways() - } else { - let mut rng = rand::thread_rng(); - current_gateways(&mut rng, &config.base.client.nym_api_urls).await? - }; - - let gateway_setup = GatewaySetup::New { - specification: selection_spec, - available_gateways, - overwrite_data: register_gateway, - }; - - let init_details = - nym_client_core::init::setup_gateway(gateway_setup, &key_store, &details_store) - .await - .tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?; - - let config_save_location = config.default_location(); - config.save_to_default_location().tap_err(|_| { - log::error!("Failed to save the config file"); - })?; - eprintln!( - "Saved configuration file to {}", - config_save_location.display() - ); - - let address = init_details.client_address()?; - - eprintln!("Client configuration completed.\n"); - - let GatewayDetails::Configured(gateway_details) = init_details.gateway_details else { - return Err(ClientCoreError::UnexpectedPersistedCustomGatewayDetails)?; - }; - let init_results = InitResults::new(&config, &address, &gateway_details); - println!("{}", args.output.format(&init_results)); + let init_results = InitResults::new(res); + println!("{}", output.format(&init_results)); Ok(()) } diff --git a/clients/socks5/Cargo.toml b/clients/socks5/Cargo.toml index 7ea0469c74..a3fa47e4a0 100644 --- a/clients/socks5/Cargo.toml +++ b/clients/socks5/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" rust-version = "1.56" [dependencies] -clap = { version = "4.0", features = ["cargo", "derive"] } +clap = { workspace = true, features = ["cargo", "derive"] } lazy_static = "1.4.0" log = { workspace = true } pretty_env_logger = "0.4" @@ -21,7 +21,7 @@ url = { workspace = true } # internal nym-bin-common = { path = "../../common/bin-common", features = ["output_format"] } -nym-client-core = { path = "../../common/client-core", features = ["fs-surb-storage"] } +nym-client-core = { path = "../../common/client-core", features = ["fs-surb-storage", "cli"] } nym-coconut-interface = { path = "../../common/coconut-interface" } nym-config = { path = "../../common/config" } nym-credentials = { path = "../../common/credentials" } diff --git a/clients/socks5/src/commands/init.rs b/clients/socks5/src/commands/init.rs index 3c6a2a943f..d0c7e5e842 100644 --- a/clients/socks5/src/commands/init.rs +++ b/clients/socks5/src/commands/init.rs @@ -11,27 +11,50 @@ use crate::{ }; use clap::Args; use nym_bin_common::output_format::OutputFormat; -use nym_client_core::client::base_client::storage::gateway_details::OnDiskGatewayDetails; -use nym_client_core::client::key_manager::persistence::OnDiskKeys; -use nym_client_core::config::GatewayEndpointConfig; -use nym_client_core::error::ClientCoreError; -use nym_client_core::init::helpers::current_gateways; -use nym_client_core::init::types::{GatewayDetails, GatewaySelectionSpecification, GatewaySetup}; -use nym_crypto::asymmetric::identity; +use nym_client_core::init::client_init::{ + initialise_client, CommonClientInitArgs, InitResultsWithConfig, InitialisableClient, +}; use nym_sphinx::addressing::clients::Recipient; -use nym_topology::NymTopology; use serde::Serialize; use std::fmt::Display; +use std::fs; use std::net::{IpAddr, SocketAddr}; use std::path::PathBuf; -use std::{fs, io}; -use tap::TapFallible; + +struct Socks5ClientInit; + +impl InitialisableClient for Socks5ClientInit { + const NAME: &'static str = "socks5"; + type Error = Socks5ClientError; + type InitArgs = Init; + type Config = Config; + + fn try_upgrade_outdated_config(id: &str) -> Result<(), Self::Error> { + try_upgrade_config(id) + } + + fn initialise_storage_paths(id: &str) -> Result<(), Self::Error> { + fs::create_dir_all(default_data_directory(id))?; + fs::create_dir_all(default_config_directory(id))?; + Ok(()) + } + + fn default_config_path(id: &str) -> PathBuf { + default_config_filepath(id) + } + + fn construct_config(init_args: &Self::InitArgs) -> Self::Config { + override_config( + Config::new(&init_args.common_args.id, &init_args.provider.to_string()), + OverrideConfig::from(init_args.clone()), + ) + } +} #[derive(Args, Clone)] pub(crate) struct Init { - /// Id of the nym-mixnet-client we want to create config for. - #[clap(long)] - id: String, + #[command(flatten)] + common_args: CommonClientInitArgs, /// Address of the socks5 provider to send messages to. #[clap(long)] @@ -46,34 +69,6 @@ pub(crate) struct Init { #[clap(long, alias = "use_anonymous_sender_tag")] use_reply_surbs: Option, - /// Id of the gateway we are going to connect to. - #[clap(long)] - gateway: Option, - - /// Specifies whether the new gateway should be determined based by latency as opposed to being chosen - /// uniformly. - #[clap(long, conflicts_with = "gateway")] - latency_based_selection: bool, - - /// Force register gateway. WARNING: this will overwrite any existing keys for the given id, - /// potentially causing loss of access. - #[clap(long)] - force_register_gateway: bool, - - /// Comma separated list of rest endpoints of the nyxd validators - #[clap(long, alias = "nyxd_validators", value_delimiter = ',', hide = true)] - nyxd_urls: Option>, - - /// Comma separated list of rest endpoints of the API validators - #[clap( - long, - alias = "api_validators", - value_delimiter = ',', - group = "network" - )] - // the alias here is included for backwards compatibility (1.1.4 and before) - nym_apis: Option>, - /// Port for the socket to listen on in all subsequent runs #[clap(short, long)] port: Option, @@ -82,10 +77,6 @@ pub(crate) struct Init { #[clap(long)] host: Option, - /// Path to .json file containing custom network specification. - #[clap(long, group = "network", hide = true)] - custom_mixnet: Option, - /// Mostly debug-related option to increase default traffic rate so that you would not need to /// modify config post init #[clap(long, hide = true)] @@ -95,19 +86,20 @@ pub(crate) struct Init { #[clap(long, hide = true)] no_cover: bool, - /// Set this client to work in a enabled credentials mode that would attempt to use gateway - /// with bandwidth credential requirement. - #[clap(long, hide = true)] - enabled_credentials_mode: Option, - #[clap(short, long, default_value_t = OutputFormat::default())] output: OutputFormat, } +impl AsRef for Init { + fn as_ref(&self) -> &CommonClientInitArgs { + &self.common_args + } +} + impl From for OverrideConfig { fn from(init_config: Init) -> Self { OverrideConfig { - nym_apis: init_config.nym_apis, + nym_apis: init_config.common_args.nym_apis, ip: init_config.host, port: init_config.port, use_anonymous_replies: init_config.use_reply_surbs, @@ -115,8 +107,8 @@ impl From for OverrideConfig { no_cover: init_config.no_cover, geo_routing: None, medium_toggle: false, - nyxd_urls: init_config.nyxd_urls, - enabled_credentials_mode: init_config.enabled_credentials_mode, + nyxd_urls: init_config.common_args.nyxd_urls, + enabled_credentials_mode: init_config.common_args.enabled_credentials_mode, outfox: false, } } @@ -131,15 +123,11 @@ pub struct InitResults { } impl InitResults { - fn new(config: &Config, address: &Recipient, gateway: &GatewayEndpointConfig) -> Self { + fn new(res: InitResultsWithConfig) -> Self { Self { - client_core: nym_client_core::init::types::InitResults::new( - &config.core.base, - address, - gateway, - ), - socks5_listening_address: config.core.socks5.bind_adddress, - client_address: address.to_string(), + client_address: res.init_results.address.to_string(), + client_core: res.init_results, + socks5_listening_address: res.config.core.socks5.bind_adddress, } } } @@ -156,101 +144,14 @@ impl Display for InitResults { } } -fn init_paths(id: &str) -> io::Result<()> { - fs::create_dir_all(default_data_directory(id))?; - fs::create_dir_all(default_config_directory(id)) -} - pub(crate) async fn execute(args: Init) -> Result<(), Socks5ClientError> { eprintln!("Initialising client..."); - let id = &args.id; - let provider_address = &args.provider; + let output = args.output; + let res = initialise_client::(args).await?; - let already_init = if default_config_filepath(id).exists() { - // in case we're using old config, try to upgrade it - // (if we're using the current version, it's a no-op) - try_upgrade_config(id)?; - eprintln!("SOCKS5 client \"{id}\" was already initialised before"); - true - } else { - init_paths(id)?; - false - }; - - // Usually you only register with the gateway on the first init, however you can force - // re-registering if wanted. - let user_wants_force_register = args.force_register_gateway; - if user_wants_force_register { - eprintln!("Instructed to force registering gateway. This might overwrite keys!"); - } - - // If the client was already initialized, don't generate new keys and don't re-register with - // the gateway (because this would create a new shared key). - // Unless the user really wants to. - let register_gateway = !already_init || user_wants_force_register; - - // Attempt to use a user-provided gateway, if possible - let user_chosen_gateway_id = args.gateway; - let selection_spec = GatewaySelectionSpecification::new( - user_chosen_gateway_id.map(|id| id.to_base58_string()), - Some(args.latency_based_selection), - false, - ); - - // Load and potentially override config - let config = override_config( - Config::new(id, &provider_address.to_string()), - OverrideConfig::from(args.clone()), - ); - - // Setup gateway by either registering a new one, or creating a new config from the selected - // one but with keys kept, or reusing the gateway configuration. - let key_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone()); - let details_store = - OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details); - - let available_gateways = if let Some(hardcoded_topology) = args - .custom_mixnet - .map(NymTopology::new_from_file) - .transpose()? - { - // hardcoded_topology - hardcoded_topology.get_gateways() - } else { - let mut rng = rand::thread_rng(); - current_gateways(&mut rng, &config.core.base.client.nym_api_urls).await? - }; - - let gateway_setup = GatewaySetup::New { - specification: selection_spec, - available_gateways, - overwrite_data: register_gateway, - }; - - let init_details = - nym_client_core::init::setup_gateway(gateway_setup, &key_store, &details_store) - .await - .tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?; - - // TODO: ask the service provider we specified for its interface version and set it in the config - - let config_save_location = config.default_location(); - config.save_to_default_location().tap_err(|_| { - log::error!("Failed to save the config file"); - })?; - eprintln!( - "Saved configuration file to {}", - config_save_location.display() - ); - - let address = init_details.client_address()?; - - let GatewayDetails::Configured(gateway_details) = init_details.gateway_details else { - return Err(ClientCoreError::UnexpectedPersistedCustomGatewayDetails)?; - }; - let init_results = InitResults::new(&config, &address, &gateway_details); - println!("{}", args.output.format(&init_results)); + let init_results = InitResults::new(res); + println!("{}", output.format(&init_results)); Ok(()) } diff --git a/clients/socks5/src/config/mod.rs b/clients/socks5/src/config/mod.rs index 8a8681c50f..9f4c23720f 100644 --- a/clients/socks5/src/config/mod.rs +++ b/clients/socks5/src/config/mod.rs @@ -15,7 +15,9 @@ use std::path::{Path, PathBuf}; use std::str::FromStr; pub use crate::config::persistence::SocksClientPaths; +use nym_client_core::config::disk_persistence::CommonClientPaths; pub use nym_client_core::config::Config as BaseClientConfig; +use nym_client_core::init::client_init::ClientConfig; pub use nym_socks5_client_core::config::Config as CoreConfig; pub mod old_config_v1_1_13; @@ -69,6 +71,24 @@ impl NymConfigTemplate for Config { } } +impl ClientConfig for Config { + fn common_paths(&self) -> &CommonClientPaths { + &self.storage_paths.common_paths + } + + fn core_config(&self) -> &BaseClientConfig { + &self.core.base + } + + fn default_store_location(&self) -> PathBuf { + self.default_location() + } + + fn save_to>(&self, path: P) -> io::Result<()> { + save_formatted_config_to_file(self, path) + } +} + impl Config { pub fn new>(id: S, provider_mix_address: S) -> Self { Config { diff --git a/common/bin-common/Cargo.toml b/common/bin-common/Cargo.toml index 7f9ae7fe89..9ed9958a07 100644 --- a/common/bin-common/Cargo.toml +++ b/common/bin-common/Cargo.toml @@ -9,7 +9,7 @@ repository = { workspace = true } [dependencies] atty = "0.2" -clap = { version = "4.0", features = ["derive"] } +clap = { workspace = true, features = ["derive"] } clap_complete = "4.0" clap_complete_fig = "4.0" log = { workspace = true } diff --git a/common/client-core/Cargo.toml b/common/client-core/Cargo.toml index cd17b7e979..ccd2113a28 100644 --- a/common/client-core/Cargo.toml +++ b/common/client-core/Cargo.toml @@ -11,6 +11,7 @@ rust-version = "1.66" async-trait = { workspace = true } base64 = "0.21.2" cfg-if = "1.0.0" +clap = { workspace = true, optional = true } dashmap = { workspace = true } dirs = "4.0" futures = { workspace = true } @@ -92,6 +93,7 @@ sqlx = { version = "0.6.2", features = ["runtime-tokio-rustls", "sqlite", "macro [features] default = [] +cli = ["clap"] fs-surb-storage = ["sqlx"] wasm = ["nym-gateway-client/wasm"] diff --git a/common/client-core/src/error.rs b/common/client-core/src/error.rs index 8118936730..8a193d8a75 100644 --- a/common/client-core/src/error.rs +++ b/common/client-core/src/error.rs @@ -8,6 +8,7 @@ use nym_topology::gateway::GatewayConversionError; use nym_topology::NymTopologyError; use nym_validator_client::ValidatorClientError; use std::error::Error; +use std::path::PathBuf; #[derive(thiserror::Error, Debug)] pub enum ClientCoreError { @@ -132,6 +133,26 @@ pub enum ClientCoreError { #[error("the specified gateway '{gateway}' does not support the wss protocol")] UnsupportedWssProtocol { gateway: String }, + + #[error( + "failed to load custom topology using path '{}'. detailed message: {source}", file_path.display() + )] + CustomTopologyLoadFailure { + file_path: PathBuf, + #[source] + source: std::io::Error, + }, + + #[error( + "failed to save config file for client-{typ} id {id} using path '{}'. detailed message: {source}", path.display() + )] + ConfigSaveFailure { + typ: String, + id: String, + path: PathBuf, + #[source] + source: std::io::Error, + }, } /// Set of messages that the client can send to listeners via the task manager diff --git a/common/client-core/src/init/client_init.rs b/common/client-core/src/init/client_init.rs new file mode 100644 index 0000000000..42f9987f44 --- /dev/null +++ b/common/client-core/src/init/client_init.rs @@ -0,0 +1,201 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::config::disk_persistence::CommonClientPaths; +use crate::error::ClientCoreError; +use crate::{ + client::{ + base_client::storage::gateway_details::OnDiskGatewayDetails, + key_manager::persistence::OnDiskKeys, + }, + init::types::{GatewayDetails, GatewaySelectionSpecification, GatewaySetup, InitResults}, +}; +use log::info; +use nym_crypto::asymmetric::identity; +use nym_topology::NymTopology; +use std::path::{Path, PathBuf}; + +pub trait InitialisableClient { + const NAME: &'static str; + type Error: From; + type InitArgs: AsRef; + type Config: ClientConfig; + + fn try_upgrade_outdated_config(id: &str) -> Result<(), Self::Error>; + + fn initialise_storage_paths(id: &str) -> Result<(), Self::Error>; + + fn default_config_path(id: &str) -> PathBuf; + + fn construct_config(init_args: &Self::InitArgs) -> Self::Config; +} + +pub trait ClientConfig { + fn common_paths(&self) -> &CommonClientPaths; + + fn core_config(&self) -> &crate::config::Config; + + fn default_store_location(&self) -> PathBuf; + + fn save_to>(&self, path: P) -> std::io::Result<()>; +} + +#[cfg_attr(feature = "cli", derive(clap::Args))] +#[derive(Debug, Clone)] +pub struct CommonClientInitArgs { + /// Id of client we want to create config for. + #[cfg_attr(feature = "cli", clap(long))] + pub id: String, + + /// Id of the gateway we are going to connect to. + #[cfg_attr(feature = "cli", clap(long))] + pub gateway: Option, + + /// Specifies whether the new gateway should be determined based by latency as opposed to being chosen + /// uniformly. + #[cfg_attr(feature = "cli", clap(long, conflicts_with = "gateway"))] + pub latency_based_selection: bool, + + /// Force register gateway. WARNING: this will overwrite any existing keys for the given id, + /// potentially causing loss of access. + #[cfg_attr(feature = "cli", clap(long))] + pub force_register_gateway: bool, + + /// Comma separated list of rest endpoints of the nyxd validators + #[cfg_attr( + feature = "cli", + clap(long, alias = "nyxd_validators", value_delimiter = ',', hide = true) + )] + pub nyxd_urls: Option>, + + /// Comma separated list of rest endpoints of the API validators + #[cfg_attr( + feature = "cli", + clap( + long, + alias = "api_validators", + value_delimiter = ',', + group = "network" + ) + )] + pub nym_apis: Option>, + + /// Path to .json file containing custom network specification. + #[cfg_attr(feature = "cli", clap(long, group = "network", hide = true))] + pub custom_mixnet: Option, + + /// Set this client to work in a enabled credentials mode that would attempt to use gateway + /// with bandwidth credential requirement. + #[cfg_attr(feature = "cli", clap(long, hide = true))] + pub enabled_credentials_mode: Option, +} + +pub struct InitResultsWithConfig { + pub config: T, + pub init_results: InitResults, +} + +pub async fn initialise_client( + init_args: C::InitArgs, +) -> Result, C::Error> +where + C: InitialisableClient, +{ + info!("initialising {} client", C::NAME); + + let common_args = init_args.as_ref(); + let id = &common_args.id; + + let already_init = if C::default_config_path(id).exists() { + // in case we're using old config, try to upgrade it + // (if we're using the current version, it's a no-op) + C::try_upgrade_outdated_config(id)?; + eprintln!("{} client \"{id}\" was already initialised before", C::NAME); + true + } else { + C::initialise_storage_paths(id)?; + false + }; + + // Usually you only register with the gateway on the first init, however you can force + // re-registering if wanted. + let user_wants_force_register = common_args.force_register_gateway; + if user_wants_force_register { + eprintln!("Instructed to force registering gateway. This might overwrite keys!"); + } + + // If the client was already initialized, don't generate new keys and don't re-register with + // the gateway (because this would create a new shared key). + // Unless the user really wants to. + let register_gateway = !already_init || user_wants_force_register; + + // Attempt to use a user-provided gateway, if possible + let user_chosen_gateway_id = common_args.gateway; + let selection_spec = GatewaySelectionSpecification::new( + user_chosen_gateway_id.map(|id| id.to_base58_string()), + Some(common_args.latency_based_selection), + false, + ); + + // Load and potentially override config + let config = C::construct_config(&init_args); + let paths = config.common_paths(); + let core = config.core_config(); + + // Setup gateway by either registering a new one, or creating a new config from the selected + // one but with keys kept, or reusing the gateway configuration. + let key_store = OnDiskKeys::new(paths.keys.clone()); + let details_store = OnDiskGatewayDetails::new(&paths.gateway_details); + + let available_gateways = if let Some(custom_mixnet) = common_args.custom_mixnet.as_ref() { + let hardcoded_topology = NymTopology::new_from_file(custom_mixnet).map_err(|source| { + ClientCoreError::CustomTopologyLoadFailure { + file_path: custom_mixnet.clone(), + source, + } + })?; + hardcoded_topology.get_gateways() + } else { + let mut rng = rand::thread_rng(); + crate::init::helpers::current_gateways(&mut rng, &core.client.nym_api_urls).await? + }; + + let gateway_setup = GatewaySetup::New { + specification: selection_spec, + available_gateways, + overwrite_data: register_gateway, + }; + + let init_details = + crate::init::setup_gateway(gateway_setup, &key_store, &details_store).await?; + + // TODO: ask the service provider we specified for its interface version and set it in the config + + let config_save_location = config.default_store_location(); + if let Err(err) = config.save_to(&config_save_location) { + return Err(ClientCoreError::ConfigSaveFailure { + typ: C::NAME.to_string(), + id: id.to_string(), + path: config_save_location, + source: err, + } + .into()); + } + + eprintln!( + "Saved configuration file to {}", + config_save_location.display() + ); + + let address = init_details.client_address()?; + + let GatewayDetails::Configured(gateway_details) = init_details.gateway_details else { + return Err(ClientCoreError::UnexpectedPersistedCustomGatewayDetails)?; + }; + let init_results = InitResults::new(config.core_config(), address, &gateway_details); + + Ok(InitResultsWithConfig { + config, + init_results, + }) +} diff --git a/common/client-core/src/init/mod.rs b/common/client-core/src/init/mod.rs index 395fdd45c2..ab153378c2 100644 --- a/common/client-core/src/init/mod.rs +++ b/common/client-core/src/init/mod.rs @@ -24,6 +24,7 @@ use serde::de::DeserializeOwned; use serde::Serialize; use std::sync::Arc; +pub mod client_init; pub mod helpers; pub mod types; diff --git a/common/client-core/src/init/types.rs b/common/client-core/src/init/types.rs index 3e8a88d133..bf9df9edb2 100644 --- a/common/client-core/src/init/types.rs +++ b/common/client-core/src/init/types.rs @@ -296,16 +296,17 @@ impl GatewaySetup { /// Struct describing the results of the client initialization procedure. #[derive(Debug, Serialize)] pub struct InitResults { - version: String, - id: String, - identity_key: String, - encryption_key: String, - gateway_id: String, - gateway_listener: String, + pub version: String, + pub id: String, + pub identity_key: String, + pub encryption_key: String, + pub gateway_id: String, + pub gateway_listener: String, + pub address: Recipient, } impl InitResults { - pub fn new(config: &Config, address: &Recipient, gateway: &GatewayEndpointConfig) -> Self { + pub fn new(config: &Config, address: Recipient, gateway: &GatewayEndpointConfig) -> Self { Self { version: config.client.version.clone(), id: config.client.id.clone(), @@ -313,6 +314,7 @@ impl InitResults { encryption_key: address.encryption_key().to_base58_string(), gateway_id: gateway.gateway_id.clone(), gateway_listener: gateway.gateway_listener.clone(), + address, } } } diff --git a/common/commands/Cargo.toml b/common/commands/Cargo.toml index df6860641e..e538a7ed31 100644 --- a/common/commands/Cargo.toml +++ b/common/commands/Cargo.toml @@ -11,7 +11,7 @@ bip39 = { workspace = true } bs58 = "0.4" comfy-table = "6.0.0" cfg-if = "1.0.0" -clap = { version = "4.0", features = ["derive"] } +clap = { workspace = true, features = ["derive"] } cw-utils = { workspace = true } handlebars = "3.0.1" humantime-serde = "1.0" diff --git a/ephemera/Cargo.toml b/ephemera/Cargo.toml index 63ccaa9ab7..f0af6af3a9 100644 --- a/ephemera/Cargo.toml +++ b/ephemera/Cargo.toml @@ -21,7 +21,7 @@ bs58 = "0.4.0" bytes = "1.3.0" cfg-if = "1.0.0" chrono = { version = "0.4.24", default-features = false, features = ["clock"] } -clap = { version = "4.0.32", features = ["derive"] } +clap = { workspace = true, features = ["derive"] } config = { version = "0.13", default-features = false, features = ["toml"] } digest = "0.10.6" dirs = "5.0.0" diff --git a/explorer-api/Cargo.toml b/explorer-api/Cargo.toml index 935a76cb52..16c55da6c5 100644 --- a/explorer-api/Cargo.toml +++ b/explorer-api/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] chrono = { version = "0.4.31", features = ["serde"] } -clap = { version = "4.0", features = ["cargo", "derive"] } +clap = { workspace = true, features = ["cargo", "derive"] } dotenvy = "0.15.6" humantime-serde = "1.0" isocountry = "0.3.2" diff --git a/gateway/Cargo.toml b/gateway/Cargo.toml index c1d6503199..777032d128 100644 --- a/gateway/Cargo.toml +++ b/gateway/Cargo.toml @@ -20,7 +20,7 @@ async-trait = { workspace = true } atty = "0.2" bip39 = { workspace = true } bs58 = "0.4.0" -clap = { version = "4.0", features = ["cargo", "derive"] } +clap = { workspace = true, features = ["cargo", "derive"] } colored = "2.0" dashmap = { workspace = true } dirs = "4.0" diff --git a/mixnode/Cargo.toml b/mixnode/Cargo.toml index 39061c4ca4..751aa06e04 100644 --- a/mixnode/Cargo.toml +++ b/mixnode/Cargo.toml @@ -19,7 +19,7 @@ rust-version = "1.58.1" axum = { workspace = true } anyhow = "1.0.40" bs58 = "0.4.0" -clap = { version = "4.0", features = ["cargo", "derive"] } +clap = { workspace = true, features = ["cargo", "derive"] } colored = "2.0" cupid = "0.6.1" dirs = "4.0" diff --git a/nym-api/Cargo.toml b/nym-api/Cargo.toml index 4691bf250f..85c20d6e59 100644 --- a/nym-api/Cargo.toml +++ b/nym-api/Cargo.toml @@ -19,7 +19,7 @@ async-trait = { workspace = true } bs58 = { version = "0.4.0" } bip39 = { workspace = true } cfg-if = "1.0" -clap = { version = "4.0", features = ["cargo", "derive"] } +clap = { workspace = true, features = ["cargo", "derive"] } console-subscriber = { version = "0.1.1", optional = true } # validator-api needs to be built with RUSTFLAGS="--cfg tokio_unstable" dirs = "4.0" futures = { workspace = true } diff --git a/nym-connect/desktop/Cargo.lock b/nym-connect/desktop/Cargo.lock index 6dbf35cc44..e3a9fce4c2 100644 --- a/nym-connect/desktop/Cargo.lock +++ b/nym-connect/desktop/Cargo.lock @@ -126,16 +126,15 @@ dependencies = [ [[package]] name = "anstream" -version = "0.3.2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", - "is-terminal", "utf8parse", ] @@ -165,9 +164,9 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "1.0.2" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" dependencies = [ "anstyle", "windows-sys 0.48.0", @@ -868,20 +867,19 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.21" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c27cdf28c0f604ba3f512b0c9a409f8de8513e4816705deb0498b627e7c3a3fd" +checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" dependencies = [ "clap_builder", "clap_derive", - "once_cell", ] [[package]] name = "clap_builder" -version = "4.3.21" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a9f1ab5e9f01a9b81f202e8562eb9a10de70abf9eaeac1be465c28b75aa4aa" +checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" dependencies = [ "anstream", "anstyle", @@ -910,9 +908,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.3.12" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" dependencies = [ "heck 0.4.1", "proc-macro2", @@ -922,9 +920,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "cocoa" diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index a2922d8391..ec32ea6a33 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/Cargo.lock @@ -108,16 +108,15 @@ dependencies = [ [[package]] name = "anstream" -version = "0.3.2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", - "is-terminal", "utf8parse", ] @@ -147,9 +146,9 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "1.0.2" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" dependencies = [ "anstyle", "windows-sys 0.48.0", @@ -662,20 +661,19 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.21" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c27cdf28c0f604ba3f512b0c9a409f8de8513e4816705deb0498b627e7c3a3fd" +checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" dependencies = [ "clap_builder", "clap_derive", - "once_cell", ] [[package]] name = "clap_builder" -version = "4.3.21" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a9f1ab5e9f01a9b81f202e8562eb9a10de70abf9eaeac1be465c28b75aa4aa" +checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" dependencies = [ "anstream", "anstyle", @@ -704,9 +702,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.3.12" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" dependencies = [ "heck 0.4.1", "proc-macro2", @@ -716,9 +714,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "cocoa" @@ -3431,6 +3429,15 @@ dependencies = [ "nym-contracts-common", ] +[[package]] +name = "nym-exit-policy" +version = "0.1.0" +dependencies = [ + "serde", + "thiserror", + "tracing", +] + [[package]] name = "nym-group-contract-common" version = "0.1.0" @@ -3510,6 +3517,7 @@ dependencies = [ "base64 0.21.4", "nym-bin-common", "nym-crypto", + "nym-exit-policy", "nym-wireguard-types", "schemars", "serde", diff --git a/service-providers/network-requester/Cargo.toml b/service-providers/network-requester/Cargo.toml index 42c54bc0b2..a13ee1fed4 100644 --- a/service-providers/network-requester/Cargo.toml +++ b/service-providers/network-requester/Cargo.toml @@ -17,7 +17,7 @@ path = "src/lib.rs" [dependencies] async-trait = { workspace = true } bs58 = "0.4.0" -clap = {version = "4.0", features = ["cargo", "derive"]} +clap = { workspace = true, features = ["cargo", "derive"]} dirs = "4.0" futures = { workspace = true } humantime-serde = "1.1.1" @@ -41,7 +41,7 @@ url = { workspace = true } # internal async-file-watcher = { path = "../../common/async-file-watcher" } nym-bin-common = { path = "../../common/bin-common", features = ["output_format"] } -nym-client-core = { path = "../../common/client-core" } +nym-client-core = { path = "../../common/client-core", features = ["cli"] } nym-client-websocket-requests = { path = "../../clients/native/websocket-requests" } nym-config = { path = "../../common/config" } nym-credential-storage = { path = "../../common/credential-storage" } diff --git a/service-providers/network-requester/src/cli/init.rs b/service-providers/network-requester/src/cli/init.rs index d6c1960da6..35ddedc279 100644 --- a/service-providers/network-requester/src/cli/init.rs +++ b/service-providers/network-requester/src/cli/init.rs @@ -10,27 +10,48 @@ use crate::{ }; use clap::Args; use nym_bin_common::output_format::OutputFormat; -use nym_client_core::client::base_client::storage::gateway_details::OnDiskGatewayDetails; -use nym_client_core::client::key_manager::persistence::OnDiskKeys; -use nym_client_core::config::GatewayEndpointConfig; -use nym_client_core::error::ClientCoreError; -use nym_client_core::init::helpers::current_gateways; -use nym_client_core::init::types::GatewaySetup; -use nym_client_core::init::types::{GatewayDetails, GatewaySelectionSpecification}; -use nym_crypto::asymmetric::identity; -use nym_sdk::mixnet::NymTopology; -use nym_sphinx::addressing::clients::Recipient; +use nym_client_core::init::client_init::{ + initialise_client, CommonClientInitArgs, InitResultsWithConfig, InitialisableClient, +}; use serde::Serialize; use std::fmt::Display; +use std::fs; use std::path::PathBuf; -use std::{fs, io}; -use tap::TapFallible; + +struct NetworkRequesterInit; + +impl InitialisableClient for NetworkRequesterInit { + const NAME: &'static str = "network requester"; + type Error = NetworkRequesterError; + type InitArgs = Init; + type Config = Config; + + fn try_upgrade_outdated_config(id: &str) -> Result<(), Self::Error> { + try_upgrade_config(id) + } + + fn initialise_storage_paths(id: &str) -> Result<(), Self::Error> { + fs::create_dir_all(default_data_directory(id))?; + fs::create_dir_all(default_config_directory(id))?; + Ok(()) + } + + fn default_config_path(id: &str) -> PathBuf { + default_config_filepath(id) + } + + fn construct_config(init_args: &Self::InitArgs) -> Self::Config { + override_config( + Config::new(&init_args.common_args.id), + OverrideConfig::from(init_args.clone()), + ) + } +} #[derive(Args, Clone)] pub(crate) struct Init { - /// Id of the nym-mixnet-client we want to create config for. - #[clap(long)] - id: String, + #[command(flatten)] + common_args: CommonClientInitArgs, /// Specifies whether this network requester should run in 'open-proxy' mode #[clap(long)] @@ -45,43 +66,6 @@ pub(crate) struct Init { #[clap(long)] statistics_recipient: Option, - /// Id of the gateway we are going to connect to. - #[clap(long)] - gateway: Option, - - /// Specifies whether the new gateway should be determined based by latency as opposed to being chosen - /// uniformly. - #[clap(long, conflicts_with = "gateway")] - latency_based_selection: bool, - - /// Force register gateway. WARNING: this will overwrite any existing keys for the given id, - /// potentially causing loss of access. - #[clap(long)] - force_register_gateway: bool, - - /// Comma separated list of rest endpoints of the nyxd validators - #[clap(long, alias = "nymd_validators", value_delimiter = ',')] - nyxd_urls: Option>, - - /// Comma separated list of rest endpoints of the API validators - #[clap( - long, - alias = "api_validators", - value_delimiter = ',', - group = "network" - )] - // the alias here is included for backwards compatibility (1.1.4 and before) - nym_apis: Option>, - - /// Path to .json file containing custom network specification. - #[clap(long, group = "network", hide = true)] - custom_mixnet: Option, - - /// Set this client to work in a enabled credentials mode that would attempt to use gateway - /// with bandwidth credential requirement. - #[clap(long)] - enabled_credentials_mode: Option, - /// Specifies whether this network requester will run using the default ExitPolicy /// as opposed to the allow list. /// Note: this setting will become the default in the future releases. @@ -95,20 +79,26 @@ pub(crate) struct Init { impl From for OverrideConfig { fn from(init_config: Init) -> Self { OverrideConfig { - nym_apis: init_config.nym_apis, + nym_apis: init_config.common_args.nym_apis, fastmode: false, no_cover: false, medium_toggle: false, - nyxd_urls: init_config.nyxd_urls, - enabled_credentials_mode: init_config.enabled_credentials_mode, + nyxd_urls: init_config.common_args.nyxd_urls, + enabled_credentials_mode: init_config.common_args.enabled_credentials_mode, enable_exit_policy: init_config.with_exit_policy, open_proxy: init_config.open_proxy, - enable_statistics: init_config.enabled_credentials_mode, + enable_statistics: init_config.enable_statistics, statistics_recipient: init_config.statistics_recipient, } } } +impl AsRef for Init { + fn as_ref(&self) -> &CommonClientInitArgs { + &self.common_args + } +} + #[derive(Debug, Serialize)] pub struct InitResults { #[serde(flatten)] @@ -117,14 +107,10 @@ pub struct InitResults { } impl InitResults { - fn new(config: &Config, address: &Recipient, gateway: &GatewayEndpointConfig) -> Self { + fn new(res: InitResultsWithConfig) -> Self { Self { - client_core: nym_client_core::init::types::InitResults::new( - &config.base, - address, - gateway, - ), - client_address: address.to_string(), + client_address: res.init_results.address.to_string(), + client_core: res.init_results, } } } @@ -140,99 +126,14 @@ impl Display for InitResults { } } -fn init_paths(id: &str) -> io::Result<()> { - fs::create_dir_all(default_data_directory(id))?; - fs::create_dir_all(default_config_directory(id)) -} +pub(crate) async fn execute(args: Init) -> Result<(), NetworkRequesterError> { + eprintln!("Initialising client..."); -pub(crate) async fn execute(args: &Init) -> Result<(), NetworkRequesterError> { - log::info!("Initialising client..."); + let output = args.output; + let res = initialise_client::(args).await?; - let id = &args.id; - - let already_init = if default_config_filepath(id).exists() { - // in case we're using old config, try to upgrade it - // (if we're using the current version, it's a no-op) - try_upgrade_config(id)?; - log::info!("Client \"{id}\" was already initialised before"); - true - } else { - init_paths(&args.id)?; - false - }; - - // Usually you only register with the gateway on the first init, however you can force - // re-registering if wanted. - let user_wants_force_register = args.force_register_gateway; - if user_wants_force_register { - log::warn!("Instructed to force registering gateway. This might overwrite keys!"); - } - - // If the client was already initialized, don't generate new keys and don't re-register with - // the gateway (because this would create a new shared key). - // Unless the user really wants to. - let register_gateway = !already_init || user_wants_force_register; - - // Attempt to use a user-provided gateway, if possible - let user_chosen_gateway_id = args.gateway; - let selection_spec = GatewaySelectionSpecification::new( - user_chosen_gateway_id.map(|id| id.to_base58_string()), - Some(args.latency_based_selection), - false, - ); - - // Load and potentially override config - let config = override_config(Config::new(id), OverrideConfig::from(args.clone())); - log::debug!("Using config: {:#?}", config); - - // Setup gateway by either registering a new one, or creating a new config from the selected - // one but with keys kept, or reusing the gateway configuration. - let key_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone()); - let details_store = - OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details); - - let available_gateways = if let Some(hardcoded_topology) = args - .custom_mixnet - .as_ref() - .map(NymTopology::new_from_file) - .transpose()? - { - // hardcoded_topology - hardcoded_topology.get_gateways() - } else { - let mut rng = rand::thread_rng(); - current_gateways(&mut rng, &config.base.client.nym_api_urls).await? - }; - - let gateway_setup = GatewaySetup::New { - specification: selection_spec, - available_gateways, - overwrite_data: register_gateway, - }; - - let init_details = - nym_client_core::init::setup_gateway(gateway_setup, &key_store, &details_store) - .await - .tap_err(|err| log::error!("Failed to setup gateway\nError: {err}"))?; - - let config_save_location = config.default_location(); - config.save_to_default_location().tap_err(|_| { - log::error!("Failed to save the config file"); - })?; - log::info!( - "Saved configuration file to {}", - config_save_location.display() - ); - - let address = init_details.client_address()?; - - log::info!("Client configuration completed.\n"); - - let GatewayDetails::Configured(gateway_details) = init_details.gateway_details else { - return Err(ClientCoreError::UnexpectedPersistedCustomGatewayDetails)?; - }; - let init_results = InitResults::new(&config, &address, &gateway_details); - println!("{}", args.output.format(&init_results)); + let init_results = InitResults::new(res); + println!("{}", output.format(&init_results)); Ok(()) } diff --git a/service-providers/network-requester/src/cli/mod.rs b/service-providers/network-requester/src/cli/mod.rs index c090912743..fcc2fe6750 100644 --- a/service-providers/network-requester/src/cli/mod.rs +++ b/service-providers/network-requester/src/cli/mod.rs @@ -154,7 +154,7 @@ pub(crate) async fn execute(args: Cli) -> Result<(), NetworkRequesterError> { let bin_name = "nym-network-requester"; match args.command { - Commands::Init(m) => init::execute(&m).await?, + Commands::Init(m) => init::execute(m).await?, Commands::Run(m) => run::execute(&m).await?, Commands::Sign(m) => sign::execute(&m).await?, Commands::BuildInfo(m) => build_info::execute(m), diff --git a/service-providers/network-requester/src/config/mod.rs b/service-providers/network-requester/src/config/mod.rs index 81ace42e36..8fee1c6725 100644 --- a/service-providers/network-requester/src/config/mod.rs +++ b/service-providers/network-requester/src/config/mod.rs @@ -17,8 +17,10 @@ use std::str::FromStr; use std::time::Duration; use url::Url; +use nym_client_core::config::disk_persistence::CommonClientPaths; pub use nym_client_core::config::Config as BaseClientConfig; pub use nym_client_core::config::{DebugConfig, GatewayEndpointConfig}; +use nym_client_core::init::client_init::ClientConfig; use nym_network_defaults::mainnet; use nym_sphinx::params::PacketSize; @@ -83,6 +85,24 @@ impl NymConfigTemplate for Config { } } +impl ClientConfig for Config { + fn common_paths(&self) -> &CommonClientPaths { + &self.storage_paths.common_paths + } + + fn core_config(&self) -> &BaseClientConfig { + &self.base + } + + fn default_store_location(&self) -> PathBuf { + self.default_location() + } + + fn save_to>(&self, path: P) -> io::Result<()> { + save_formatted_config_to_file(self, path) + } +} + impl Config { pub fn new>(id: S) -> Self { Config { diff --git a/tools/internal/sdk-version-bump/Cargo.toml b/tools/internal/sdk-version-bump/Cargo.toml index 07bb45a875..b4ab71b34f 100644 --- a/tools/internal/sdk-version-bump/Cargo.toml +++ b/tools/internal/sdk-version-bump/Cargo.toml @@ -13,7 +13,7 @@ license.workspace = true [dependencies] anyhow = { workspace = true } cargo-edit = "0.11.0" -clap = { version = "4.3.19", features = ["derive", "string"] } +clap = { workspace = true, features = ["derive", "string"] } semver = "1.0.18" serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } diff --git a/tools/nym-cli/Cargo.toml b/tools/nym-cli/Cargo.toml index def3dc32eb..5640b96840 100644 --- a/tools/nym-cli/Cargo.toml +++ b/tools/nym-cli/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] base64 = "0.13.0" bs58 = "0.4" -clap = { version = "4.0", features = ["derive"] } +clap = { workspace = true, features = ["derive"] } clap_complete = "4.0" clap_complete_fig = "4.0" dotenvy = "0.15.6" diff --git a/tools/nym-nr-query/Cargo.toml b/tools/nym-nr-query/Cargo.toml index 9034b536db..804577e583 100644 --- a/tools/nym-nr-query/Cargo.toml +++ b/tools/nym-nr-query/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] anyhow = { workspace = true } -clap = {version = "4.0", features = ["cargo", "derive"]} +clap = { workspace = true, features = ["cargo", "derive"]} log = { workspace = true } nym-bin-common = { path = "../../common/bin-common", features = ["output_format"] } nym-network-defaults = { path = "../../common/network-defaults" }