From ee2026e53d21dc0561fc9b0d159c849e8bec6ff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Fri, 6 Oct 2023 11:10:44 +0100 Subject: [PATCH] clients using self-described gateway information --- Cargo.lock | 2 + clients/native/src/commands/init.rs | 1 - clients/socks5/src/commands/init.rs | 1 - common/bin-common/Cargo.toml | 2 + .../bin-common/src/build_information/mod.rs | 1 + .../topology_control/nym_api_provider.rs | 2 +- common/client-core/src/config/mod.rs | 19 +--- common/client-core/src/init/mod.rs | 15 ++- common/client-core/src/init/types.rs | 28 ++---- .../validator-client/src/client.rs | 8 +- .../validator-client/src/nym_api/mod.rs | 16 +-- .../validator-client/src/nym_api/routes.rs | 1 + common/topology/Cargo.toml | 4 + common/topology/src/gateway.rs | 98 ++++++++++++++++++- common/topology/src/lib.rs | 32 +++++- common/topology/src/serde.rs | 17 +++- common/wasm/client-core/src/helpers.rs | 10 +- nym-api/nym-api-requests/src/models.rs | 5 +- nym-api/src/node_describe_cache/mod.rs | 10 ++ nym-connect/desktop/Cargo.lock | 2 + .../desktop/src-tauri/src/config/mod.rs | 3 +- nym-node/nym-node-requests/Cargo.toml | 2 +- nym-node/nym-node-requests/src/api/client.rs | 5 + sdk/rust/nym-sdk/src/mixnet/client.rs | 9 +- .../network-requester/src/cli/init.rs | 1 - 25 files changed, 205 insertions(+), 89 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 98aae9c4ed..787df8bf3f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6072,6 +6072,7 @@ dependencies = [ "opentelemetry", "opentelemetry-jaeger", "pretty_env_logger", + "schemars", "semver 0.11.0", "serde", "serde_json", @@ -7338,6 +7339,7 @@ dependencies = [ "async-trait", "bs58 0.4.0", "log", + "nym-api-requests", "nym-bin-common", "nym-config", "nym-crypto", diff --git a/clients/native/src/commands/init.rs b/clients/native/src/commands/init.rs index 0fc27590dd..f403a3e5dc 100644 --- a/clients/native/src/commands/init.rs +++ b/clients/native/src/commands/init.rs @@ -180,7 +180,6 @@ pub(crate) async fn execute(args: Init) -> Result<(), ClientError> { let selection_spec = GatewaySelectionSpecification::new( user_chosen_gateway_id.map(|id| id.to_base58_string()), Some(args.latency_based_selection), - false, ); // Load and potentially override config diff --git a/clients/socks5/src/commands/init.rs b/clients/socks5/src/commands/init.rs index 8adc4e891b..bde41dc68f 100644 --- a/clients/socks5/src/commands/init.rs +++ b/clients/socks5/src/commands/init.rs @@ -185,7 +185,6 @@ pub(crate) async fn execute(args: Init) -> Result<(), Socks5ClientError> { let selection_spec = GatewaySelectionSpecification::new( user_chosen_gateway_id.map(|id| id.to_base58_string()), Some(args.latency_based_selection), - false, ); // Load and potentially override config diff --git a/common/bin-common/Cargo.toml b/common/bin-common/Cargo.toml index 34cd570a60..7f9ae7fe89 100644 --- a/common/bin-common/Cargo.toml +++ b/common/bin-common/Cargo.toml @@ -15,6 +15,7 @@ clap_complete_fig = "4.0" log = { workspace = true } pretty_env_logger = "0.4.0" semver = "0.11" +schemars = { workspace = true, features = ["preserve_order"], optional = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true, optional = true } @@ -45,6 +46,7 @@ vergen = { version = "=7.4.3", default-features = false, features = [ default = [] openapi = ["utoipa"] output_format = ["serde_json"] +bin_info_schema = ["schemars"] tracing = [ "tracing-subscriber", "tracing-tree", diff --git a/common/bin-common/src/build_information/mod.rs b/common/bin-common/src/build_information/mod.rs index 34d0aaf456..06dc1da51c 100644 --- a/common/bin-common/src/build_information/mod.rs +++ b/common/bin-common/src/build_information/mod.rs @@ -82,6 +82,7 @@ impl BinaryBuildInformation { #[derive(Clone, Debug, Serialize, Deserialize)] #[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] +#[cfg_attr(feature = "bin_info_schema", derive(schemars::JsonSchema))] pub struct BinaryBuildInformationOwned { /// Provides the name of the binary, i.e. the content of `CARGO_PKG_NAME` environmental variable. pub binary_name: String, diff --git a/common/client-core/src/client/topology_control/nym_api_provider.rs b/common/client-core/src/client/topology_control/nym_api_provider.rs index 5de16663de..824294793b 100644 --- a/common/client-core/src/client/topology_control/nym_api_provider.rs +++ b/common/client-core/src/client/topology_control/nym_api_provider.rs @@ -69,7 +69,7 @@ impl NymApiTopologyProvider { Ok(mixes) => mixes, }; - let gateways = match self.validator_client.get_cached_gateways().await { + let gateways = match self.validator_client.get_cached_described_gateways().await { Err(err) => { error!("failed to get network gateways - {err}"); return None; diff --git a/common/client-core/src/config/mod.rs b/common/client-core/src/config/mod.rs index b3e8afabff..2137ec687d 100644 --- a/common/client-core/src/config/mod.rs +++ b/common/client-core/src/config/mod.rs @@ -280,30 +280,13 @@ impl GatewayEndpointConfig { .map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId) } - pub fn from_node(node: nym_topology::gateway::Node, use_tls: bool) -> Self { - // TODO: in the future this shall return a Result and explicit `use_tls` will be removed in favour of the tls info being available on the struct - if use_tls { - Self::from_topology_node_tls(node) - } else { - Self::from_topology_node_no_tls(node) - } - } - - pub fn from_topology_node_no_tls(node: nym_topology::gateway::Node) -> Self { + pub fn from_node(node: nym_topology::gateway::Node) -> Self { GatewayEndpointConfig { gateway_id: node.identity_key.to_base58_string(), gateway_listener: node.clients_address(), gateway_owner: node.owner, } } - - pub fn from_topology_node_tls(node: nym_topology::gateway::Node) -> Self { - GatewayEndpointConfig { - gateway_id: node.identity_key.to_base58_string(), - gateway_listener: node.clients_address_tls(), - gateway_owner: node.owner, - } - } } #[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)] diff --git a/common/client-core/src/init/mod.rs b/common/client-core/src/init/mod.rs index 27279cfadb..0a7ced0d1a 100644 --- a/common/client-core/src/init/mod.rs +++ b/common/client-core/src/init/mod.rs @@ -107,20 +107,17 @@ where let mut new_keys = ManagedKeys::generate_new(&mut rng); let gateway_details = match selection_specification { - GatewaySelectionSpecification::UniformRemote { must_use_tls } => { + GatewaySelectionSpecification::UniformRemote => { let gateway = uniformly_random_gateway(&mut rng, &available_gateways)?; - GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls)) + GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway)) } - GatewaySelectionSpecification::RemoteByLatency { must_use_tls } => { + GatewaySelectionSpecification::RemoteByLatency => { let gateway = choose_gateway_by_latency(&mut rng, &available_gateways).await?; - GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls)) + GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway)) } - GatewaySelectionSpecification::Specified { - must_use_tls, - identity, - } => { + GatewaySelectionSpecification::Specified { identity } => { let gateway = get_specified_gateway(&identity, &available_gateways)?; - GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls)) + GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway)) } GatewaySelectionSpecification::Custom { gateway_identity, diff --git a/common/client-core/src/init/types.rs b/common/client-core/src/init/types.rs index 3e8a88d133..f1fc8e40cc 100644 --- a/common/client-core/src/init/types.rs +++ b/common/client-core/src/init/types.rs @@ -181,17 +181,14 @@ impl From> for GatewayDetails { #[derive(Clone)] pub enum GatewaySelectionSpecification { /// Uniformly choose a random remote gateway. - UniformRemote { must_use_tls: bool }, + UniformRemote, /// Should the new, remote, gateway be selected based on latency. - RemoteByLatency { must_use_tls: bool }, + RemoteByLatency, /// Gateway with this specific identity should be chosen. // JS: I don't really like the name of this enum variant but couldn't think of anything better at the time - Specified { - must_use_tls: bool, - identity: IdentityKey, - }, + Specified { identity: IdentityKey }, // TODO: this doesn't really fit in here..., but where else to put it? /// This client has handled the selection by itself @@ -203,27 +200,18 @@ pub enum GatewaySelectionSpecification { impl Default for GatewaySelectionSpecification { fn default() -> Self { - GatewaySelectionSpecification::UniformRemote { - must_use_tls: false, - } + GatewaySelectionSpecification::UniformRemote } } impl GatewaySelectionSpecification { - pub fn new( - gateway_identity: Option, - latency_based_selection: Option, - must_use_tls: bool, - ) -> Self { + pub fn new(gateway_identity: Option, latency_based_selection: Option) -> Self { if let Some(identity) = gateway_identity { - GatewaySelectionSpecification::Specified { - identity, - must_use_tls, - } + GatewaySelectionSpecification::Specified { identity } } else if let Some(true) = latency_based_selection { - GatewaySelectionSpecification::RemoteByLatency { must_use_tls } + GatewaySelectionSpecification::RemoteByLatency } else { - GatewaySelectionSpecification::UniformRemote { must_use_tls } + GatewaySelectionSpecification::UniformRemote } } } diff --git a/common/client-libs/validator-client/src/client.rs b/common/client-libs/validator-client/src/client.rs index a3d827aa16..a04b8f05d5 100644 --- a/common/client-libs/validator-client/src/client.rs +++ b/common/client-libs/validator-client/src/client.rs @@ -11,7 +11,7 @@ use crate::{ use nym_api_requests::coconut::{ BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse, }; -use nym_api_requests::models::MixNodeBondAnnotated; +use nym_api_requests::models::{DescribedGateway, MixNodeBondAnnotated}; use nym_api_requests::models::{ GatewayCoreStatusResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse, RewardEstimationResponse, StakeSaturationResponse, @@ -275,6 +275,12 @@ impl NymApiClient { Ok(self.nym_api.get_gateways().await?) } + pub async fn get_cached_described_gateways( + &self, + ) -> Result, ValidatorClientError> { + Ok(self.nym_api.get_gateways_described().await?) + } + pub async fn get_gateway_core_status_count( &self, identity: IdentityKeyRef<'_>, diff --git a/common/client-libs/validator-client/src/nym_api/mod.rs b/common/client-libs/validator-client/src/nym_api/mod.rs index 6b0ce6b731..1d9f1d136f 100644 --- a/common/client-libs/validator-client/src/nym_api/mod.rs +++ b/common/client-libs/validator-client/src/nym_api/mod.rs @@ -8,13 +8,7 @@ use http_api_client::{ApiClient, NO_PARAMS}; use nym_api_requests::coconut::{ BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse, }; -use nym_api_requests::models::{ - ComputeRewardEstParam, GatewayBondAnnotated, GatewayCoreStatusResponse, - GatewayStatusReportResponse, GatewayUptimeHistoryResponse, InclusionProbabilityResponse, - MixNodeBondAnnotated, MixnodeCoreStatusResponse, MixnodeStatusReportResponse, - MixnodeStatusResponse, MixnodeUptimeHistoryResponse, RewardEstimationResponse, - StakeSaturationResponse, UptimeResponse, -}; +use nym_api_requests::models::{ComputeRewardEstParam, DescribedGateway, GatewayBondAnnotated, GatewayCoreStatusResponse, GatewayStatusReportResponse, GatewayUptimeHistoryResponse, InclusionProbabilityResponse, MixNodeBondAnnotated, MixnodeCoreStatusResponse, MixnodeStatusReportResponse, MixnodeStatusResponse, MixnodeUptimeHistoryResponse, RewardEstimationResponse, StakeSaturationResponse, UptimeResponse}; use nym_mixnet_contract_common::mixnode::MixNodeDetails; use nym_mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixId}; use nym_name_service_common::response::NamesListResponse; @@ -79,6 +73,14 @@ pub trait NymApiClientExt: ApiClient { .await } + async fn get_gateways_described(&self) -> Result, NymAPIError> { + self.get_json( + &[routes::API_VERSION, routes::GATEWAYS, routes::DESCRIBED], + NO_PARAMS, + ) + .await + } + async fn get_active_mixnodes(&self) -> Result, NymAPIError> { self.get_json( &[routes::API_VERSION, routes::MIXNODES, routes::ACTIVE], diff --git a/common/client-libs/validator-client/src/nym_api/routes.rs b/common/client-libs/validator-client/src/nym_api/routes.rs index fd97047b6a..df063a114d 100644 --- a/common/client-libs/validator-client/src/nym_api/routes.rs +++ b/common/client-libs/validator-client/src/nym_api/routes.rs @@ -6,6 +6,7 @@ use nym_network_defaults::NYM_API_VERSION; pub const API_VERSION: &str = NYM_API_VERSION; pub const MIXNODES: &str = "mixnodes"; pub const GATEWAYS: &str = "gateways"; +pub const DESCRIBED: &str = "described"; pub const DETAILED: &str = "detailed"; pub const DETAILED_UNFILTERED: &str = "detailed-unfiltered"; diff --git a/common/topology/Cargo.toml b/common/topology/Cargo.toml index 42c2c75094..3d43cfe229 100644 --- a/common/topology/Cargo.toml +++ b/common/topology/Cargo.toml @@ -35,6 +35,10 @@ nym-sphinx-types = { path = "../nymsphinx/types", features = ["sphinx", "outfox" nym-sphinx-routing = { path = "../nymsphinx/routing" } nym-bin-common = { path = "../bin-common" } +# I'm not sure how to feel about pulling in this dependency here... +nym-api-requests = { path = "../../nym-api/nym-api-requests" } + + # 'serializable' feature nym-config = { path = "../config", optional = true } diff --git a/common/topology/src/gateway.rs b/common/topology/src/gateway.rs index ce5acc7638..7c255cb8e1 100644 --- a/common/topology/src/gateway.rs +++ b/common/topology/src/gateway.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::{filter, NetworkAddress, NodeVersion}; +use nym_api_requests::models::DescribedGateway; use nym_crypto::asymmetric::{encryption, identity}; use nym_mixnet_contract_common::GatewayBond; use nym_sphinx_addressing::nodes::{NodeIdentity, NymNodeRoutingAddress}; @@ -10,6 +11,7 @@ use std::convert::{TryFrom, TryInto}; use std::fmt; use std::fmt::Formatter; use std::io; +use std::net::AddrParseError; use std::net::SocketAddr; use thiserror::Error; @@ -27,6 +29,17 @@ pub enum GatewayConversionError { #[source] source: io::Error, }, + + #[error("'{gateway}' has not provided any valid ip addresses")] + NoIpAddressesProvided { gateway: String }, + + #[error("'{gateway}' has provided a malformed ip address: {err}")] + MalformedIpAddress { + gateway: String, + + #[source] + err: AddrParseError, + }, } #[derive(Clone)] @@ -36,7 +49,13 @@ pub struct Node { // we're keeping this as separate resolved field since we do not want to be resolving the potential // hostname every time we want to construct a path via this node pub mix_host: SocketAddr, - pub clients_port: u16, + + // #[serde(alias = "clients_port")] + pub clients_ws_port: u16, + + // #[serde(default)] + pub clients_wss_port: Option, + pub identity_key: identity::PublicKey, pub sphinx_key: encryption::PublicKey, // TODO: or nymsphinx::PublicKey? both are x25519 pub version: NodeVersion, @@ -82,11 +101,17 @@ impl Node { } pub fn clients_address(&self) -> String { - format!("ws://{}:{}", self.host, self.clients_port) + self.clients_address_tls() + .unwrap_or_else(|| self.clients_address_no_tls()) } - pub fn clients_address_tls(&self) -> String { - format!("wss://{}:443", self.host) + pub fn clients_address_no_tls(&self) -> String { + format!("ws://{}:{}", self.host, self.clients_ws_port) + } + + pub fn clients_address_tls(&self) -> Option { + self.clients_wss_port + .map(|p| format!("wss://{}:{p}", self.host)) } } @@ -131,7 +156,8 @@ impl<'a> TryFrom<&'a GatewayBond> for Node { owner: bond.owner.as_str().to_owned(), host, mix_host, - clients_port: bond.gateway.clients_port, + clients_ws_port: bond.gateway.clients_port, + clients_wss_port: None, identity_key: identity::PublicKey::from_base58_string(&bond.gateway.identity_key)?, sphinx_key: encryption::PublicKey::from_base58_string(&bond.gateway.sphinx_key)?, version: bond.gateway.version.as_str().into(), @@ -146,3 +172,65 @@ impl TryFrom for Node { Node::try_from(&bond) } } + +impl<'a> TryFrom<&'a DescribedGateway> for Node { + type Error = GatewayConversionError; + + fn try_from(value: &'a DescribedGateway) -> Result { + let Some(ref self_described) = value.self_described else { + return (&value.bond).try_into(); + }; + + let ips = &self_described.host_information.ip_address; + if ips.is_empty() { + return Err(GatewayConversionError::NoIpAddressesProvided { + gateway: value.bond.gateway.identity_key.clone(), + }); + } + + let ips = ips + .iter() + .map(|ip| ip.parse()) + .collect::, _>>() + .map_err(|err| GatewayConversionError::MalformedIpAddress { + gateway: value.bond.gateway.identity_key.clone(), + err, + })?; + + let host = match &self_described.host_information.hostname { + None => NetworkAddress::IpAddr(ips[0]), + Some(hostname) => NetworkAddress::Hostname(hostname.clone()), + }; + + // get ip from the self-reported values so we wouldn't need to do any hostname resolution + // (which doesn't really work in wasm) + let mix_host = SocketAddr::new(ips[0], value.bond.gateway.mix_port); + + Ok(Node { + owner: value.bond.owner.as_str().to_owned(), + host, + mix_host, + clients_ws_port: self_described.mixnet_websockets.ws_port, + clients_wss_port: self_described.mixnet_websockets.wss_port, + identity_key: identity::PublicKey::from_base58_string( + &self_described.host_information.keys.ed25519, + )?, + sphinx_key: encryption::PublicKey::from_base58_string( + &self_described.host_information.keys.x25519, + )?, + version: self_described + .build_information + .build_version + .as_str() + .into(), + }) + } +} + +impl TryFrom for Node { + type Error = GatewayConversionError; + + fn try_from(value: DescribedGateway) -> Result { + Node::try_from(&value) + } +} diff --git a/common/topology/src/lib.rs b/common/topology/src/lib.rs index 0273cf4a3a..039e30eb94 100644 --- a/common/topology/src/lib.rs +++ b/common/topology/src/lib.rs @@ -19,6 +19,7 @@ use std::str::FromStr; #[cfg(feature = "serializable")] use ::serde::{Deserialize, Deserializer, Serialize, Serializer}; +use nym_api_requests::models::DescribedGateway; pub mod error; pub mod filter; @@ -403,10 +404,33 @@ impl<'de> Deserialize<'de> for NymTopology { } } -pub fn nym_topology_from_detailed( +pub trait IntoGatewayNode: TryInto +where + >::Error: Display, +{ + fn identity(&self) -> IdentityKeyRef; +} + +impl IntoGatewayNode for GatewayBond { + fn identity(&self) -> IdentityKeyRef { + &self.gateway.identity_key + } +} + +impl IntoGatewayNode for DescribedGateway { + fn identity(&self) -> IdentityKeyRef { + &self.bond.gateway.identity_key + } +} + +pub fn nym_topology_from_detailed( mix_details: Vec, - gateway_bonds: Vec, -) -> NymTopology { + gateway_bonds: Vec, +) -> NymTopology +where + G: IntoGatewayNode, + >::Error: Display, +{ let mut mixes = BTreeMap::new(); for bond in mix_details .into_iter() @@ -435,7 +459,7 @@ pub fn nym_topology_from_detailed( let mut gateways = Vec::with_capacity(gateway_bonds.len()); for bond in gateway_bonds.into_iter() { - let gate_id = bond.gateway.identity_key.clone(); + let gate_id = bond.identity().to_owned(); match bond.try_into() { Ok(gate) => gateways.push(gate), Err(err) => { diff --git a/common/topology/src/serde.rs b/common/topology/src/serde.rs index 89e943e03e..84770a4868 100644 --- a/common/topology/src/serde.rs +++ b/common/topology/src/serde.rs @@ -190,7 +190,12 @@ pub struct SerializableGateway { #[cfg_attr(feature = "wasm-serde-types", tsify(optional))] #[serde(alias = "clients_port")] - pub clients_port: Option, + #[serde(alias = "clients_ws_port")] + pub clients_ws_port: Option, + + #[cfg_attr(feature = "wasm-serde-types", tsify(optional))] + #[serde(alias = "clients_wss_port")] + pub clients_wss_port: Option, #[serde(alias = "identity_key")] pub identity_key: String, @@ -209,7 +214,9 @@ impl TryFrom for gateway::Node { let host = gateway::Node::parse_host(&value.host)?; let mix_port = value.mix_port.unwrap_or(DEFAULT_MIX_LISTENING_PORT); - let clients_port = value.clients_port.unwrap_or(DEFAULT_CLIENT_LISTENING_PORT); + let clients_ws_port = value + .clients_ws_port + .unwrap_or(DEFAULT_CLIENT_LISTENING_PORT); let version = value.version.map(|v| v.as_str().into()).unwrap_or_default(); // try to completely resolve the host in the mix situation to avoid doing it every @@ -224,7 +231,8 @@ impl TryFrom for gateway::Node { owner: value.owner, host, mix_host, - clients_port, + clients_ws_port, + clients_wss_port: value.clients_wss_port, identity_key: identity::PublicKey::from_base58_string(&value.identity_key) .map_err(GatewayConversionError::from)?, sphinx_key: encryption::PublicKey::from_base58_string(&value.sphinx_key) @@ -241,7 +249,8 @@ impl<'a> From<&'a gateway::Node> for SerializableGateway { host: value.host.to_string(), explicit_ip: Some(value.mix_host.ip()), mix_port: Some(value.mix_host.port()), - clients_port: Some(value.clients_port), + clients_ws_port: Some(value.clients_ws_port), + clients_wss_port: value.clients_wss_port, identity_key: value.identity_key.to_base58_string(), sphinx_key: value.sphinx_key.to_base58_string(), version: Some(value.version.to_string()), diff --git a/common/wasm/client-core/src/helpers.rs b/common/wasm/client-core/src/helpers.rs index 6b59d30d80..0c0a966e6f 100644 --- a/common/wasm/client-core/src/helpers.rs +++ b/common/wasm/client-core/src/helpers.rs @@ -86,15 +86,13 @@ pub fn current_network_topology(nym_api_url: String) -> Promise { pub async fn setup_gateway_wasm( client_store: &ClientStorage, - force_tls: bool, chosen_gateway: Option, gateways: &[gateway::Node], ) -> Result { let setup = if client_store.has_full_gateway_info().await? { GatewaySetup::MustLoad } else { - let selection_spec = - GatewaySelectionSpecification::new(chosen_gateway.clone(), None, force_tls); + let selection_spec = GatewaySelectionSpecification::new(chosen_gateway.clone(), None); GatewaySetup::New { specification: selection_spec, @@ -110,21 +108,19 @@ pub async fn setup_gateway_wasm( pub async fn setup_gateway_from_api( client_store: &ClientStorage, - force_tls: bool, chosen_gateway: Option, nym_apis: &[Url], ) -> Result { let mut rng = thread_rng(); let gateways = current_gateways(&mut rng, nym_apis).await?; - setup_gateway_wasm(client_store, force_tls, chosen_gateway, &gateways).await + setup_gateway_wasm(client_store, chosen_gateway, &gateways).await } pub async fn setup_from_topology( explicit_gateway: Option, - force_tls: bool, topology: &NymTopology, client_store: &ClientStorage, ) -> Result { let gateways = topology.gateways(); - setup_gateway_wasm(client_store, force_tls, explicit_gateway, gateways).await + setup_gateway_wasm(client_store, explicit_gateway, gateways).await } diff --git a/nym-api/nym-api-requests/src/models.rs b/nym-api/nym-api-requests/src/models.rs index a9c8e0d7d9..1c026950d8 100644 --- a/nym-api/nym-api-requests/src/models.rs +++ b/nym-api/nym-api-requests/src/models.rs @@ -10,7 +10,7 @@ use nym_mixnet_contract_common::{ GatewayBond, IdentityKey, Interval, MixId, MixNode, Percent, RewardedSetNodeStatus, }; use nym_node_requests::api::v1::gateway::models::WebSockets; -use nym_node_requests::api::v1::node::models::HostInformation; +use nym_node_requests::api::v1::node::models::{BinaryBuildInformationOwned, HostInformation}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::fmt::{Display, Formatter}; @@ -360,6 +360,9 @@ pub struct CirculatingSupplyResponse { pub struct NymNodeDescription { pub host_information: HostInformation, + // TODO: do we really care about ALL build info or just the version? + pub build_information: BinaryBuildInformationOwned, + // for now we only care about their ws/wss situation, nothing more pub mixnet_websockets: WebSockets, } diff --git a/nym-api/src/node_describe_cache/mod.rs b/nym-api/src/node_describe_cache/mod.rs index f50aabfcf1..ea612239b8 100644 --- a/nym-api/src/node_describe_cache/mod.rs +++ b/nym-api/src/node_describe_cache/mod.rs @@ -100,6 +100,15 @@ async fn get_gateway_description( }); } + let build_info = + client + .get_build_information() + .await + .map_err(|err| NodeDescribeCacheError::ApiFailure { + gateway: gateway.identity_key.clone(), + source: err, + })?; + let websockets = client .get_mixnet_websockets() @@ -111,6 +120,7 @@ async fn get_gateway_description( let description = NymNodeDescription { host_information: host_info.data, + build_information: build_info, mixnet_websockets: websockets, }; diff --git a/nym-connect/desktop/Cargo.lock b/nym-connect/desktop/Cargo.lock index a574a51bf3..2dc02927e3 100644 --- a/nym-connect/desktop/Cargo.lock +++ b/nym-connect/desktop/Cargo.lock @@ -3918,6 +3918,7 @@ dependencies = [ "clap_complete_fig", "log", "pretty_env_logger", + "schemars", "semver 0.11.0", "serde", "vergen", @@ -4628,6 +4629,7 @@ dependencies = [ "async-trait", "bs58 0.4.0", "log", + "nym-api-requests", "nym-bin-common", "nym-config", "nym-crypto", diff --git a/nym-connect/desktop/src-tauri/src/config/mod.rs b/nym-connect/desktop/src-tauri/src/config/mod.rs index 0b8d9b0789..96ef14c6e8 100644 --- a/nym-connect/desktop/src-tauri/src/config/mod.rs +++ b/nym-connect/desktop/src-tauri/src/config/mod.rs @@ -204,8 +204,7 @@ pub async fn init_socks5_config(provider_address: String, chosen_gateway_id: Str } let gateway_setup = if register_gateway { - let selection_spec = - GatewaySelectionSpecification::new(Some(chosen_gateway_id), None, false); + let selection_spec = GatewaySelectionSpecification::new(Some(chosen_gateway_id), None); let mut rng = rand_07::thread_rng(); let available_gateways = current_gateways(&mut rng, &config.core.base.client.nym_api_urls).await?; diff --git a/nym-node/nym-node-requests/Cargo.toml b/nym-node/nym-node-requests/Cargo.toml index 4ee8464503..9870e1b318 100644 --- a/nym-node/nym-node-requests/Cargo.toml +++ b/nym-node/nym-node-requests/Cargo.toml @@ -25,7 +25,7 @@ http-api-client = { path = "../../common/http-api-client", optional = true } ## openapi: utoipa = { workspace = true, optional = true } -nym-bin-common = { path = "../../common/bin-common" } +nym-bin-common = { path = "../../common/bin-common", features = ["bin_info_schema"] } [features] default = ["client"] diff --git a/nym-node/nym-node-requests/src/api/client.rs b/nym-node/nym-node-requests/src/api/client.rs index 26c43cd6d9..acd3fbb503 100644 --- a/nym-node/nym-node-requests/src/api/client.rs +++ b/nym-node/nym-node-requests/src/api/client.rs @@ -8,6 +8,7 @@ use async_trait::async_trait; use http_api_client::{ApiClient, HttpClientError}; pub use http_api_client::Client; +use nym_bin_common::build_information::BinaryBuildInformationOwned; pub type NymNodeApiClientError = HttpClientError; @@ -18,6 +19,10 @@ pub trait NymNodeApiClientExt: ApiClient { self.get_json_from(routes::api::v1::host_info_absolute()) .await } + + async fn get_build_information(&self) -> Result { + self.get_json_from(routes::api::v1::build_info_absolute()).await + } // TODO: implement calls for other endpoints; for now I only care about the wss async fn get_mixnet_websockets(&self) -> Result { diff --git a/sdk/rust/nym-sdk/src/mixnet/client.rs b/sdk/rust/nym-sdk/src/mixnet/client.rs index 81f6ddd730..06e878fbbc 100644 --- a/sdk/rust/nym-sdk/src/mixnet/client.rs +++ b/sdk/rust/nym-sdk/src/mixnet/client.rs @@ -419,11 +419,8 @@ where let gateway_setup = if self.has_valid_gateway_info().await { GatewaySetup::MustLoad } else { - let selection_spec = GatewaySelectionSpecification::new( - self.config.user_chosen_gateway.clone(), - None, - false, - ); + let selection_spec = + GatewaySelectionSpecification::new(self.config.user_chosen_gateway.clone(), None); let mut rng = OsRng; @@ -487,7 +484,7 @@ where if !known_gateway { let selection_spec = - GatewaySelectionSpecification::new(self.config.user_chosen_gateway, None, false); + GatewaySelectionSpecification::new(self.config.user_chosen_gateway, None); let mut rng = OsRng; let setup = GatewaySetup::New { diff --git a/service-providers/network-requester/src/cli/init.rs b/service-providers/network-requester/src/cli/init.rs index d4321846e7..bb5a7b97e3 100644 --- a/service-providers/network-requester/src/cli/init.rs +++ b/service-providers/network-requester/src/cli/init.rs @@ -171,7 +171,6 @@ pub(crate) async fn execute(args: &Init) -> Result<(), NetworkRequesterError> { let selection_spec = GatewaySelectionSpecification::new( user_chosen_gateway_id.map(|id| id.to_base58_string()), Some(args.latency_based_selection), - false, ); // Load and potentially override config