Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 41 additions & 11 deletions nym-vpn-core/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 5 additions & 4 deletions nym-vpn-core/crates/nym-gateway-directory/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,22 @@ license.workspace = true

[dependencies]
async-trait.workspace = true
chrono = "0.4.38"
chrono.workspace = true
futures.workspace = true
hickory-resolver.workspace = true
itertools.workspace = true
log.workspace = true
nym-client-core.workspace = true
nym-config.workspace = true
nym-explorer-client.workspace = true
nym-harbour-master-client = { path = "../nym-harbour-master-client" }
nym-sdk.workspace = true
nym-topology.workspace = true
nym-validator-client.workspace = true
nym-vpn-api-client = { path = "../nym-vpn-api-client" }
rand.workspace = true
serde.workspace = true
thiserror.workspace = true
tokio = { workspace = true }
tokio-tungstenite = { version = "0.23" }
tungstenite = { version = "0.23" }
tracing.workspace = true
url.workspace = true

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ impl EntryPoint {
matches!(self, EntryPoint::Location { .. })
}

pub fn lookup_gateway(&self, gateways: &GatewayList) -> Result<Gateway> {
pub async fn lookup_gateway(&self, gateways: &GatewayList) -> Result<Gateway> {
match &self {
EntryPoint::Gateway { identity } => {
debug!("Selecting gateway by identity: {}", identity);
Expand All @@ -73,7 +73,7 @@ impl EntryPoint {
}
EntryPoint::RandomLowLatency => {
debug!("Selecting a random low latency gateway");
todo!("Need to add client address to Gateway type");
gateways.random_low_latency_gateway().await
}
EntryPoint::Random => {
debug!("Selecting a random gateway");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::fmt::{Display, Formatter};
use crate::{error::Result, Error, IpPacketRouterAddress};
use nym_sdk::mixnet::{NodeIdentity, Recipient};
use serde::{Deserialize, Serialize};
use tracing::debug;
use tracing::{debug, info};

use super::gateway::{Gateway, GatewayList};

Expand Down Expand Up @@ -82,7 +82,7 @@ impl ExitPoint {
})
}
ExitPoint::Random => {
log::info!("Selecting a random exit gateway");
info!("Selecting a random exit gateway");
gateways
.random_gateway()
.ok_or_else(|| Error::FailedToSelectGatewayRandomly)
Expand Down
58 changes: 58 additions & 0 deletions nym-vpn-core/crates/nym-gateway-directory/src/entries/gateway.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,20 @@ use tracing::error;

use crate::{error::Result, AuthAddress, Country, Error, IpPacketRouterAddress};

// Decimal between 0 and 1 representing the performance of a gateway, measured over 24h.
type Perfomance = f64;

#[derive(Clone, Debug)]
pub struct Gateway {
pub identity: NodeIdentity,
pub location: Option<Location>,
pub ipr_address: Option<IpPacketRouterAddress>,
pub authenticator_address: Option<AuthAddress>,
pub last_probe: Option<Probe>,
pub host: Option<nym_topology::NetworkAddress>,
pub clients_ws_port: Option<u16>,
pub clients_wss_port: Option<u16>,
pub performance: Option<Perfomance>,
}

impl Gateway {
Expand All @@ -37,6 +44,20 @@ impl Gateway {
pub fn has_ipr_address(&self) -> bool {
self.ipr_address.is_some()
}

pub fn clients_address_no_tls(&self) -> Option<String> {
match (&self.host, &self.clients_ws_port) {
(Some(host), Some(port)) => Some(format!("ws://{}:{}", host, port)),
_ => None,
}
}

pub fn clients_address_tls(&self) -> Option<String> {
match (&self.host, &self.clients_wss_port) {
(Some(host), Some(port)) => Some(format!("wss://{}:{}", host, port)),
_ => None,
}
}
}

#[derive(Debug, Default, Clone, PartialEq)]
Expand Down Expand Up @@ -139,6 +160,10 @@ impl TryFrom<nym_vpn_api_client::Gateway> for Gateway {
ipr_address: None,
authenticator_address: None,
last_probe: gateway.last_probe.map(Probe::from),
host: None,
clients_ws_port: None,
clients_wss_port: None,
performance: None,
})
}
}
Expand Down Expand Up @@ -179,12 +204,20 @@ impl TryFrom<nym_validator_client::models::DescribedGateway> for Gateway {
.inspect_err(|err| error!("Failed to parse authenticator address: {err}"))
.ok()
});
let gateway = nym_topology::gateway::Node::try_from(gateway).ok();
let host = gateway.clone().map(|g| g.host);
let clients_ws_port = gateway.as_ref().map(|g| g.clients_ws_port);
let clients_wss_port = gateway.and_then(|g| g.clients_wss_port);
Ok(Gateway {
identity,
location,
ipr_address,
authenticator_address,
last_probe: None,
host,
clients_ws_port,
clients_wss_port,
performance: None,
})
}
}
Expand Down Expand Up @@ -277,6 +310,13 @@ impl GatewayList {
pub fn into_inner(self) -> Vec<Gateway> {
self.gateways
}

pub(crate) async fn random_low_latency_gateway(&self) -> Result<Gateway> {
let mut rng = rand::rngs::OsRng;
nym_client_core::init::helpers::choose_gateway_by_latency(&mut rng, &self.gateways, false)
.await
.map_err(|err| Error::FailedToSelectGatewayBasedOnLowLatency { source: err })
}
}

