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:
@@ -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());
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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> {
|
||||
|
||||
Generated
+2
@@ -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,2 +1,3 @@
|
||||
[workspace]
|
||||
members = ["src-tauri"]
|
||||
resolver = "2"
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user