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,