From 6ffd211e511fa069fd7fa29dcbefbafa109eb2db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Mon, 20 Mar 2023 16:11:03 +0000 Subject: [PATCH] backwards compatibility --- clients/client-core/src/config/mod.rs | 33 +-- clients/client-core/src/config/old_config.rs | 198 ++++++++++++++++++ clients/native/src/client/config/mod.rs | 1 + .../native/src/client/config/old_config.rs | 60 ++++++ clients/native/src/client/config/template.rs | 14 +- clients/native/src/commands/run.rs | 37 +++- clients/socks5/src/client/config/mod.rs | 1 + .../socks5/src/client/config/old_config.rs | 66 ++++++ clients/socks5/src/client/config/template.rs | 14 +- clients/socks5/src/commands/run.rs | 41 +++- 10 files changed, 419 insertions(+), 46 deletions(-) create mode 100644 clients/client-core/src/config/old_config.rs create mode 100644 clients/native/src/client/config/old_config.rs create mode 100644 clients/socks5/src/client/config/old_config.rs diff --git a/clients/client-core/src/config/mod.rs b/clients/client-core/src/config/mod.rs index 9ea3ad3756..181c5b5fe2 100644 --- a/clients/client-core/src/config/mod.rs +++ b/clients/client-core/src/config/mod.rs @@ -13,6 +13,7 @@ use url::Url; #[cfg(target_arch = "wasm32")] use wasm_bindgen::prelude::*; +pub mod old_config; pub mod persistence; pub const MISSING_VALUE: &str = "MISSING VALUE"; @@ -457,63 +458,63 @@ impl From for GatewayEndpointConfig { pub struct Client { /// Version of the client for which this configuration was created. #[serde(default = "missing_string_value")] - version: String, + pub version: String, /// ID specifies the human readable ID of this particular client. - id: String, + pub id: String, /// Indicates whether this client is running in a disabled credentials mode, thus attempting /// to claim bandwidth without presenting bandwidth credentials. #[serde(default)] - disabled_credentials_mode: bool, + pub disabled_credentials_mode: bool, /// Addresses to nyxd validators via which the client can communicate with the chain. #[serde(alias = "validator_urls")] - nyxd_urls: Vec, + pub nyxd_urls: Vec, /// Addresses to APIs running on validator from which the client gets the view of the network. #[serde(alias = "validator_api_urls")] - nym_api_urls: Vec, + pub nym_api_urls: Vec, /// Path to file containing private identity key. - private_identity_key_file: PathBuf, + pub private_identity_key_file: PathBuf, /// Path to file containing public identity key. - public_identity_key_file: PathBuf, + pub public_identity_key_file: PathBuf, /// Path to file containing private encryption key. - private_encryption_key_file: PathBuf, + pub private_encryption_key_file: PathBuf, /// Path to file containing public encryption key. - public_encryption_key_file: PathBuf, + pub public_encryption_key_file: PathBuf, /// Path to file containing shared key derived with the specified gateway that is used /// for all communication with it. - gateway_shared_key_file: PathBuf, + pub gateway_shared_key_file: PathBuf, /// Path to file containing key used for encrypting and decrypting the content of an /// acknowledgement so that nobody besides the client knows which packet it refers to. - ack_key_file: PathBuf, + pub ack_key_file: PathBuf, /// Information regarding how the client should send data to gateway. - gateway_endpoint: GatewayEndpointConfig, + pub gateway_endpoint: GatewayEndpointConfig, /// Path to the database containing bandwidth credentials of this client. - database_path: PathBuf, + pub database_path: PathBuf, /// Path to the persistent store for received reply surbs, unused encryption keys and used sender tags. // this was set to use #[serde(default)] for the purposes of compatibility for multi-surbs introduced in 1.1.4. // if you're reading this message and we have already introduced some breaking changes, feel free // to remove that attribute since at this point the client configs should have gotten regenerated #[serde(default)] - reply_surb_database_path: PathBuf, + pub reply_surb_database_path: PathBuf, /// nym_home_directory specifies absolute path to the home nym Clients directory. /// It is expected to use default value and hence .toml file should not redefine this field. - nym_root_directory: PathBuf, + pub nym_root_directory: PathBuf, #[serde(skip)] - super_struct: PhantomData, + pub super_struct: PhantomData, } impl Default for Client { diff --git a/clients/client-core/src/config/old_config.rs b/clients/client-core/src/config/old_config.rs new file mode 100644 index 0000000000..a83de79995 --- /dev/null +++ b/clients/client-core/src/config/old_config.rs @@ -0,0 +1,198 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::config::{ + Acknowledgements, Client, Config, CoverTraffic, DebugConfig, ExtendedPacketSize, + GatewayConnection, Logging, ReplySurbs, Topology, Traffic, DEFAULT_ACK_WAIT_ADDITION, + DEFAULT_ACK_WAIT_MULTIPLIER, DEFAULT_AVERAGE_PACKET_DELAY, DEFAULT_GATEWAY_RESPONSE_TIMEOUT, + DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY, DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE, + DEFAULT_MAXIMUM_REPLY_KEY_AGE, DEFAULT_MAXIMUM_REPLY_SURB_AGE, + DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD, DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE, + DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD, + DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD, DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY, + DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE, DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD, + DEFAULT_TOPOLOGY_REFRESH_RATE, DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT, +}; +use nym_config::NymConfig; +use serde::{Deserialize, Serialize}; +use std::marker::PhantomData; +use std::time::Duration; + +#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct OldConfig { + pub client: Client, + + #[serde(default)] + logging: Logging, + #[serde(default)] + debug: OldDebugConfig, +} + +impl Default for OldConfig { + fn default() -> Self { + OldConfig { + client: Client::::default(), + logging: Default::default(), + debug: Default::default(), + } + } +} + +#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)] +#[serde(default, deny_unknown_fields)] +pub struct OldDebugConfig { + #[serde(with = "humantime_serde")] + pub average_packet_delay: Duration, + + #[serde(with = "humantime_serde")] + pub average_ack_delay: Duration, + + pub ack_wait_multiplier: f64, + #[serde(with = "humantime_serde")] + pub ack_wait_addition: Duration, + + #[serde(with = "humantime_serde")] + pub loop_cover_traffic_average_delay: Duration, + + #[serde(with = "humantime_serde")] + pub message_sending_average_delay: Duration, + + #[serde(with = "humantime_serde")] + pub gateway_response_timeout: Duration, + + #[serde(with = "humantime_serde")] + pub topology_refresh_rate: Duration, + + #[serde(with = "humantime_serde")] + pub topology_resolution_timeout: Duration, + + pub disable_loop_cover_traffic_stream: bool, + + pub disable_main_poisson_packet_distribution: bool, + + pub use_extended_packet_size: Option, + + pub minimum_reply_surb_storage_threshold: usize, + + pub maximum_reply_surb_storage_threshold: usize, + + pub minimum_reply_surb_request_size: u32, + + pub maximum_reply_surb_request_size: u32, + + pub maximum_allowed_reply_surb_request_size: u32, + + #[serde(with = "humantime_serde")] + pub maximum_reply_surb_rerequest_waiting_period: Duration, + + #[serde(with = "humantime_serde")] + pub maximum_reply_surb_drop_waiting_period: Duration, + + #[serde(with = "humantime_serde")] + pub maximum_reply_surb_age: Duration, + + #[serde(with = "humantime_serde")] + pub maximum_reply_key_age: Duration, +} + +impl From for DebugConfig { + fn from(value: OldDebugConfig) -> Self { + DebugConfig { + traffic: Traffic { + average_packet_delay: value.average_packet_delay, + message_sending_average_delay: value.message_sending_average_delay, + disable_main_poisson_packet_distribution: value + .disable_main_poisson_packet_distribution, + use_extended_packet_size: value.use_extended_packet_size, + }, + cover_traffic: CoverTraffic { + loop_cover_traffic_average_delay: value.loop_cover_traffic_average_delay, + disable_loop_cover_traffic_stream: value.disable_loop_cover_traffic_stream, + }, + gateway_connection: GatewayConnection { + gateway_response_timeout: value.gateway_response_timeout, + }, + acknowledgements: Acknowledgements { + average_ack_delay: value.average_ack_delay, + ack_wait_multiplier: value.ack_wait_multiplier, + ack_wait_addition: value.ack_wait_addition, + }, + topology: Topology { + topology_refresh_rate: value.topology_refresh_rate, + topology_resolution_timeout: value.topology_resolution_timeout, + }, + reply_surbs: ReplySurbs { + minimum_reply_surb_storage_threshold: value.minimum_reply_surb_storage_threshold, + maximum_reply_surb_storage_threshold: value.maximum_reply_surb_storage_threshold, + minimum_reply_surb_request_size: value.minimum_reply_surb_request_size, + maximum_reply_surb_request_size: value.maximum_reply_surb_request_size, + maximum_allowed_reply_surb_request_size: value + .maximum_allowed_reply_surb_request_size, + maximum_reply_surb_rerequest_waiting_period: value + .maximum_reply_surb_rerequest_waiting_period, + maximum_reply_surb_drop_waiting_period: value + .maximum_reply_surb_drop_waiting_period, + maximum_reply_surb_age: value.maximum_reply_surb_age, + maximum_reply_key_age: value.maximum_reply_key_age, + }, + } + } +} + +impl Default for OldDebugConfig { + fn default() -> Self { + OldDebugConfig { + average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY, + average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY, + ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER, + ack_wait_addition: DEFAULT_ACK_WAIT_ADDITION, + loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY, + message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY, + gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT, + topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE, + topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT, + disable_loop_cover_traffic_stream: false, + disable_main_poisson_packet_distribution: false, + use_extended_packet_size: None, + minimum_reply_surb_storage_threshold: DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD, + maximum_reply_surb_storage_threshold: DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD, + minimum_reply_surb_request_size: DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE, + maximum_reply_surb_request_size: DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE, + maximum_allowed_reply_surb_request_size: DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE, + maximum_reply_surb_rerequest_waiting_period: + DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD, + maximum_reply_surb_drop_waiting_period: DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD, + maximum_reply_surb_age: DEFAULT_MAXIMUM_REPLY_SURB_AGE, + maximum_reply_key_age: DEFAULT_MAXIMUM_REPLY_KEY_AGE, + } + } +} + +impl From> for Config { + fn from(value: OldConfig) -> Self { + Config { + client: Client { + version: value.client.version, + id: value.client.id, + disabled_credentials_mode: value.client.disabled_credentials_mode, + nyxd_urls: value.client.nyxd_urls, + nym_api_urls: value.client.nym_api_urls, + private_identity_key_file: value.client.private_identity_key_file, + public_identity_key_file: value.client.public_identity_key_file, + private_encryption_key_file: value.client.private_encryption_key_file, + public_encryption_key_file: value.client.public_encryption_key_file, + gateway_shared_key_file: value.client.gateway_shared_key_file, + ack_key_file: value.client.ack_key_file, + gateway_endpoint: value.client.gateway_endpoint, + database_path: value.client.database_path, + reply_surb_database_path: value.client.reply_surb_database_path, + nym_root_directory: value.client.nym_root_directory, + + super_struct: PhantomData, + }, + logging: value.logging, + debug: value.debug.into(), + } + } +} diff --git a/clients/native/src/client/config/mod.rs b/clients/native/src/client/config/mod.rs index 80795ca450..a2261610eb 100644 --- a/clients/native/src/client/config/mod.rs +++ b/clients/native/src/client/config/mod.rs @@ -15,6 +15,7 @@ pub use client_core::config::Config as BaseConfig; pub use client_core::config::MISSING_VALUE; pub use client_core::config::{DebugConfig, GatewayEndpointConfig}; +pub mod old_config; mod template; #[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone, Copy)] diff --git a/clients/native/src/client/config/old_config.rs b/clients/native/src/client/config/old_config.rs new file mode 100644 index 0000000000..d83ffeabcb --- /dev/null +++ b/clients/native/src/client/config/old_config.rs @@ -0,0 +1,60 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::client::config::{Config, Socket}; +use client_core::config::old_config::OldConfig as OldBaseConfig; +use nym_config::NymConfig; +use serde::{Deserialize, Serialize}; +use std::path::PathBuf; + +#[derive(Debug, Default, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct OldConfig { + #[serde(flatten)] + base: OldBaseConfig, + + socket: Socket, +} + +impl NymConfig for OldConfig { + fn template() -> &'static str { + // not intended to be used + unimplemented!() + } + + fn default_root_directory() -> PathBuf { + dirs::home_dir() + .expect("Failed to evaluate $HOME value") + .join(".nym") + .join("clients") + } + + fn try_default_root_directory() -> Option { + dirs::home_dir().map(|path| path.join(".nym").join("clients")) + } + + fn root_directory(&self) -> PathBuf { + self.base.client.nym_root_directory.clone() + } + + fn config_directory(&self) -> PathBuf { + self.root_directory() + .join(&self.base.client.id) + .join("config") + } + + fn data_directory(&self) -> PathBuf { + self.root_directory() + .join(&self.base.client.id) + .join("data") + } +} + +impl From for Config { + fn from(value: OldConfig) -> Self { + Config { + base: value.base.into(), + socket: value.socket, + } + } +} diff --git a/clients/native/src/client/config/template.rs b/clients/native/src/client/config/template.rs index dc7b48ba56..1c9a19f061 100644 --- a/clients/native/src/client/config/template.rs +++ b/clients/native/src/client/config/template.rs @@ -110,15 +110,15 @@ host = '{{ socket.host }}' [debug] -[[debug.traffic]] -average_packet_delay = '{{ debug.average_packet_delay }}' -message_sending_average_delay = '{{ debug.message_sending_average_delay }}' +[debug.traffic] +average_packet_delay = '{{ debug.traffic.average_packet_delay }}' +message_sending_average_delay = '{{ debug.traffic.message_sending_average_delay }}' -[[debug.acknowledgements]] -average_ack_delay = '{{ debug.average_ack_delay }}' +[debug.acknowledgements] +average_ack_delay = '{{ debug.acknowledgements.average_ack_delay }}' -[[debug.cover_traffic]] -loop_cover_traffic_average_delay = '{{ debug.loop_cover_traffic_average_delay }}' +[debug.cover_traffic] +loop_cover_traffic_average_delay = '{{ debug.cover_traffic.loop_cover_traffic_average_delay }}' "# } diff --git a/clients/native/src/commands/run.rs b/clients/native/src/commands/run.rs index 62ca82e4dd..bf527a5d3d 100644 --- a/clients/native/src/commands/run.rs +++ b/clients/native/src/commands/run.rs @@ -10,6 +10,7 @@ use crate::{ error::ClientError, }; +use crate::client::config::old_config::OldConfig; use clap::Args; use log::*; use nym_bin_common::version_checker::is_minor_version_compatible; @@ -97,16 +98,38 @@ fn version_check(cfg: &Config) -> bool { } } +fn load_config(id: &str) -> Result> { + // try to load the current config + match Config::load_from_file(id) { + Ok(config) => Ok(config), + Err(err) => { + warn!("Failed to load config for {id} - {err}...\nAttempting to use the old config template..."); + + // if this failed, try to use the old template + let old_config = match OldConfig::load_from_file(id) { + Ok(cfg) => { + info!("Managed to load config for {id} using old template"); + cfg + } + Err(err) => { + error!("Failed to load config for {id} with the old template. Are you sure you have run `init` before? (Error was: {err})"); + return Err(Box::new(ClientError::FailedToLoadConfig(id.to_string()))); + } + }; + + info!("Updating the client config template..."); + let updated: Config = old_config.into(); + updated.save_to_file(None)?; + + Ok(updated) + } + } +} + pub(crate) async fn execute(args: &Run) -> Result<(), Box> { let id = &args.id; - let mut config = match Config::load_from_file(id) { - Ok(cfg) => cfg, - Err(err) => { - error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {err})", id); - return Err(Box::new(ClientError::FailedToLoadConfig(id.to_string()))); - } - }; + let mut config = load_config(id)?; let override_config_fields = OverrideConfig::from(args.clone()); config = override_config(config, override_config_fields); diff --git a/clients/socks5/src/client/config/mod.rs b/clients/socks5/src/client/config/mod.rs index cf974199a3..f341a04872 100644 --- a/clients/socks5/src/client/config/mod.rs +++ b/clients/socks5/src/client/config/mod.rs @@ -15,6 +15,7 @@ use std::fmt::Debug; use std::path::PathBuf; use std::str::FromStr; +pub mod old_config; mod template; const DEFAULT_CONNECTION_START_SURBS: u32 = 20; diff --git a/clients/socks5/src/client/config/old_config.rs b/clients/socks5/src/client/config/old_config.rs new file mode 100644 index 0000000000..add6a28d27 --- /dev/null +++ b/clients/socks5/src/client/config/old_config.rs @@ -0,0 +1,66 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::client::config::{Config, Socks5, Socks5Debug}; +use client_core::config::old_config::OldConfig as OldBaseConfig; +use nym_config::NymConfig; +use serde::{Deserialize, Serialize}; +use std::path::PathBuf; + +#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct OldConfig { + #[serde(flatten)] + base: OldBaseConfig, + + socks5: Socks5, + + #[serde(default)] + socks5_debug: Socks5Debug, +} + +impl NymConfig for OldConfig { + fn template() -> &'static str { + // not intended to be used + unimplemented!() + } + + fn default_root_directory() -> PathBuf { + #[cfg(not(feature = "mobile"))] + let base_dir = dirs::home_dir().expect("Failed to evaluate $HOME value"); + #[cfg(feature = "mobile")] + let base_dir = PathBuf::from("/tmp"); + + base_dir.join(".nym").join("socks5-clients") + } + + fn try_default_root_directory() -> Option { + dirs::home_dir().map(|path| path.join(".nym").join("socks5-clients")) + } + + fn root_directory(&self) -> PathBuf { + self.base.client.nym_root_directory.clone() + } + + fn config_directory(&self) -> PathBuf { + self.root_directory() + .join(&self.base.client.id) + .join("config") + } + + fn data_directory(&self) -> PathBuf { + self.root_directory() + .join(&self.base.client.id) + .join("data") + } +} + +impl From for Config { + fn from(value: OldConfig) -> Self { + Config { + base: value.base.into(), + socks5: value.socks5, + socks5_debug: value.socks5_debug, + } + } +} diff --git a/clients/socks5/src/client/config/template.rs b/clients/socks5/src/client/config/template.rs index 2840be9a93..43858fbc2d 100644 --- a/clients/socks5/src/client/config/template.rs +++ b/clients/socks5/src/client/config/template.rs @@ -114,15 +114,15 @@ send_anonymously = {{ socks5.send_anonymously }} [debug] -[[debug.traffic]] -average_packet_delay = '{{ debug.average_packet_delay }}' -message_sending_average_delay = '{{ debug.message_sending_average_delay }}' +[debug.traffic] +average_packet_delay = '{{ debug.traffic.average_packet_delay }}' +message_sending_average_delay = '{{ debug.traffic.message_sending_average_delay }}' -[[debug.acknowledgements]] -average_ack_delay = '{{ debug.average_ack_delay }}' +[debug.acknowledgements] +average_ack_delay = '{{ debug.acknowledgements.average_ack_delay }}' -[[debug.cover_traffic]] -loop_cover_traffic_average_delay = '{{ debug.loop_cover_traffic_average_delay }}' +[debug.cover_traffic] +loop_cover_traffic_average_delay = '{{ debug.cover_traffic.loop_cover_traffic_average_delay }}' "# } diff --git a/clients/socks5/src/commands/run.rs b/clients/socks5/src/commands/run.rs index 6bd381b678..bd8525497d 100644 --- a/clients/socks5/src/commands/run.rs +++ b/clients/socks5/src/commands/run.rs @@ -7,6 +7,7 @@ use crate::{ error::Socks5ClientError, }; +use crate::client::config::old_config::OldConfig; use clap::Args; use log::*; use nym_bin_common::version_checker::is_minor_version_compatible; @@ -105,18 +106,40 @@ fn version_check(cfg: &Config) -> bool { } } +fn load_config(id: &str) -> Result> { + // try to load the current config + match Config::load_from_file(id) { + Ok(config) => Ok(config), + Err(err) => { + warn!("Failed to load config for {id} - {err}...\nAttempting to use the old config template..."); + + // if this failed, try to use the old template + let old_config = match OldConfig::load_from_file(id) { + Ok(cfg) => { + info!("Managed to load config for {id} using old template"); + cfg + } + Err(err) => { + error!("Failed to load config for {id} with the old template. Are you sure you have run `init` before? (Error was: {err})"); + return Err(Box::new(Socks5ClientError::FailedToLoadConfig( + id.to_string(), + ))); + } + }; + + info!("Updating the client config template..."); + let updated: Config = old_config.into(); + updated.save_to_file(None)?; + + Ok(updated) + } + } +} + pub(crate) async fn execute(args: &Run) -> Result<(), Box> { let id = &args.id; - let mut config = match Config::load_from_file(id) { - Ok(cfg) => cfg, - Err(err) => { - error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {err})", id); - return Err(Box::new(Socks5ClientError::FailedToLoadConfig( - id.to_string(), - ))); - } - }; + let mut config = load_config(id)?; let override_config_fields = OverrideConfig::from(args.clone()); config = override_config(config, override_config_fields);