diff --git a/common/client-core/gateways-storage/fs_gateways_migrations/20240304120000_create_initial_tables.sql b/common/client-core/gateways-storage/fs_gateways_migrations/20240304120000_create_initial_tables.sql index bb4d59f7a1..77c32b4fa9 100644 --- a/common/client-core/gateways-storage/fs_gateways_migrations/20240304120000_create_initial_tables.sql +++ b/common/client-core/gateways-storage/fs_gateways_migrations/20240304120000_create_initial_tables.sql @@ -24,7 +24,8 @@ CREATE TABLE remote_gateway_details gateway_id_bs58 TEXT NOT NULL UNIQUE PRIMARY KEY REFERENCES registered_gateway (gateway_id_bs58), derived_aes128_ctr_blake3_hmac_keys_bs58 TEXT NOT NULL, gateway_owner_address TEXT NOT NULL, - gateway_listener TEXT NOT NULL + gateway_listener TEXT NOT NULL, + wg_tun_address TEXT ); CREATE TABLE custom_gateway_details diff --git a/common/client-core/gateways-storage/src/backend/fs_backend/mod.rs b/common/client-core/gateways-storage/src/backend/fs_backend/mod.rs index 4b759c0e5f..5061fe60fc 100644 --- a/common/client-core/gateways-storage/src/backend/fs_backend/mod.rs +++ b/common/client-core/gateways-storage/src/backend/fs_backend/mod.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::types::GatewayRegistration; -use crate::{GatewaysDetailsStore, StorageError}; +use crate::{ActiveGateway, GatewaysDetailsStore, StorageError}; use async_trait::async_trait; use manager::StorageManager; use std::path::Path; @@ -26,11 +26,12 @@ impl OnDiskGatewaysDetails { #[async_trait] impl GatewaysDetailsStore for OnDiskGatewaysDetails { type StorageError = error::StorageError; - + async fn has_gateway_details(&self, gateway_id: &str) -> Result { todo!() } - async fn active_gateway(&self) -> Result, Self::StorageError> { + + async fn active_gateway(&self) -> Result { todo!() } diff --git a/common/client-core/gateways-storage/src/backend/mem_backend.rs b/common/client-core/gateways-storage/src/backend/mem_backend.rs index b06e4eb5e9..451f6b2187 100644 --- a/common/client-core/gateways-storage/src/backend/mem_backend.rs +++ b/common/client-core/gateways-storage/src/backend/mem_backend.rs @@ -1,7 +1,7 @@ // Copyright 2024 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::types::GatewayRegistration; +use crate::types::{ActiveGateway, GatewayRegistration}; use crate::{BadGateway, GatewaysDetailsStore}; use async_trait::async_trait; use std::collections::HashMap; @@ -38,14 +38,16 @@ impl GatewaysDetailsStore for InMemGatewaysDetails { Ok(self.inner.read().await.gateways.contains_key(gateway_id)) } - async fn active_gateway(&self) -> Result, Self::StorageError> { + async fn active_gateway(&self) -> Result { let guard = self.inner.read().await; - Ok(guard.active_gateway.as_ref().map(|id| { + let registration = guard.active_gateway.as_ref().map(|id| { // SAFETY: if particular gateway is set as active, its details MUST exist #[allow(clippy::unwrap_used)] guard.gateways.get(id).unwrap().clone() - })) + }); + + Ok(ActiveGateway { registration }) } async fn set_active_gateway(&self, gateway_id: &str) -> Result<(), Self::StorageError> { diff --git a/common/client-core/gateways-storage/src/lib.rs b/common/client-core/gateways-storage/src/lib.rs index 3f87cdf526..26deeec049 100644 --- a/common/client-core/gateways-storage/src/lib.rs +++ b/common/client-core/gateways-storage/src/lib.rs @@ -13,8 +13,8 @@ pub mod types; // todo: export port types pub use crate::types::{ - CustomGatewayDetails, GatewayDetails, GatewayRegistration, GatewayType, RegisteredGateway, - RemoteGatewayDetails, + ActiveGateway, CustomGatewayDetails, GatewayDetails, GatewayRegistration, GatewayType, + RegisteredGateway, RemoteGatewayDetails, }; pub use backend::mem_backend::{InMemGatewaysDetails, InMemStorageError}; pub use error::BadGateway; @@ -29,7 +29,7 @@ pub trait GatewaysDetailsStore { type StorageError: Error + From; /// Returns details of the currently active gateway, if available. - async fn active_gateway(&self) -> Result, Self::StorageError>; + async fn active_gateway(&self) -> Result; /// Set the provided gateway as the currently active gateway. async fn set_active_gateway(&self, gateway_id: &str) -> Result<(), Self::StorageError>; diff --git a/common/client-core/gateways-storage/src/types.rs b/common/client-core/gateways-storage/src/types.rs index c165147bba..a63cd4f5d8 100644 --- a/common/client-core/gateways-storage/src/types.rs +++ b/common/client-core/gateways-storage/src/types.rs @@ -16,6 +16,11 @@ use zeroize::{Zeroize, ZeroizeOnDrop}; pub const REMOTE_GATEWAY_TYPE: &str = "remote"; pub const CUSTOM_GATEWAY_TYPE: &str = "custom"; +#[derive(Debug, Clone, Default)] +pub struct ActiveGateway { + pub registration: Option, +} + #[derive(Debug, Clone)] pub struct GatewayRegistration { pub details: GatewayDetails, @@ -46,12 +51,14 @@ impl GatewayDetails { derived_aes128_ctr_blake3_hmac_keys: Arc, gateway_owner_address: AccountId, gateway_listener: Url, + wg_tun_address: Option, ) -> Self { GatewayDetails::Remote(RemoteGatewayDetails { gateway_id, derived_aes128_ctr_blake3_hmac_keys, gateway_owner_address, gateway_listener, + wg_tun_address, }) } @@ -136,6 +143,7 @@ pub struct RawRemoteGatewayDetails { pub derived_aes128_ctr_blake3_hmac_keys_bs58: String, pub gateway_owner_address: String, pub gateway_listener: String, + pub wg_tun_address: Option, } impl TryFrom for RemoteGatewayDetails { @@ -175,11 +183,24 @@ impl TryFrom for RemoteGatewayDetails { } })?; + let wg_tun_address = value + .wg_tun_address + .as_ref() + .map(|addr| { + Url::parse(addr).map_err(|source| BadGateway::MalformedListener { + gateway_id: value.gateway_id_bs58.clone(), + raw_listener: addr.clone(), + source, + }) + }) + .transpose()?; + Ok(RemoteGatewayDetails { gateway_id, derived_aes128_ctr_blake3_hmac_keys, gateway_owner_address, gateway_listener, + wg_tun_address, }) } } @@ -193,6 +214,7 @@ impl From for RawRemoteGatewayDetails { .to_base58_string(), gateway_owner_address: value.gateway_owner_address.to_string(), gateway_listener: value.gateway_listener.to_string(), + wg_tun_address: value.wg_tun_address.map(|addr| addr.to_string()), } } } @@ -208,6 +230,8 @@ pub struct RemoteGatewayDetails { pub gateway_owner_address: AccountId, pub gateway_listener: Url, + + pub wg_tun_address: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/common/client-core/src/cli_helpers/client_init.rs b/common/client-core/src/cli_helpers/client_init.rs index 93ad844b08..e878a12028 100644 --- a/common/client-core/src/cli_helpers/client_init.rs +++ b/common/client-core/src/cli_helpers/client_init.rs @@ -236,6 +236,7 @@ where specification: selection_spec, available_gateways, overwrite_data: common_args.force_register_gateway, + wg_tun_address: None, }; let init_details = diff --git a/common/client-core/src/client/base_client/mod.rs b/common/client-core/src/client/base_client/mod.rs index 7870c5cc39..42adcf2da4 100644 --- a/common/client-core/src/client/base_client/mod.rs +++ b/common/client-core/src/client/base_client/mod.rs @@ -35,7 +35,7 @@ use crate::init::{ }; use crate::{config, spawn_future}; use futures::channel::mpsc; -use log::{debug, error, info}; +use log::{debug, error, info, warn}; use nym_bandwidth_controller::BandwidthController; use nym_client_core_gateways_storage::{GatewayDetails, GatewaysDetailsStore}; use nym_credential_storage::storage::Storage as CredentialStorage; @@ -43,6 +43,7 @@ use nym_crypto::asymmetric::encryption; use nym_gateway_client::{ AcknowledgementReceiver, GatewayClient, GatewayConfig, MixnetMessageReceiver, PacketRouter, }; +use nym_network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, WG_TUN_DEVICE_ADDRESS}; use nym_sphinx::acknowledgements::AckKey; use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::addressing::nodes::NodeIdentity; @@ -179,6 +180,7 @@ pub struct BaseClientBuilder<'a, C, S: MixnetClientStorage> { dkg_query_client: Option, wait_for_gateway: bool, + wireguard_connection: bool, custom_topology_provider: Option>, custom_gateway_transceiver: Option>, shutdown: Option, @@ -201,6 +203,7 @@ where client_store, dkg_query_client, wait_for_gateway: false, + wireguard_connection: false, custom_topology_provider: None, custom_gateway_transceiver: None, shutdown: None, @@ -220,6 +223,12 @@ where self } + #[must_use] + pub fn with_wireguard_connection(mut self, wireguard_connection: bool) -> Self { + self.wireguard_connection = wireguard_connection; + self + } + #[must_use] pub fn with_topology_provider( mut self, @@ -343,6 +352,7 @@ where async fn start_gateway_client( config: &Config, + wireguard_connection: bool, initialisation_result: InitialisationResult, bandwidth_controller: Option>, packet_router: PacketRouter, @@ -358,27 +368,41 @@ where return Err(ClientCoreError::UnexpectedPersistedCustomGatewayDetails); }; - let mut gateway_client = - if let Some(existing_client) = initialisation_result.authenticated_ephemeral_client { - existing_client.upgrade(packet_router, bandwidth_controller, shutdown) + let mut gateway_client = if let Some(existing_client) = + initialisation_result.authenticated_ephemeral_client + { + existing_client.upgrade(packet_router, bandwidth_controller, shutdown) + } else { + let gateway_listener = if wireguard_connection { + if let Some(tun_address) = details.wg_tun_address { + tun_address.to_string() + } else { + let default = + format!("ws://{WG_TUN_DEVICE_ADDRESS}:{DEFAULT_CLIENT_LISTENING_PORT}"); + warn!("gateway {} does not have tun device address set. defaulting to '{default}'", details.gateway_id); + default + } } else { - let cfg = GatewayConfig::new( - details.gateway_id, - Some(details.gateway_owner_address.to_string()), - details.gateway_listener.to_string(), - ); - GatewayClient::new( - cfg, - managed_keys.identity_keypair(), - Some(details.derived_aes128_ctr_blake3_hmac_keys), - packet_router, - bandwidth_controller, - shutdown, - ) - .with_disabled_credentials_mode(config.client.disabled_credentials_mode) - .with_response_timeout(config.debug.gateway_connection.gateway_response_timeout) + details.gateway_listener.to_string() }; + let cfg = GatewayConfig::new( + details.gateway_id, + Some(details.gateway_owner_address.to_string()), + gateway_listener, + ); + GatewayClient::new( + cfg, + managed_keys.identity_keypair(), + Some(details.derived_aes128_ctr_blake3_hmac_keys), + packet_router, + bandwidth_controller, + shutdown, + ) + .with_disabled_credentials_mode(config.client.disabled_credentials_mode) + .with_response_timeout(config.debug.gateway_connection.gateway_response_timeout) + }; + gateway_client .authenticate_and_start() .await @@ -396,6 +420,7 @@ where async fn setup_gateway_transceiver( custom_gateway_transceiver: Option>, config: &Config, + wireguard_connection: bool, initialisation_result: InitialisationResult, bandwidth_controller: Option>, packet_router: PacketRouter, @@ -424,6 +449,7 @@ where // otherwise, setup normal gateway client, etc let gateway_client = Self::start_gateway_client( config, + wireguard_connection, initialisation_result, bandwidth_controller, packet_router, @@ -680,6 +706,7 @@ where let gateway_transceiver = Self::setup_gateway_transceiver( self.custom_gateway_transceiver, self.config, + self.wireguard_connection, init_res, bandwidth_controller, gateway_packet_router, diff --git a/common/client-core/src/client/base_client/storage/helpers.rs b/common/client-core/src/client/base_client/storage/helpers.rs index c6f2cf65c2..d760499c31 100644 --- a/common/client-core/src/client/base_client/storage/helpers.rs +++ b/common/client-core/src/client/base_client/storage/helpers.rs @@ -5,7 +5,7 @@ use crate::client::key_manager::persistence::KeyStore; use crate::client::key_manager::ClientKeys; use crate::error::ClientCoreError; use nym_client_core_gateways_storage::{ - GatewayRegistration, GatewaysDetailsStore, GatewaysDetailsStoreExt, + ActiveGateway, GatewayRegistration, GatewaysDetailsStore, GatewaysDetailsStoreExt, }; use nym_crypto::asymmetric::identity; @@ -59,18 +59,16 @@ where pub async fn load_active_gateway_details( details_store: &D, -) -> Result +) -> Result where D: GatewaysDetailsStore, D::StorageError: Send + Sync + 'static, { - details_store - .active_gateway() - .await - .map_err(|source| ClientCoreError::GatewaysDetailsStoreError { + details_store.active_gateway().await.map_err(|source| { + ClientCoreError::GatewaysDetailsStoreError { source: Box::new(source), - })? - .ok_or(ClientCoreError::NoActiveGatewaySet) + } + }) } pub async fn load_gateway_details( diff --git a/common/client-core/src/client/base_client/storage/migration_helpers.rs b/common/client-core/src/client/base_client/storage/migration_helpers.rs index c81011a37f..83869b5796 100644 --- a/common/client-core/src/client/base_client/storage/migration_helpers.rs +++ b/common/client-core/src/client/base_client/storage/migration_helpers.rs @@ -102,6 +102,7 @@ pub mod v1_1_33 { message: format!("the stored gateway listener address was malformed: {err}"), } })?, + wg_tun_address: None, })) } diff --git a/common/client-core/src/init/mod.rs b/common/client-core/src/init/mod.rs index d82faaba69..6bc0fa8e91 100644 --- a/common/client-core/src/init/mod.rs +++ b/common/client-core/src/init/mod.rs @@ -23,6 +23,7 @@ use nym_topology::gateway; use rand::rngs::OsRng; use rand::{CryptoRng, RngCore}; use serde::Serialize; +use std::net::IpAddr; pub mod helpers; pub mod types; @@ -46,19 +47,13 @@ where }) } -// fn ensure_valid_details( -// details: &PersistedGatewayDetails, -// loaded_keys: &ManagedKeys, -// ) -> Result<(), ClientCoreError> { -// details.validate(loaded_keys.gateway_shared_key().as_deref()) -// } - async fn setup_new_gateway( key_store: &K, details_store: &D, overwrite_data: bool, selection_specification: GatewaySelectionSpecification, available_gateways: Vec, + wg_tun_ip_address: Option, ) -> Result where K: KeyStore, @@ -76,19 +71,19 @@ where let selected_gateway = match selection_specification { GatewaySelectionSpecification::UniformRemote { must_use_tls } => { let gateway = uniformly_random_gateway(&mut rng, &available_gateways, must_use_tls)?; - SelectedGateway::from_topology_node(gateway, must_use_tls)? + SelectedGateway::from_topology_node(gateway, wg_tun_ip_address, must_use_tls)? } GatewaySelectionSpecification::RemoteByLatency { must_use_tls } => { let gateway = choose_gateway_by_latency(&mut rng, &available_gateways, must_use_tls).await?; - SelectedGateway::from_topology_node(gateway, must_use_tls)? + SelectedGateway::from_topology_node(gateway, wg_tun_ip_address, must_use_tls)? } GatewaySelectionSpecification::Specified { must_use_tls, identity, } => { let gateway = get_specified_gateway(&identity, &available_gateways, must_use_tls)?; - SelectedGateway::from_topology_node(gateway, must_use_tls)? + SelectedGateway::from_topology_node(gateway, wg_tun_ip_address, must_use_tls)? } GatewaySelectionSpecification::Custom { gateway_identity, @@ -110,18 +105,23 @@ where gateway_id, gateway_owner_address, gateway_listener, + wg_tun_address, } => { // if we're using a 'normal' gateway setup, do register let our_identity = client_keys.identity_keypair(); + + // if wg address is set, use that one + let url = wg_tun_address.clone().unwrap_or(gateway_listener.clone()); + let registration = - helpers::register_with_gateway(gateway_id, gateway_listener.clone(), our_identity) - .await?; + helpers::register_with_gateway(gateway_id, url, our_identity).await?; ( GatewayDetails::new_remote( gateway_id, registration.shared_keys, gateway_owner_address, gateway_listener, + wg_tun_address, ), Some(registration.authenticated_ephemeral_client), ) @@ -161,7 +161,10 @@ where let loaded_details = if let Some(gateway_id) = gateway_id { load_gateway_details(details_store, &gateway_id).await? } else { - load_active_gateway_details(details_store).await? + load_active_gateway_details(details_store) + .await? + .registration + .ok_or(ClientCoreError::NoActiveGatewaySet)? }; let loaded_keys = load_client_keys(key_store).await?; @@ -205,6 +208,7 @@ where specification, available_gateways, overwrite_data, + wg_tun_address, } => { setup_new_gateway( key_store, @@ -212,6 +216,7 @@ where overwrite_data, specification, available_gateways, + wg_tun_address, ) .await } diff --git a/common/client-core/src/init/types.rs b/common/client-core/src/init/types.rs index 7c0f1ae71b..cfbac41121 100644 --- a/common/client-core/src/init/types.rs +++ b/common/client-core/src/init/types.rs @@ -6,6 +6,7 @@ use crate::client::key_manager::ClientKeys; use crate::config::Config; use crate::error::ClientCoreError; use crate::init::{setup_gateway, use_loaded_gateway_details}; +use log::info; use nym_client_core_gateways_storage::{ GatewayRegistration, GatewaysDetailsStore, RemoteGatewayDetails, }; @@ -18,6 +19,7 @@ use nym_validator_client::client::IdentityKey; use nym_validator_client::nyxd::AccountId; use serde::Serialize; use std::fmt::Display; +use std::net::IpAddr; use std::str::FromStr; use std::sync::Arc; use time::OffsetDateTime; @@ -30,6 +32,8 @@ pub enum SelectedGateway { gateway_owner_address: AccountId, gateway_listener: Url, + + wg_tun_address: Option, }, Custom { gateway_id: identity::PublicKey, @@ -37,9 +41,36 @@ pub enum SelectedGateway { }, } +fn wg_tun_address( + tun_ip: Option, + gateway: &gateway::Node, +) -> Result, ClientCoreError> { + let Some(tun_ip) = tun_ip else { + return Ok(None); + }; + + // log this so we'd remember about it if we ever decided to actually use that port + if gateway.clients_wss_port.is_some() { + info!( + "gateway {} exposes wss but for wireguard we're going to use ws", + gateway.identity_key + ); + } + + let raw_url = format!("ws://{tun_ip}:{}", gateway.clients_ws_port); + Ok(Some(raw_url.as_str().parse().map_err(|source| { + ClientCoreError::MalformedListener { + gateway_id: gateway.identity_key.to_base58_string(), + raw_listener: raw_url, + source, + } + })?)) +} + impl SelectedGateway { pub fn from_topology_node( node: gateway::Node, + wg_tun_ip_address: Option, must_use_tls: bool, ) -> Result { let gateway_listener = if must_use_tls { @@ -51,6 +82,8 @@ impl SelectedGateway { node.clients_address() }; + let wg_tun_address = wg_tun_address(wg_tun_ip_address, &node)?; + let gateway_owner_address = AccountId::from_str(&node.owner).map_err(|source| { ClientCoreError::MalformedGatewayOwnerAccountAddress { gateway_id: node.identity_key.to_base58_string(), @@ -70,6 +103,7 @@ impl SelectedGateway { gateway_id: node.identity_key, gateway_owner_address, gateway_listener, + wg_tun_address, }) } @@ -209,6 +243,12 @@ pub enum GatewaySetup { /// Specifies whether old data should be overwritten whilst setting up new gateway client. overwrite_data: bool, + + /// Implicitly specify whether the chosen gateway must use wireguard mode by setting the tun address. + /// + /// Currently this is imperfect solution as I'd imagine this address could vary from gateway to gateway + /// so perhaps it should be part of gateway::Node struct + wg_tun_address: Option, }, ReuseConnection { @@ -235,6 +275,19 @@ impl GatewaySetup { } } + /// new gateway setup performed by each client that's inbuilt in a gateway (like NR or IPR) + pub fn new_inbuilt(identity: identity::PublicKey) -> Self { + GatewaySetup::New { + specification: GatewaySelectionSpecification::Custom { + gateway_identity: identity.to_base58_string(), + additional_data: None, + }, + available_gateways: vec![], + overwrite_data: false, + wg_tun_address: None, + } + } + pub async fn try_setup( self, key_store: &K, diff --git a/common/network-defaults/src/lib.rs b/common/network-defaults/src/lib.rs index 17968821bb..55037c50ee 100644 --- a/common/network-defaults/src/lib.rs +++ b/common/network-defaults/src/lib.rs @@ -4,6 +4,7 @@ use crate::var_names::{DEPRECATED_API_VALIDATOR, DEPRECATED_NYMD_VALIDATOR, NYM_API, NYXD}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +use std::net::{IpAddr, Ipv4Addr}; use std::path::Path; use std::{ env::{var, VarError}, @@ -521,4 +522,5 @@ pub const WG_PORT: u16 = 51822; // The interface used to route traffic pub const WG_TUN_BASE_NAME: &str = "nymwg"; pub const WG_TUN_DEVICE_ADDRESS: &str = "10.1.0.1"; +pub const WG_TUN_DEVICE_IP_ADDRESS: IpAddr = IpAddr::V4(Ipv4Addr::new(10, 1, 0, 1)); pub const WG_TUN_DEVICE_NETMASK: &str = "255.255.255.0"; diff --git a/gateway/src/commands/helpers.rs b/gateway/src/commands/helpers.rs index 8ea29169f2..e12cc337f1 100644 --- a/gateway/src/commands/helpers.rs +++ b/gateway/src/commands/helpers.rs @@ -16,10 +16,7 @@ use nym_network_defaults::mainnet; use nym_network_defaults::var_names::NYXD; use nym_network_defaults::var_names::{BECH32_PREFIX, NYM_API, STATISTICS_SERVICE_DOMAIN_ADDRESS}; use nym_network_requester::config::BaseClientConfig; -use nym_network_requester::{ - setup_fs_gateways_storage, setup_gateway, GatewaySelectionSpecification, GatewaySetup, - OnDiskKeys, -}; +use nym_network_requester::{setup_fs_gateways_storage, setup_gateway, GatewaySetup, OnDiskKeys}; use nym_types::gateway::{GatewayIpPacketRouterDetails, GatewayNetworkRequesterDetails}; use nym_validator_client::nyxd::AccountId; use std::net::IpAddr; @@ -251,7 +248,7 @@ pub(crate) fn override_ip_packet_router_config( // disable poisson rate in the BASE client if the IPR option is enabled if cfg.ip_packet_router.disable_poisson_rate { - log::info!("Disabling poisson rate for ip packet router"); + info!("Disabling poisson rate for ip packet router"); cfg.set_no_poisson_process(); } @@ -280,14 +277,7 @@ pub(crate) async fn initialise_local_network_requester( // gateway setup here is way simpler as we're 'connecting' to ourselves let init_res = setup_gateway( - GatewaySetup::New { - specification: GatewaySelectionSpecification::Custom { - gateway_identity: identity.to_base58_string(), - additional_data: Default::default(), - }, - available_gateways: vec![], - overwrite_data: false, - }, + GatewaySetup::new_inbuilt(identity), &key_store, &details_store, ) @@ -353,14 +343,7 @@ pub(crate) async fn initialise_local_ip_packet_router( // gateway setup here is way simpler as we're 'connecting' to ourselves let init_res = setup_gateway( - GatewaySetup::New { - specification: GatewaySelectionSpecification::Custom { - gateway_identity: identity.to_base58_string(), - additional_data: Default::default(), - }, - available_gateways: vec![], - overwrite_data: false, - }, + GatewaySetup::new_inbuilt(identity), &key_store, &details_store, ) diff --git a/sdk/rust/nym-sdk/src/mixnet.rs b/sdk/rust/nym-sdk/src/mixnet.rs index 41fb99998d..23c951119b 100644 --- a/sdk/rust/nym-sdk/src/mixnet.rs +++ b/sdk/rust/nym-sdk/src/mixnet.rs @@ -56,7 +56,7 @@ pub use nym_client_core::{ }, topology_control::geo_aware_provider::{CountryGroup, GeoAwareTopologyProvider}, }, - config::{GatewayEndpointConfig, GroupBy}, + config::GroupBy, }; pub use nym_credential_storage::{ ephemeral_storage::EphemeralStorage as EphemeralCredentialStorage, diff --git a/sdk/rust/nym-sdk/src/mixnet/client.rs b/sdk/rust/nym-sdk/src/mixnet/client.rs index 56666b1c0d..de0299b314 100644 --- a/sdk/rust/nym-sdk/src/mixnet/client.rs +++ b/sdk/rust/nym-sdk/src/mixnet/client.rs @@ -11,25 +11,27 @@ use crate::{Error, Result}; use futures::channel::mpsc; use futures::StreamExt; use log::warn; +use nym_client_core::client::base_client::storage::helpers::get_all_registered_identities; use nym_client_core::client::base_client::storage::{ Ephemeral, GatewaysDetailsStore, MixnetClientStorage, OnDiskPersistent, }; use nym_client_core::client::base_client::BaseClient; use nym_client_core::client::key_manager::persistence::KeyStore; +use nym_client_core::client::{ + base_client::BaseClientBuilder, replies::reply_storage::ReplyStorageBackend, +}; use nym_client_core::config::DebugConfig; +use nym_client_core::error::ClientCoreError; use nym_client_core::init::helpers::current_gateways; use nym_client_core::init::types::{GatewaySelectionSpecification, GatewaySetup}; -use nym_client_core::{ - client::{base_client::BaseClientBuilder, replies::reply_storage::ReplyStorageBackend}, - config::GatewayEndpointConfig, -}; -use nym_network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, WG_TUN_DEVICE_ADDRESS}; +use nym_network_defaults::WG_TUN_DEVICE_IP_ADDRESS; use nym_socks5_client_core::config::Socks5; use nym_task::manager::TaskStatus; use nym_task::{TaskClient, TaskHandle}; use nym_topology::provider_trait::TopologyProvider; use nym_validator_client::{nyxd, QueryHttpRpcNyxdClient}; use rand::rngs::OsRng; +use std::net::IpAddr; use std::path::Path; use std::path::PathBuf; use url::Url; @@ -41,7 +43,6 @@ const DEFAULT_NUMBER_OF_SURBS: u32 = 10; pub struct MixnetClientBuilder { config: Config, storage_paths: Option, - gateway_config: Option, socks5_config: Option, wireguard_mode: bool, @@ -78,7 +79,6 @@ impl MixnetClientBuilder { Ok(MixnetClientBuilder { config: Default::default(), storage_paths: None, - gateway_config: None, socks5_config: None, wireguard_mode: false, wait_for_gateway: false, @@ -98,6 +98,7 @@ impl MixnetClientBuilder where S: MixnetClientStorage + 'static, S::ReplyStore: Send + Sync, + S::GatewaysDetailsStore: Sync, ::StorageError: Sync + Send, ::StorageError: Send + Sync, ::StorageError: Send + Sync, @@ -109,7 +110,6 @@ where MixnetClientBuilder { config: Default::default(), storage_paths: None, - gateway_config: None, socks5_config: None, wireguard_mode: false, wait_for_gateway: false, @@ -128,7 +128,6 @@ where MixnetClientBuilder { config: self.config, storage_paths: self.storage_paths, - gateway_config: self.gateway_config, socks5_config: self.socks5_config, wireguard_mode: self.wireguard_mode, wait_for_gateway: self.wait_for_gateway, @@ -303,6 +302,7 @@ impl DisconnectedMixnetClient where S: MixnetClientStorage + 'static, S::ReplyStore: Send + Sync, + S::GatewaysDetailsStore: Sync, ::StorageError: Sync + Send, ::StorageError: Send + Sync, ::StorageError: Send + Sync, @@ -410,40 +410,58 @@ where .collect() } - /// Client keys are generated at client creation if none were found. The gateway shared - /// key, however, is created during the gateway registration handshake, so it might not - /// necessarily be available. - /// Furthermore, it has to be coupled with particular gateway's config. - async fn has_valid_gateway_info(&self) -> bool { - let keys = match self.storage.key_store().load_keys().await { - Ok(keys) => keys, + fn wireguard_tun_address(&self) -> Option { + // currently use a hardcoded value here, but perhaps we should change that later + if self.wireguard_mode { + Some(WG_TUN_DEVICE_IP_ADDRESS) + } else { + None + } + } + + async fn new_gateway_setup(&self) -> Result { + let nym_api_endpoints = self.get_api_endpoints(); + + let selection_spec = GatewaySelectionSpecification::new( + self.config.user_chosen_gateway.clone(), + None, + self.force_tls, + ); + + let mut rng = OsRng; + let available_gateways = current_gateways(&mut rng, &nym_api_endpoints).await?; + + Ok(GatewaySetup::New { + specification: selection_spec, + available_gateways, + overwrite_data: !self.config.key_mode.is_keep(), + wg_tun_address: self.wireguard_tun_address(), + }) + } + + /// Check if the client already has an active gateway enabled. + async fn has_active_gateway(&self) -> bool { + let storage = self.storage.gateway_details_store(); + + if storage.active_gateway().await.is_ok() { + return true; + } + + match get_all_registered_identities(storage).await { Err(err) => { - warn!("failed to load stored keys: {err}"); - return false; + warn!("failed to query for all registered gateways: {err}") } - }; + Ok(all_ids) => { + if !all_ids.is_empty() { + warn!("this client doesn't have an active gateway set, however, it's already registered with the following gateways (consider making one of them active):"); + for id in all_ids { + warn!("{id}") + } + } + } + } - todo!() - - // let gateway_details = match self - // .storage - // .gateway_details_store() - // .load_gateway_details() - // .await - // { - // Ok(details) => details, - // Err(err) => { - // warn!("failed to load stored gateway details: {err}"); - // return false; - // } - // }; - // - // if let Err(err) = gateway_details.validate(keys.gateway_shared_key().as_deref()) { - // warn!("stored key verification failure: {err}"); - // return false; - // } - // - // true + false } /// Register with a gateway. If a gateway is provided in the config then that will try to be @@ -460,24 +478,10 @@ where log::debug!("Registering with gateway"); - let api_endpoints = self.get_api_endpoints(); - - let gateway_setup = if self.has_valid_gateway_info().await { + let gateway_setup = if self.has_active_gateway().await { GatewaySetup::MustLoad { gateway_id: None } } else { - let selection_spec = GatewaySelectionSpecification::new( - self.config.user_chosen_gateway.clone(), - None, - self.force_tls, - ); - - let mut rng = OsRng; - - GatewaySetup::New { - specification: selection_spec, - available_gateways: current_gateways(&mut rng, &api_endpoints).await?, - overwrite_data: !self.config.key_mode.is_keep(), - } + self.new_gateway_setup().await? }; // this will perform necessary key and details load and optional store @@ -525,60 +529,70 @@ where .config .as_base_client_config(nyxd_endpoints, nym_api_endpoints.clone()); - let known_gateway = self.has_valid_gateway_info().await; + let known_gateway = self.has_active_gateway().await; - let mut base_builder: BaseClientBuilder<_, _> = if !known_gateway { - let selection_spec = GatewaySelectionSpecification::new( - self.config.user_chosen_gateway, - None, - self.force_tls, - ); - - let mut rng = OsRng; - let mut available_gateways = current_gateways(&mut rng, &nym_api_endpoints).await?; - if self.wireguard_mode { - available_gateways - .iter_mut() - .for_each(|node| node.host = WG_TUN_DEVICE_ADDRESS.parse().unwrap()); - } - let setup = GatewaySetup::New { - specification: selection_spec, - available_gateways, - overwrite_data: !self.config.key_mode.is_keep(), - }; - - BaseClientBuilder::new(&base_config, self.storage, self.dkg_query_client) - .with_wait_for_gateway(self.wait_for_gateway) - .with_gateway_setup(setup) - } else if self.wireguard_mode { - todo!() - // if let Ok(PersistedGatewayDetails::Default(mut config)) = self - // .storage - // .gateway_details_store() - // .load_gateway_details() - // .await - // { - // config.details.gateway_listener = format!( - // "ws://{}:{}", - // WG_TUN_DEVICE_ADDRESS, DEFAULT_CLIENT_LISTENING_PORT - // ); - // if let Err(e) = self - // .storage - // .gateway_details_store() - // .store_gateway_details(&PersistedGatewayDetails::Default(config)) - // .await - // { - // warn!("Could not switch to using wireguard mode - {:?}", e); - // } - // } else { - // warn!("Storage type not supported with wireguard mode"); - // } - // BaseClientBuilder::new(&base_config, self.storage, self.dkg_query_client) - // .with_wait_for_gateway(self.wait_for_gateway) + // if we have a known gateway, don't bother doing all of those queries + let gateway_setup = if known_gateway { + None } else { + Some(self.new_gateway_setup().await?) + }; + + let mut base_builder: BaseClientBuilder<_, _> = BaseClientBuilder::new(&base_config, self.storage, self.dkg_query_client) .with_wait_for_gateway(self.wait_for_gateway) - }; + .with_wireguard_connection(self.wireguard_mode); + + if !known_gateway { + // safety: `gateway_setup` is always set whenever `known_gateway` is false + base_builder = base_builder.with_gateway_setup(gateway_setup.unwrap()); + } + + // let mut base_builder: BaseClientBuilder<_, _> = if !known_gateway { + // // we need to setup a new gateway + // let setup = self.new_gateway_setup().await; + // + // BaseClientBuilder::new(&base_config, self.storage, self.dkg_query_client) + // .with_wait_for_gateway(self.wait_for_gateway) + // .with_gateway_setup(setup) + // // } else if self.wireguard_mode { + // // // load current active gateway in wireguard mode + // // details_store.set_wireguard_mode(true).await?; + // // + // // if let Ok(PersistedGatewayDetails::Default(mut config)) = self + // // .storage + // // .gateway_details_store() + // // .load_gateway_details() + // // .await + // // { + // // config.details.gateway_listener = format!( + // // "ws://{}:{}", + // // WG_TUN_DEVICE_ADDRESS, DEFAULT_CLIENT_LISTENING_PORT + // // ); + // // if let Err(e) = self + // // .storage + // // .gateway_details_store() + // // .store_gateway_details(&PersistedGatewayDetails::Default(config)) + // // .await + // // { + // // warn!("Could not switch to using wireguard mode - {:?}", e); + // // } + // // } else { + // // warn!("Storage type not supported with wireguard mode"); + // // } + // // BaseClientBuilder::new(&base_config, self.storage, self.dkg_query_client) + // // .with_wait_for_gateway(self.wait_for_gateway) + // } else { + // // load current active gateway in non-wireguard mode + // + // // make sure our current storage mode matches the desired wg mode + // details_store + // .set_wireguard_mode(self.wireguard_mode) + // .await?; + // + // BaseClientBuilder::new(&base_config, self.storage, self.dkg_query_client) + // .with_wait_for_gateway(self.wait_for_gateway) + // }; if let Some(topology_provider) = self.custom_topology_provider { base_builder = base_builder.with_topology_provider(topology_provider);