diff --git a/Cargo.lock b/Cargo.lock
index 1182310e62..1fc2048511 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4669,6 +4669,7 @@ version = "0.1.0"
dependencies = [
"async-trait",
"http 1.1.0",
+ "nym-bin-common",
"reqwest 0.12.4",
"serde",
"serde_json",
@@ -5279,6 +5280,7 @@ dependencies = [
"nym-socks5-client-core",
"nym-sphinx",
"nym-topology",
+ "nym-validator-client",
"rand 0.8.5",
"serde",
"serde_json",
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
+
+```
[](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).
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/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:
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));