From b424c6a8ffa33c2bd5919c41896aba00b6dd0f8b Mon Sep 17 00:00:00 2001 From: Simon Wicky Date: Wed, 13 Nov 2024 08:38:35 +0100 Subject: [PATCH] [Product Data] Add stats reporting configuration in client config (#5115) * add stats_reporting_config in config and env var * fix serializazion issue * remove duplicate stats reporting config * cargo toml cleanup * more cleanup * draft of wasm sdk for stats reporting * fix wasm sdk? * again * make stats sending possible from outside the sdk * make sure stats_id from client and gateway reported ared different --- Cargo.lock | 1 + clients/native/src/client/config/template.rs | 5 ++ clients/native/src/commands/init.rs | 1 + clients/native/src/commands/mod.rs | 7 +++ clients/native/src/commands/run.rs | 1 + clients/socks5/src/commands/init.rs | 1 + clients/socks5/src/commands/mod.rs | 7 +++ clients/socks5/src/commands/run.rs | 1 + clients/socks5/src/config/template.rs | 5 ++ common/client-core/config-types/Cargo.toml | 3 +- common/client-core/config-types/src/lib.rs | 42 +++++++++++++++ common/client-core/config-types/src/old/v5.rs | 1 + .../src/cli_helpers/client_init.rs | 5 ++ .../client-core/src/cli_helpers/client_run.rs | 5 ++ .../client-core/src/client/base_client/mod.rs | 23 ++++---- .../src/client/statistics_control.rs | 35 ++++++------ common/network-defaults/src/var_names.rs | 1 + common/statistics/src/clients/mod.rs | 4 +- common/statistics/src/lib.rs | 48 ----------------- common/wasm/client-core/src/config/mod.rs | 53 ++++++++++++++++++- .../wasm/client-core/src/config/override.rs | 40 +++++++++++++- sdk/rust/nym-sdk/src/mixnet.rs | 1 + sdk/rust/nym-sdk/src/mixnet/client.rs | 23 ++------ sdk/rust/nym-sdk/src/mixnet/native_client.rs | 10 ++++ wasm/client/src/client.rs | 2 +- wasm/mix-fetch/src/fetch.rs | 2 +- 26 files changed, 220 insertions(+), 107 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2a09a35770..3d734c4294 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4883,6 +4883,7 @@ dependencies = [ "nym-sphinx-addressing", "nym-sphinx-params", "serde", + "serde_with", "thiserror", "url", ] diff --git a/clients/native/src/client/config/template.rs b/clients/native/src/client/config/template.rs index 965fc07991..0e09ea8a38 100644 --- a/clients/native/src/client/config/template.rs +++ b/clients/native/src/client/config/template.rs @@ -102,5 +102,10 @@ average_ack_delay = '{{ debug.acknowledgements.average_ack_delay }}' [debug.cover_traffic] loop_cover_traffic_average_delay = '{{ debug.cover_traffic.loop_cover_traffic_average_delay }}' +[debug.stats_reporting] +enabled = {{ debug.stats_reporting.enabled }} +provider_address = '{{ debug.stats_reporting.provider_address }}' +reporting_interval = '{{ debug.stats_reporting.reporting_interval }}' + "#; diff --git a/clients/native/src/commands/init.rs b/clients/native/src/commands/init.rs index 209d1a556a..3bf0c8a710 100644 --- a/clients/native/src/commands/init.rs +++ b/clients/native/src/commands/init.rs @@ -81,6 +81,7 @@ impl From for OverrideConfig { nyxd_urls: init_config.common_args.nyxd_urls, enabled_credentials_mode: init_config.common_args.enabled_credentials_mode, + stats_reporting_address: init_config.common_args.stats_reporting_address, } } } diff --git a/clients/native/src/commands/mod.rs b/clients/native/src/commands/mod.rs index 6c5e8a9497..cfeb0ab511 100644 --- a/clients/native/src/commands/mod.rs +++ b/clients/native/src/commands/mod.rs @@ -13,6 +13,7 @@ use clap::{Parser, Subcommand}; use log::{error, info}; use nym_bin_common::bin_info; use nym_bin_common::completions::{fig_generate, ArgShell}; +use nym_client::client::Recipient; use nym_client_core::cli_helpers::CliClient; use nym_client_core::client::base_client::storage::migration_helpers::v1_1_33; use nym_config::OptionalSet; @@ -104,6 +105,7 @@ pub(crate) struct OverrideConfig { no_cover: bool, nyxd_urls: Option>, enabled_credentials_mode: Option, + stats_reporting_address: Option, } pub(crate) async fn execute(args: Cli) -> Result<(), Box> { @@ -149,6 +151,11 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config { BaseClientConfig::with_disabled_credentials, args.enabled_credentials_mode.map(|b| !b), ) + .with_optional_env_ext( + BaseClientConfig::with_enabled_stats_reporting_address, + args.stats_reporting_address, + nym_network_defaults::var_names::CLIENT_STATS_COLLECTION_PROVIDER, + ) } async fn try_upgrade_v1_1_13_config(id: &str) -> Result { diff --git a/clients/native/src/commands/run.rs b/clients/native/src/commands/run.rs index 2ab3fc8489..eac8271a95 100644 --- a/clients/native/src/commands/run.rs +++ b/clients/native/src/commands/run.rs @@ -43,6 +43,7 @@ impl From for OverrideConfig { no_cover: run_config.common_args.no_cover, nyxd_urls: run_config.common_args.nyxd_urls, enabled_credentials_mode: run_config.common_args.enabled_credentials_mode, + stats_reporting_address: run_config.common_args.stats_reporting_address, } } } diff --git a/clients/socks5/src/commands/init.rs b/clients/socks5/src/commands/init.rs index 19af718c2b..86fdb329ab 100644 --- a/clients/socks5/src/commands/init.rs +++ b/clients/socks5/src/commands/init.rs @@ -92,6 +92,7 @@ impl From for OverrideConfig { nyxd_urls: init_config.common_args.nyxd_urls, enabled_credentials_mode: init_config.common_args.enabled_credentials_mode, outfox: false, + stats_reporting_address: init_config.common_args.stats_reporting_address, } } } diff --git a/clients/socks5/src/commands/mod.rs b/clients/socks5/src/commands/mod.rs index 2c471ec3fa..545f36d778 100644 --- a/clients/socks5/src/commands/mod.rs +++ b/clients/socks5/src/commands/mod.rs @@ -19,6 +19,7 @@ use nym_client_core::client::base_client::storage::migration_helpers::v1_1_33; use nym_client_core::client::topology_control::geo_aware_provider::CountryGroup; use nym_client_core::config::{GroupBy, TopologyStructure}; use nym_config::OptionalSet; +use nym_sphinx::addressing::Recipient; use nym_sphinx::params::{PacketSize, PacketType}; use std::error::Error; use std::net::IpAddr; @@ -111,6 +112,7 @@ pub(crate) struct OverrideConfig { nyxd_urls: Option>, enabled_credentials_mode: Option, outfox: bool, + stats_reporting_address: Option, } pub(crate) async fn execute(args: Cli) -> Result<(), Box> { @@ -196,6 +198,11 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config { BaseClientConfig::with_disabled_credentials, args.enabled_credentials_mode.map(|b| !b), ) + .with_optional_base_env( + BaseClientConfig::with_enabled_stats_reporting_address, + args.stats_reporting_address, + nym_network_defaults::var_names::CLIENT_STATS_COLLECTION_PROVIDER, + ) } async fn try_upgrade_v1_1_13_config(id: &str) -> Result { diff --git a/clients/socks5/src/commands/run.rs b/clients/socks5/src/commands/run.rs index f65c0ff742..15857d986e 100644 --- a/clients/socks5/src/commands/run.rs +++ b/clients/socks5/src/commands/run.rs @@ -70,6 +70,7 @@ impl From for OverrideConfig { nyxd_urls: run_config.common_args.nyxd_urls, enabled_credentials_mode: run_config.common_args.enabled_credentials_mode, outfox: run_config.outfox, + stats_reporting_address: run_config.common_args.stats_reporting_address, } } } diff --git a/clients/socks5/src/config/template.rs b/clients/socks5/src/config/template.rs index 07556a5401..44149d1ac3 100644 --- a/clients/socks5/src/config/template.rs +++ b/clients/socks5/src/config/template.rs @@ -108,4 +108,9 @@ average_ack_delay = '{{ core.debug.acknowledgements.average_ack_delay }}' [core.debug.cover_traffic] loop_cover_traffic_average_delay = '{{ core.debug.cover_traffic.loop_cover_traffic_average_delay }}' +[core.debug.stats_reporting] +enabled = {{ core.debug.stats_reporting.enabled }} +provider_address = '{{ core.debug.stats_reporting.provider_address }}' +reporting_interval = '{{ core.debug.stats_reporting.reporting_interval }}' + "#; diff --git a/common/client-core/config-types/Cargo.toml b/common/client-core/config-types/Cargo.toml index c7d7f7fdf0..83b4d715a2 100644 --- a/common/client-core/config-types/Cargo.toml +++ b/common/client-core/config-types/Cargo.toml @@ -9,6 +9,7 @@ license.workspace = true [dependencies] humantime-serde = { workspace = true } serde = { workspace = true, features = ["derive"] } +serde_with = { workspace = true, features = ["macros"] } thiserror.workspace = true url = { workspace = true, features = ["serde"] } @@ -23,4 +24,4 @@ nym-sphinx-addressing = { path = "../../nymsphinx/addressing" } [features] -disk-persistence = ["nym-pemstore"] \ No newline at end of file +disk-persistence = ["nym-pemstore"] diff --git a/common/client-core/config-types/src/lib.rs b/common/client-core/config-types/src/lib.rs index 4eaba838df..30b34a7976 100644 --- a/common/client-core/config-types/src/lib.rs +++ b/common/client-core/config-types/src/lib.rs @@ -5,6 +5,7 @@ use nym_config::defaults::NymNetworkDetails; use nym_sphinx_addressing::Recipient; use nym_sphinx_params::{PacketSize, PacketType}; use serde::{Deserialize, Serialize}; +use serde_with::{serde_as, DisplayFromStr}; use std::time::Duration; use url::Url; @@ -61,6 +62,11 @@ const DEFAULT_MAXIMUM_REPLY_SURB_AGE: Duration = Duration::from_secs(12 * 60 * 6 // 24 hours const DEFAULT_MAXIMUM_REPLY_KEY_AGE: Duration = Duration::from_secs(24 * 60 * 60); +// stats reporting related + +/// Time interval between reporting statistics to the given provider if it exist +const STATS_REPORT_INTERVAL_SECS: Duration = Duration::from_secs(300); + use crate::error::InvalidTrafficModeFailure; pub use nym_country_group::CountryGroup; @@ -133,6 +139,12 @@ impl Config { self } + pub fn with_enabled_stats_reporting_address(mut self, address: Recipient) -> Self { + self.debug.stats_reporting.provider_address = Some(address); + self.debug.stats_reporting.enabled = true; //since we are overriding the address, we assume the reporting should be enabled + self + } + // TODO: this should be refactored properly // as of 12.09.23 the below is true (not sure how this comment will rot in the future) // medium_toggle: @@ -631,6 +643,32 @@ impl Default for ReplySurbs { } } +#[serde_as] +#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)] +#[serde(default, deny_unknown_fields)] +pub struct StatsReporting { + /// Is stats reporting enabled + pub enabled: bool, + + /// Address of the stats collector. If this is none, no reporting will happen, regardless of `enabled` + #[serde_as(as = "Option")] + pub provider_address: Option, + + /// With what frequence will statistics be sent + #[serde(with = "humantime_serde")] + pub reporting_interval: Duration, +} + +impl Default for StatsReporting { + fn default() -> Self { + StatsReporting { + enabled: true, + provider_address: None, + reporting_interval: STATS_REPORT_INTERVAL_SECS, + } + } +} + #[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)] #[serde(default, deny_unknown_fields)] pub struct DebugConfig { @@ -651,6 +689,9 @@ pub struct DebugConfig { /// Defines all configuration options related to reply SURBs. pub reply_surbs: ReplySurbs, + + /// Defines all configuration options related to stats reporting. + pub stats_reporting: StatsReporting, } impl DebugConfig { @@ -672,6 +713,7 @@ impl Default for DebugConfig { acknowledgements: Default::default(), topology: Default::default(), reply_surbs: Default::default(), + stats_reporting: Default::default(), } } } diff --git a/common/client-core/config-types/src/old/v5.rs b/common/client-core/config-types/src/old/v5.rs index 0b4dab44c7..1f73544f8e 100644 --- a/common/client-core/config-types/src/old/v5.rs +++ b/common/client-core/config-types/src/old/v5.rs @@ -181,6 +181,7 @@ impl From for Config { maximum_reply_key_age: value.debug.reply_surbs.maximum_reply_key_age, surb_mix_hops: value.debug.reply_surbs.surb_mix_hops, }, + stats_reporting: Default::default(), }, } } diff --git a/common/client-core/src/cli_helpers/client_init.rs b/common/client-core/src/cli_helpers/client_init.rs index bac6de5c60..0e3f3bf234 100644 --- a/common/client-core/src/cli_helpers/client_init.rs +++ b/common/client-core/src/cli_helpers/client_init.rs @@ -15,6 +15,7 @@ use crate::{ use log::info; use nym_client_core_gateways_storage::GatewayDetails; use nym_crypto::asymmetric::identity; +use nym_sphinx::addressing::Recipient; use nym_topology::NymTopology; use nym_validator_client::UserAgent; use rand::rngs::OsRng; @@ -88,6 +89,10 @@ pub struct CommonClientInitArgs { /// Disable loop cover traffic and the Poisson rate limiter (for debugging only) #[cfg_attr(feature = "cli", clap(long, hide = true))] pub no_cover: bool, + + /// Sets the address to report statistics + #[cfg_attr(feature = "cli", clap(long, hide = true))] + pub stats_reporting_address: Option, } pub struct InitResultsWithConfig { diff --git a/common/client-core/src/cli_helpers/client_run.rs b/common/client-core/src/cli_helpers/client_run.rs index 7864e4059f..a156206454 100644 --- a/common/client-core/src/cli_helpers/client_run.rs +++ b/common/client-core/src/cli_helpers/client_run.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use nym_crypto::asymmetric::identity; +use nym_sphinx::addressing::Recipient; use std::path::PathBuf; #[cfg_attr(feature = "cli", derive(clap::Args))] @@ -56,4 +57,8 @@ pub struct CommonClientRunArgs { // has defined the conflict on that field itself #[cfg_attr(feature = "cli", clap(long, hide = true))] pub no_cover: bool, + + /// Sets the address to report statistics + #[cfg_attr(feature = "cli", clap(long, hide = true))] + pub stats_reporting_address: Option, } diff --git a/common/client-core/src/client/base_client/mod.rs b/common/client-core/src/client/base_client/mod.rs index 8d197439f6..760b7376c3 100644 --- a/common/client-core/src/client/base_client/mod.rs +++ b/common/client-core/src/client/base_client/mod.rs @@ -49,7 +49,6 @@ use nym_sphinx::addressing::nodes::NodeIdentity; use nym_sphinx::params::PacketType; use nym_sphinx::receiver::{ReconstructedMessage, SphinxMessageReceiver}; use nym_statistics_common::clients::ClientStatsSender; -use nym_statistics_common::StatsReportingConfig; use nym_task::connections::{ConnectionCommandReceiver, ConnectionCommandSender, LaneQueueLengths}; use nym_task::{TaskClient, TaskHandle}; use nym_topology::provider_trait::TopologyProvider; @@ -187,7 +186,6 @@ pub struct BaseClientBuilder<'a, C, S: MixnetClientStorage> { custom_gateway_transceiver: Option>, shutdown: Option, user_agent: Option, - stats_reporting_config: Option, setup_method: GatewaySetup, } @@ -211,7 +209,6 @@ where custom_gateway_transceiver: None, shutdown: None, user_agent: None, - stats_reporting_config: None, setup_method: GatewaySetup::MustLoad { gateway_id: None }, } } @@ -255,12 +252,6 @@ where self } - #[must_use] - pub fn with_statistics_reporting(mut self, config: StatsReportingConfig) -> Self { - self.stats_reporting_config = Some(config); - self - } - pub fn with_stored_topology>( mut self, file: P, @@ -598,14 +589,18 @@ where } fn start_statistics_control( - stats_reporting_config: Option, + config: &Config, + user_agent: Option, client_stats_id: String, input_sender: Sender, shutdown: TaskClient, ) -> ClientStatsSender { info!("Starting statistics control..."); StatisticsControl::create_and_start_with_shutdown( - stats_reporting_config, + config.debug.stats_reporting, + user_agent + .map(|u| u.application) + .unwrap_or("unknown".to_string()), client_stats_id, input_sender.clone(), shutdown.with_suffix("controller"), @@ -739,12 +734,14 @@ where self.user_agent.clone(), ); + //make sure we don't accidentally get the same id as gateways are reporting let client_stats_id = format!( - "{:x}", + "stats_id_{:x}", sha2::Sha256::digest(self_address.identity().to_bytes()) ); let stats_reporter = Self::start_statistics_control( - self.stats_reporting_config, + self.config, + self.user_agent.clone(), client_stats_id, input_sender.clone(), shutdown.fork("statistics_control"), diff --git a/common/client-core/src/client/statistics_control.rs b/common/client-core/src/client/statistics_control.rs index df027f13fb..174865ca1b 100644 --- a/common/client-core/src/client/statistics_control.rs +++ b/common/client-core/src/client/statistics_control.rs @@ -18,10 +18,10 @@ use std::time::Duration; +use nym_client_core_config_types::StatsReporting; use nym_sphinx::addressing::Recipient; -use nym_statistics_common::{ - clients::{ClientStatsController, ClientStatsReceiver, ClientStatsSender}, - StatsReportingConfig, +use nym_statistics_common::clients::{ + ClientStatsController, ClientStatsReceiver, ClientStatsSender, }; use nym_task::connections::TransmissionLane; @@ -30,8 +30,6 @@ use crate::{ spawn_future, }; -/// Time interval between reporting statistics to the given provider if it exist -const STATS_REPORT_INTERVAL_SECS: u64 = 300; /// Time interval between reporting statistics locally (logging/task_client) const LOCAL_REPORT_INTERVAL: Duration = Duration::from_secs(2); /// Interval for taking snapshots of the statistics @@ -51,21 +49,18 @@ pub(crate) struct StatisticsControl { /// Channel to send stats report through the mixnet report_tx: InputMessageSender, - /// Service-provider address to send stats reports - reporting_address: Option, + /// Config for stats reporting (enabled, address, interval) + reporting_config: StatsReporting, } impl StatisticsControl { pub(crate) fn create( - reporting_config: Option, + reporting_config: StatsReporting, + client_type: String, client_stats_id: String, report_tx: InputMessageSender, ) -> (Self, ClientStatsSender) { let (stats_tx, stats_rx) = tokio::sync::mpsc::unbounded_channel(); - let (reporting_address, client_type) = match reporting_config { - Some(cfg) => (Some(cfg.reporting_address), cfg.reporting_type), - None => (None, "".into()), - }; let stats = ClientStatsController::new(client_stats_id, client_type); @@ -73,8 +68,8 @@ impl StatisticsControl { StatisticsControl { stats, stats_rx, - reporting_address, report_tx, + reporting_config, }, ClientStatsSender::new(Some(stats_tx)), ) @@ -99,8 +94,8 @@ impl StatisticsControl { async fn run_with_shutdown(&mut self, mut task_client: nym_task::TaskClient) { log::debug!("Started StatisticsControl with graceful shutdown support"); - let stats_report_interval = Duration::from_secs(STATS_REPORT_INTERVAL_SECS); - let mut stats_report_interval = tokio::time::interval(stats_report_interval); + let mut stats_report_interval = + tokio::time::interval(self.reporting_config.reporting_interval); let mut local_report_interval = tokio::time::interval(LOCAL_REPORT_INTERVAL); let mut snapshot_interval = tokio::time::interval(SNAPSHOT_INTERVAL); @@ -116,10 +111,10 @@ impl StatisticsControl { _ = snapshot_interval.tick() => { self.stats.snapshot(); } - _ = stats_report_interval.tick(), if self.reporting_address.is_some() => { + _ = stats_report_interval.tick(), if self.reporting_config.enabled && self.reporting_config.provider_address.is_some() => { // SAFTEY : this branch executes only if reporting is not none, so unwrapp is fine #[allow(clippy::unwrap_used)] - self.report_stats(self.reporting_address.unwrap()).await; + self.report_stats(self.reporting_config.provider_address.unwrap()).await; } _ = local_report_interval.tick() => { @@ -142,12 +137,14 @@ impl StatisticsControl { } pub(crate) fn create_and_start_with_shutdown( - reporting_config: Option, + reporting_config: StatsReporting, + client_type: String, client_stats_id: String, report_tx: InputMessageSender, task_client: nym_task::TaskClient, ) -> ClientStatsSender { - let (controller, sender) = Self::create(reporting_config, client_stats_id, report_tx); + let (controller, sender) = + Self::create(reporting_config, client_type, client_stats_id, report_tx); controller.start_with_shutdown(task_client); sender } diff --git a/common/network-defaults/src/var_names.rs b/common/network-defaults/src/var_names.rs index 4831e82122..bf92b95799 100644 --- a/common/network-defaults/src/var_names.rs +++ b/common/network-defaults/src/var_names.rs @@ -25,6 +25,7 @@ pub const NYXD_WEBSOCKET: &str = "NYXD_WS"; pub const EXPLORER_API: &str = "EXPLORER_API"; pub const EXIT_POLICY_URL: &str = "EXIT_POLICY"; pub const NYM_VPN_API: &str = "NYM_VPN_API"; +pub const CLIENT_STATS_COLLECTION_PROVIDER: &str = "CLIENT_STATS_COLLECTION_PROVIDER"; pub const DKG_TIME_CONFIGURATION: &str = "DKG_TIME_CONFIGURATION"; diff --git a/common/statistics/src/clients/mod.rs b/common/statistics/src/clients/mod.rs index 9f94cda230..1f82be1b04 100644 --- a/common/statistics/src/clients/mod.rs +++ b/common/statistics/src/clients/mod.rs @@ -32,8 +32,8 @@ impl ClientStatsSender { } /// Report a statistics event using the sender. - pub fn report(&mut self, event: ClientStatsEvents) { - if let Some(tx) = self.stats_tx.as_mut() { + pub fn report(&self, event: ClientStatsEvents) { + if let Some(tx) = &self.stats_tx { if let Err(err) = tx.send(event) { log::error!("Failed to send stats event: {:?}", err); } diff --git a/common/statistics/src/lib.rs b/common/statistics/src/lib.rs index 4c3a8856d3..dc55054ad0 100644 --- a/common/statistics/src/lib.rs +++ b/common/statistics/src/lib.rs @@ -11,8 +11,6 @@ #![warn(clippy::todo)] #![warn(clippy::dbg_macro)] -use nym_sphinx::addressing::Recipient; - /// Client specific statistics interfaces and events. pub mod clients; /// Statistics related errors. @@ -21,49 +19,3 @@ pub mod error; pub mod gateways; /// Statistics reporting abstractions and implementations. pub mod report; - -/// Stats Reporting config to use when stats reporting is wanted -#[derive(Clone, Debug)] -pub struct StatsReportingConfig { - /// Client address to report to - pub reporting_address: Recipient, - - /// Type of client reporting (vpn_client, authenticator, native-client) - pub reporting_type: String, -} - -impl StatsReportingConfig { - /// Create a StatsReportingConfig for a native client - pub fn new_native(reporting_address: Recipient) -> Self { - StatsReportingConfig { - reporting_address, - reporting_type: NATIVE_CLIENT.to_string(), - } - } - /// Create a StatsReportingConfig for a vpn client - pub fn new_vpn(reporting_address: Recipient) -> Self { - StatsReportingConfig { - reporting_address, - reporting_type: VPN_CLIENT.to_string(), - } - } - /// Create a StatsReportingConfig for a socks5 client - pub fn new_socks5(reporting_address: Recipient) -> Self { - StatsReportingConfig { - reporting_address, - reporting_type: SOCKS5_CLIENT.to_string(), - } - } - /// Create a StatsReportingConfig for an unspecified client - pub fn new_unknown(reporting_address: Recipient) -> Self { - StatsReportingConfig { - reporting_address, - reporting_type: UNKNOWN.to_string(), - } - } -} - -const VPN_CLIENT: &str = "vpn_client"; -const NATIVE_CLIENT: &str = "native_client"; -const SOCKS5_CLIENT: &str = "socks5_client"; -const UNKNOWN: &str = "unknown"; diff --git a/common/wasm/client-core/src/config/mod.rs b/common/wasm/client-core/src/config/mod.rs index eb69a735bc..60a8fcafc1 100644 --- a/common/wasm/client-core/src/config/mod.rs +++ b/common/wasm/client-core/src/config/mod.rs @@ -17,7 +17,7 @@ pub use nym_client_core::config::{ Acknowledgements as ConfigAcknowledgements, Config as BaseClientConfig, CoverTraffic as ConfigCoverTraffic, DebugConfig as ConfigDebug, GatewayConnection as ConfigGatewayConnection, ReplySurbs as ConfigReplySurbs, - Topology as ConfigTopology, Traffic as ConfigTraffic, + StatsReporting as ConfigStatsReporting, Topology as ConfigTopology, Traffic as ConfigTraffic, }; pub fn new_base_client_config( @@ -86,7 +86,7 @@ pub fn no_cover_debug_obj() -> JsValue { // just a helper structure to more easily pass through the JS boundary #[wasm_bindgen(inspectable)] -#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct DebugWasm { /// Defines all configuration options related to traffic streams. @@ -106,6 +106,10 @@ pub struct DebugWasm { /// Defines all configuration options related to reply SURBs. pub reply_surbs: ReplySurbsWasm, + + /// Defines all configuration options related to stats reporting. + #[wasm_bindgen(getter_with_clone)] + pub stats_reporting: StatsReportingWasm, } impl Default for DebugWasm { @@ -123,6 +127,7 @@ impl From for ConfigDebug { acknowledgements: debug.acknowledgements.into(), topology: debug.topology.into(), reply_surbs: debug.reply_surbs.into(), + stats_reporting: debug.stats_reporting.into(), } } } @@ -136,6 +141,7 @@ impl From for DebugWasm { acknowledgements: debug.acknowledgements.into(), topology: debug.topology.into(), reply_surbs: debug.reply_surbs.into(), + stats_reporting: debug.stats_reporting.into(), } } } @@ -505,3 +511,46 @@ impl From for ReplySurbsWasm { } } } + +#[wasm_bindgen(inspectable)] +#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct StatsReportingWasm { + /// Is stats reporting enabled + pub enabled: bool, + + /// Address of the stats collector. If this is none, no reporting will happen, regardless of `enabled` + #[wasm_bindgen(getter_with_clone)] + pub provider_address: Option, + + /// With what frequence will statistics be sent + pub reporting_interval_ms: u32, +} + +impl Default for StatsReportingWasm { + fn default() -> Self { + ConfigStatsReporting::default().into() + } +} + +impl From for ConfigStatsReporting { + fn from(stats_reporting: StatsReportingWasm) -> Self { + ConfigStatsReporting { + enabled: stats_reporting.enabled, + provider_address: stats_reporting + .provider_address + .map(|address| address.parse().expect("Invalid provider address")), + reporting_interval: Duration::from_millis(stats_reporting.reporting_interval_ms as u64), + } + } +} + +impl From for StatsReportingWasm { + fn from(stats_reporting: ConfigStatsReporting) -> Self { + StatsReportingWasm { + enabled: stats_reporting.enabled, + provider_address: stats_reporting.provider_address.map(|r| r.to_string()), + reporting_interval_ms: stats_reporting.reporting_interval.as_millis() as u32, + } + } +} diff --git a/common/wasm/client-core/src/config/override.rs b/common/wasm/client-core/src/config/override.rs index 91fa4ec20e..5ded660461 100644 --- a/common/wasm/client-core/src/config/override.rs +++ b/common/wasm/client-core/src/config/override.rs @@ -9,14 +9,14 @@ use super::{ AcknowledgementsWasm, CoverTrafficWasm, DebugWasm, GatewayConnectionWasm, ReplySurbsWasm, - TopologyWasm, TrafficWasm, + StatsReportingWasm, TopologyWasm, TrafficWasm, }; use crate::config::ConfigDebug; use serde::{Deserialize, Serialize}; use tsify::Tsify; // just a helper structure to more easily pass through the JS boundary -#[derive(Tsify, Debug, Copy, Clone, Serialize, Deserialize)] +#[derive(Tsify, Debug, Clone, Serialize, Deserialize)] #[tsify(into_wasm_abi, from_wasm_abi)] #[serde(rename_all = "camelCase")] pub struct DebugWasmOverride { @@ -43,6 +43,10 @@ pub struct DebugWasmOverride { /// Defines all configuration options related to reply SURBs. #[tsify(optional)] pub reply_surbs: Option, + + /// Defines all configuration options related to stats reporting. + #[tsify(optional)] + pub stats_reporting: Option, } impl From for DebugWasm { @@ -54,6 +58,7 @@ impl From for DebugWasm { acknowledgements: value.acknowledgements.map(Into::into).unwrap_or_default(), topology: value.topology.map(Into::into).unwrap_or_default(), reply_surbs: value.reply_surbs.map(Into::into).unwrap_or_default(), + stats_reporting: value.stats_reporting.map(Into::into).unwrap_or_default(), } } } @@ -366,3 +371,34 @@ impl From for ReplySurbsWasm { } } } + +#[derive(Tsify, Debug, Clone, Serialize, Deserialize)] +#[tsify(into_wasm_abi, from_wasm_abi)] +#[serde(rename_all = "camelCase")] +pub struct StatsReportingWasmOverride { + /// Is stats reporting enabled + #[tsify(optional)] + pub enabled: Option, + + /// Address of the stats collector. If this is none, no reporting will happen, regardless of `enabled` + #[tsify(optional)] + pub provider_address: Option>, + + /// With what frequence will statistics be sent + #[tsify(optional)] + pub reporting_interval_ms: Option, +} + +impl From for StatsReportingWasm { + fn from(value: StatsReportingWasmOverride) -> Self { + let def = StatsReportingWasm::default(); + + StatsReportingWasm { + enabled: value.enabled.unwrap_or(def.enabled), + provider_address: value.provider_address.unwrap_or(def.provider_address), + reporting_interval_ms: value + .reporting_interval_ms + .unwrap_or(def.reporting_interval_ms), + } + } +} diff --git a/sdk/rust/nym-sdk/src/mixnet.rs b/sdk/rust/nym-sdk/src/mixnet.rs index 57a17c1569..e0c7f9b7f6 100644 --- a/sdk/rust/nym-sdk/src/mixnet.rs +++ b/sdk/rust/nym-sdk/src/mixnet.rs @@ -78,6 +78,7 @@ pub use nym_sphinx::{ anonymous_replies::requests::AnonymousSenderTag, receiver::ReconstructedMessage, }; +pub use nym_statistics_common::clients::ClientStatsEvents; pub use nym_task::connections::TransmissionLane; pub use nym_topology::{provider_trait::TopologyProvider, NymTopology}; pub use paths::StoragePaths; diff --git a/sdk/rust/nym-sdk/src/mixnet/client.rs b/sdk/rust/nym-sdk/src/mixnet/client.rs index 7a70bf5b6c..54885b24eb 100644 --- a/sdk/rust/nym-sdk/src/mixnet/client.rs +++ b/sdk/rust/nym-sdk/src/mixnet/client.rs @@ -23,7 +23,7 @@ use nym_client_core::client::key_manager::persistence::KeyStore; use nym_client_core::client::{ base_client::BaseClientBuilder, replies::reply_storage::ReplyStorageBackend, }; -use nym_client_core::config::DebugConfig; +use nym_client_core::config::{DebugConfig, StatsReporting}; use nym_client_core::error::ClientCoreError; use nym_client_core::init::helpers::current_gateways; use nym_client_core::init::setup_gateway; @@ -54,7 +54,6 @@ pub struct MixnetClientBuilder { custom_shutdown: Option, force_tls: bool, user_agent: Option, - stats_reporting_config: Option, // TODO: incorporate it properly into `MixnetClientStorage` (I will need it in wasm anyway) gateway_endpoint_config_path: Option, @@ -94,7 +93,6 @@ impl MixnetClientBuilder { custom_gateway_transceiver: None, force_tls: false, user_agent: None, - stats_reporting_config: None, }) } } @@ -122,7 +120,6 @@ where custom_shutdown: None, force_tls: false, user_agent: None, - stats_reporting_config: None, gateway_endpoint_config_path: None, storage, } @@ -141,7 +138,6 @@ where custom_shutdown: self.custom_shutdown, force_tls: self.force_tls, user_agent: self.user_agent, - stats_reporting_config: self.stats_reporting_config, gateway_endpoint_config_path: self.gateway_endpoint_config_path, storage, } @@ -236,11 +232,8 @@ where } #[must_use] - pub fn with_statistics_reporting( - mut self, - config: nym_statistics_common::StatsReportingConfig, - ) -> Self { - self.stats_reporting_config = Some(config); + pub fn with_statistics_reporting(mut self, config: StatsReporting) -> Self { + self.config.debug_config.stats_reporting = config; self } @@ -272,7 +265,6 @@ where client.wait_for_gateway = self.wait_for_gateway; client.force_tls = self.force_tls; client.user_agent = self.user_agent; - client.stats_reporting_config = self.stats_reporting_config; Ok(client) } @@ -322,8 +314,6 @@ where custom_shutdown: Option, user_agent: Option, - - stats_reporting_config: Option, } impl DisconnectedMixnetClient @@ -373,7 +363,6 @@ where force_tls: false, custom_shutdown: None, user_agent: None, - stats_reporting_config: None, }) } @@ -594,10 +583,6 @@ where base_builder = base_builder.with_user_agent(user_agent); } - if let Some(stats_reporting_config) = self.stats_reporting_config { - base_builder = base_builder.with_statistics_reporting(stats_reporting_config); - } - if let Some(topology_provider) = self.custom_topology_provider { base_builder = base_builder.with_topology_provider(topology_provider); } @@ -729,6 +714,7 @@ where let mut client_output = started_client.client_output.register_consumer(); let client_state: nym_client_core::client::base_client::ClientState = started_client.client_state; + let stats_events_reporter = started_client.stats_reporter; let identity_keys = started_client.identity_keys.clone(); let reconstructed_receiver = client_output.register_receiver()?; @@ -740,6 +726,7 @@ where client_output, client_state, reconstructed_receiver, + stats_events_reporter, started_client.task_handle, None, )) diff --git a/sdk/rust/nym-sdk/src/mixnet/native_client.rs b/sdk/rust/nym-sdk/src/mixnet/native_client.rs index ad514d6513..3a26c3356c 100644 --- a/sdk/rust/nym-sdk/src/mixnet/native_client.rs +++ b/sdk/rust/nym-sdk/src/mixnet/native_client.rs @@ -13,6 +13,7 @@ use nym_client_core::client::{ use nym_crypto::asymmetric::identity; use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::{params::PacketType, receiver::ReconstructedMessage}; +use nym_statistics_common::clients::{ClientStatsEvents, ClientStatsSender}; use nym_task::{ connections::{ConnectionCommandSender, LaneQueueLengths}, TaskHandle, @@ -45,6 +46,9 @@ pub struct MixnetClient { /// A channel for messages arriving from the mixnet after they have been reconstructed. pub(crate) reconstructed_receiver: ReconstructedMessagesReceiver, + /// A channel for sending stats event to be reported. + pub(crate) stats_events_reporter: ClientStatsSender, + /// The task manager that controls all the spawned tasks that the clients uses to do it's job. pub(crate) task_handle: TaskHandle, pub(crate) packet_type: Option, @@ -62,6 +66,7 @@ impl MixnetClient { client_output: ClientOutput, client_state: ClientState, reconstructed_receiver: ReconstructedMessagesReceiver, + stats_events_reporter: ClientStatsSender, task_handle: TaskHandle, packet_type: Option, ) -> Self { @@ -72,6 +77,7 @@ impl MixnetClient { client_output, client_state, reconstructed_receiver, + stats_events_reporter, task_handle, packet_type, _buffered: Vec::new(), @@ -179,6 +185,10 @@ impl MixnetClient { } } + pub fn send_stats_event(&self, event: ClientStatsEvents) { + self.stats_events_reporter.report(event); + } + /// Disconnect from the mixnet. Currently it is not supported to reconnect a disconnected /// client. pub async fn disconnect(mut self) { diff --git a/wasm/client/src/client.rs b/wasm/client/src/client.rs index ad54cb5e6f..645fda613c 100644 --- a/wasm/client/src/client.rs +++ b/wasm/client/src/client.rs @@ -252,7 +252,7 @@ impl<'a> From<&'a ClientOpts> for ClientConfigOpts { id: value.client_id.as_ref().map(|v| v.to_owned()), nym_api: value.nym_api_url.as_ref().map(|v| v.to_owned()), nyxd: value.nyxd_url.as_ref().map(|v| v.to_owned()), - debug: value.client_override.as_ref().map(|&v| v.into()), + debug: value.client_override.as_ref().map(|v| v.clone().into()), } } } diff --git a/wasm/mix-fetch/src/fetch.rs b/wasm/mix-fetch/src/fetch.rs index 459270e621..8df0142490 100644 --- a/wasm/mix-fetch/src/fetch.rs +++ b/wasm/mix-fetch/src/fetch.rs @@ -75,7 +75,7 @@ impl<'a> From<&'a MixFetchOpts> for MixFetchConfigOpts { id: value.client_id.as_ref().map(|v| v.to_owned()), nym_api: value.nym_api_url.as_ref().map(|v| v.to_owned()), nyxd: value.nyxd_url.as_ref().map(|v| v.to_owned()), - debug: value.client_override.as_ref().map(|&v| v.into()), + debug: value.client_override.as_ref().map(|v| v.clone().into()), } } }