From d34c8291746184e53f387f20d3a9e4d8b7bf4f9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Mon, 1 Jul 2024 13:29:10 +0200 Subject: [PATCH 1/3] Create UserAgent that can be passed from the binary to the nym api client (#4677) * Create UserAgent that can be passed from the binary to the nym api client * TryFrom for UserAgent to HeaderValue * Add user agent for getting the list of gateways * Fix wasm client * Move clap behind feature flag in bin-common * Implement conversion to UserAgent * Set user_agent on base client in native and socks5 clients * Set user agent in socks5-listener lib * wip * wip * Deserialize cargo_triple to unknown by default * Abbreviate git hash * Remove unused import * Add missing dep, and remove dbg statements * Reorder string representation * Remove commented out code * Revert temporary log level change --- Cargo.lock | 2 + clients/native/Cargo.toml | 2 +- clients/native/src/client/mod.rs | 4 +- clients/native/src/commands/add_gateway.rs | 3 +- clients/native/src/commands/init.rs | 3 +- clients/socks5/Cargo.toml | 9 +-- clients/socks5/src/commands/add_gateway.rs | 3 +- clients/socks5/src/commands/init.rs | 3 +- clients/socks5/src/commands/run.rs | 12 +++- common/bin-common/Cargo.toml | 9 +-- .../bin-common/src/build_information/mod.rs | 16 +++++ common/bin-common/src/lib.rs | 4 +- .../src/cli_helpers/client_add_gateway.rs | 9 ++- .../src/cli_helpers/client_init.rs | 5 +- .../client-core/src/client/base_client/mod.rs | 13 +++- .../topology_control/nym_api_provider.rs | 21 ++++-- common/client-core/src/init/helpers.rs | 8 ++- .../validator-client/src/client.rs | 11 +++ .../client-libs/validator-client/src/lib.rs | 1 + common/http-api-client/Cargo.toml | 2 + common/http-api-client/src/lib.rs | 4 ++ common/http-api-client/src/user_agent.rs | 67 ++++++++++++++++++ common/socks5-client-core/src/lib.rs | 14 +++- common/wasm/client-core/src/helpers.rs | 2 +- mixnode/Cargo.toml | 2 +- sdk/lib/socks5-listener/src/lib.rs | 7 +- sdk/rust/nym-sdk/src/lib.rs | 1 + sdk/rust/nym-sdk/src/mixnet/client.rs | 68 ++++++------------- service-providers/ip-packet-router/Cargo.toml | 2 +- .../ip-packet-router/src/cli/add_gateway.rs | 2 +- .../ip-packet-router/src/cli/init.rs | 2 +- .../ip-packet-router/src/mixnet_client.rs | 2 + .../network-requester/src/cli/add_gateway.rs | 2 +- .../network-requester/src/cli/init.rs | 2 +- 34 files changed, 231 insertions(+), 86 deletions(-) create mode 100644 common/http-api-client/src/user_agent.rs diff --git a/Cargo.lock b/Cargo.lock index 2bddb76352..68d24153a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4623,6 +4623,7 @@ version = "0.1.0" dependencies = [ "async-trait", "http 1.1.0", + "nym-bin-common", "reqwest 0.12.4", "serde", "serde_json", @@ -5232,6 +5233,7 @@ dependencies = [ "nym-socks5-client-core", "nym-sphinx", "nym-topology", + "nym-validator-client", "rand 0.8.5", "serde", "serde_json", diff --git a/clients/native/Cargo.toml b/clients/native/Cargo.toml index 57beefe4f7..d287fa5e86 100644 --- a/clients/native/Cargo.toml +++ b/clients/native/Cargo.toml @@ -37,7 +37,7 @@ zeroize = { workspace = true } ## internal nym-bandwidth-controller = { path = "../../common/bandwidth-controller" } -nym-bin-common = { path = "../../common/bin-common", features = ["output_format"] } +nym-bin-common = { path = "../../common/bin-common", features = ["output_format", "clap"] } nym-client-core = { path = "../../common/client-core", features = ["fs-surb-storage", "fs-gateways-storage", "cli"] } nym-config = { path = "../../common/config" } nym-credential-storage = { path = "../../common/credential-storage" } diff --git a/clients/native/src/client/mod.rs b/clients/native/src/client/mod.rs index f262d5e26b..f9ddefbff2 100644 --- a/clients/native/src/client/mod.rs +++ b/clients/native/src/client/mod.rs @@ -106,8 +106,10 @@ impl SocketClient { }; let storage = self.initialise_storage().await?; + let user_agent = nym_bin_common::bin_info!().into(); - let mut base_client = BaseClientBuilder::new(&self.config.base, storage, dkg_query_client); + let mut base_client = BaseClientBuilder::new(&self.config.base, storage, dkg_query_client) + .with_user_agent(user_agent); if let Some(custom_mixnet) = &self.custom_mixnet { base_client = base_client.with_stored_topology(custom_mixnet)?; diff --git a/clients/native/src/commands/add_gateway.rs b/clients/native/src/commands/add_gateway.rs index 254839af04..2aa34596ed 100644 --- a/clients/native/src/commands/add_gateway.rs +++ b/clients/native/src/commands/add_gateway.rs @@ -22,8 +22,9 @@ impl AsRef for Args { } pub(crate) async fn execute(args: Args) -> Result<(), ClientError> { + let user_agent = nym_bin_common::bin_info!().into(); let output = args.output; - let res = add_gateway::(args).await?; + let res = add_gateway::(args, Some(user_agent)).await?; println!("{}", output.format(&res)); Ok(()) diff --git a/clients/native/src/commands/init.rs b/clients/native/src/commands/init.rs index b71b168b03..209d1a556a 100644 --- a/clients/native/src/commands/init.rs +++ b/clients/native/src/commands/init.rs @@ -114,8 +114,9 @@ impl Display for InitResults { pub(crate) async fn execute(args: Init) -> Result<(), ClientError> { eprintln!("Initialising client..."); + let user_agent = nym_bin_common::bin_info!().into(); let output = args.output; - let res = initialise_client::(args).await?; + let res = initialise_client::(args, Some(user_agent)).await?; let init_results = InitResults::new(res); println!("{}", output.format(&init_results)); diff --git a/clients/socks5/Cargo.toml b/clients/socks5/Cargo.toml index a77c01ab01..ffa16c76b9 100644 --- a/clients/socks5/Cargo.toml +++ b/clients/socks5/Cargo.toml @@ -25,17 +25,18 @@ zeroize = { workspace = true } nym-bin-common = { path = "../../common/bin-common", features = ["output_format"] } nym-client-core = { path = "../../common/client-core", features = ["fs-surb-storage", "fs-gateways-storage", "cli"] } nym-config = { path = "../../common/config" } +nym-credential-storage = { path = "../../common/credential-storage" } nym-credentials = { path = "../../common/credentials" } nym-crypto = { path = "../../common/crypto" } nym-gateway-requests = { path = "../../gateway/gateway-requests" } -nym-credential-storage = { path = "../../common/credential-storage" } +nym-id = { path = "../../common/nym-id" } nym-network-defaults = { path = "../../common/network-defaults" } -nym-sphinx = { path = "../../common/nymsphinx" } nym-ordered-buffer = { path = "../../common/socks5/ordered-buffer" } nym-pemstore = { path = "../../common/pemstore" } -nym-topology = { path = "../../common/topology" } nym-socks5-client-core = { path = "../../common/socks5-client-core" } -nym-id = { path = "../../common/nym-id" } +nym-sphinx = { path = "../../common/nymsphinx" } +nym-topology = { path = "../../common/topology" } +nym-validator-client = { path = "../../common/client-libs/validator-client", features = ["http-client"] } [features] default = [] diff --git a/clients/socks5/src/commands/add_gateway.rs b/clients/socks5/src/commands/add_gateway.rs index fb7b645619..b8cc99efac 100644 --- a/clients/socks5/src/commands/add_gateway.rs +++ b/clients/socks5/src/commands/add_gateway.rs @@ -22,8 +22,9 @@ impl AsRef for Args { } pub(crate) async fn execute(args: Args) -> Result<(), Socks5ClientError> { + let user_agent = nym_bin_common::bin_info!().into(); let output = args.output; - let res = add_gateway::(args).await?; + let res = add_gateway::(args, Some(user_agent)).await?; println!("{}", output.format(&res)); Ok(()) diff --git a/clients/socks5/src/commands/init.rs b/clients/socks5/src/commands/init.rs index 83c26e7a3c..19af718c2b 100644 --- a/clients/socks5/src/commands/init.rs +++ b/clients/socks5/src/commands/init.rs @@ -129,8 +129,9 @@ impl Display for InitResults { pub(crate) async fn execute(args: Init) -> Result<(), Socks5ClientError> { eprintln!("Initialising client..."); + let user_agent = nym_bin_common::bin_info!().into(); let output = args.output; - let res = initialise_client::(args).await?; + let res = initialise_client::(args, Some(user_agent)).await?; let init_results = InitResults::new(res); println!("{}", output.format(&init_results)); diff --git a/clients/socks5/src/commands/run.rs b/clients/socks5/src/commands/run.rs index 87234d676c..f65c0ff742 100644 --- a/clients/socks5/src/commands/run.rs +++ b/clients/socks5/src/commands/run.rs @@ -116,7 +116,13 @@ pub(crate) async fn execute(args: Run) -> Result<(), Box String { + "unknown".to_string() } impl Display for BinaryBuildInformationOwned { diff --git a/common/bin-common/src/lib.rs b/common/bin-common/src/lib.rs index ca642f4883..1c6c42e6ac 100644 --- a/common/bin-common/src/lib.rs +++ b/common/bin-common/src/lib.rs @@ -2,9 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 pub mod build_information; -pub mod completions; pub mod logging; pub mod version_checker; +#[cfg(feature = "clap")] +pub mod completions; + #[cfg(feature = "output_format")] pub mod output_format; diff --git a/common/client-core/src/cli_helpers/client_add_gateway.rs b/common/client-core/src/cli_helpers/client_add_gateway.rs index 05c367f49a..7b6448062f 100644 --- a/common/client-core/src/cli_helpers/client_add_gateway.rs +++ b/common/client-core/src/cli_helpers/client_add_gateway.rs @@ -16,6 +16,7 @@ use log::info; use nym_client_core_gateways_storage::GatewayDetails; use nym_crypto::asymmetric::identity; use nym_topology::NymTopology; +use nym_validator_client::UserAgent; use std::path::PathBuf; #[cfg_attr(feature = "cli", derive(clap::Args))] @@ -60,7 +61,10 @@ pub struct CommonClientAddGatewayArgs { pub custom_mixnet: Option, } -pub async fn add_gateway(args: A) -> Result +pub async fn add_gateway( + args: A, + user_agent: Option, +) -> Result where A: AsRef, C: CliClient, @@ -111,7 +115,8 @@ where hardcoded_topology.get_gateways() } else { let mut rng = rand::thread_rng(); - crate::init::helpers::current_gateways(&mut rng, &core.client.nym_api_urls).await? + crate::init::helpers::current_gateways(&mut rng, &core.client.nym_api_urls, user_agent) + .await? }; // since we're registering with a brand new gateway, diff --git a/common/client-core/src/cli_helpers/client_init.rs b/common/client-core/src/cli_helpers/client_init.rs index 019416fb2d..646083ce5b 100644 --- a/common/client-core/src/cli_helpers/client_init.rs +++ b/common/client-core/src/cli_helpers/client_init.rs @@ -16,6 +16,7 @@ use log::info; use nym_client_core_gateways_storage::GatewayDetails; use nym_crypto::asymmetric::identity; use nym_topology::NymTopology; +use nym_validator_client::UserAgent; use rand::rngs::OsRng; use std::path::PathBuf; @@ -96,6 +97,7 @@ pub struct InitResultsWithConfig { pub async fn initialise_client( init_args: C::InitArgs, + user_agent: Option, ) -> Result, C::Error> where C: InitialisableClient, @@ -163,7 +165,8 @@ where hardcoded_topology.get_gateways() } else { let mut rng = rand::thread_rng(); - crate::init::helpers::current_gateways(&mut rng, &core.client.nym_api_urls).await? + crate::init::helpers::current_gateways(&mut rng, &core.client.nym_api_urls, user_agent) + .await? }; let gateway_setup = GatewaySetup::New { diff --git a/common/client-core/src/client/base_client/mod.rs b/common/client-core/src/client/base_client/mod.rs index 3d159fb485..804f16869c 100644 --- a/common/client-core/src/client/base_client/mod.rs +++ b/common/client-core/src/client/base_client/mod.rs @@ -53,7 +53,7 @@ use nym_task::connections::{ConnectionCommandReceiver, ConnectionCommandSender, use nym_task::{TaskClient, TaskHandle}; use nym_topology::provider_trait::TopologyProvider; use nym_topology::HardcodedTopologyProvider; -use nym_validator_client::nyxd::contract_traits::DkgQueryClient; +use nym_validator_client::{nyxd::contract_traits::DkgQueryClient, UserAgent}; use rand::rngs::OsRng; use std::fmt::Debug; use std::os::raw::c_int as RawFd; @@ -184,6 +184,7 @@ pub struct BaseClientBuilder<'a, C, S: MixnetClientStorage> { custom_topology_provider: Option>, custom_gateway_transceiver: Option>, shutdown: Option, + user_agent: Option, setup_method: GatewaySetup, } @@ -207,6 +208,7 @@ where custom_topology_provider: None, custom_gateway_transceiver: None, shutdown: None, + user_agent: None, setup_method: GatewaySetup::MustLoad { gateway_id: None }, } } @@ -250,6 +252,12 @@ where self } + #[must_use] + pub fn with_user_agent(mut self, user_agent: UserAgent) -> Self { + self.user_agent = Some(user_agent); + self + } + pub fn with_stored_topology>( mut self, file: P, @@ -467,6 +475,7 @@ where custom_provider: Option>, config_topology: config::Topology, nym_api_urls: Vec, + user_agent: Option, ) -> Box { // if no custom provider was ... provided ..., create one using nym-api custom_provider.unwrap_or_else(|| match config_topology.topology_structure { @@ -477,6 +486,7 @@ where }, nym_api_urls, env!("CARGO_PKG_VERSION").to_string(), + user_agent, )), config::TopologyStructure::GeoAware(group_by) => { Box::new(GeoAwareTopologyProvider::new( @@ -689,6 +699,7 @@ where self.custom_topology_provider.take(), self.config.debug.topology, self.config.get_nym_api_endpoints(), + self.user_agent.clone(), ); // needs to be started as the first thing to block if required waiting for the gateway 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 e3c0f7455f..3a00f462bd 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 @@ -5,6 +5,7 @@ use async_trait::async_trait; use log::{debug, error, warn}; use nym_topology::provider_trait::TopologyProvider; use nym_topology::{NymTopology, NymTopologyError}; +use nym_validator_client::UserAgent; use rand::prelude::SliceRandom; use rand::thread_rng; use url::Url; @@ -39,14 +40,26 @@ pub(crate) struct NymApiTopologyProvider { } impl NymApiTopologyProvider { - pub(crate) fn new(config: Config, mut nym_api_urls: Vec, client_version: String) -> Self { + pub(crate) fn new( + config: Config, + mut nym_api_urls: Vec, + client_version: String, + user_agent: Option, + ) -> Self { nym_api_urls.shuffle(&mut thread_rng()); + let validator_client = if let Some(user_agent) = user_agent { + nym_validator_client::client::NymApiClient::new_with_user_agent( + nym_api_urls[0].clone(), + user_agent, + ) + } else { + nym_validator_client::client::NymApiClient::new(nym_api_urls[0].clone()) + }; + NymApiTopologyProvider { config, - validator_client: nym_validator_client::client::NymApiClient::new( - nym_api_urls[0].clone(), - ), + validator_client, nym_api_urls, client_version, currently_used_api: 0, diff --git a/common/client-core/src/init/helpers.rs b/common/client-core/src/init/helpers.rs index 4d15327634..f0fbe7c23f 100644 --- a/common/client-core/src/init/helpers.rs +++ b/common/client-core/src/init/helpers.rs @@ -9,6 +9,7 @@ use nym_crypto::asymmetric::identity; use nym_gateway_client::GatewayClient; use nym_topology::{filter::VersionFilterable, gateway, mix}; use nym_validator_client::client::IdentityKeyRef; +use nym_validator_client::UserAgent; use rand::{seq::SliceRandom, Rng}; use std::{sync::Arc, time::Duration}; use tungstenite::Message; @@ -59,11 +60,16 @@ impl<'a> GatewayWithLatency<'a> { pub async fn current_gateways( rng: &mut R, nym_apis: &[Url], + user_agent: Option, ) -> Result, ClientCoreError> { let nym_api = nym_apis .choose(rng) .ok_or(ClientCoreError::ListOfNymApisIsEmpty)?; - let client = nym_validator_client::client::NymApiClient::new(nym_api.clone()); + let client = if let Some(user_agent) = user_agent { + nym_validator_client::client::NymApiClient::new_with_user_agent(nym_api.clone(), user_agent) + } else { + nym_validator_client::client::NymApiClient::new(nym_api.clone()) + }; log::debug!("Fetching list of gateways from: {nym_api}"); diff --git a/common/client-libs/validator-client/src/client.rs b/common/client-libs/validator-client/src/client.rs index 84bc95f0a0..b7b1e62c23 100644 --- a/common/client-libs/validator-client/src/client.rs +++ b/common/client-libs/validator-client/src/client.rs @@ -19,6 +19,7 @@ use nym_api_requests::models::{ RewardEstimationResponse, StakeSaturationResponse, }; use nym_api_requests::nym_nodes::SkimmedNode; +use nym_http_api_client::UserAgent; use nym_network_defaults::NymNetworkDetails; use url::Url; @@ -258,6 +259,16 @@ impl NymApiClient { NymApiClient { nym_api } } + pub fn new_with_user_agent(api_url: Url, user_agent: UserAgent) -> Self { + let nym_api = nym_api::Client::builder::<_, ValidatorClientError>(api_url) + .expect("invalid api url") + .with_user_agent(user_agent) + .build::() + .expect("failed to build nym api client"); + + NymApiClient { nym_api } + } + pub fn api_url(&self) -> &Url { self.nym_api.current_url() } diff --git a/common/client-libs/validator-client/src/lib.rs b/common/client-libs/validator-client/src/lib.rs index b3dca0a473..5af96ffe23 100644 --- a/common/client-libs/validator-client/src/lib.rs +++ b/common/client-libs/validator-client/src/lib.rs @@ -17,6 +17,7 @@ pub use crate::signing::direct_wallet::DirectSecp256k1HdWallet; pub use client::NymApiClient; pub use client::{Client, CoconutApiClient, Config}; pub use nym_api_requests::*; +pub use nym_http_api_client::UserAgent; #[cfg(feature = "http-client")] pub use cosmrs::rpc::HttpClient as HttpRpcClient; diff --git a/common/http-api-client/Cargo.toml b/common/http-api-client/Cargo.toml index 48c41cff28..a0dab4b00a 100644 --- a/common/http-api-client/Cargo.toml +++ b/common/http-api-client/Cargo.toml @@ -20,6 +20,8 @@ serde_json = { workspace = true } thiserror = { workspace = true } tracing = { workspace = true } +nym-bin-common = { path = "../bin-common" } + # for request timeout until https://github.com/seanmonstar/reqwest/issues/1135 is fixed [target."cfg(target_arch = \"wasm32\")".dependencies.wasmtimer] workspace = true diff --git a/common/http-api-client/src/lib.rs b/common/http-api-client/src/lib.rs index 0102a019bb..7430a8924b 100644 --- a/common/http-api-client/src/lib.rs +++ b/common/http-api-client/src/lib.rs @@ -14,6 +14,10 @@ use url::Url; pub use reqwest::IntoUrl; +pub use user_agent::UserAgent; + +mod user_agent; + pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10); pub type PathSegments<'a> = &'a [&'a str]; diff --git a/common/http-api-client/src/user_agent.rs b/common/http-api-client/src/user_agent.rs new file mode 100644 index 0000000000..6ca8375056 --- /dev/null +++ b/common/http-api-client/src/user_agent.rs @@ -0,0 +1,67 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use std::fmt; + +use http::HeaderValue; +use nym_bin_common::build_information::{BinaryBuildInformation, BinaryBuildInformationOwned}; + +#[derive(Clone, Debug)] +pub struct UserAgent { + application: String, + platform: String, + version: String, + git_commit: String, +} + +impl UserAgent { + pub fn new(application: String, platform: String, version: String, git_commit: String) -> Self { + UserAgent { + application, + platform, + version, + git_commit, + } + } +} + +impl fmt::Display for UserAgent { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let abbreviated_commit = self.git_commit.chars().take(7).collect::(); + write!( + f, + "{}/{}/{}/{}", + self.application, self.version, self.platform, abbreviated_commit + ) + } +} + +impl TryFrom for HeaderValue { + type Error = http::header::InvalidHeaderValue; + + fn try_from(user_agent: UserAgent) -> Result { + HeaderValue::from_str(&user_agent.to_string()) + } +} + +impl From for UserAgent { + fn from(build_info: BinaryBuildInformation) -> Self { + UserAgent { + application: build_info.binary_name.to_string(), + platform: build_info.cargo_triple.to_string(), + version: build_info.build_version.to_string(), + git_commit: build_info.commit_sha.to_string(), + } + } +} + +impl From for UserAgent { + fn from(build_info: BinaryBuildInformationOwned) -> Self { + UserAgent { + application: build_info.binary_name, + platform: build_info.cargo_triple, + version: build_info.build_version, + git_commit: build_info.commit_sha, + } + } +} diff --git a/common/socks5-client-core/src/lib.rs b/common/socks5-client-core/src/lib.rs index 41a7105dad..939333f2ab 100644 --- a/common/socks5-client-core/src/lib.rs +++ b/common/socks5-client-core/src/lib.rs @@ -27,6 +27,7 @@ use nym_task::manager::TaskStatus; use nym_task::{TaskClient, TaskHandle}; use anyhow::anyhow; +use nym_validator_client::UserAgent; use std::error::Error; use std::path::PathBuf; @@ -61,6 +62,8 @@ pub struct NymClient { setup_method: GatewaySetup, + user_agent: UserAgent, + /// Optional path to a .json file containing standalone network details. custom_mixnet: Option, } @@ -74,11 +77,17 @@ where ::StorageError: Sync + Send, ::StorageError: Send + Sync, { - pub fn new(config: Config, storage: S, custom_mixnet: Option) -> Self { + pub fn new( + config: Config, + storage: S, + user_agent: UserAgent, + custom_mixnet: Option, + ) -> Self { NymClient { config, storage, setup_method: GatewaySetup::MustLoad { gateway_id: None }, + user_agent, custom_mixnet, } } @@ -226,7 +235,8 @@ where let mut base_builder = BaseClientBuilder::new(&self.config.base, self.storage, dkg_query_client) - .with_gateway_setup(self.setup_method); + .with_gateway_setup(self.setup_method) + .with_user_agent(self.user_agent); if let Some(custom_mixnet) = &self.custom_mixnet { base_builder = base_builder.with_stored_topology(custom_mixnet)?; diff --git a/common/wasm/client-core/src/helpers.rs b/common/wasm/client-core/src/helpers.rs index 80c9e90135..fb38508162 100644 --- a/common/wasm/client-core/src/helpers.rs +++ b/common/wasm/client-core/src/helpers.rs @@ -122,7 +122,7 @@ pub async fn setup_gateway_from_api( nym_apis: &[Url], ) -> Result { let mut rng = thread_rng(); - let gateways = current_gateways(&mut rng, nym_apis).await?; + let gateways = current_gateways(&mut rng, nym_apis, None).await?; setup_gateway_wasm(client_store, force_tls, chosen_gateway, &gateways).await } diff --git a/mixnode/Cargo.toml b/mixnode/Cargo.toml index fe9ce479bd..db4473b530 100644 --- a/mixnode/Cargo.toml +++ b/mixnode/Cargo.toml @@ -56,7 +56,7 @@ nym-task = { path = "../common/task" } nym-types = { path = "../common/types" } nym-topology = { path = "../common/topology" } nym-validator-client = { path = "../common/client-libs/validator-client" } -nym-bin-common = { path = "../common/bin-common", features = ["output_format"] } +nym-bin-common = { path = "../common/bin-common", features = ["output_format", "clap"] } [dev-dependencies] tokio = { workspace = true, features = [ diff --git a/sdk/lib/socks5-listener/src/lib.rs b/sdk/lib/socks5-listener/src/lib.rs index 7b36e74496..46afb57678 100644 --- a/sdk/lib/socks5-listener/src/lib.rs +++ b/sdk/lib/socks5-listener/src/lib.rs @@ -269,12 +269,13 @@ where let nym_apis = config.core.base.client.nym_api_urls.clone(); let storage = MobileClientStorage::new(&config); - let socks5_client = - Socks5NymClient::new(config.core, storage, None).with_gateway_setup(GatewaySetup::New { + let user_agent = nym_bin_common::bin_info!().into(); + let socks5_client = Socks5NymClient::new(config.core, storage, user_agent, None) + .with_gateway_setup(GatewaySetup::New { specification: GatewaySelectionSpecification::UniformRemote { must_use_tls: false, }, - available_gateways: current_gateways(&mut rng, &nym_apis).await?, + available_gateways: current_gateways(&mut rng, &nym_apis, None).await?, wg_tun_address: None, }); diff --git a/sdk/rust/nym-sdk/src/lib.rs b/sdk/rust/nym-sdk/src/lib.rs index 7834aca487..f06989693f 100644 --- a/sdk/rust/nym-sdk/src/lib.rs +++ b/sdk/rust/nym-sdk/src/lib.rs @@ -13,6 +13,7 @@ pub use nym_network_defaults::{ ChainDetails, DenomDetails, DenomDetailsOwned, NymContracts, NymNetworkDetails, ValidatorDetails, }; +pub use nym_validator_client::UserAgent; // we have to re-expose TaskClient since we're allowing custom shutdown in public API // (which is quite a shame if you ask me...) pub use nym_task::TaskClient; diff --git a/sdk/rust/nym-sdk/src/mixnet/client.rs b/sdk/rust/nym-sdk/src/mixnet/client.rs index f6239d62ff..52bfa59883 100644 --- a/sdk/rust/nym-sdk/src/mixnet/client.rs +++ b/sdk/rust/nym-sdk/src/mixnet/client.rs @@ -33,7 +33,7 @@ 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 nym_validator_client::{nyxd, QueryHttpRpcNyxdClient, UserAgent}; use rand::rngs::OsRng; use std::net::IpAddr; use std::path::Path; @@ -55,6 +55,7 @@ pub struct MixnetClientBuilder { custom_gateway_transceiver: Option>, custom_shutdown: Option, force_tls: bool, + user_agent: Option, // TODO: incorporate it properly into `MixnetClientStorage` (I will need it in wasm anyway) gateway_endpoint_config_path: Option, @@ -94,6 +95,7 @@ impl MixnetClientBuilder { custom_shutdown: None, custom_gateway_transceiver: None, force_tls: false, + user_agent: None, }) } } @@ -121,6 +123,7 @@ where custom_gateway_transceiver: None, custom_shutdown: None, force_tls: false, + user_agent: None, gateway_endpoint_config_path: None, storage, } @@ -139,6 +142,7 @@ where custom_gateway_transceiver: self.custom_gateway_transceiver, custom_shutdown: self.custom_shutdown, force_tls: self.force_tls, + user_agent: self.user_agent, gateway_endpoint_config_path: self.gateway_endpoint_config_path, storage, } @@ -233,6 +237,12 @@ where self } + #[must_use] + pub fn with_user_agent(mut self, user_agent: UserAgent) -> Self { + self.user_agent = Some(user_agent); + self + } + /// Use custom mixnet sender that might not be the default websocket gateway connection. /// only for advanced use #[must_use] @@ -261,6 +271,7 @@ where client.wireguard_mode = self.wireguard_mode; client.wait_for_gateway = self.wait_for_gateway; client.force_tls = self.force_tls; + client.user_agent = self.user_agent; Ok(client) } @@ -309,6 +320,8 @@ where /// Allows passing an externally controlled shutdown handle. custom_shutdown: Option, + + user_agent: Option, } impl DisconnectedMixnetClient @@ -358,6 +371,7 @@ where wait_for_gateway: false, force_tls: false, custom_shutdown: None, + user_agent: None, }) } @@ -458,8 +472,10 @@ where self.force_tls, ); + let user_agent = self.user_agent.clone(); + let mut rng = OsRng; - let available_gateways = current_gateways(&mut rng, &nym_api_endpoints).await?; + let available_gateways = current_gateways(&mut rng, &nym_api_endpoints, user_agent).await?; Ok(GatewaySetup::New { specification: selection_spec, @@ -566,51 +582,9 @@ where .with_wait_for_gateway(self.wait_for_gateway) .with_wireguard_connection(self.wireguard_mode); - // 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(user_agent) = self.user_agent { + base_builder = base_builder.with_user_agent(user_agent); + } if let Some(topology_provider) = self.custom_topology_provider { base_builder = base_builder.with_topology_provider(topology_provider); diff --git a/service-providers/ip-packet-router/Cargo.toml b/service-providers/ip-packet-router/Cargo.toml index 5852901b3e..d54239428f 100644 --- a/service-providers/ip-packet-router/Cargo.toml +++ b/service-providers/ip-packet-router/Cargo.toml @@ -17,7 +17,7 @@ clap.workspace = true etherparse = { workspace = true } futures = { workspace = true } log = { workspace = true } -nym-bin-common = { path = "../../common/bin-common" } +nym-bin-common = { path = "../../common/bin-common", features = ["clap"] } nym-client-core = { path = "../../common/client-core" } nym-config = { path = "../../common/config" } nym-crypto = { path = "../../common/crypto" } diff --git a/service-providers/ip-packet-router/src/cli/add_gateway.rs b/service-providers/ip-packet-router/src/cli/add_gateway.rs index ebfffcf149..251e2cb1a3 100644 --- a/service-providers/ip-packet-router/src/cli/add_gateway.rs +++ b/service-providers/ip-packet-router/src/cli/add_gateway.rs @@ -23,7 +23,7 @@ impl AsRef for Args { pub(crate) async fn execute(args: Args) -> Result<(), IpPacketRouterError> { let output = args.output; - let res = add_gateway::(args).await?; + let res = add_gateway::(args, None).await?; println!("{}", output.format(&res)); Ok(()) diff --git a/service-providers/ip-packet-router/src/cli/init.rs b/service-providers/ip-packet-router/src/cli/init.rs index 6b41b287d8..024e1606fe 100644 --- a/service-providers/ip-packet-router/src/cli/init.rs +++ b/service-providers/ip-packet-router/src/cli/init.rs @@ -88,7 +88,7 @@ pub(crate) async fn execute(args: Init) -> Result<(), IpPacketRouterError> { eprintln!("Initialising client..."); let output = args.output; - let res = initialise_client::(args).await?; + let res = initialise_client::(args, None).await?; let init_results = InitResults::new(res); println!("{}", output.format(&init_results)); diff --git a/service-providers/ip-packet-router/src/mixnet_client.rs b/service-providers/ip-packet-router/src/mixnet_client.rs index 1d9695c001..178832a164 100644 --- a/service-providers/ip-packet-router/src/mixnet_client.rs +++ b/service-providers/ip-packet-router/src/mixnet_client.rs @@ -19,6 +19,7 @@ pub(crate) async fn create_mixnet_client( let debug_config = config.debug; let storage_paths = nym_sdk::mixnet::StoragePaths::from(paths.clone()); + let user_agent = nym_bin_common::bin_info!().into(); let mut client_builder = nym_sdk::mixnet::MixnetClientBuilder::new_with_default_storage(storage_paths) @@ -27,6 +28,7 @@ pub(crate) async fn create_mixnet_client( .network_details(NymNetworkDetails::new_from_env()) .debug_config(debug_config) .custom_shutdown(shutdown) + .with_user_agent(user_agent) .with_wait_for_gateway(wait_for_gateway); if !config.get_disabled_credentials_mode() { client_builder = client_builder.enable_credentials_mode(); diff --git a/service-providers/network-requester/src/cli/add_gateway.rs b/service-providers/network-requester/src/cli/add_gateway.rs index e07f16f46d..67595f7fd9 100644 --- a/service-providers/network-requester/src/cli/add_gateway.rs +++ b/service-providers/network-requester/src/cli/add_gateway.rs @@ -23,7 +23,7 @@ impl AsRef for Args { pub(crate) async fn execute(args: Args) -> Result<(), NetworkRequesterError> { let output = args.output; - let res = add_gateway::(args).await?; + let res = add_gateway::(args, None).await?; println!("{}", output.format(&res)); Ok(()) diff --git a/service-providers/network-requester/src/cli/init.rs b/service-providers/network-requester/src/cli/init.rs index 70e68892c0..9d6da8f3ea 100644 --- a/service-providers/network-requester/src/cli/init.rs +++ b/service-providers/network-requester/src/cli/init.rs @@ -103,7 +103,7 @@ pub(crate) async fn execute(args: Init) -> Result<(), NetworkRequesterError> { eprintln!("Initialising client..."); let output = args.output; - let res = initialise_client::(args).await?; + let res = initialise_client::(args, None).await?; let init_results = InitResults::new(res); println!("{}", output.format(&init_results)); From 4a25725a116ba25917d66ea631c993be3fb04318 Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Mon, 1 Jul 2024 14:27:59 +0000 Subject: [PATCH 2/3] README.md update (#4685) * start README update * test alpha on github preview * test alpha on github preview * comment out rewards * finish README update --- README.md | 92 ++++++++++++++++++++++--------------------------------- 1 file changed, 36 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index c75bcc7c3e..9f0e97d181 100644 --- a/README.md +++ b/README.md @@ -7,86 +7,66 @@ SPDX-License-Identifier: Apache-2.0 The platform is composed of multiple Rust crates. Top-level executable binary crates include: -* nym-mixnode - shuffles [Sphinx](https://github.com/nymtech/sphinx) packets together to provide privacy against network-level attackers. -* nym-client - an executable which you can build into your own applications. Use it for interacting with Nym nodes. -* nym-socks5-client - a Socks5 proxy you can run on your machine and use with existing applications. -* nym-gateway - acts sort of like a mailbox for mixnet messages, which removes the need for direct delivery to potentially offline or firewalled devices. -* nym-network-monitor - sends packets through the full system to check that they are working as expected, and stores node uptime histories as the basis of a rewards system ("mixmining" or "proof-of-mixing"). -* nym-explorer - a (projected) block explorer and (existing) mixnet viewer. -* nym-wallet - a desktop wallet implemented using the [Tauri](https://tauri.studio/en/docs/about/intro) framework. +* `nym-node` - a tool for running a node within the Nym network. Nym Nodes containing functionality such as `mixnode`, `entry-gateway` and `exit-gateway` are fundamental components of Nym Mixnet architecture. Nym Nodes are ran by decentralised node operators. Read more about `nym-node` in [Operators Guide documentation](https://nymtech.net/operators/nodes/nym-node.html). Network functionality of `nym-node` (labeled with `--mode` flag) can be: + - `mixnode` - shuffles [Sphinx](https://github.com/nymtech/sphinx) packets together to provide privacy against network-level attackers. + - `gateway` - acts sort of like a mailbox for mixnet messages, which removes the need for direct delivery to potentially offline or firewalled devices. Gateways can be further categorized as `entry-gateway` and `exit-gateway`. The latter has an extra embedded IP packet router and Network requester to route data to the internet. +* `nym-client` - an executable which you can build into your own applications. Use it for interacting with Nym nodes. +* `nym-socks5-client` - a Socks5 proxy you can run on your machine and use with existing applications. +* `nym-explorer` - a (projected) block explorer and (existing) mixnet viewer. +* `nym-wallet` - a desktop wallet implemented using the [Tauri](https://tauri.studio/en/docs/about/intro) framework. + + +```ascii + ┌─►mix──┐ mix mix + │ │ + Entry │ │ Exit +client ───► Gateway ──┘ mix │ mix ┌─►mix ───► Gateway ───► internet + │ │ + │ │ + mix └─►mix──┘ mix + +``` [![Build Status](https://img.shields.io/github/actions/workflow/status/nymtech/nym/build.yml?branch=develop&style=for-the-badge&logo=github-actions)](https://github.com/nymtech/nym/actions?query=branch%3Adevelop) ### Building -Platform build instructions are available on [our docs site](https://nymtech.net/docs/binaries/pre-built-binaries.html). -Wallet build instructions are also available on [our docs site](https://nymtech.net/docs/wallet/desktop-wallet.html). +* Platform build instructions are available on Nym [Operators Guide documentation](https://nymtech.net/operators/binaries/building-nym.html). +* Wallet build instructions are available on Nym [Technical docs](https://nymtech.net/docs/wallet/desktop-wallet.html). ### Developing -There's a `.env.sample-dev` file provided which you can rename to `.env` if you want convenient logging, backtrace, or other environment variables pre-set. The `.env` file is ignored so you don't need to worry about checking it in. +There's a [`sandbox.env`](https://github.com/nymtech/nym/envs/sandbox.env) file provided which you can rename to `.env` if you want convenient testing environment. Read more about sandbox environment in our [Operators Guide page](https://nymtech.net/operators/sandbox.html). -For Typescript components, please see [ts-packages](./ts-packages). +References for developers: + +* [Developers Portal](https://nymtech.net/developers) +* [Typescript SDKs](https://sdk.nymtech.net/) +* [Technical Documentation - Nym network overview](https://nymtech.net/docs/) +* [Release Cycle - git flow](https://nymtech.net/operators/release-cycle.html) ### Developer chat -> We used to use Keybase for developer chats, but we have since migrated to Matrix and Discord. We no longer check the old **nymtech.friends** Keybase team. - You can chat to us in two places: * The #dev channel on [Matrix](https://matrix.to/#/#dev:nymtech.chat) -* The various developer channels on [Discord](https://discord.gg/nym) +* The various developer channels on [Discord](discord.gg/nymproject) -### Rewards +### Tokenomics & Rewards -Node, node operator and delegator rewards are determined according to the principles laid out in the section 6 of [Nym Whitepaper](https://nymtech.net/nym-whitepaper.pdf). Below is a TLDR of the variables and formulas involved in calculating the epoch rewards. Initial reward pool is set to 250 million Nym, making the circulating supply 750 million Nym. - -|Symbol|Definition| -|---|---| -||global share of rewards available, starts at 2% of the reward pool. -||node reward for mixnode `i`. -||ratio of total node stake (node bond + all delegations) to the token circulating supply. -||ratio of stake operator has pledged to their node to the token circulating supply. -||fraction of total effort undertaken by node `i`, set to `1/k`. -||number of nodes stakeholders are incentivised to create, set by the validators, a matter of governance. Currently determined by the `reward set` size, and set to 720 in testnet Sandbox. -||A Sybil attack resistance parameter - the higher this parameter is set, the stronger the reduction in competitiveness for a Sybil attacker. -||declared profit margin of operator `i`, defaults to 10%. -||uptime of node `i`, scaled to 0 - 1, for the rewarding epoch -||cost of operating node `i` for the duration of the rewarding epoch, set to 40 NYMs. - -Node reward for node `i` is determined as: - - - -where: - - - - -and - - - - -Operator of node `i` is credited with the following amount: - - - - -Delegate with stake `s` receives: - - - - -where `s'` is stake `s` scaled over total token circulating supply. +Nym network economic incentives, operator and validator rewards, and scalability of the network are determined according to the principles laid out in the section 6 of [Nym Whitepaper](https://nymtech.net/nym-whitepaper.pdf). +Initial reward pool is set to 250 million Nym, making the circulating supply 750 million Nym. ### Licensing and copyright information This is a monorepo and components that make up Nym as a system are licensed individually, so for accurate information, please check individual files. As a general approach, licensing is as follows this pattern: + - applications and binaries are GPLv3 - libraries and components are Apache 2.0 or MIT - documentation is Apache 2.0 or CC0-1.0 -Again, for accurate information, please check individual files. +Nym Node Operators and Validators Temrs and Conditions can be found [here](https://nymtech.net/terms-and-conditions/operators/v1.0.0). From e65e6118595d3f97df6b4c100e97e6ff0ed25a1a Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Tue, 2 Jul 2024 12:37:23 +0000 Subject: [PATCH 3/3] remove roles selection on blacklisting node - PR done (#4687) --- scripts/node_api_check.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/node_api_check.py b/scripts/node_api_check.py index 8fdb11f469..8fcfe52b72 100755 --- a/scripts/node_api_check.py +++ b/scripts/node_api_check.py @@ -135,10 +135,13 @@ class MainFunctions: routing_history = api_data[f"/status/gateway/{identity}/history"]["history"] del api_data[f"/status/gateway/{identity}/history"]["history"] swagger_data = self.get_swagger_data(host,swagger,swagger_data) - if swagger_data["/roles"]["network_requester_enabled"]== True and swagger_data["/roles"]["ip_packet_router_enabled"] == True: - role = "exit-gateway" + if swagger_data: + if swagger_data["/roles"]["network_requester_enabled"] == True and swagger_data["/roles"]["ip_packet_router_enabled"] == True: + role = "exit-gateway" + else: + role = "entry-gateway" else: - role = "entry-gateway" + role = None elif mode == "mixnode": mix_id = str(node_dict["mixnode_details"]["bond_information"]["mix_id"]) for key in endpoints: