From cfc86ba9f5aaa3991b5d7ca68d1c14c4bd87fcb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Mon, 28 Aug 2023 11:32:55 +0200 Subject: [PATCH 1/2] nym-connect: select gateway based on latency in medium mode --- .../topology_control/geo_aware_provider.rs | 2 +- common/client-core/src/config/mod.rs | 9 +++++ common/client-core/src/init/helpers.rs | 2 +- nym-connect/desktop/Cargo.lock | 2 + nym-connect/desktop/Cargo.toml | 1 + nym-connect/desktop/src-tauri/Cargo.toml | 2 + nym-connect/desktop/src-tauri/src/main.rs | 4 +- .../src-tauri/src/operations/directory/mod.rs | 39 +++++++++++++++++++ nym-connect/desktop/src-tauri/src/tasks.rs | 2 +- nym-connect/desktop/src/context/main.tsx | 7 +++- 10 files changed, 65 insertions(+), 5 deletions(-) diff --git a/common/client-core/src/client/topology_control/geo_aware_provider.rs b/common/client-core/src/client/topology_control/geo_aware_provider.rs index dabece05e5..5239f87411 100644 --- a/common/client-core/src/client/topology_control/geo_aware_provider.rs +++ b/common/client-core/src/client/topology_control/geo_aware_provider.rs @@ -303,7 +303,7 @@ impl GeoAwareTopologyProvider { filter_on: GroupBy, ) -> GeoAwareTopologyProvider { log::info!( - "Creating geo-aware topology provider with filter on {:?}", + "Creating geo-aware topology provider with filter on {}", filter_on ); nym_api_urls.shuffle(&mut thread_rng()); diff --git a/common/client-core/src/config/mod.rs b/common/client-core/src/config/mod.rs index f3cee8200f..c1d5b9d3cb 100644 --- a/common/client-core/src/config/mod.rs +++ b/common/client-core/src/config/mod.rs @@ -498,6 +498,15 @@ pub enum GroupBy { NymAddress(Recipient), } +impl std::fmt::Display for GroupBy { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + GroupBy::CountryGroup(group) => write!(f, "group: {}", group), + GroupBy::NymAddress(address) => write!(f, "address: {}", address), + } + } +} + impl Default for Topology { fn default() -> Self { Topology { diff --git a/common/client-core/src/init/helpers.rs b/common/client-core/src/init/helpers.rs index adfd9ad623..6237321a1b 100644 --- a/common/client-core/src/init/helpers.rs +++ b/common/client-core/src/init/helpers.rs @@ -149,7 +149,7 @@ async fn measure_latency(gateway: &gateway::Node) -> Result( +pub async fn choose_gateway_by_latency( rng: &mut R, gateways: &[gateway::Node], ) -> Result { diff --git a/nym-connect/desktop/Cargo.lock b/nym-connect/desktop/Cargo.lock index ecfe3bf851..e2b2acee27 100644 --- a/nym-connect/desktop/Cargo.lock +++ b/nym-connect/desktop/Cargo.lock @@ -4014,8 +4014,10 @@ dependencies = [ "nym-socks5-client-core", "nym-sphinx", "nym-task", + "nym-topology", "nym-validator-client", "pretty_env_logger", + "rand 0.7.3", "rand 0.8.5", "reqwest", "rust-embed", diff --git a/nym-connect/desktop/Cargo.toml b/nym-connect/desktop/Cargo.toml index 3ee3f20be3..5322fc260c 100644 --- a/nym-connect/desktop/Cargo.toml +++ b/nym-connect/desktop/Cargo.toml @@ -1,2 +1,3 @@ [workspace] members = ["src-tauri"] +resolver = "2" diff --git a/nym-connect/desktop/src-tauri/Cargo.toml b/nym-connect/desktop/src-tauri/Cargo.toml index 630adca45b..10a049af6a 100644 --- a/nym-connect/desktop/src-tauri/Cargo.toml +++ b/nym-connect/desktop/src-tauri/Cargo.toml @@ -30,6 +30,7 @@ itertools = "0.10.5" log = { version = "0.4", features = ["serde"] } pretty_env_logger = "0.4.0" rand = "0.8" +rand-07 = { package = "rand", version = "0.7.3" } reqwest = { version = "0.11.18", features = ["json", "socks"] } rust-embed = { version = "6.4.2", features = ["include-exclude"] } serde = { version = "1.0", features = ["derive"] } @@ -58,6 +59,7 @@ nym-bin-common = { path = "../../../common/bin-common"} nym-socks5-client-core = { path = "../../../common/socks5-client-core" } nym-sphinx = { path = "../../../common/nymsphinx" } nym-task = { path = "../../../common/task" } +nym-topology = { path = "../../../common/topology" } nym-validator-client = { path = "../../../common/client-libs/validator-client" } [dev-dependencies] diff --git a/nym-connect/desktop/src-tauri/src/main.rs b/nym-connect/desktop/src-tauri/src/main.rs index fc36d44c96..9897caf0e8 100644 --- a/nym-connect/desktop/src-tauri/src/main.rs +++ b/nym-connect/desktop/src-tauri/src/main.rs @@ -86,8 +86,10 @@ fn main() { crate::operations::connection::status::get_connection_status, crate::operations::connection::status::get_gateway_connection_status, crate::operations::connection::status::start_connection_health_check_task, - crate::operations::directory::get_services, + crate::operations::directory::get_gateway_with_low_latency, + crate::operations::directory::get_gateway_with_low_latency_from_list, crate::operations::directory::get_gateways, + crate::operations::directory::get_services, crate::operations::export::export_keys, crate::operations::window::hide_window, crate::operations::growth::test_and_earn::growth_tne_get_client_id, diff --git a/nym-connect/desktop/src-tauri/src/operations/directory/mod.rs b/nym-connect/desktop/src-tauri/src/operations/directory/mod.rs index 3842720161..522488d060 100644 --- a/nym-connect/desktop/src-tauri/src/operations/directory/mod.rs +++ b/nym-connect/desktop/src-tauri/src/operations/directory/mod.rs @@ -11,6 +11,7 @@ use nym_api_requests::models::GatewayBondAnnotated; use nym_bin_common::version_checker::is_minor_version_compatible; use nym_config::defaults::var_names::{NETWORK_NAME, NYM_API}; use nym_contracts_common::types::Percent; +use nym_topology::gateway; use nym_validator_client::nym_api::Client as ApiClient; use std::str::FromStr; use std::sync::Arc; @@ -164,3 +165,41 @@ pub async fn get_gateways() -> Result> { Ok(filtered_gateways) } + +async fn select_gateway_by_latency(gateways: Vec) -> Result { + let gateways_as_nodes: Vec = gateways + .into_iter() + .filter_map(|g| g.gateway_bond.try_into().ok()) + .collect(); + + let mut rng = rand_07::rngs::OsRng; + let selected_gateway = + nym_client_core::init::helpers::choose_gateway_by_latency(&mut rng, &gateways_as_nodes) + .await?; + Ok(selected_gateway) +} + +#[tauri::command] +pub async fn get_gateway_with_low_latency() -> Result { + let all_gateways = fetch_gateways().await?; + let selected_gateway = select_gateway_by_latency(all_gateways).await?; + Ok(Gateway { + identity: selected_gateway.identity().to_base58_string(), + }) +} + +#[tauri::command] +pub async fn get_gateway_with_low_latency_from_list(gateways: Vec) -> Result { + log::debug!("Selecting a gateway with low latency"); + let gateways = gateways.into_iter().map(|g| g.identity).collect_vec(); + let all_gateways = fetch_gateways().await?; + let gateways_union_set: Vec = all_gateways + .into_iter() + .filter(|g| gateways.contains(g.identity())) + .collect(); + let selected_gateway = select_gateway_by_latency(gateways_union_set).await?; + log::debug!("Selected gateway: {}", selected_gateway); + Ok(Gateway { + identity: selected_gateway.identity().to_base58_string(), + }) +} diff --git a/nym-connect/desktop/src-tauri/src/tasks.rs b/nym-connect/desktop/src-tauri/src/tasks.rs index 4af366ca48..b18cb8277d 100644 --- a/nym-connect/desktop/src-tauri/src/tasks.rs +++ b/nym-connect/desktop/src-tauri/src/tasks.rs @@ -57,7 +57,7 @@ fn override_config_from_env(config: &mut Config, privacy_level: &PrivacyLevel) { .provider_mix_address .parse() .expect("failed to parse provider mix address"); - log::warn!("Using geo-aware mixnode selection baseon the location of: {address}"); + log::warn!("Using geo-aware mixnode selection based on the location of: {address}"); config .core .base diff --git a/nym-connect/desktop/src/context/main.tsx b/nym-connect/desktop/src/context/main.tsx index 288c9be5a9..3eb88f63c6 100644 --- a/nym-connect/desktop/src/context/main.tsx +++ b/nym-connect/desktop/src/context/main.tsx @@ -214,7 +214,12 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => { const setGateway = async () => { if (gateways) { - const randomGateway = getRandomFromList(gateways); + let randomGateway; + if (userData?.privacy_level === 'Medium') { + randomGateway = await invoke('get_gateway_with_low_latency_from_list', { gateways }); + } else { + randomGateway = getRandomFromList(gateways); + } const withUserDefinitions = await buildGateway(randomGateway); await invoke('set_gateway', { gateway: shouldUseUserGateway ? userDefinedGateway.address : withUserDefinitions.identity, From 7f0a02f6ec1559b5b54f168d1f52755c0f3fe497 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Mon, 28 Aug 2023 14:10:51 +0200 Subject: [PATCH 2/2] Also filter on performance --- nym-connect/desktop/src-tauri/src/main.rs | 2 +- .../src-tauri/src/operations/directory/mod.rs | 73 ++++++++++++------- nym-connect/desktop/src/context/main.tsx | 2 +- 3 files changed, 47 insertions(+), 30 deletions(-) diff --git a/nym-connect/desktop/src-tauri/src/main.rs b/nym-connect/desktop/src-tauri/src/main.rs index 9897caf0e8..9a3c8c7b6c 100644 --- a/nym-connect/desktop/src-tauri/src/main.rs +++ b/nym-connect/desktop/src-tauri/src/main.rs @@ -87,9 +87,9 @@ fn main() { crate::operations::connection::status::get_gateway_connection_status, crate::operations::connection::status::start_connection_health_check_task, crate::operations::directory::get_gateway_with_low_latency, - crate::operations::directory::get_gateway_with_low_latency_from_list, crate::operations::directory::get_gateways, crate::operations::directory::get_services, + crate::operations::directory::select_gateway_with_low_latency_from_list, crate::operations::export::export_keys, crate::operations::window::hide_window, crate::operations::growth::test_and_earn::growth_tne_get_client_id, diff --git a/nym-connect/desktop/src-tauri/src/operations/directory/mod.rs b/nym-connect/desktop/src-tauri/src/operations/directory/mod.rs index 522488d060..05e0b9de6e 100644 --- a/nym-connect/desktop/src-tauri/src/operations/directory/mod.rs +++ b/nym-connect/desktop/src-tauri/src/operations/directory/mod.rs @@ -135,35 +135,16 @@ async fn fetch_gateways() -> Result> { Ok(gateways) } -#[tauri::command] -pub async fn get_gateways() -> Result> { - log::trace!("Fetching gateways"); - let all_gateways = fetch_gateways().await?; - log::trace!("Received: {:#?}", all_gateways); - - let filtered_gateways = all_gateways - .iter() +fn filter_out_low_performance_gateways( + gateways: Vec, +) -> Vec { + gateways + .into_iter() .filter(|g| { g.node_performance.most_recent > Percent::from_percentage_value(GATEWAY_PERFORMANCE_SCORE_THRESHOLD).unwrap() }) - .map(|g| Gateway { - identity: g.identity().clone(), - }) - .collect_vec(); - log::trace!("Filtered: {:#?}", filtered_gateways); - - if filtered_gateways.is_empty() { - log::warn!("No gateways with high enough performance score found! Using all gateways instead as fallback"); - return Ok(all_gateways - .iter() - .map(|g| Gateway { - identity: g.identity().clone(), - }) - .collect_vec()); - } - - Ok(filtered_gateways) + .collect() } async fn select_gateway_by_latency(gateways: Vec) -> Result { @@ -179,17 +160,52 @@ async fn select_gateway_by_latency(gateways: Vec) -> Resul Ok(selected_gateway) } +// Get all gateways satisfying the performance threshold. +#[tauri::command] +pub async fn get_gateways() -> Result> { + log::trace!("Fetching gateways"); + let all_gateways = fetch_gateways().await?; + log::trace!("Received: {:#?}", all_gateways); + + let gateways_filtered = filter_out_low_performance_gateways(all_gateways.clone()) + .into_iter() + .map(|g| Gateway { + identity: g.identity().clone(), + }) + .collect_vec(); + log::trace!("Filtered: {:#?}", gateways_filtered); + + if gateways_filtered.is_empty() { + log::warn!("No gateways with high enough performance score found! Using all gateways instead as fallback"); + return Ok(all_gateways + .iter() + .map(|g| Gateway { + identity: g.identity().clone(), + }) + .collect_vec()); + } + + Ok(gateways_filtered) +} + +// Lookup and select a single gateway with low latency. #[tauri::command] pub async fn get_gateway_with_low_latency() -> Result { + log::trace!("Fetching gateways"); let all_gateways = fetch_gateways().await?; - let selected_gateway = select_gateway_by_latency(all_gateways).await?; + log::trace!("Received: {:#?}", all_gateways); + + let gateways_filtered = filter_out_low_performance_gateways(all_gateways); + let selected_gateway = select_gateway_by_latency(gateways_filtered).await?; + log::debug!("Selected gateway: {}", selected_gateway); Ok(Gateway { identity: selected_gateway.identity().to_base58_string(), }) } +// From a given list of gateways, select the one with low latency. #[tauri::command] -pub async fn get_gateway_with_low_latency_from_list(gateways: Vec) -> Result { +pub async fn select_gateway_with_low_latency_from_list(gateways: Vec) -> Result { log::debug!("Selecting a gateway with low latency"); let gateways = gateways.into_iter().map(|g| g.identity).collect_vec(); let all_gateways = fetch_gateways().await?; @@ -197,7 +213,8 @@ pub async fn get_gateway_with_low_latency_from_list(gateways: Vec) -> R .into_iter() .filter(|g| gateways.contains(g.identity())) .collect(); - let selected_gateway = select_gateway_by_latency(gateways_union_set).await?; + let gateways_filtered = filter_out_low_performance_gateways(gateways_union_set); + let selected_gateway = select_gateway_by_latency(gateways_filtered).await?; log::debug!("Selected gateway: {}", selected_gateway); Ok(Gateway { identity: selected_gateway.identity().to_base58_string(), diff --git a/nym-connect/desktop/src/context/main.tsx b/nym-connect/desktop/src/context/main.tsx index 3eb88f63c6..2f553b5153 100644 --- a/nym-connect/desktop/src/context/main.tsx +++ b/nym-connect/desktop/src/context/main.tsx @@ -216,7 +216,7 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => { if (gateways) { let randomGateway; if (userData?.privacy_level === 'Medium') { - randomGateway = await invoke('get_gateway_with_low_latency_from_list', { gateways }); + randomGateway = await invoke('select_gateway_with_low_latency_from_list', { gateways }); } else { randomGateway = getRandomFromList(gateways); }