Merge remote-tracking branch 'origin/develop' into feature/authenticator

This commit is contained in:
Bogdan-Ștefan Neacşu
2024-07-02 16:25:29 +03:00
36 changed files with 273 additions and 145 deletions
Generated
+2
View File
@@ -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",
+36 -56
View File
@@ -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.
<!-- coming soon
* `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").
-->
```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|
|---|---|
|<img src="https://render.githubusercontent.com/render/math?math=R#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}R#gh-dark-mode-only">|global share of rewards available, starts at 2% of the reward pool.
|<img src="https://render.githubusercontent.com/render/math?math=R_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}R_{i}#gh-dark-mode-only">|node reward for mixnode `i`.
|<img src="https://render.githubusercontent.com/render/math?math=\sigma_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}\sigma_{i}#gh-dark-mode-only">|ratio of total node stake (node bond + all delegations) to the token circulating supply.
|<img src="https://render.githubusercontent.com/render/math?math=\lambda_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}\lambda_{i}#gh-dark-mode-only">|ratio of stake operator has pledged to their node to the token circulating supply.
|<img src="https://render.githubusercontent.com/render/math?math=\omega_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}\omega_{i}#gh-dark-mode-only">|fraction of total effort undertaken by node `i`, set to `1/k`.
|<img src="https://render.githubusercontent.com/render/math?math=k#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}k#gh-dark-mode-only">|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.
|<img src="https://render.githubusercontent.com/render/math?math=\alpha#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}\alpha#gh-dark-mode-only">|A Sybil attack resistance parameter - the higher this parameter is set, the stronger the reduction in competitiveness for a Sybil attacker.
|<img src="https://render.githubusercontent.com/render/math?math=PM_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}PM_{i}#gh-dark-mode-only">|declared profit margin of operator `i`, defaults to 10%.
|<img src="https://render.githubusercontent.com/render/math?math=PF_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}PF_{i}#gh-dark-mode-only">|uptime of node `i`, scaled to 0 - 1, for the rewarding epoch
|<img src="https://render.githubusercontent.com/render/math?math=PP_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}PP_{i}#gh-dark-mode-only">|cost of operating node `i` for the duration of the rewarding epoch, set to 40 NYMs.
Node reward for node `i` is determined as:
<img src="https://render.githubusercontent.com/render/math?math=R_{i}=PF_{i} \cdot R \cdot (\sigma^'_{i} \cdot \omega_{i} \cdot k %2b \alpha \cdot \lambda^'_{i} \cdot \sigma^'_{i} \cdot k)/(1 %2b \alpha)#gh-light-mode-only">
<img src="https://render.githubusercontent.com/render/math?math=\color{white}R_{i}=PF_{i} \cdot R \cdot (\sigma^'_{i} \cdot \omega_{i} \cdot k %2b \alpha \cdot \lambda^'_{i} \cdot \sigma^'_{i} \cdot k)/(1 %2b \alpha)#gh-dark-mode-only">
where:
<img src="https://render.githubusercontent.com/render/math?math=\sigma^'_{i} = min\{\sigma_{i}, 1/k\}#gh-light-mode-only">
<img src="https://render.githubusercontent.com/render/math?math=\color{white}\sigma^'_{i} = min\{\sigma_{i}, 1/k\}#gh-dark-mode-only">
and
<img src="https://render.githubusercontent.com/render/math?math=\lambda^'_{i} = min\{\lambda_{i}, 1/k\}#gh-light-mode-only">
<img src="https://render.githubusercontent.com/render/math?math=\color{white}\lambda^'_{i} = min\{\lambda_{i}, 1/k\}#gh-dark-mode-only">
Operator of node `i` is credited with the following amount:
<img src="https://render.githubusercontent.com/render/math?math=min\{PP_{i},R_{i})\} %2b max\{0, (PM_{i} %2b (1 - PM_{i}) \cdot \lambda_{i}/\delta_{i}) \cdot (R_{i} - PP_{i})\}#gh-light-mode-only">
<img src="https://render.githubusercontent.com/render/math?math=\color{white}min\{PP_{i},R_{i})\} %2b max\{0, (PM_{i} %2b (1 - PM_{i}) \cdot \lambda_{i}/\delta_{i}) \cdot (R_{i} - PP_{i})\}#gh-dark-mode-only">
Delegate with stake `s` receives:
<img src="https://render.githubusercontent.com/render/math?math=max\{0, (1-PM_{i}) \cdot (s^'/\sigma_{i}) \cdot (R_{i} - PP_{i})\}#gh-light-mode-only">
<img src="https://render.githubusercontent.com/render/math?math=\color{white}max\{0, (1-PM_{i}) \cdot (s^'/\sigma_{i}) \cdot (R_{i} - PP_{i})\}#gh-dark-mode-only">
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).
+1 -1
View File
@@ -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" }
+3 -1
View File
@@ -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)?;
+2 -1
View File
@@ -22,8 +22,9 @@ impl AsRef<CommonClientAddGatewayArgs> 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::<CliNativeClient, _>(args).await?;
let res = add_gateway::<CliNativeClient, _>(args, Some(user_agent)).await?;
println!("{}", output.format(&res));
Ok(())
+2 -1
View File
@@ -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::<CliNativeClient>(args).await?;
let res = initialise_client::<CliNativeClient>(args, Some(user_agent)).await?;
let init_results = InitResults::new(res);
println!("{}", output.format(&init_results));
+5 -4
View File
@@ -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 = []
+2 -1
View File
@@ -22,8 +22,9 @@ impl AsRef<CommonClientAddGatewayArgs> 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::<CliSocks5Client, _>(args).await?;
let res = add_gateway::<CliSocks5Client, _>(args, Some(user_agent)).await?;
println!("{}", output.format(&res));
Ok(())
+2 -1
View File
@@ -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::<CliSocks5Client>(args).await?;
let res = initialise_client::<CliSocks5Client>(args, Some(user_agent)).await?;
let init_results = InitResults::new(res);
println!("{}", output.format(&init_results));
+9 -3
View File
@@ -116,7 +116,13 @@ pub(crate) async fn execute(args: Run) -> Result<(), Box<dyn std::error::Error +
let storage =
OnDiskPersistent::from_paths(config.storage_paths.common_paths, &config.core.base.debug)
.await?;
NymClient::new(config.core, storage, args.common_args.custom_mixnet)
.run_forever()
.await
let user_agent = nym_bin_common::bin_info!().into();
NymClient::new(
config.core,
storage,
user_agent,
args.common_args.custom_mixnet,
)
.run_forever()
.await
}
+5 -4
View File
@@ -9,9 +9,9 @@ repository = { workspace = true }
[dependencies]
const-str = { workspace = true }
clap = { workspace = true, features = ["derive"] }
clap_complete = { workspace = true }
clap_complete_fig = { workspace = true }
clap = { workspace = true, features = ["derive"], optional = true }
clap_complete = { workspace = true, optional = true }
clap_complete_fig = { workspace = true, optional = true }
log = { workspace = true }
pretty_env_logger = { workspace = true }
semver = "0.11"
@@ -34,7 +34,7 @@ vergen = { workspace = true, features = ["build", "git", "gitcl", "rustc", "carg
[features]
default = []
openapi = ["utoipa"]
output_format = ["serde_json"]
output_format = ["serde_json", "dep:clap"]
bin_info_schema = ["schemars"]
basic_tracing = ["tracing-subscriber"]
tracing = [
@@ -44,3 +44,4 @@ tracing = [
"tracing-opentelemetry",
"opentelemetry",
]
clap = [ "dep:clap", "dep:clap_complete", "dep:clap_complete_fig" ]
@@ -44,6 +44,10 @@ pub struct BinaryBuildInformation {
/// Provides the cargo debug mode that was used for the build.
// NOTE: keep the old name cargo_profile instead of cargo_debug for backwards compatibility
pub cargo_profile: &'static str,
// VERGEN_CARGO_TARGET_TRIPLE
/// Provides the cargo target triple that was used for the build.
pub cargo_triple: &'static str,
}
impl BinaryBuildInformation {
@@ -66,6 +70,7 @@ impl BinaryBuildInformation {
rustc_version: env!("VERGEN_RUSTC_SEMVER"),
rustc_channel: env!("VERGEN_RUSTC_CHANNEL"),
cargo_profile,
cargo_triple: env!("VERGEN_CARGO_TARGET_TRIPLE"),
}
}
@@ -95,6 +100,7 @@ impl BinaryBuildInformation {
rustc_version: env!("VERGEN_RUSTC_SEMVER"),
rustc_channel: env!("VERGEN_RUSTC_CHANNEL"),
cargo_profile,
cargo_triple: env!("VERGEN_CARGO_TARGET_TRIPLE"),
}
}
@@ -109,6 +115,7 @@ impl BinaryBuildInformation {
rustc_version: self.rustc_version.to_owned(),
rustc_channel: self.rustc_channel.to_owned(),
cargo_profile: self.cargo_profile.to_owned(),
cargo_triple: self.cargo_triple.to_owned(),
}
}
@@ -156,6 +163,15 @@ pub struct BinaryBuildInformationOwned {
/// Provides the cargo debug mode that was used for the build.
// NOTE: keep the old name cargo_profile instead of cargo_debug for backwards compatibility
pub cargo_profile: String,
// VERGEN_CARGO_TARGET_TRIPLE
/// Provides the cargo target triple that was used for the build.
#[serde(default = "unknown")]
pub cargo_triple: String,
}
fn unknown() -> String {
"unknown".to_string()
}
impl Display for BinaryBuildInformationOwned {
+3 -1
View File
@@ -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;
@@ -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<PathBuf>,
}
pub async fn add_gateway<C, A>(args: A) -> Result<GatewayInfo, C::Error>
pub async fn add_gateway<C, A>(
args: A,
user_agent: Option<UserAgent>,
) -> Result<GatewayInfo, C::Error>
where
A: AsRef<CommonClientAddGatewayArgs>,
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,
@@ -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<T> {
pub async fn initialise_client<C>(
init_args: C::InitArgs,
user_agent: Option<UserAgent>,
) -> Result<InitResultsWithConfig<C::Config>, 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 {
@@ -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<Box<dyn TopologyProvider + Send + Sync>>,
custom_gateway_transceiver: Option<Box<dyn GatewayTransceiver + Send>>,
shutdown: Option<TaskClient>,
user_agent: Option<UserAgent>,
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<P: AsRef<Path>>(
mut self,
file: P,
@@ -467,6 +475,7 @@ where
custom_provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
config_topology: config::Topology,
nym_api_urls: Vec<Url>,
user_agent: Option<UserAgent>,
) -> Box<dyn TopologyProvider + Send + Sync> {
// 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
@@ -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<Url>, client_version: String) -> Self {
pub(crate) fn new(
config: Config,
mut nym_api_urls: Vec<Url>,
client_version: String,
user_agent: Option<UserAgent>,
) -> 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,
+7 -1
View File
@@ -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<R: Rng>(
rng: &mut R,
nym_apis: &[Url],
user_agent: Option<UserAgent>,
) -> Result<Vec<gateway::Node>, 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}");
@@ -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::<ValidatorClientError>()
.expect("failed to build nym api client");
NymApiClient { nym_api }
}
pub fn api_url(&self) -> &Url {
self.nym_api.current_url()
}
@@ -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;
+2
View File
@@ -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
+4
View File
@@ -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];
+67
View File
@@ -0,0 +1,67 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// 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::<String>();
write!(
f,
"{}/{}/{}/{}",
self.application, self.version, self.platform, abbreviated_commit
)
}
}
impl TryFrom<UserAgent> for HeaderValue {
type Error = http::header::InvalidHeaderValue;
fn try_from(user_agent: UserAgent) -> Result<Self, Self::Error> {
HeaderValue::from_str(&user_agent.to_string())
}
}
impl From<BinaryBuildInformation> 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<BinaryBuildInformationOwned> 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,
}
}
}
+12 -2
View File
@@ -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<S> {
setup_method: GatewaySetup,
user_agent: UserAgent,
/// Optional path to a .json file containing standalone network details.
custom_mixnet: Option<PathBuf>,
}
@@ -74,11 +77,17 @@ where
<S::GatewaysDetailsStore as GatewaysDetailsStore>::StorageError: Sync + Send,
<S::KeyStore as KeyStore>::StorageError: Send + Sync,
{
pub fn new(config: Config, storage: S, custom_mixnet: Option<PathBuf>) -> Self {
pub fn new(
config: Config,
storage: S,
user_agent: UserAgent,
custom_mixnet: Option<PathBuf>,
) -> 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)?;
+1 -1
View File
@@ -122,7 +122,7 @@ pub async fn setup_gateway_from_api(
nym_apis: &[Url],
) -> Result<InitialisationResult, WasmCoreError> {
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
}
+1 -1
View File
@@ -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 = [
+6 -3
View File
@@ -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:
+4 -3
View File
@@ -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,
});
+1
View File
@@ -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;
+21 -47
View File
@@ -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<S: MixnetClientStorage = Ephemeral> {
custom_gateway_transceiver: Option<Box<dyn GatewayTransceiver + Send + Sync>>,
custom_shutdown: Option<TaskClient>,
force_tls: bool,
user_agent: Option<UserAgent>,
// TODO: incorporate it properly into `MixnetClientStorage` (I will need it in wasm anyway)
gateway_endpoint_config_path: Option<PathBuf>,
@@ -94,6 +95,7 @@ impl MixnetClientBuilder<OnDiskPersistent> {
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<TaskClient>,
user_agent: Option<UserAgent>,
}
impl<S> DisconnectedMixnetClient<S>
@@ -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);
@@ -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" }
@@ -23,7 +23,7 @@ impl AsRef<CommonClientAddGatewayArgs> for Args {
pub(crate) async fn execute(args: Args) -> Result<(), IpPacketRouterError> {
let output = args.output;
let res = add_gateway::<CliIpPacketRouterClient, _>(args).await?;
let res = add_gateway::<CliIpPacketRouterClient, _>(args, None).await?;
println!("{}", output.format(&res));
Ok(())
@@ -88,7 +88,7 @@ pub(crate) async fn execute(args: Init) -> Result<(), IpPacketRouterError> {
eprintln!("Initialising client...");
let output = args.output;
let res = initialise_client::<CliIpPacketRouterClient>(args).await?;
let res = initialise_client::<CliIpPacketRouterClient>(args, None).await?;
let init_results = InitResults::new(res);
println!("{}", output.format(&init_results));
@@ -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();
@@ -23,7 +23,7 @@ impl AsRef<CommonClientAddGatewayArgs> for Args {
pub(crate) async fn execute(args: Args) -> Result<(), NetworkRequesterError> {
let output = args.output;
let res = add_gateway::<CliNetworkRequesterClient, _>(args).await?;
let res = add_gateway::<CliNetworkRequesterClient, _>(args, None).await?;
println!("{}", output.format(&res));
Ok(())
@@ -103,7 +103,7 @@ pub(crate) async fn execute(args: Init) -> Result<(), NetworkRequesterError> {
eprintln!("Initialising client...");
let output = args.output;
let res = initialise_client::<CliNetworkRequesterClient>(args).await?;
let res = initialise_client::<CliNetworkRequesterClient>(args, None).await?;
let init_results = InitResults::new(res);
println!("{}", output.format(&init_results));