Merge pull request #3818 from nymtech/jon/nc-latency-based-gateway-selection

nym-connect: select gateway based on latency in medium mode
This commit is contained in:
Tommy Verrall
2023-08-29 16:33:07 +02:00
committed by GitHub
10 changed files with 91 additions and 14 deletions
@@ -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());
+9
View File
@@ -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 {
+1 -1
View File
@@ -149,7 +149,7 @@ async fn measure_latency(gateway: &gateway::Node) -> Result<GatewayWithLatency,
Ok(GatewayWithLatency::new(gateway, avg))
}
pub(super) async fn choose_gateway_by_latency<R: Rng>(
pub async fn choose_gateway_by_latency<R: Rng>(
rng: &mut R,
gateways: &[gateway::Node],
) -> Result<gateway::Node, ClientCoreError> {
+2
View File
@@ -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",
+1
View File
@@ -1,2 +1,3 @@
[workspace]
members = ["src-tauri"]
resolver = "2"
+2
View File
@@ -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]
+3 -1
View File
@@ -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_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,
@@ -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;
@@ -134,25 +135,47 @@ async fn fetch_gateways() -> Result<Vec<GatewayBondAnnotated>> {
Ok(gateways)
}
fn filter_out_low_performance_gateways(
gateways: Vec<GatewayBondAnnotated>,
) -> Vec<GatewayBondAnnotated> {
gateways
.into_iter()
.filter(|g| {
g.node_performance.most_recent
> Percent::from_percentage_value(GATEWAY_PERFORMANCE_SCORE_THRESHOLD).unwrap()
})
.collect()
}
async fn select_gateway_by_latency(gateways: Vec<GatewayBondAnnotated>) -> Result<gateway::Node> {
let gateways_as_nodes: Vec<gateway::Node> = 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)
}
// Get all gateways satisfying the performance threshold.
#[tauri::command]
pub async fn get_gateways() -> Result<Vec<Gateway>> {
log::trace!("Fetching gateways");
let all_gateways = fetch_gateways().await?;
log::trace!("Received: {:#?}", all_gateways);
let filtered_gateways = all_gateways
.iter()
.filter(|g| {
g.node_performance.most_recent
> Percent::from_percentage_value(GATEWAY_PERFORMANCE_SCORE_THRESHOLD).unwrap()
})
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: {:#?}", filtered_gateways);
log::trace!("Filtered: {:#?}", gateways_filtered);
if filtered_gateways.is_empty() {
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()
@@ -162,5 +185,38 @@ pub async fn get_gateways() -> Result<Vec<Gateway>> {
.collect_vec());
}
Ok(filtered_gateways)
Ok(gateways_filtered)
}
// Lookup and select a single gateway with low latency.
#[tauri::command]
pub async fn get_gateway_with_low_latency() -> Result<Gateway> {
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);
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 select_gateway_with_low_latency_from_list(gateways: Vec<Gateway>) -> Result<Gateway> {
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<GatewayBondAnnotated> = all_gateways
.into_iter()
.filter(|g| gateways.contains(g.identity()))
.collect();
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(),
})
}
+1 -1
View File
@@ -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
+6 -1
View File
@@ -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<Gateway>('select_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,