impl IntoIterator for GatewayList {
Expand All @@ -287,3 +327,21 @@ impl IntoIterator for GatewayList {
self.gateways.into_iter()
}
}

impl nym_client_core::init::helpers::ConnectableGateway for Gateway {
fn identity(&self) -> &nym_sdk::mixnet::NodeIdentity {
self.identity()
}

fn clients_address(&self) -> String {
// This is a bit of a sharp edge, but temporary until we can remove Option from host
// and tls port when we add these to the vpn API endpoints.
self.clients_address_tls()
.or(self.clients_address_no_tls())
.unwrap_or("ws://".to_string())
}

fn is_wss(&self) -> bool {
self.clients_address_tls().is_some()
}
}
22 changes: 6 additions & 16 deletions nym-vpn-core/crates/nym-gateway-directory/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,11 @@ pub enum Error {
ValidatorClientError(#[from] nym_validator_client::ValidatorClientError),

#[error(transparent)]
ExplorerApiError(#[from] nym_explorer_client::ExplorerApiError),

#[error(transparent)]
HarbourMasterError(#[from] nym_harbour_master_client::HarbourMasterError),

#[error(transparent)]
HarbourMasterApiError(#[from] nym_harbour_master_client::HarbourMasterApiError),
NymHttpApiError(#[from] nym_vpn_api_client::VpnApiError),

#[error(transparent)]
NymVpnApiClientError(#[from] nym_vpn_api_client::VpnApiClientError),

#[error("failed to fetch location data from explorer-api: {error}")]
FailedFetchLocationData {
error: nym_explorer_client::ExplorerApiError,
},

#[error("failed to resolve gateway hostname: {hostname}: {source}")]
FailedToDnsResolveGateway {
hostname: String,
Expand All @@ -44,10 +33,11 @@ pub enum Error {
#[error("resolved hostname {0} but no IP address found")]
ResolvedHostnameButNoIp(String),

#[error("failed to lookup described gateways: {source}")]
FailedToLookupDescribedGateways {
source: nym_validator_client::ValidatorClientError,
},
#[error("failed to lookup described gateways: {0}")]
FailedToLookupDescribedGateways(#[source] nym_validator_client::ValidatorClientError),

#[error("failed to lookup skimmed gateways: {0}")]
FailedToLookupSkimmedGateways(#[source] nym_validator_client::ValidatorClientError),

#[error("requested gateway not found in the remote list: {0}")]
RequestedGatewayIdNotFound(String),
Expand Down
Loading