diff --git a/Cargo.lock b/Cargo.lock index c221eb6583..2cdb505504 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6673,21 +6673,27 @@ name = "nym-ip-packet-router" version = "0.1.0" dependencies = [ "bincode", + "bs58 0.4.0", "bytes", + "clap 4.4.7", "etherparse", "futures", + "lazy_static", "log", "nym-bin-common", "nym-client-core", "nym-config", + "nym-crypto", "nym-exit-policy", "nym-ip-packet-requests", + "nym-network-defaults", "nym-network-requester", "nym-sdk", "nym-service-providers-common", "nym-sphinx", "nym-task", "nym-tun", + "nym-types", "nym-wireguard", "nym-wireguard-types", "rand 0.8.5", @@ -6846,6 +6852,7 @@ dependencies = [ "cfg-if", "dotenvy", "hex-literal", + "log", "once_cell", "schemars", "serde", diff --git a/clients/native/src/commands/init.rs b/clients/native/src/commands/init.rs index 27f62bd05b..def0072d26 100644 --- a/clients/native/src/commands/init.rs +++ b/clients/native/src/commands/init.rs @@ -51,7 +51,7 @@ impl InitialisableClient for NativeClientInit { } } -#[derive(Args, Clone)] +#[derive(Args, Clone, Debug)] pub(crate) struct Init { #[command(flatten)] common_args: CommonClientInitArgs, diff --git a/clients/socks5/src/commands/init.rs b/clients/socks5/src/commands/init.rs index 44a247316e..ce13a1b7cc 100644 --- a/clients/socks5/src/commands/init.rs +++ b/clients/socks5/src/commands/init.rs @@ -51,7 +51,7 @@ impl InitialisableClient for Socks5ClientInit { } } -#[derive(Args, Clone)] +#[derive(Args, Clone, Debug)] pub(crate) struct Init { #[command(flatten)] common_args: CommonClientInitArgs, diff --git a/common/client-core/src/cli_helpers/client_init.rs b/common/client-core/src/cli_helpers/client_init.rs index c43ebf4545..9550cffdb2 100644 --- a/common/client-core/src/cli_helpers/client_init.rs +++ b/common/client-core/src/cli_helpers/client_init.rs @@ -109,6 +109,8 @@ pub async fn initialise_client( ) -> Result, C::Error> where C: InitialisableClient, + ::Config: std::fmt::Debug, + ::InitArgs: std::fmt::Debug, { info!("initialising {} client", C::NAME); @@ -140,17 +142,32 @@ where // Attempt to use a user-provided gateway, if possible let user_chosen_gateway_id = common_args.gateway; + log::debug!("User chosen gateway id: {user_chosen_gateway_id:?}"); + let selection_spec = GatewaySelectionSpecification::new( user_chosen_gateway_id.map(|id| id.to_base58_string()), Some(common_args.latency_based_selection), false, ); + log::debug!("Gateway selection specification: {selection_spec:?}"); // Load and potentially override config + log::debug!("Init arguments: {init_args:#?}"); let config = C::construct_config(&init_args); + log::debug!("Constructed config: {config:#?}"); let paths = config.common_paths(); let core = config.core_config(); + log::info!( + "Using nym-api: {}", + core.client + .nym_api_urls + .iter() + .map(|url| url.as_str()) + .collect::>() + .join(",") + ); + // 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()); diff --git a/common/client-core/src/client/packet_statistics_control.rs b/common/client-core/src/client/packet_statistics_control.rs index 85bf237928..dd1dffc39f 100644 --- a/common/client-core/src/client/packet_statistics_control.rs +++ b/common/client-core/src/client/packet_statistics_control.rs @@ -13,8 +13,9 @@ const PACKET_REPORT_INTERVAL_SECS: u64 = 2; const SNAPSHOT_INTERVAL_MS: u64 = 500; // When computing rates, we include snapshots that are up to this old. We set it to some odd number // a tad larger than an integer number of snapshot intervals, so that we don't have to worry about -// threshold effects -const RECORDING_WINDOW_MS: u64 = 1700; +// threshold effects. +// Also, set it larger than the packet report interval so that we don't miss notable singular events +const RECORDING_WINDOW_MS: u64 = 2300; #[derive(Default, Debug, Clone)] struct PacketStatistics { @@ -434,10 +435,18 @@ impl PacketStatisticsControl { // If we get a burst of retransmissions // TODO: consider making this the number of retransmissions since last report instead. if latest_rates.retransmissions_queued > 0.0 { - log::info!( - "retransmissions: {} pkt/s", + log::debug!( + "retransmissions: {:.2} pkt/s", latest_rates.retransmissions_queued ); + + // Check what the number of retransmissions was during the recording window + if let Some((_, start_stats)) = self.history.front() { + let delta = self.stats.clone() - start_stats.clone(); + log::info!("retransmissions: {}", delta.retransmissions_queued,); + } else { + log::warn!("Unable to check retransmissions during recording window"); + } } // IDEA: if there is a burst of acks, that could indicate tokio task starvation. diff --git a/common/client-core/src/init/helpers.rs b/common/client-core/src/init/helpers.rs index 38a0c3e841..06c5c8ef59 100644 --- a/common/client-core/src/init/helpers.rs +++ b/common/client-core/src/init/helpers.rs @@ -65,7 +65,7 @@ pub async fn current_gateways( .ok_or(ClientCoreError::ListOfNymApisIsEmpty)?; let client = nym_validator_client::client::NymApiClient::new(nym_api.clone()); - log::trace!("Fetching list of gateways from: {nym_api}"); + log::debug!("Fetching list of gateways from: {nym_api}"); let gateways = client.get_cached_described_gateways().await?; log::debug!("Found {} gateways", gateways.len()); diff --git a/common/client-core/src/init/types.rs b/common/client-core/src/init/types.rs index bf9df9edb2..141bf8dd2d 100644 --- a/common/client-core/src/init/types.rs +++ b/common/client-core/src/init/types.rs @@ -178,7 +178,7 @@ impl From> for GatewayDetails { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum GatewaySelectionSpecification { /// Uniformly choose a random remote gateway. UniformRemote { must_use_tls: bool }, diff --git a/common/network-defaults/Cargo.toml b/common/network-defaults/Cargo.toml index 5d5e071343..3ed6da8de6 100644 --- a/common/network-defaults/Cargo.toml +++ b/common/network-defaults/Cargo.toml @@ -11,6 +11,7 @@ repository.workspace = true cfg-if = { workspace = true } dotenvy = { workspace = true } hex-literal = "0.3.3" +log = { workspace = true } once_cell = { workspace = true } schemars = { workspace = true, features = ["preserve_order"] } serde = { workspace = true, features = ["derive"]} diff --git a/common/network-defaults/src/lib.rs b/common/network-defaults/src/lib.rs index 1d2af785c1..ea265e83d3 100644 --- a/common/network-defaults/src/lib.rs +++ b/common/network-defaults/src/lib.rs @@ -400,11 +400,25 @@ fn fix_deprecated_environmental_variables() { } } +// Read the variables from the file and log what the corresponding values in the environment are. +fn print_env_vars_with_keys_in_file + Copy>(config_env_file: P) { + let items = dotenvy::from_path_iter(config_env_file) + .expect("Invalid path to environment configuration file"); + for item in items { + let (key, val) = item.expect("Invalid item in environment configuration file"); + log::debug!("{}: {}", key, val); + } +} + pub fn setup_env>(config_env_file: Option

) { match std::env::var(var_names::CONFIGURED) { // if the configuration is not already set in the env vars Err(std::env::VarError::NotPresent) => { - if let Some(config_env_file) = config_env_file { + if let Some(config_env_file) = &config_env_file { + log::debug!( + "Loading environment variables from {:?}", + config_env_file.as_ref() + ); dotenvy::from_path(config_env_file) .expect("Invalid path to environment configuration file"); fix_deprecated_environmental_variables(); @@ -412,17 +426,25 @@ pub fn setup_env>(config_env_file: Option

) { // if nothing is set, the use mainnet defaults // if the user has not set `CONFIGURED`, then even if they set any of the env variables, // overwrite them + log::debug!("Loading mainnet defaults"); crate::mainnet::export_to_env(); } } - Err(_) => crate::mainnet::export_to_env(), + Err(_) => { + log::debug!("Environment variables already set. Using them"); + crate::mainnet::export_to_env() + } _ => { fix_deprecated_environmental_variables(); } } // if we haven't explicitly defined any of the constants, fallback to defaults - crate::mainnet::export_to_env_if_not_set() + crate::mainnet::export_to_env_if_not_set(); + + if let Some(config_env_file) = &config_env_file { + print_env_vars_with_keys_in_file(config_env_file); + } } // Name of the event triggered by the eth contract. If the event name is changed, diff --git a/gateway/src/commands/helpers.rs b/gateway/src/commands/helpers.rs index 3b97212ae6..4b91876b75 100644 --- a/gateway/src/commands/helpers.rs +++ b/gateway/src/commands/helpers.rs @@ -240,6 +240,7 @@ pub(crate) fn override_network_requester_config( ) } +// NOTE: make sure this is in sync with service-providers/ip-packet-router/src/cli/mod.rs::override_config pub(crate) fn override_ip_packet_router_config( mut cfg: nym_ip_packet_router::Config, opts: Option, diff --git a/service-providers/ip-packet-router/Cargo.toml b/service-providers/ip-packet-router/Cargo.toml index 01ae4fb897..1b3355af9a 100644 --- a/service-providers/ip-packet-router/Cargo.toml +++ b/service-providers/ip-packet-router/Cargo.toml @@ -10,21 +10,27 @@ license.workspace = true [dependencies] bincode = "1.3.3" +bs58 = "0.4.0" bytes = "1.5.0" +clap.workspace = true etherparse = "0.13.0" futures = { workspace = true } +lazy_static.workspace = true log = { workspace = true } nym-bin-common = { path = "../../common/bin-common" } nym-client-core = { path = "../../common/client-core" } nym-config = { path = "../../common/config" } +nym-crypto = { path = "../../common/crypto" } nym-exit-policy = { path = "../../common/exit-policy" } nym-ip-packet-requests = { path = "../../common/ip-packet-requests" } +nym-network-defaults = { path = "../../common/network-defaults" } nym-network-requester = { path = "../network-requester" } nym-sdk = { path = "../../sdk/rust/nym-sdk" } nym-service-providers-common = { path = "../common" } nym-sphinx = { path = "../../common/nymsphinx" } nym-task = { path = "../../common/task" } nym-tun = { path = "../../common/tun" } +nym-types = { path = "../../common/types" } nym-wireguard = { path = "../../common/wireguard" } nym-wireguard-types = { path = "../../common/wireguard-types" } rand = "0.8.5" diff --git a/service-providers/ip-packet-router/src/cli/build_info.rs b/service-providers/ip-packet-router/src/cli/build_info.rs new file mode 100644 index 0000000000..48bdf32eb0 --- /dev/null +++ b/service-providers/ip-packet-router/src/cli/build_info.rs @@ -0,0 +1,13 @@ +use clap::Args; +use nym_bin_common::bin_info_owned; +use nym_bin_common::output_format::OutputFormat; + +#[derive(Args)] +pub(crate) struct BuildInfo { + #[arg(short, long, default_value_t = OutputFormat::default())] + output: OutputFormat, +} + +pub(crate) fn execute(args: BuildInfo) { + println!("{}", args.output.format(&bin_info_owned!())) +} diff --git a/service-providers/ip-packet-router/src/cli/init.rs b/service-providers/ip-packet-router/src/cli/init.rs new file mode 100644 index 0000000000..207a6a748c --- /dev/null +++ b/service-providers/ip-packet-router/src/cli/init.rs @@ -0,0 +1,108 @@ +use std::{fmt::Display, fs, path::PathBuf}; + +use clap::Args; +use nym_bin_common::output_format::OutputFormat; +use nym_client_core::cli_helpers::client_init::{ + initialise_client, CommonClientInitArgs, InitResultsWithConfig, InitialisableClient, +}; +use serde::Serialize; + +use crate::{ + cli::{override_config, try_upgrade_config, OverrideConfig}, + config::{default_config_directory, default_config_filepath, default_data_directory, Config}, + error::IpPacketRouterError, +}; + +struct IpPacketRouterInit; + +impl InitialisableClient for IpPacketRouterInit { + const NAME: &'static str = "ip packet router"; + type Error = IpPacketRouterError; + 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, Debug)] +pub(crate) struct Init { + #[command(flatten)] + common_args: CommonClientInitArgs, + + #[clap(short, long, default_value_t = OutputFormat::default())] + output: OutputFormat, +} + +impl From for OverrideConfig { + fn from(init_config: Init) -> Self { + OverrideConfig { + nym_apis: init_config.common_args.nym_apis, + nyxd_urls: init_config.common_args.nyxd_urls, + enabled_credentials_mode: init_config.common_args.enabled_credentials_mode, + } + } +} + +impl AsRef for Init { + fn as_ref(&self) -> &CommonClientInitArgs { + &self.common_args + } +} + +#[derive(Debug, Serialize)] +pub struct InitResults { + #[serde(flatten)] + client_core: nym_client_core::init::types::InitResults, + client_address: String, +} + +impl InitResults { + fn new(res: InitResultsWithConfig) -> Self { + Self { + client_address: res.init_results.address.to_string(), + client_core: res.init_results, + } + } +} + +impl Display for InitResults { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "{}", self.client_core)?; + write!( + f, + "Address of this ip-packet-router: {}", + self.client_address + ) + } +} + +pub(crate) async fn execute(args: Init) -> Result<(), IpPacketRouterError> { + eprintln!("Initialising client..."); + + let output = args.output; + let res = initialise_client::(args).await?; + + let init_results = InitResults::new(res); + println!("{}", output.format(&init_results)); + + Ok(()) +} diff --git a/service-providers/ip-packet-router/src/cli/mod.rs b/service-providers/ip-packet-router/src/cli/mod.rs new file mode 100644 index 0000000000..d17391eaed --- /dev/null +++ b/service-providers/ip-packet-router/src/cli/mod.rs @@ -0,0 +1,197 @@ +use clap::{CommandFactory, Parser, Subcommand}; +use log::{error, trace}; +use nym_bin_common::completions::{fig_generate, ArgShell}; +use nym_bin_common::{bin_info, version_checker}; +use nym_client_core::client::base_client::storage::gateway_details::{ + OnDiskGatewayDetails, PersistedGatewayDetails, +}; +use nym_client_core::client::key_manager::persistence::OnDiskKeys; +use nym_client_core::config::GatewayEndpointConfig; +use nym_client_core::error::ClientCoreError; + +use crate::config::{BaseClientConfig, Config}; +use crate::error::IpPacketRouterError; + +mod build_info; +mod init; +mod run; +mod sign; + +lazy_static::lazy_static! { + pub static ref PRETTY_BUILD_INFORMATION: String = bin_info!().pretty_print(); +} + +// Helper for passing LONG_VERSION to clap +fn pretty_build_info_static() -> &'static str { + &PRETTY_BUILD_INFORMATION +} + +#[derive(Parser)] +#[command(author = "Nymtech", version, about, long_version = pretty_build_info_static())] +pub(crate) struct Cli { + /// Path pointing to an env file that configures the client. + #[arg(short, long)] + pub(crate) config_env_file: Option, + + /// Flag used for disabling the printed banner in tty. + #[arg(long)] + pub(crate) no_banner: bool, + + #[command(subcommand)] + command: Commands, +} + +#[allow(clippy::large_enum_variant)] +#[derive(Subcommand)] +pub(crate) enum Commands { + /// Initialize a network-requester. Do this first! + Init(init::Init), + + /// Run the network requester with the provided configuration and optionally override + /// parameters. + Run(run::Run), + + /// Sign to prove ownership of this network requester + Sign(sign::Sign), + + /// Show build information of this binary + BuildInfo(build_info::BuildInfo), + + /// Generate shell completions + Completions(ArgShell), + + /// Generate Fig specification + GenerateFigSpec, +} + +// Configuration that can be overridden. +pub(crate) struct OverrideConfig { + nym_apis: Option>, + nyxd_urls: Option>, + enabled_credentials_mode: Option, +} + +pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Config { + // disable poisson rate in the BASE client if the IPR option is enabled + if config.ip_packet_router.disable_poisson_rate { + log::info!("Disabling poisson rate for ip packet router"); + config.set_no_poisson_process(); + } + + config + .with_optional_base_custom_env( + BaseClientConfig::with_custom_nym_apis, + args.nym_apis, + nym_network_defaults::var_names::NYM_API, + nym_config::parse_urls, + ) + .with_optional_base_custom_env( + BaseClientConfig::with_custom_nyxd, + args.nyxd_urls, + nym_network_defaults::var_names::NYXD, + nym_config::parse_urls, + ) + .with_optional_base( + BaseClientConfig::with_disabled_credentials, + args.enabled_credentials_mode.map(|b| !b), + ) +} + +pub(crate) async fn execute(args: Cli) -> Result<(), IpPacketRouterError> { + let bin_name = "nym-ip-packet-router"; + + match args.command { + 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), + Commands::Completions(s) => s.generate(&mut Cli::command(), bin_name), + Commands::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name), + } + Ok(()) +} + +// Unused until we need to start implementing config upgrades +#[allow(unused)] +fn persist_gateway_details( + config: &Config, + details: GatewayEndpointConfig, +) -> Result<(), IpPacketRouterError> { + let details_store = + OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details); + let keys_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone()); + let shared_keys = keys_store.ephemeral_load_gateway_keys().map_err(|source| { + IpPacketRouterError::ClientCoreError(ClientCoreError::KeyStoreError { + source: Box::new(source), + }) + })?; + let persisted_details = PersistedGatewayDetails::new(details.into(), Some(&shared_keys))?; + details_store + .store_to_disk(&persisted_details) + .map_err(|source| { + IpPacketRouterError::ClientCoreError(ClientCoreError::GatewayDetailsStoreError { + source: Box::new(source), + }) + }) +} + +fn try_upgrade_config(_id: &str) -> Result<(), IpPacketRouterError> { + trace!("Attempting to upgrade config"); + Ok(()) +} + +fn try_load_current_config(id: &str) -> Result { + // try to load the config as is + if let Ok(cfg) = Config::read_from_default_path(id) { + return if !cfg.validate() { + Err(IpPacketRouterError::ConfigValidationFailure) + } else { + Ok(cfg) + }; + } + + // we couldn't load it - try upgrading it from older revisions + try_upgrade_config(id)?; + + let config = match Config::read_from_default_path(id) { + Ok(cfg) => cfg, + Err(err) => { + error!("Failed to load config for {id}. Are you sure you have run `init` before? (Error was: {err})"); + return Err(IpPacketRouterError::FailedToLoadConfig(id.to_string())); + } + }; + + if !config.validate() { + return Err(IpPacketRouterError::ConfigValidationFailure); + } + + Ok(config) +} + +// this only checks compatibility between config the binary. It does not take into consideration +// network version. It might do so in the future. +fn version_check(cfg: &Config) -> bool { + let binary_version = env!("CARGO_PKG_VERSION"); + let config_version = &cfg.base.client.version; + if binary_version == config_version { + true + } else { + log::warn!( + "The native-client binary has different version than what is specified \ + in config file! {binary_version} and {config_version}", + ); + if version_checker::is_minor_version_compatible(binary_version, config_version) { + log::info!( + "but they are still semver compatible. \ + However, consider running the `upgrade` command" + ); + true + } else { + log::error!( + "and they are semver incompatible! - \ + please run the `upgrade` command before attempting `run` again" + ); + false + } + } +} diff --git a/service-providers/ip-packet-router/src/cli/run.rs b/service-providers/ip-packet-router/src/cli/run.rs new file mode 100644 index 0000000000..cbfb3f8453 --- /dev/null +++ b/service-providers/ip-packet-router/src/cli/run.rs @@ -0,0 +1,44 @@ +use crate::cli::{try_load_current_config, version_check}; +use crate::{ + cli::{override_config, OverrideConfig}, + error::IpPacketRouterError, +}; +use clap::Args; +use log::error; +use nym_client_core::cli_helpers::client_run::CommonClientRunArgs; + +#[allow(clippy::struct_excessive_bools)] +#[derive(Args, Clone)] +pub(crate) struct Run { + #[command(flatten)] + common_args: CommonClientRunArgs, +} + +impl From for OverrideConfig { + fn from(run_config: Run) -> Self { + OverrideConfig { + nym_apis: None, + nyxd_urls: run_config.common_args.nyxd_urls, + enabled_credentials_mode: run_config.common_args.enabled_credentials_mode, + } + } +} + +pub(crate) async fn execute(args: &Run) -> Result<(), IpPacketRouterError> { + let mut config = try_load_current_config(&args.common_args.id)?; + config = override_config(config, OverrideConfig::from(args.clone())); + log::debug!("Using config: {:#?}", config); + + if !version_check(&config) { + error!("failed the local version check"); + return Err(IpPacketRouterError::FailedLocalVersionCheck); + } + + log::info!("Starting ip packet router service provider"); + let mut server = crate::ip_packet_router::IpPacketRouter::new(config); + if let Some(custom_mixnet) = &args.common_args.custom_mixnet { + server = server.with_stored_topology(custom_mixnet)? + } + + server.run_service_provider().await +} diff --git a/service-providers/ip-packet-router/src/cli/sign.rs b/service-providers/ip-packet-router/src/cli/sign.rs new file mode 100644 index 0000000000..c282483de3 --- /dev/null +++ b/service-providers/ip-packet-router/src/cli/sign.rs @@ -0,0 +1,76 @@ +use crate::cli::{try_load_current_config, version_check}; +use crate::error::IpPacketRouterError; +use clap::Args; +use nym_bin_common::output_format::OutputFormat; +use nym_client_core::client::key_manager::persistence::OnDiskKeys; +use nym_client_core::error::ClientCoreError; +use nym_crypto::asymmetric::identity; +use nym_types::helpers::ConsoleSigningOutput; + +#[derive(Args, Clone)] +pub(crate) struct Sign { + /// The id of the mixnode you want to sign with + #[arg(long)] + id: String, + + /// Signs a transaction-specific payload, that is going to be sent to the smart contract, with your identity key + #[arg(long)] + contract_msg: String, + + #[arg(short, long, default_value_t = OutputFormat::default())] + output: OutputFormat, +} + +fn print_signed_contract_msg( + private_key: &identity::PrivateKey, + raw_msg: &str, + output: OutputFormat, +) { + let trimmed = raw_msg.trim(); + eprintln!(">>> attempting to sign {trimmed}"); + + let Ok(decoded) = bs58::decode(trimmed).into_vec() else { + println!("it seems you have incorrectly copied the message to sign. Make sure you didn't accidentally skip any characters"); + return; + }; + + eprintln!(">>> decoding the message..."); + + // we don't really care about what particular information is embedded inside of it, + // we just want to know if user correctly copied the string, i.e. whether it's a valid bs58 encoded json + if serde_json::from_slice::(&decoded).is_err() { + println!("it seems you have incorrectly copied the message to sign. Make sure you didn't accidentally skip any characters"); + return; + }; + + // if this is a valid json, it MUST be a valid string + let decoded_string = String::from_utf8(decoded.clone()).unwrap(); + let signature = private_key.sign(&decoded).to_base58_string(); + + let sign_output = ConsoleSigningOutput::new(decoded_string, signature); + println!("{}", output.format(&sign_output)); +} + +pub(crate) async fn execute(args: &Sign) -> Result<(), IpPacketRouterError> { + let config = try_load_current_config(&args.id)?; + + if !version_check(&config) { + log::error!("Failed the local version check"); + return Err(IpPacketRouterError::FailedLocalVersionCheck); + } + + let key_store = OnDiskKeys::new(config.storage_paths.common_paths.keys); + let identity_keypair = key_store.load_identity_keypair().map_err(|source| { + IpPacketRouterError::ClientCoreError(ClientCoreError::KeyStoreError { + source: Box::new(source), + }) + })?; + + print_signed_contract_msg( + identity_keypair.private_key(), + &args.contract_msg, + args.output, + ); + + Ok(()) +} diff --git a/service-providers/ip-packet-router/src/config/mod.rs b/service-providers/ip-packet-router/src/config/mod.rs index d8c9aac583..1ea0ee755b 100644 --- a/service-providers/ip-packet-router/src/config/mod.rs +++ b/service-providers/ip-packet-router/src/config/mod.rs @@ -1,9 +1,12 @@ pub use nym_client_core::config::Config as BaseClientConfig; use nym_bin_common::logging::LoggingSettings; +use nym_client_core::{ + cli_helpers::client_init::ClientConfig, config::disk_persistence::CommonClientPaths, +}; use nym_config::{ defaults::mainnet, must_get_home, save_formatted_config_to_file, - serde_helpers::de_maybe_stringified, NymConfigTemplate, DEFAULT_CONFIG_DIR, + serde_helpers::de_maybe_stringified, NymConfigTemplate, OptionalSet, DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, DEFAULT_DATA_DIR, NYM_DIR, }; use nym_service_providers_common::DEFAULT_SERVICE_PROVIDERS_DIR; @@ -11,6 +14,7 @@ use serde::{Deserialize, Serialize}; use std::{ io, path::{Path, PathBuf}, + str::FromStr, }; use url::Url; @@ -71,6 +75,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 { @@ -81,6 +103,7 @@ impl Config { } } + #[allow(unused)] pub fn with_data_directory>(mut self, data_directory: P) -> Self { self.storage_paths = IpPacketRouterPaths::new_base(data_directory); self @@ -98,6 +121,7 @@ impl Config { default_config_filepath(&self.base.client.id) } + #[allow(unused)] pub fn save_to_default_location(&self) -> io::Result<()> { let config_save_location: PathBuf = self.default_location(); save_formatted_config_to_file(self, config_save_location) @@ -112,6 +136,52 @@ impl Config { pub fn set_no_poisson_process(&mut self) { self.base.set_no_poisson_process() } + + // poor man's 'builder' method + #[allow(unused)] + pub fn with_base(mut self, f: F, val: T) -> Self + where + F: Fn(BaseClientConfig, T) -> BaseClientConfig, + { + self.base = f(self.base, val); + self + } + + // helper methods to use `OptionalSet` trait. Those are defined due to very... ehm. 'specific' structure of this config + // (plz, lets refactor it) + pub fn with_optional_base(mut self, f: F, val: Option) -> Self + where + F: Fn(BaseClientConfig, T) -> BaseClientConfig, + { + self.base = self.base.with_optional(f, val); + self + } + + #[allow(unused)] + pub fn with_optional_base_env(mut self, f: F, val: Option, env_var: &str) -> Self + where + F: Fn(BaseClientConfig, T) -> BaseClientConfig, + T: FromStr, + ::Err: std::fmt::Debug, + { + self.base = self.base.with_optional_env(f, val, env_var); + self + } + + pub fn with_optional_base_custom_env( + mut self, + f: F, + val: Option, + env_var: &str, + parser: G, + ) -> Self + where + F: Fn(BaseClientConfig, T) -> BaseClientConfig, + G: Fn(&str) -> T, + { + self.base = self.base.with_optional_custom_env(f, val, env_var, parser); + self + } } #[derive(Debug, Clone, Deserialize, PartialEq, Serialize)] diff --git a/service-providers/ip-packet-router/src/error.rs b/service-providers/ip-packet-router/src/error.rs index 77b2797c8e..64c4cd426b 100644 --- a/service-providers/ip-packet-router/src/error.rs +++ b/service-providers/ip-packet-router/src/error.rs @@ -1,4 +1,4 @@ -use std::net::{IpAddr, SocketAddr}; +use std::net::SocketAddr; pub use nym_client_core::error::ClientCoreError; use nym_exit_policy::PolicyError; @@ -57,15 +57,13 @@ pub enum IpPacketRouterError { #[error("the provided socket address, '{addr}' is not covered by the exit policy!")] AddressNotCoveredByExitPolicy { addr: SocketAddr }, - #[error("the provided ip address, '{ip}' is not covered by the exit policy!")] - IpNotCoveredByExitPolicy { ip: IpAddr }, - + // #[error("the provided ip address, '{ip}' is not covered by the exit policy!")] + // IpNotCoveredByExitPolicy { ip: IpAddr }, #[error("failed filter check: '{addr}'")] AddressFailedFilterCheck { addr: SocketAddr }, - #[error("failed filter check: '{ip}'")] - IpFailedFilterCheck { ip: IpAddr }, - + // #[error("failed filter check: '{ip}'")] + // IpFailedFilterCheck { ip: IpAddr }, #[error("failed to apply the exit policy: {source}")] ExitPolicyFailure { #[from] diff --git a/service-providers/ip-packet-router/src/ip_packet_router.rs b/service-providers/ip-packet-router/src/ip_packet_router.rs index bcc0bf0601..844e40c8ca 100644 --- a/service-providers/ip-packet-router/src/ip_packet_router.rs +++ b/service-providers/ip-packet-router/src/ip_packet_router.rs @@ -12,9 +12,9 @@ use nym_sdk::mixnet::Recipient; use nym_task::{TaskClient, TaskHandle}; use crate::{ + config::Config, error::IpPacketRouterError, request_filter::{self, RequestFilter}, - Config, }; pub struct OnStartData { @@ -56,12 +56,14 @@ impl IpPacketRouter { } #[must_use] + #[allow(unused)] pub fn with_shutdown(mut self, shutdown: TaskClient) -> Self { self.shutdown = Some(shutdown); self } #[must_use] + #[allow(unused)] pub fn with_custom_gateway_transceiver( mut self, gateway_transceiver: Box, @@ -71,18 +73,21 @@ impl IpPacketRouter { } #[must_use] + #[allow(unused)] pub fn with_wait_for_gateway(mut self, wait_for_gateway: bool) -> Self { self.wait_for_gateway = wait_for_gateway; self } #[must_use] + #[allow(unused)] pub fn with_on_start(mut self, on_start: oneshot::Sender) -> Self { self.on_start = Some(on_start); self } #[must_use] + #[allow(unused)] pub fn with_custom_topology_provider( mut self, topology_provider: Box, diff --git a/service-providers/ip-packet-router/src/lib.rs b/service-providers/ip-packet-router/src/lib.rs index d3dab9bde1..f8aa663be8 100644 --- a/service-providers/ip-packet-router/src/lib.rs +++ b/service-providers/ip-packet-router/src/lib.rs @@ -10,6 +10,6 @@ pub mod error; mod ip_packet_router; mod mixnet_client; mod mixnet_listener; -mod request_filter; +pub mod request_filter; mod tun_listener; mod util; diff --git a/service-providers/ip-packet-router/src/main.rs b/service-providers/ip-packet-router/src/main.rs new file mode 100644 index 0000000000..8167e0eb51 --- /dev/null +++ b/service-providers/ip-packet-router/src/main.rs @@ -0,0 +1,41 @@ +#[cfg(target_os = "linux")] +mod cli; +#[cfg(target_os = "linux")] +mod config; +#[cfg(target_os = "linux")] +mod constants; +#[cfg(target_os = "linux")] +mod error; +#[cfg(target_os = "linux")] +mod ip_packet_router; +#[cfg(target_os = "linux")] +mod mixnet_client; +#[cfg(target_os = "linux")] +mod mixnet_listener; +#[cfg(target_os = "linux")] +mod request_filter; +#[cfg(target_os = "linux")] +mod tun_listener; +#[cfg(target_os = "linux")] +mod util; + +#[cfg(target_os = "linux")] +#[tokio::main] +async fn main() -> Result<(), error::IpPacketRouterError> { + use clap::Parser; + + let args = cli::Cli::parse(); + nym_bin_common::logging::setup_logging(); + nym_network_defaults::setup_env(args.config_env_file.as_ref()); + + if !args.no_banner { + nym_bin_common::logging::maybe_print_banner(clap::crate_name!(), clap::crate_version!()); + } + + cli::execute(args).await +} + +#[cfg(not(target_os = "linux"))] +fn main() { + println!("This binary is currently only supported on linux"); +} diff --git a/service-providers/ip-packet-router/src/mixnet_listener.rs b/service-providers/ip-packet-router/src/mixnet_listener.rs index 7eef17cec2..43fdd2c96c 100644 --- a/service-providers/ip-packet-router/src/mixnet_listener.rs +++ b/service-providers/ip-packet-router/src/mixnet_listener.rs @@ -15,6 +15,7 @@ use nym_task::TaskHandle; use tokio::io::AsyncWriteExt; use crate::{ + config::Config, constants::{CLIENT_INACTIVITY_TIMEOUT, DISCONNECT_TIMER_INTERVAL}, error::{IpPacketRouterError, Result}, request_filter::{self}, @@ -23,7 +24,6 @@ use crate::{ create_message::create_input_message, parse_ip::{parse_packet, ParsedPacket}, }, - Config, }; #[cfg(target_os = "linux")] diff --git a/service-providers/ip-packet-router/src/request_filter/exit_policy/mod.rs b/service-providers/ip-packet-router/src/request_filter/exit_policy/mod.rs index a0eee039bc..9dcecbdea3 100644 --- a/service-providers/ip-packet-router/src/request_filter/exit_policy/mod.rs +++ b/service-providers/ip-packet-router/src/request_filter/exit_policy/mod.rs @@ -10,6 +10,7 @@ use reqwest::IntoUrl; use url::Url; pub struct ExitPolicyRequestFilter { + #[allow(unused)] upstream: Option, policy: ExitPolicy, } @@ -34,10 +35,12 @@ impl ExitPolicyRequestFilter { } } + #[allow(unused)] pub fn policy(&self) -> &ExitPolicy { &self.policy } + #[allow(unused)] pub fn upstream(&self) -> Option<&Url> { self.upstream.as_ref() } diff --git a/service-providers/ip-packet-router/src/request_filter/mod.rs b/service-providers/ip-packet-router/src/request_filter/mod.rs index 2e3ca58b7e..c11fb6c9d6 100644 --- a/service-providers/ip-packet-router/src/request_filter/mod.rs +++ b/service-providers/ip-packet-router/src/request_filter/mod.rs @@ -26,6 +26,7 @@ impl RequestFilter { Self::new_exit_policy_filter(config).await } + #[allow(unused)] pub fn current_exit_policy_filter(&self) -> Option<&ExitPolicyRequestFilter> { match &*self.inner { RequestFilterInner::ExitPolicy { policy_filter } => Some(policy_filter), diff --git a/service-providers/network-requester/src/cli/init.rs b/service-providers/network-requester/src/cli/init.rs index 4333aa4970..630c4546f4 100644 --- a/service-providers/network-requester/src/cli/init.rs +++ b/service-providers/network-requester/src/cli/init.rs @@ -48,7 +48,7 @@ impl InitialisableClient for NetworkRequesterInit { } } -#[derive(Args, Clone)] +#[derive(Args, Clone, Debug)] pub(crate) struct Init { #[command(flatten)] common_args: CommonClientInitArgs,