Compare commits

..

11 Commits

Author SHA1 Message Date
Jon Häggblad 7a9f13c08d Rename to set_active_gateway_if_previously_registered 2024-04-05 13:21:46 +02:00
Jon Häggblad 6b1b04f53c Revert set_active_gateway in base_client 2024-04-05 13:21:31 +02:00
Jon Häggblad 6723690584 Downgrade log statement to debug 2024-04-04 23:14:13 +02:00
Jon Häggblad c4d7c9eb16 Make create_bandwidth_client pub 2024-04-04 23:07:18 +02:00
Jon Häggblad afa2f07636 Remove pseudo builder methods 2024-04-04 22:38:23 +02:00
Jon Häggblad 5dd2acfcac Remove pub from internal setters 2024-04-04 22:38:23 +02:00
Jon Häggblad 03c702d498 Remove unused KeyMode type 2024-04-04 22:38:23 +02:00
Jon Häggblad c7dbcc097d Rework gateway setup in rust sdk mixnet client 2024-04-04 22:38:23 +02:00
Jon Häggblad 80f6c2c32a Add debug implementations for some gateway setup types 2024-04-04 22:38:23 +02:00
Jon Häggblad c9a36a21a1 Elevate log statements in gateway setup 2024-04-04 21:52:41 +02:00
Jon Häggblad 9eb4ca3aad Set active gateway after setting up gateway in base client 2024-04-04 21:51:51 +02:00
259 changed files with 3532 additions and 11129 deletions
@@ -112,7 +112,6 @@ jobs:
target/release/nym-network-statistics
target/release/nym-cli
target/release/nymvisor
target/release/nym-node
retention-days: 30
# If this was a pull_request or nightly, upload to build server
@@ -131,7 +130,6 @@ jobs:
cp target/release/nym-network-requester $OUTPUT_DIR
cp target/release/nym-network-statistics $OUTPUT_DIR
cp target/release/nymvisor $OUTPUT_DIR
cp target/release/nym-node $OUTPUT_DIR
cp target/release/nym-cli $OUTPUT_DIR
cp target/release/explorer-api $OUTPUT_DIR
if [ ${{ github.event_name == 'workflow_dispatch' && inputs.enable_deb == true }} = true ]; then
@@ -30,7 +30,6 @@ jobs:
mixnode_hash: ${{ steps.binary-hashes.outputs.mixnode_hash }}
gateway_hash: ${{ steps.binary-hashes.outputs.gateway_hash }}
nymvisor_hash: ${{ steps.binary-hashes.outputs.nymvisor_hash }}
nymnode_hash: ${{ steps.binary-hashes.outputs.nymnode_hash }}
socks5_hash: ${{ steps.binary-hashes.outputs.socks5_hash }}
netreq_hash: ${{ steps.binary-hashes.outputs.netreq_hash }}
cli_hash: ${{ steps.binary-hashes.outputs.cli_hash }}
@@ -39,7 +38,6 @@ jobs:
mixnode_version: ${{ steps.binary-versions.outputs.mixnode_version }}
gateway_version: ${{ steps.binary-versions.outputs.gateway_version }}
nymvisor_version: ${{ steps.binary-versions.outputs.nymvisor_version }}
nymnode_version: ${{ steps.binary-versions.outputs.nymnode_version }}
socks5_version: ${{ steps.binary-versions.outputs.socks5_version }}
netreq_version: ${{ steps.binary-versions.outputs.netreq_version }}
cli_version: ${{ steps.binary-versions.outputs.cli_version }}
@@ -83,7 +81,6 @@ jobs:
target/release/nym-network-statistics
target/release/nym-cli
target/release/nymvisor
target/release/nym-node
retention-days: 30
- id: create-release
@@ -102,7 +99,6 @@ jobs:
target/release/nym-network-statistics
target/release/nym-cli
target/release/nymvisor
target/release/nym-node
push-release-data-client:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-binaries-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
-14
View File
@@ -4,20 +4,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [2024.3-eclipse] (2024-04-22)
- Initial release of the first iteration of the Nym Node
- Improvements to gateway functionality
- IPR development
- Removal of allow list in favour of implementing an exit policy
- Explorer delegation: enables direct delegation to nodes via the Nym Explorer
## [2024.2-fast-and-furious] (2024-03-25)
- Internal testing pre-release
## [2024.1-marabou] (2024-02-15)
**New Features:**
Generated
+120 -382
View File
File diff suppressed because it is too large Load Diff
+1 -6
View File
@@ -24,7 +24,6 @@ members = [
"common/bandwidth-controller",
"common/bin-common",
"common/client-core",
"common/client-core/config-types",
"common/client-core/surb-storage",
"common/client-core/gateways-storage",
"common/client-libs/gateway-client",
@@ -42,7 +41,6 @@ members = [
"common/cosmwasm-smart-contracts/name-service",
"common/cosmwasm-smart-contracts/service-provider-directory",
"common/cosmwasm-smart-contracts/vesting-contract",
"common/country-group",
"common/credential-storage",
"common/credentials",
"common/credential-utils",
@@ -52,7 +50,6 @@ members = [
"common/execute",
"common/exit-policy",
"common/http-api-client",
"common/http-api-common",
"common/inclusion-probability",
"common/ip-packet-requests",
"common/ledger",
@@ -107,7 +104,6 @@ members = [
"nym-browser-extension/storage",
"nym-api/nym-api-requests",
"nym-node",
"nym-node/nym-node-http-api",
"nym-node/nym-node-requests",
"nym-outfox",
"nym-validator-rewarder",
@@ -122,6 +118,7 @@ members = [
# "wasm/full-nym-wasm",
"wasm/mix-fetch",
"wasm/node-tester",
"common/nym-metrics",
]
default-members = [
@@ -135,7 +132,6 @@ default-members = [
"tools/nymvisor",
"explorer-api",
"nym-validator-rewarder",
"nym-node"
]
exclude = [
@@ -171,7 +167,6 @@ dotenvy = "0.15.6"
futures = "0.3.28"
generic-array = "0.14.7"
getrandom = "0.2.10"
humantime-serde = "1.1.1"
hyper = "0.14.27"
k256 = "0.13"
lazy_static = "1.4.0"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client"
version = "1.1.34"
version = "1.1.33"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021"
@@ -8,7 +8,5 @@ use nym_client_core::cli_helpers::client_import_credential::{
};
pub(crate) async fn execute(args: CommonClientImportCredentialArgs) -> Result<(), ClientError> {
import_credential::<CliNativeClient, _>(args).await?;
println!("successfully imported credential!");
Ok(())
import_credential::<CliNativeClient, _>(args).await
}
-3
View File
@@ -23,9 +23,6 @@ pub enum ClientError {
#[error("Attempted to start the client in invalid socket mode")]
InvalidSocketMode,
#[error(transparent)]
ConfigUpgradeFailure(#[from] nym_client_core::config::ConfigUpgradeFailure),
#[error(transparent)]
NymIdError(#[from] NymIdError),
}
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "1.1.34"
version = "1.1.33"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021"
@@ -10,7 +10,5 @@ use nym_client_core::cli_helpers::client_import_credential::{
pub(crate) async fn execute(
args: CommonClientImportCredentialArgs,
) -> Result<(), Socks5ClientError> {
import_credential::<CliSocks5Client, _>(args).await?;
println!("successfully imported credential!");
Ok(())
import_credential::<CliSocks5Client, _>(args).await
}
-3
View File
@@ -23,9 +23,6 @@ pub enum Socks5ClientError {
#[error(transparent)]
ClientCoreError(#[from] ClientCoreError),
#[error(transparent)]
ConfigUpgradeFailure(#[from] nym_client_core::config::ConfigUpgradeFailure),
#[error(transparent)]
NymIdError(#[from] NymIdError),
}
+1
View File
@@ -8,6 +8,7 @@ license = { workspace = true }
repository = { workspace = true }
[dependencies]
atty = "0.2"
const-str = "0.5.6"
clap = { workspace = true, features = ["derive"] }
clap_complete = "4.0"
+2 -3
View File
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
use serde::{Deserialize, Serialize};
use std::io::IsTerminal;
#[cfg(feature = "tracing")]
pub use opentelemetry;
@@ -15,7 +14,7 @@ pub use tracing_subscriber;
#[cfg(feature = "tracing")]
pub use tracing_tree;
#[derive(Debug, Default, Copy, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[derive(Debug, Default, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct LoggingSettings {
// well, we need to implement something here at some point...
@@ -121,7 +120,7 @@ pub fn banner(crate_name: &str, crate_version: &str) -> String {
}
pub fn maybe_print_banner(crate_name: &str, crate_version: &str) {
if std::io::stdout().is_terminal() {
if atty::is(atty::Stream::Stdout) {
println!("{}", banner(crate_name, crate_version))
}
}
+1 -3
View File
@@ -15,7 +15,7 @@ bs58 = { workspace = true }
cfg-if = "1.0.0"
clap = { workspace = true, optional = true }
futures = { workspace = true }
humantime-serde = { workspace = true }
humantime-serde = "1.0"
log = { workspace = true }
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
serde = { workspace = true, features = ["derive"] }
@@ -34,7 +34,6 @@ zeroize = { workspace = true }
nym-id = { path = "../nym-id" }
nym-bandwidth-controller = { path = "../bandwidth-controller" }
nym-config = { path = "../config" }
nym-country-group = { path = "../country-group" }
nym-crypto = { path = "../crypto" }
nym-explorer-client = { path = "../../explorer-api/explorer-client" }
nym-gateway-client = { path = "../client-libs/gateway-client" }
@@ -48,7 +47,6 @@ nym-validator-client = { path = "../client-libs/validator-client", default-featu
nym-task = { path = "../task" }
nym-credential-storage = { path = "../credential-storage" }
nym-network-defaults = { path = "../network-defaults" }
nym-client-core-config-types = { path = "./config-types", features = ["disk-persistence"]}
nym-client-core-surb-storage = { path = "./surb-storage" }
nym-client-core-gateways-storage = { path = "./gateways-storage" }
@@ -1,26 +0,0 @@
[package]
name = "nym-client-core-config-types"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
humantime-serde = { workspace = true }
serde = { workspace = true, features = ["derive"] }
thiserror.workspace = true
url = { workspace = true, features = ["serde"] }
nym-config = { path = "../../config" }
nym-country-group = { path = "../../country-group" }
nym-pemstore = { path = "../../pemstore", optional = true }
# those are pulling so many deps T.T
nym-sphinx-params = { path = "../../nymsphinx/params" }
nym-sphinx-addressing = { path = "../../nymsphinx/addressing" }
[features]
disk-persistence = ["nym-pemstore"]
@@ -1,9 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod v1;
pub mod v2;
// aliases for backwards compatibility
pub use v1 as old_v1_1_20_2;
pub use v2 as old_v1_1_33;
@@ -1,37 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::disk_persistence::old::v2::{
ClientKeysPathsV2, CommonClientPathsV2, DEFAULT_GATEWAY_DETAILS_FILENAME,
};
use crate::error::ConfigUpgradeFailure;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
// aliases for backwards compatibility
pub type CommonClientPathsV1_1_20_2 = CommonClientPathsV1;
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct CommonClientPathsV1 {
pub keys: ClientKeysPathsV2,
pub credentials_database: PathBuf,
pub reply_surb_database: PathBuf,
}
impl CommonClientPathsV1 {
pub fn upgrade_default(self) -> Result<CommonClientPathsV2, ConfigUpgradeFailure> {
let data_dir = self
.reply_surb_database
.parent()
.ok_or_else(|| ConfigUpgradeFailure {
current_version: "1.1.20-2".to_string(),
})?;
Ok(CommonClientPathsV2 {
keys: self.keys,
gateway_details: data_dir.join(DEFAULT_GATEWAY_DETAILS_FILENAME),
credentials_database: self.credentials_database,
reply_surb_database: self.reply_surb_database,
})
}
}
@@ -1,10 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use thiserror::Error;
#[derive(Debug, Error)]
#[error("unable to upgrade config file from `{current_version}`")]
pub struct ConfigUpgradeFailure {
pub current_version: String,
}
-602
View File
@@ -1,602 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_config::defaults::NymNetworkDetails;
use nym_sphinx_addressing::Recipient;
use nym_sphinx_params::{PacketSize, PacketType};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use url::Url;
#[cfg(feature = "disk-persistence")]
pub mod disk_persistence;
pub mod error;
pub mod old;
pub use error::ConfigUpgradeFailure;
// 'DEBUG'
const DEFAULT_ACK_WAIT_MULTIPLIER: f64 = 1.5;
const DEFAULT_ACK_WAIT_ADDITION: Duration = Duration::from_millis(1_500);
const DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(200);
const DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(20);
const DEFAULT_AVERAGE_PACKET_DELAY: Duration = Duration::from_millis(50);
const DEFAULT_TOPOLOGY_REFRESH_RATE: Duration = Duration::from_secs(5 * 60); // every 5min
const DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT: Duration = Duration::from_millis(5_000);
const DEFAULT_MAX_STARTUP_GATEWAY_WAITING_PERIOD: Duration = Duration::from_secs(70 * 60); // 70min -> full epoch (1h) + a bit of overhead
// Set this to a high value for now, so that we don't risk sporadic timeouts that might cause
// bought bandwidth tokens to not have time to be spent; Once we remove the gateway from the
// bandwidth bridging protocol, we can come back to a smaller timeout value
const DEFAULT_GATEWAY_RESPONSE_TIMEOUT: Duration = Duration::from_secs(5 * 60);
const DEFAULT_COVER_TRAFFIC_PRIMARY_SIZE_RATIO: f64 = 0.70;
// reply-surbs related:
// define when to request
// clients/client-core/src/client/replies/reply_storage/surb_storage.rs
const DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD: usize = 10;
const DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD: usize = 200;
// define how much to request at once
// clients/client-core/src/client/replies/reply_controller.rs
const DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE: u32 = 10;
const DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE: u32 = 100;
const DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE: u32 = 500;
const DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD: Duration = Duration::from_secs(10);
const DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD: Duration = Duration::from_secs(5 * 60);
// 12 hours
const DEFAULT_MAXIMUM_REPLY_SURB_AGE: Duration = Duration::from_secs(12 * 60 * 60);
// 24 hours
const DEFAULT_MAXIMUM_REPLY_KEY_AGE: Duration = Duration::from_secs(24 * 60 * 60);
pub use nym_country_group::CountryGroup;
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Config {
pub client: Client,
#[serde(default)]
pub debug: DebugConfig,
}
impl Config {
pub fn new<S1, S2>(id: S1, version: S2) -> Self
where
S1: Into<String>,
S2: Into<String>,
{
Config {
client: Client::new_default(id, version),
debug: Default::default(),
}
}
pub fn from_client_config(client: Client, debug: DebugConfig) -> Self {
Config { client, debug }
}
pub fn validate(&self) -> bool {
self.debug.validate()
}
pub fn with_debug_config(mut self, debug: DebugConfig) -> Self {
self.debug = debug;
self
}
pub fn with_disabled_credentials(mut self, disabled_credentials_mode: bool) -> Self {
self.client.disabled_credentials_mode = disabled_credentials_mode;
self
}
pub fn with_custom_nyxd(mut self, urls: Vec<Url>) -> Self {
self.client.nyxd_urls = urls;
self
}
pub fn set_custom_nyxd(&mut self, nyxd_urls: Vec<Url>) {
self.client.nyxd_urls = nyxd_urls;
}
pub fn with_custom_nym_apis(mut self, nym_api_urls: Vec<Url>) -> Self {
self.client.nym_api_urls = nym_api_urls;
self
}
pub fn set_custom_nym_apis(&mut self, nym_api_urls: Vec<Url>) {
self.client.nym_api_urls = nym_api_urls;
}
pub fn with_high_default_traffic_volume(mut self, enabled: bool) -> Self {
if enabled {
self.set_high_default_traffic_volume();
}
self
}
pub fn with_packet_type(mut self, packet_type: PacketType) -> Self {
self.debug.traffic.packet_type = packet_type;
self
}
pub fn set_high_default_traffic_volume(&mut self) {
self.debug.traffic.average_packet_delay = Duration::from_millis(10);
// basically don't really send cover messages
self.debug.cover_traffic.loop_cover_traffic_average_delay =
Duration::from_millis(2_000_000);
// 250 "real" messages / s
self.debug.traffic.message_sending_average_delay = Duration::from_millis(4);
}
pub fn with_disabled_poisson_process(mut self, disabled: bool) -> Self {
if disabled {
self.set_no_poisson_process()
}
self
}
pub fn set_no_poisson_process(&mut self) {
self.debug.traffic.disable_main_poisson_packet_distribution = true;
}
pub fn with_disabled_cover_traffic(mut self, disabled: bool) -> Self {
if disabled {
self.set_no_cover_traffic()
}
self
}
pub fn set_no_cover_traffic(&mut self) {
self.debug.cover_traffic.disable_loop_cover_traffic_stream = true;
self.debug.traffic.disable_main_poisson_packet_distribution = true;
}
pub fn with_disabled_cover_traffic_with_keepalive(mut self, disabled: bool) -> Self {
if disabled {
self.set_no_cover_traffic_with_keepalive()
}
self
}
pub fn set_no_cover_traffic_with_keepalive(&mut self) {
self.debug.traffic.disable_main_poisson_packet_distribution = true;
self.debug.cover_traffic.loop_cover_traffic_average_delay = Duration::from_secs(5);
}
pub fn with_disabled_topology_refresh(mut self, disable_topology_refresh: bool) -> Self {
self.debug.topology.disable_refreshing = disable_topology_refresh;
self
}
pub fn with_topology_structure(mut self, topology_structure: TopologyStructure) -> Self {
self.set_topology_structure(topology_structure);
self
}
pub fn set_topology_structure(&mut self, topology_structure: TopologyStructure) {
self.debug.topology.topology_structure = topology_structure;
}
pub fn with_no_per_hop_delays(mut self, no_per_hop_delays: bool) -> Self {
if no_per_hop_delays {
self.set_no_per_hop_delays()
}
self
}
pub fn set_no_per_hop_delays(&mut self) {
self.debug.traffic.average_packet_delay = Duration::ZERO;
self.debug.acknowledgements.average_ack_delay = Duration::ZERO;
}
pub fn with_secondary_packet_size(mut self, secondary_packet_size: Option<PacketSize>) -> Self {
self.set_secondary_packet_size(secondary_packet_size);
self
}
pub fn set_secondary_packet_size(&mut self, secondary_packet_size: Option<PacketSize>) {
self.debug.traffic.secondary_packet_size = secondary_packet_size;
}
pub fn set_custom_version(&mut self, version: &str) {
self.client.version = version.to_string();
}
pub fn get_id(&self) -> String {
self.client.id.clone()
}
pub fn get_disabled_credentials_mode(&self) -> bool {
self.client.disabled_credentials_mode
}
pub fn get_validator_endpoints(&self) -> Vec<Url> {
self.client.nyxd_urls.clone()
}
pub fn get_nym_api_endpoints(&self) -> Vec<Url> {
self.client.nym_api_urls.clone()
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
// note: the deny_unknown_fields is VITAL here to allow upgrades from v1.1.20_2
#[serde(deny_unknown_fields)]
pub struct Client {
/// Version of the client for which this configuration was created.
pub version: String,
/// ID specifies the human readable ID of this particular client.
pub id: String,
/// Indicates whether this client is running in a disabled credentials mode, thus attempting
/// to claim bandwidth without presenting bandwidth credentials.
// TODO: this should be moved to `debug.gateway_connection`
#[serde(default)]
pub disabled_credentials_mode: bool,
/// Addresses to nyxd validators via which the client can communicate with the chain.
#[serde(alias = "validator_urls")]
pub nyxd_urls: Vec<Url>,
/// Addresses to APIs running on validator from which the client gets the view of the network.
#[serde(alias = "validator_api_urls")]
pub nym_api_urls: Vec<Url>,
}
impl Client {
pub fn new_default<S1, S2>(id: S1, version: S2) -> Self
where
S1: Into<String>,
S2: Into<String>,
{
let network = NymNetworkDetails::new_mainnet();
let nyxd_urls = network
.endpoints
.iter()
.map(|validator| validator.nyxd_url())
.collect();
let nym_api_urls = network
.endpoints
.iter()
.filter_map(|validator| validator.api_url())
.collect::<Vec<_>>();
Client {
version: version.into(),
id: id.into(),
disabled_credentials_mode: true,
nyxd_urls,
nym_api_urls,
}
}
pub fn new<S: Into<String>>(
id: S,
version: S,
disabled_credentials_mode: bool,
nyxd_urls: Vec<Url>,
nym_api_urls: Vec<Url>,
) -> Self {
Client {
version: version.into(),
id: id.into(),
disabled_credentials_mode,
nyxd_urls,
nym_api_urls,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct Traffic {
/// The parameter of Poisson distribution determining how long, on average,
/// sent packet is going to be delayed at any given mix node.
/// So for a packet going through three mix nodes, on average, it will take three times this value
/// until the packet reaches its destination.
#[serde(with = "humantime_serde")]
pub average_packet_delay: Duration,
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take another 'real traffic stream' message to be sent.
/// If no real packets are available and cover traffic is enabled,
/// a loop cover message is sent instead in order to preserve the rate.
#[serde(with = "humantime_serde")]
pub message_sending_average_delay: Duration,
/// Controls whether the main packet stream constantly produces packets according to the predefined
/// poisson distribution.
pub disable_main_poisson_packet_distribution: bool,
/// Specifies the packet size used for sent messages.
/// Do not override it unless you understand the consequences of that change.
pub primary_packet_size: PacketSize,
/// Specifies the optional auxiliary packet size for optimizing message streams.
/// Note that its use decreases overall anonymity.
/// Do not set it it unless you understand the consequences of that change.
pub secondary_packet_size: Option<PacketSize>,
pub packet_type: PacketType,
}
impl Traffic {
pub fn validate(&self) -> bool {
if let Some(secondary_packet_size) = self.secondary_packet_size {
if secondary_packet_size == PacketSize::AckPacket
|| secondary_packet_size == self.primary_packet_size
{
return false;
}
}
true
}
}
impl Default for Traffic {
fn default() -> Self {
Traffic {
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
disable_main_poisson_packet_distribution: false,
primary_packet_size: PacketSize::RegularPacket,
secondary_packet_size: None,
packet_type: PacketType::Mix,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct CoverTraffic {
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take for another loop cover traffic message to be sent.
#[serde(with = "humantime_serde")]
pub loop_cover_traffic_average_delay: Duration,
/// Specifies the ratio of `primary_packet_size` to `secondary_packet_size` used in cover traffic.
/// Only applicable if `secondary_packet_size` is enabled.
pub cover_traffic_primary_size_ratio: f64,
/// Controls whether the dedicated loop cover traffic stream should be enabled.
/// (and sending packets, on average, every [Self::loop_cover_traffic_average_delay])
pub disable_loop_cover_traffic_stream: bool,
}
impl Default for CoverTraffic {
fn default() -> Self {
CoverTraffic {
loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
cover_traffic_primary_size_ratio: DEFAULT_COVER_TRAFFIC_PRIMARY_SIZE_RATIO,
disable_loop_cover_traffic_stream: false,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct GatewayConnection {
/// How long we're willing to wait for a response to a message sent to the gateway,
/// before giving up on it.
#[serde(with = "humantime_serde")]
pub gateway_response_timeout: Duration,
}
impl Default for GatewayConnection {
fn default() -> Self {
GatewayConnection {
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct Acknowledgements {
/// The parameter of Poisson distribution determining how long, on average,
/// sent acknowledgement is going to be delayed at any given mix node.
/// So for an ack going through three mix nodes, on average, it will take three times this value
/// until the packet reaches its destination.
#[serde(with = "humantime_serde")]
pub average_ack_delay: Duration,
/// Value multiplied with the expected round trip time of an acknowledgement packet before
/// it is assumed it was lost and retransmission of the data packet happens.
/// In an ideal network with 0 latency, this value would have been 1.
pub ack_wait_multiplier: f64,
/// Value added to the expected round trip time of an acknowledgement packet before
/// it is assumed it was lost and retransmission of the data packet happens.
/// In an ideal network with 0 latency, this value would have been 0.
#[serde(with = "humantime_serde")]
pub ack_wait_addition: Duration,
}
impl Default for Acknowledgements {
fn default() -> Self {
Acknowledgements {
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
ack_wait_addition: DEFAULT_ACK_WAIT_ADDITION,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct Topology {
/// The uniform delay every which clients are querying the directory server
/// to try to obtain a compatible network topology to send sphinx packets through.
#[serde(with = "humantime_serde")]
pub topology_refresh_rate: Duration,
/// During topology refresh, test packets are sent through every single possible network
/// path. This timeout determines waiting period until it is decided that the packet
/// did not reach its destination.
#[serde(with = "humantime_serde")]
pub topology_resolution_timeout: Duration,
/// Specifies whether the client should not refresh the network topology after obtaining
/// the first valid instance.
/// Supersedes `topology_refresh_rate_ms`.
pub disable_refreshing: bool,
/// Defines how long the client is going to wait on startup for its gateway to come online,
/// before abandoning the procedure.
#[serde(with = "humantime_serde")]
pub max_startup_gateway_waiting_period: Duration,
/// Specifies the mixnode topology to be used for sending packets.
pub topology_structure: TopologyStructure,
}
#[allow(clippy::large_enum_variant)]
#[derive(Default, Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum TopologyStructure {
#[default]
NymApi,
GeoAware(GroupBy),
}
#[allow(clippy::large_enum_variant)]
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum GroupBy {
CountryGroup(CountryGroup),
NymAddress(Recipient),
}
impl std::fmt::Display for GroupBy {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GroupBy::CountryGroup(group) => write!(f, "group: {group}"),
GroupBy::NymAddress(address) => write!(f, "address: {address}"),
}
}
}
impl Default for Topology {
fn default() -> Self {
Topology {
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
disable_refreshing: false,
max_startup_gateway_waiting_period: DEFAULT_MAX_STARTUP_GATEWAY_WAITING_PERIOD,
topology_structure: TopologyStructure::default(),
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct ReplySurbs {
/// Defines the minimum number of reply surbs the client wants to keep in its storage at all times.
/// It can only allow to go below that value if its to request additional reply surbs.
pub minimum_reply_surb_storage_threshold: usize,
/// Defines the maximum number of reply surbs the client wants to keep in its storage at any times.
pub maximum_reply_surb_storage_threshold: usize,
/// Defines the minimum number of reply surbs the client would request.
pub minimum_reply_surb_request_size: u32,
/// Defines the maximum number of reply surbs the client would request.
pub maximum_reply_surb_request_size: u32,
/// Defines the maximum number of reply surbs a remote party is allowed to request from this client at once.
pub maximum_allowed_reply_surb_request_size: u32,
/// Defines maximum amount of time the client is going to wait for reply surbs before explicitly asking
/// for more even though in theory they wouldn't need to.
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_rerequest_waiting_period: Duration,
/// Defines maximum amount of time the client is going to wait for reply surbs before
/// deciding it's never going to get them and would drop all pending messages
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_drop_waiting_period: Duration,
/// Defines maximum amount of time given reply surb is going to be valid for.
/// This is going to be superseded by key rotation once implemented.
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_age: Duration,
/// Defines maximum amount of time given reply key is going to be valid for.
/// This is going to be superseded by key rotation once implemented.
#[serde(with = "humantime_serde")]
pub maximum_reply_key_age: Duration,
/// Specifies the number of mixnet hops the packet should go through. If not specified, then
/// the default value is used.
pub surb_mix_hops: Option<u8>,
}
impl Default for ReplySurbs {
fn default() -> Self {
ReplySurbs {
minimum_reply_surb_storage_threshold: DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
maximum_reply_surb_storage_threshold: DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD,
minimum_reply_surb_request_size: DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE,
maximum_reply_surb_request_size: DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE,
maximum_allowed_reply_surb_request_size: DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE,
maximum_reply_surb_rerequest_waiting_period:
DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD,
maximum_reply_surb_drop_waiting_period: DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD,
maximum_reply_surb_age: DEFAULT_MAXIMUM_REPLY_SURB_AGE,
maximum_reply_key_age: DEFAULT_MAXIMUM_REPLY_KEY_AGE,
surb_mix_hops: None,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct DebugConfig {
/// Defines all configuration options related to traffic streams.
pub traffic: Traffic,
/// Defines all configuration options related to cover traffic stream(s).
pub cover_traffic: CoverTraffic,
/// Defines all configuration options related to the gateway connection.
pub gateway_connection: GatewayConnection,
/// Defines all configuration options related to acknowledgements, such as delays or wait timeouts.
pub acknowledgements: Acknowledgements,
/// Defines all configuration options related topology, such as refresh rates or timeouts.
pub topology: Topology,
/// Defines all configuration options related to reply SURBs.
pub reply_surbs: ReplySurbs,
}
impl DebugConfig {
pub fn validate(&self) -> bool {
// no other sections have explicit requirements (yet)
self.traffic.validate()
}
}
// it could be derived, sure, but I'd rather have an explicit implementation in case we had to change
// something manually at some point
#[allow(clippy::derivable_impls)]
impl Default for DebugConfig {
fn default() -> Self {
DebugConfig {
traffic: Default::default(),
cover_traffic: Default::default(),
gateway_connection: Default::default(),
acknowledgements: Default::default(),
topology: Default::default(),
reply_surbs: Default::default(),
}
}
}
@@ -1,15 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod v1;
pub mod v2;
pub mod v3;
pub mod v4;
pub mod v5;
// aliases for backwards compatibility
pub use v1 as old_config_v1_1_13;
pub use v2 as old_config_v1_1_20;
pub use v3 as old_config_v1_1_20_2;
pub use v4 as old_config_v1_1_30;
pub use v5 as old_config_v1_1_33;
@@ -27,12 +27,6 @@ pub struct GatewayRegistration {
pub registration_timestamp: OffsetDateTime,
}
impl GatewayRegistration {
pub fn gateway_id(&self) -> identity::PublicKey {
self.details.gateway_id()
}
}
impl<'a> From<&'a GatewayRegistration> for RawRegisteredGateway {
fn from(value: &'a GatewayRegistration) -> Self {
RawRegisteredGateway {
@@ -304,12 +298,3 @@ pub struct CustomGatewayDetails {
pub gateway_id: identity::PublicKey,
pub data: Option<Vec<u8>>,
}
impl CustomGatewayDetails {
pub fn new(gateway_id: identity::PublicKey) -> CustomGatewayDetails {
Self {
gateway_id,
data: None,
}
}
}
@@ -7,7 +7,7 @@ use std::error::Error;
use tokio::sync::Mutex;
#[cfg(not(target_arch = "wasm32"))]
use crate::config::disk_persistence::ClientKeysPaths;
use crate::config::disk_persistence::keys_paths::ClientKeysPaths;
#[cfg(not(target_arch = "wasm32"))]
use nym_crypto::asymmetric::{encryption, identity};
#[cfg(not(target_arch = "wasm32"))]
@@ -1,5 +1,6 @@
use crate::config::GroupBy;
use log::{debug, error};
use std::{collections::HashMap, fmt};
use log::{debug, error, info};
use nym_explorer_client::{ExplorerClient, PrettyDetailedMixNodeBond};
use nym_network_defaults::var_names::EXPLORER_API;
use nym_topology::{
@@ -9,11 +10,11 @@ use nym_topology::{
};
use nym_validator_client::client::MixId;
use rand::{prelude::SliceRandom, thread_rng};
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use tap::TapOptional;
use url::Url;
pub use nym_country_group::CountryGroup;
use crate::config::GroupBy;
const MIN_NODES_PER_LAYER: usize = 1;
@@ -37,6 +38,158 @@ fn create_explorer_client() -> Option<ExplorerClient> {
Some(client)
}
#[derive(Copy, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub enum CountryGroup {
Europe,
NorthAmerica,
SouthAmerica,
Oceania,
Asia,
Africa,
Unknown,
}
impl CountryGroup {
// We map contry codes into group, which initially are continent codes to a first approximation,
// but we do it manually to reserve the right to tweak this distribution for our purposes.
// NOTE: I did this quickly and it's not a complete list of all countries, but only those that
// were present in the network at the time. Please add more as needed.
fn new(country_code: &str) -> Self {
let country_code = country_code.to_uppercase();
use CountryGroup::*;
match country_code.as_ref() {
// Europe
"AT" => Europe,
"BG" => Europe,
"CH" => Europe,
"CY" => Europe,
"CZ" => Europe,
"DE" => Europe,
"DK" => Europe,
"ES" => Europe,
"FI" => Europe,
"FR" => Europe,
"GB" => Europe,
"GR" => Europe,
"IE" => Europe,
"IT" => Europe,
"LT" => Europe,
"LU" => Europe,
"LV" => Europe,
"MD" => Europe,
"MT" => Europe,
"NL" => Europe,
"NO" => Europe,
"PL" => Europe,
"RO" => Europe,
"SE" => Europe,
"SK" => Europe,
"TR" => Europe,
"UA" => Europe,
// North America
"CA" => NorthAmerica,
"MX" => NorthAmerica,
"US" => NorthAmerica,
// South America
"AR" => SouthAmerica,
"BR" => SouthAmerica,
"CL" => SouthAmerica,
"CO" => SouthAmerica,
"CR" => SouthAmerica,
"GT" => SouthAmerica,
// Oceania
"AU" => Oceania,
// Asia
"AM" => Asia,
"BH" => Asia,
"CN" => Asia,
"GE" => Asia,
"HK" => Asia,
"ID" => Asia,
"IL" => Asia,
"IN" => Asia,
"JP" => Asia,
"KH" => Asia,
"KR" => Asia,
"KZ" => Asia,
"MY" => Asia,
"RU" => Asia,
"SG" => Asia,
"TH" => Asia,
"VN" => Asia,
// Africa
"SC" => Africa,
"UG" => Africa,
"ZA" => Africa,
// And group level codes work too
"EU" => Europe,
"NA" => NorthAmerica,
"SA" => SouthAmerica,
"OC" => Oceania,
"AS" => Asia,
"AF" => Africa,
// And some aliases
"EUROPE" => Europe,
"NORTHAMERICA" => NorthAmerica,
"SOUTHAMERICA" => SouthAmerica,
"OCEANIA" => Oceania,
"ASIA" => Asia,
"AFRICA" => Africa,
_ => {
info!("Unknown country code: {}", country_code);
Unknown
}
}
}
}
impl fmt::Display for CountryGroup {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use CountryGroup::*;
match self {
Europe => write!(f, "EU"),
NorthAmerica => write!(f, "NA"),
SouthAmerica => write!(f, "SA"),
Oceania => write!(f, "OC"),
Asia => write!(f, "AS"),
Africa => write!(f, "AF"),
Unknown => write!(f, "Unknown"),
}
}
}
impl std::str::FromStr for CountryGroup {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let group = CountryGroup::new(s);
if group == CountryGroup::Unknown {
Err(())
} else {
Ok(group)
}
}
}
impl CountryGroup {
#[allow(unused)]
fn known(self) -> Option<CountryGroup> {
use CountryGroup::*;
match self {
Europe | NorthAmerica | SouthAmerica | Oceania | Asia | Africa => Some(self),
Unknown => None,
}
}
}
fn group_mixnodes_by_country_code(
mixnodes: Vec<PrettyDetailedMixNodeBond>,
) -> HashMap<CountryGroup, Vec<MixId>> {
@@ -1,53 +1,15 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
pub mod old;
// preserve old structure for easier migration
pub use old::{old_v1_1_20_2, old_v1_1_33};
pub const DEFAULT_REPLY_SURB_DB_FILENAME: &str = "persistent_reply_store.sqlite";
pub const DEFAULT_CREDENTIALS_DB_FILENAME: &str = "credentials_database.db";
pub const DEFAULT_GATEWAYS_DETAILS_DB_FILENAME: &str = "gateways_registrations.sqlite";
pub const DEFAULT_PRIVATE_IDENTITY_KEY_FILENAME: &str = "private_identity.pem";
pub const DEFAULT_PUBLIC_IDENTITY_KEY_FILENAME: &str = "public_identity.pem";
pub const DEFAULT_PRIVATE_ENCRYPTION_KEY_FILENAME: &str = "private_encryption.pem";
pub const DEFAULT_PUBLIC_ENCRYPTION_KEY_FILENAME: &str = "public_encryption.pem";
pub const DEFAULT_ACK_KEY_FILENAME: &str = "ack_key.pem";
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct CommonClientPaths {
pub keys: ClientKeysPaths,
/// Path to the file containing information about gateways used by this client,
/// i.e. details such as their public keys, owner addresses or the network information.
pub gateway_registrations: PathBuf,
/// Path to the database containing bandwidth credentials of this client.
pub credentials_database: PathBuf,
/// Path to the persistent store for received reply surbs, unused encryption keys and used sender tags.
pub reply_surb_database: PathBuf,
}
impl CommonClientPaths {
pub fn new_base<P: AsRef<Path>>(base_data_directory: P) -> Self {
let base_dir = base_data_directory.as_ref();
CommonClientPaths {
credentials_database: base_dir.join(DEFAULT_CREDENTIALS_DB_FILENAME),
reply_surb_database: base_dir.join(DEFAULT_REPLY_SURB_DB_FILENAME),
gateway_registrations: base_dir.join(DEFAULT_GATEWAYS_DETAILS_DB_FILENAME),
keys: ClientKeysPaths::new_base(base_data_directory),
}
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
pub struct ClientKeysPaths {
/// Path to file containing private identity key.
@@ -0,0 +1,43 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::disk_persistence::keys_paths::ClientKeysPaths;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
pub mod keys_paths;
pub mod old_v1_1_20_2;
pub mod old_v1_1_33;
pub const DEFAULT_REPLY_SURB_DB_FILENAME: &str = "persistent_reply_store.sqlite";
pub const DEFAULT_CREDENTIALS_DB_FILENAME: &str = "credentials_database.db";
pub const DEFAULT_GATEWAYS_DETAILS_DB_FILENAME: &str = "gateways_registrations.sqlite";
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct CommonClientPaths {
pub keys: ClientKeysPaths,
/// Path to the file containing information about gateways used by this client,
/// i.e. details such as their public keys, owner addresses or the network information.
pub gateway_registrations: PathBuf,
/// Path to the database containing bandwidth credentials of this client.
pub credentials_database: PathBuf,
/// Path to the persistent store for received reply surbs, unused encryption keys and used sender tags.
pub reply_surb_database: PathBuf,
}
impl CommonClientPaths {
pub fn new_base<P: AsRef<Path>>(base_data_directory: P) -> Self {
let base_dir = base_data_directory.as_ref();
CommonClientPaths {
credentials_database: base_dir.join(DEFAULT_CREDENTIALS_DB_FILENAME),
reply_surb_database: base_dir.join(DEFAULT_REPLY_SURB_DB_FILENAME),
gateway_registrations: base_dir.join(DEFAULT_GATEWAYS_DETAILS_DB_FILENAME),
keys: ClientKeysPaths::new_base(base_data_directory),
}
}
}
@@ -0,0 +1,33 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::disk_persistence::old_v1_1_33::{
ClientKeysPathsV1_1_33, CommonClientPathsV1_1_33, DEFAULT_GATEWAY_DETAILS_FILENAME,
};
use crate::error::ClientCoreError;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct CommonClientPathsV1_1_20_2 {
pub keys: ClientKeysPathsV1_1_33,
pub credentials_database: PathBuf,
pub reply_surb_database: PathBuf,
}
impl CommonClientPathsV1_1_20_2 {
pub fn upgrade_default(self) -> Result<CommonClientPathsV1_1_33, ClientCoreError> {
let data_dir = self.reply_surb_database.parent().ok_or_else(|| {
ClientCoreError::ConfigFileUpgradeFailure {
current_version: "1.1.20-2".to_string(),
}
})?;
Ok(CommonClientPathsV1_1_33 {
keys: self.keys,
gateway_details: data_dir.join(DEFAULT_GATEWAY_DETAILS_FILENAME),
credentials_database: self.credentials_database,
reply_surb_database: self.reply_surb_database,
})
}
}
@@ -1,20 +1,16 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::disk_persistence::ClientKeysPaths;
use crate::disk_persistence::{CommonClientPaths, DEFAULT_GATEWAYS_DETAILS_DB_FILENAME};
use crate::error::ConfigUpgradeFailure;
use crate::config::disk_persistence::keys_paths::ClientKeysPaths;
use crate::config::disk_persistence::{CommonClientPaths, DEFAULT_GATEWAYS_DETAILS_DB_FILENAME};
use crate::error::ClientCoreError;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
pub const DEFAULT_GATEWAY_DETAILS_FILENAME: &str = "gateway_details.json";
// aliases for backwards compatibility
pub type CommonClientPathsV1_1_33 = CommonClientPathsV2;
pub type ClientKeysPathsV1_1_33 = ClientKeysPathsV2;
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
pub struct ClientKeysPathsV2 {
pub struct ClientKeysPathsV1_1_33 {
/// Path to file containing private identity key.
pub private_identity_key_file: PathBuf,
@@ -36,7 +32,7 @@ pub struct ClientKeysPathsV2 {
pub ack_key_file: PathBuf,
}
impl ClientKeysPathsV2 {
impl ClientKeysPathsV1_1_33 {
pub fn upgrade(self) -> ClientKeysPaths {
ClientKeysPaths {
private_identity_key_file: self.private_identity_key_file,
@@ -50,8 +46,8 @@ impl ClientKeysPathsV2 {
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct CommonClientPathsV2 {
pub keys: ClientKeysPathsV2,
pub struct CommonClientPathsV1_1_33 {
pub keys: ClientKeysPathsV1_1_33,
/// Path to the file containing information about gateway used by this client,
/// i.e. details such as its public key, owner address or the network information.
@@ -64,16 +60,15 @@ pub struct CommonClientPathsV2 {
pub reply_surb_database: PathBuf,
}
impl CommonClientPathsV2 {
impl CommonClientPathsV1_1_33 {
// note that during the upgrade process, the caller will need to extract the key and gateway details
// manually and resave them in the new database
pub fn upgrade_default(self) -> Result<CommonClientPaths, ConfigUpgradeFailure> {
let data_dir = self
.gateway_details
.parent()
.ok_or_else(|| ConfigUpgradeFailure {
pub fn upgrade_default(self) -> Result<CommonClientPaths, ClientCoreError> {
let data_dir = self.gateway_details.parent().ok_or_else(|| {
ClientCoreError::ConfigFileUpgradeFailure {
current_version: "1.1.33".to_string(),
})?;
}
})?;
Ok(CommonClientPaths {
keys: self.keys.upgrade(),
+599 -6
View File
@@ -1,9 +1,602 @@
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub use nym_client_core_config_types::disk_persistence;
pub use nym_client_core_config_types::old::{
old_config_v1_1_13, old_config_v1_1_20, old_config_v1_1_20_2, old_config_v1_1_30,
old_config_v1_1_33,
use crate::client::topology_control::geo_aware_provider::CountryGroup;
use nym_config::defaults::NymNetworkDetails;
use nym_sphinx::{
addressing::clients::Recipient,
params::{PacketSize, PacketType},
};
pub use nym_client_core_config_types::*;
use serde::{Deserialize, Serialize};
use std::time::Duration;
use url::Url;
pub mod disk_persistence;
pub mod old_config_v1_1_13;
pub mod old_config_v1_1_20;
pub mod old_config_v1_1_20_2;
pub mod old_config_v1_1_30;
pub mod old_config_v1_1_33;
// 'DEBUG'
const DEFAULT_ACK_WAIT_MULTIPLIER: f64 = 1.5;
const DEFAULT_ACK_WAIT_ADDITION: Duration = Duration::from_millis(1_500);
const DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(200);
const DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(20);
const DEFAULT_AVERAGE_PACKET_DELAY: Duration = Duration::from_millis(50);
const DEFAULT_TOPOLOGY_REFRESH_RATE: Duration = Duration::from_secs(5 * 60); // every 5min
const DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT: Duration = Duration::from_millis(5_000);
const DEFAULT_MAX_STARTUP_GATEWAY_WAITING_PERIOD: Duration = Duration::from_secs(70 * 60); // 70min -> full epoch (1h) + a bit of overhead
// Set this to a high value for now, so that we don't risk sporadic timeouts that might cause
// bought bandwidth tokens to not have time to be spent; Once we remove the gateway from the
// bandwidth bridging protocol, we can come back to a smaller timeout value
const DEFAULT_GATEWAY_RESPONSE_TIMEOUT: Duration = Duration::from_secs(5 * 60);
const DEFAULT_COVER_TRAFFIC_PRIMARY_SIZE_RATIO: f64 = 0.70;
// reply-surbs related:
// define when to request
// clients/client-core/src/client/replies/reply_storage/surb_storage.rs
const DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD: usize = 10;
const DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD: usize = 200;
// define how much to request at once
// clients/client-core/src/client/replies/reply_controller.rs
const DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE: u32 = 10;
const DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE: u32 = 100;
const DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE: u32 = 500;
const DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD: Duration = Duration::from_secs(10);
const DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD: Duration = Duration::from_secs(5 * 60);
// 12 hours
const DEFAULT_MAXIMUM_REPLY_SURB_AGE: Duration = Duration::from_secs(12 * 60 * 60);
// 24 hours
const DEFAULT_MAXIMUM_REPLY_KEY_AGE: Duration = Duration::from_secs(24 * 60 * 60);
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Config {
pub client: Client,
#[serde(default)]
pub debug: DebugConfig,
}
impl Config {
pub fn new<S1, S2>(id: S1, version: S2) -> Self
where
S1: Into<String>,
S2: Into<String>,
{
Config {
client: Client::new_default(id, version),
debug: Default::default(),
}
}
pub fn from_client_config(client: Client, debug: DebugConfig) -> Self {
Config { client, debug }
}
pub fn validate(&self) -> bool {
self.debug.validate()
}
pub fn with_debug_config(mut self, debug: DebugConfig) -> Self {
self.debug = debug;
self
}
pub fn with_disabled_credentials(mut self, disabled_credentials_mode: bool) -> Self {
self.client.disabled_credentials_mode = disabled_credentials_mode;
self
}
pub fn with_custom_nyxd(mut self, urls: Vec<Url>) -> Self {
self.client.nyxd_urls = urls;
self
}
pub fn set_custom_nyxd(&mut self, nyxd_urls: Vec<Url>) {
self.client.nyxd_urls = nyxd_urls;
}
pub fn with_custom_nym_apis(mut self, nym_api_urls: Vec<Url>) -> Self {
self.client.nym_api_urls = nym_api_urls;
self
}
pub fn set_custom_nym_apis(&mut self, nym_api_urls: Vec<Url>) {
self.client.nym_api_urls = nym_api_urls;
}
pub fn with_high_default_traffic_volume(mut self, enabled: bool) -> Self {
if enabled {
self.set_high_default_traffic_volume();
}
self
}
pub fn with_packet_type(mut self, packet_type: PacketType) -> Self {
self.debug.traffic.packet_type = packet_type;
self
}
pub fn set_high_default_traffic_volume(&mut self) {
self.debug.traffic.average_packet_delay = Duration::from_millis(10);
// basically don't really send cover messages
self.debug.cover_traffic.loop_cover_traffic_average_delay =
Duration::from_millis(2_000_000);
// 250 "real" messages / s
self.debug.traffic.message_sending_average_delay = Duration::from_millis(4);
}
pub fn with_disabled_poisson_process(mut self, disabled: bool) -> Self {
if disabled {
self.set_no_poisson_process()
}
self
}
pub fn set_no_poisson_process(&mut self) {
self.debug.traffic.disable_main_poisson_packet_distribution = true;
}
pub fn with_disabled_cover_traffic(mut self, disabled: bool) -> Self {
if disabled {
self.set_no_cover_traffic()
}
self
}
pub fn set_no_cover_traffic(&mut self) {
self.debug.cover_traffic.disable_loop_cover_traffic_stream = true;
self.debug.traffic.disable_main_poisson_packet_distribution = true;
}
pub fn with_disabled_cover_traffic_with_keepalive(mut self, disabled: bool) -> Self {
if disabled {
self.set_no_cover_traffic_with_keepalive()
}
self
}
pub fn set_no_cover_traffic_with_keepalive(&mut self) {
self.debug.traffic.disable_main_poisson_packet_distribution = true;
self.debug.cover_traffic.loop_cover_traffic_average_delay = Duration::from_secs(5);
}
pub fn with_disabled_topology_refresh(mut self, disable_topology_refresh: bool) -> Self {
self.debug.topology.disable_refreshing = disable_topology_refresh;
self
}
pub fn with_topology_structure(mut self, topology_structure: TopologyStructure) -> Self {
self.set_topology_structure(topology_structure);
self
}
pub fn set_topology_structure(&mut self, topology_structure: TopologyStructure) {
self.debug.topology.topology_structure = topology_structure;
}
pub fn with_no_per_hop_delays(mut self, no_per_hop_delays: bool) -> Self {
if no_per_hop_delays {
self.set_no_per_hop_delays()
}
self
}
pub fn set_no_per_hop_delays(&mut self) {
self.debug.traffic.average_packet_delay = Duration::ZERO;
self.debug.acknowledgements.average_ack_delay = Duration::ZERO;
}
pub fn with_secondary_packet_size(mut self, secondary_packet_size: Option<PacketSize>) -> Self {
self.set_secondary_packet_size(secondary_packet_size);
self
}
pub fn set_secondary_packet_size(&mut self, secondary_packet_size: Option<PacketSize>) {
self.debug.traffic.secondary_packet_size = secondary_packet_size;
}
pub fn set_custom_version(&mut self, version: &str) {
self.client.version = version.to_string();
}
pub fn get_id(&self) -> String {
self.client.id.clone()
}
pub fn get_disabled_credentials_mode(&self) -> bool {
self.client.disabled_credentials_mode
}
pub fn get_validator_endpoints(&self) -> Vec<Url> {
self.client.nyxd_urls.clone()
}
pub fn get_nym_api_endpoints(&self) -> Vec<Url> {
self.client.nym_api_urls.clone()
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
// note: the deny_unknown_fields is VITAL here to allow upgrades from v1.1.20_2
#[serde(deny_unknown_fields)]
pub struct Client {
/// Version of the client for which this configuration was created.
pub version: String,
/// ID specifies the human readable ID of this particular client.
pub id: String,
/// Indicates whether this client is running in a disabled credentials mode, thus attempting
/// to claim bandwidth without presenting bandwidth credentials.
// TODO: this should be moved to `debug.gateway_connection`
#[serde(default)]
pub disabled_credentials_mode: bool,
/// Addresses to nyxd validators via which the client can communicate with the chain.
#[serde(alias = "validator_urls")]
pub nyxd_urls: Vec<Url>,
/// Addresses to APIs running on validator from which the client gets the view of the network.
#[serde(alias = "validator_api_urls")]
pub nym_api_urls: Vec<Url>,
}
impl Client {
pub fn new_default<S1, S2>(id: S1, version: S2) -> Self
where
S1: Into<String>,
S2: Into<String>,
{
let network = NymNetworkDetails::new_mainnet();
let nyxd_urls = network
.endpoints
.iter()
.map(|validator| validator.nyxd_url())
.collect();
let nym_api_urls = network
.endpoints
.iter()
.filter_map(|validator| validator.api_url())
.collect::<Vec<_>>();
Client {
version: version.into(),
id: id.into(),
disabled_credentials_mode: true,
nyxd_urls,
nym_api_urls,
}
}
pub fn new<S: Into<String>>(
id: S,
disabled_credentials_mode: bool,
nyxd_urls: Vec<Url>,
nym_api_urls: Vec<Url>,
) -> Self {
Client {
version: env!("CARGO_PKG_VERSION").to_string(),
id: id.into(),
disabled_credentials_mode,
nyxd_urls,
nym_api_urls,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct Traffic {
/// The parameter of Poisson distribution determining how long, on average,
/// sent packet is going to be delayed at any given mix node.
/// So for a packet going through three mix nodes, on average, it will take three times this value
/// until the packet reaches its destination.
#[serde(with = "humantime_serde")]
pub average_packet_delay: Duration,
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take another 'real traffic stream' message to be sent.
/// If no real packets are available and cover traffic is enabled,
/// a loop cover message is sent instead in order to preserve the rate.
#[serde(with = "humantime_serde")]
pub message_sending_average_delay: Duration,
/// Controls whether the main packet stream constantly produces packets according to the predefined
/// poisson distribution.
pub disable_main_poisson_packet_distribution: bool,
/// Specifies the packet size used for sent messages.
/// Do not override it unless you understand the consequences of that change.
pub primary_packet_size: PacketSize,
/// Specifies the optional auxiliary packet size for optimizing message streams.
/// Note that its use decreases overall anonymity.
/// Do not set it it unless you understand the consequences of that change.
pub secondary_packet_size: Option<PacketSize>,
pub packet_type: PacketType,
}
impl Traffic {
pub fn validate(&self) -> bool {
if let Some(secondary_packet_size) = self.secondary_packet_size {
if secondary_packet_size == PacketSize::AckPacket
|| secondary_packet_size == self.primary_packet_size
{
return false;
}
}
true
}
}
impl Default for Traffic {
fn default() -> Self {
Traffic {
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
disable_main_poisson_packet_distribution: false,
primary_packet_size: PacketSize::RegularPacket,
secondary_packet_size: None,
packet_type: PacketType::Mix,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct CoverTraffic {
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take for another loop cover traffic message to be sent.
#[serde(with = "humantime_serde")]
pub loop_cover_traffic_average_delay: Duration,
/// Specifies the ratio of `primary_packet_size` to `secondary_packet_size` used in cover traffic.
/// Only applicable if `secondary_packet_size` is enabled.
pub cover_traffic_primary_size_ratio: f64,
/// Controls whether the dedicated loop cover traffic stream should be enabled.
/// (and sending packets, on average, every [Self::loop_cover_traffic_average_delay])
pub disable_loop_cover_traffic_stream: bool,
}
impl Default for CoverTraffic {
fn default() -> Self {
CoverTraffic {
loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
cover_traffic_primary_size_ratio: DEFAULT_COVER_TRAFFIC_PRIMARY_SIZE_RATIO,
disable_loop_cover_traffic_stream: false,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct GatewayConnection {
/// How long we're willing to wait for a response to a message sent to the gateway,
/// before giving up on it.
#[serde(with = "humantime_serde")]
pub gateway_response_timeout: Duration,
}
impl Default for GatewayConnection {
fn default() -> Self {
GatewayConnection {
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct Acknowledgements {
/// The parameter of Poisson distribution determining how long, on average,
/// sent acknowledgement is going to be delayed at any given mix node.
/// So for an ack going through three mix nodes, on average, it will take three times this value
/// until the packet reaches its destination.
#[serde(with = "humantime_serde")]
pub average_ack_delay: Duration,
/// Value multiplied with the expected round trip time of an acknowledgement packet before
/// it is assumed it was lost and retransmission of the data packet happens.
/// In an ideal network with 0 latency, this value would have been 1.
pub ack_wait_multiplier: f64,
/// Value added to the expected round trip time of an acknowledgement packet before
/// it is assumed it was lost and retransmission of the data packet happens.
/// In an ideal network with 0 latency, this value would have been 0.
#[serde(with = "humantime_serde")]
pub ack_wait_addition: Duration,
}
impl Default for Acknowledgements {
fn default() -> Self {
Acknowledgements {
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
ack_wait_addition: DEFAULT_ACK_WAIT_ADDITION,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct Topology {
/// The uniform delay every which clients are querying the directory server
/// to try to obtain a compatible network topology to send sphinx packets through.
#[serde(with = "humantime_serde")]
pub topology_refresh_rate: Duration,
/// During topology refresh, test packets are sent through every single possible network
/// path. This timeout determines waiting period until it is decided that the packet
/// did not reach its destination.
#[serde(with = "humantime_serde")]
pub topology_resolution_timeout: Duration,
/// Specifies whether the client should not refresh the network topology after obtaining
/// the first valid instance.
/// Supersedes `topology_refresh_rate_ms`.
pub disable_refreshing: bool,
/// Defines how long the client is going to wait on startup for its gateway to come online,
/// before abandoning the procedure.
#[serde(with = "humantime_serde")]
pub max_startup_gateway_waiting_period: Duration,
/// Specifies the mixnode topology to be used for sending packets.
pub topology_structure: TopologyStructure,
}
#[allow(clippy::large_enum_variant)]
#[derive(Default, Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum TopologyStructure {
#[default]
NymApi,
GeoAware(GroupBy),
}
#[allow(clippy::large_enum_variant)]
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum GroupBy {
CountryGroup(CountryGroup),
NymAddress(Recipient),
}
impl std::fmt::Display for GroupBy {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GroupBy::CountryGroup(group) => write!(f, "group: {}", group),
GroupBy::NymAddress(address) => write!(f, "address: {}", address),
}
}
}
impl Default for Topology {
fn default() -> Self {
Topology {
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
disable_refreshing: false,
max_startup_gateway_waiting_period: DEFAULT_MAX_STARTUP_GATEWAY_WAITING_PERIOD,
topology_structure: TopologyStructure::default(),
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct ReplySurbs {
/// Defines the minimum number of reply surbs the client wants to keep in its storage at all times.
/// It can only allow to go below that value if its to request additional reply surbs.
pub minimum_reply_surb_storage_threshold: usize,
/// Defines the maximum number of reply surbs the client wants to keep in its storage at any times.
pub maximum_reply_surb_storage_threshold: usize,
/// Defines the minimum number of reply surbs the client would request.
pub minimum_reply_surb_request_size: u32,
/// Defines the maximum number of reply surbs the client would request.
pub maximum_reply_surb_request_size: u32,
/// Defines the maximum number of reply surbs a remote party is allowed to request from this client at once.
pub maximum_allowed_reply_surb_request_size: u32,
/// Defines maximum amount of time the client is going to wait for reply surbs before explicitly asking
/// for more even though in theory they wouldn't need to.
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_rerequest_waiting_period: Duration,
/// Defines maximum amount of time the client is going to wait for reply surbs before
/// deciding it's never going to get them and would drop all pending messages
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_drop_waiting_period: Duration,
/// Defines maximum amount of time given reply surb is going to be valid for.
/// This is going to be superseded by key rotation once implemented.
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_age: Duration,
/// Defines maximum amount of time given reply key is going to be valid for.
/// This is going to be superseded by key rotation once implemented.
#[serde(with = "humantime_serde")]
pub maximum_reply_key_age: Duration,
/// Specifies the number of mixnet hops the packet should go through. If not specified, then
/// the default value is used.
pub surb_mix_hops: Option<u8>,
}
impl Default for ReplySurbs {
fn default() -> Self {
ReplySurbs {
minimum_reply_surb_storage_threshold: DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
maximum_reply_surb_storage_threshold: DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD,
minimum_reply_surb_request_size: DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE,
maximum_reply_surb_request_size: DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE,
maximum_allowed_reply_surb_request_size: DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE,
maximum_reply_surb_rerequest_waiting_period:
DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD,
maximum_reply_surb_drop_waiting_period: DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD,
maximum_reply_surb_age: DEFAULT_MAXIMUM_REPLY_SURB_AGE,
maximum_reply_key_age: DEFAULT_MAXIMUM_REPLY_KEY_AGE,
surb_mix_hops: None,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct DebugConfig {
/// Defines all configuration options related to traffic streams.
pub traffic: Traffic,
/// Defines all configuration options related to cover traffic stream(s).
pub cover_traffic: CoverTraffic,
/// Defines all configuration options related to the gateway connection.
pub gateway_connection: GatewayConnection,
/// Defines all configuration options related to acknowledgements, such as delays or wait timeouts.
pub acknowledgements: Acknowledgements,
/// Defines all configuration options related topology, such as refresh rates or timeouts.
pub topology: Topology,
/// Defines all configuration options related to reply SURBs.
pub reply_surbs: ReplySurbs,
}
impl DebugConfig {
pub fn validate(&self) -> bool {
// no other sections have explicit requirements (yet)
self.traffic.validate()
}
}
// it could be derived, sure, but I'd rather have an explicit implementation in case we had to change
// something manually at some point
#[allow(clippy::derivable_impls)]
impl Default for DebugConfig {
fn default() -> Self {
DebugConfig {
traffic: Default::default(),
cover_traffic: Default::default(),
gateway_connection: Default::default(),
acknowledgements: Default::default(),
topology: Default::default(),
reply_surbs: Default::default(),
}
}
}
@@ -1,28 +1,23 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::old::v2::{
AcknowledgementsV2, ClientV2, ConfigV2, CoverTrafficV2, DebugConfigV2, GatewayConnectionV2,
LoggingV2, ReplySurbsV2, TopologyV2, TrafficV2, DEFAULT_ACK_WAIT_ADDITION,
DEFAULT_ACK_WAIT_MULTIPLIER, DEFAULT_AVERAGE_PACKET_DELAY, DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY, DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE,
DEFAULT_MAXIMUM_REPLY_KEY_AGE, DEFAULT_MAXIMUM_REPLY_SURB_AGE,
DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD, DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE,
DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD,
use crate::config::old_config_v1_1_20::{
AcknowledgementsV1_1_20, ClientV1_1_20, ConfigV1_1_20, CoverTrafficV1_1_20, DebugConfigV1_1_20,
GatewayConnectionV1_1_20, LoggingV1_1_20, ReplySurbsV1_1_20, TopologyV1_1_20, TrafficV1_1_20,
DEFAULT_ACK_WAIT_ADDITION, DEFAULT_ACK_WAIT_MULTIPLIER, DEFAULT_AVERAGE_PACKET_DELAY,
DEFAULT_GATEWAY_RESPONSE_TIMEOUT, DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE, DEFAULT_MAXIMUM_REPLY_KEY_AGE,
DEFAULT_MAXIMUM_REPLY_SURB_AGE, DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD,
DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE, DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD,
DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD, DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE, DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
DEFAULT_TOPOLOGY_REFRESH_RATE, DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
};
use nym_sphinx_params::PacketSize;
use nym_sphinx::params::PacketSize;
use serde::{Deserialize, Serialize};
use std::marker::PhantomData;
use std::time::Duration;
// aliases for backwards compatibility
pub type OldConfigV1_1_13<T> = ConfigV1<T>;
pub type OldLoggingV1_1_13 = LoggingV1;
pub type OldDebugConfigV1_1_13 = DebugConfigV1;
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum ExtendedPacketSize {
@@ -43,28 +38,28 @@ impl From<ExtendedPacketSize> for PacketSize {
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1<T> {
pub client: ClientV2<T>,
pub struct OldConfigV1_1_13<T> {
pub client: ClientV1_1_20<T>,
#[serde(default)]
pub logging: LoggingV1,
pub logging: OldLoggingV1_1_13,
#[serde(default)]
pub debug: DebugConfigV1,
pub debug: OldDebugConfigV1_1_13,
}
#[derive(Debug, Clone, Default, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct LoggingV1 {}
pub struct OldLoggingV1_1_13 {}
impl From<LoggingV1> for LoggingV2 {
fn from(_value: LoggingV1) -> Self {
LoggingV2 {}
impl From<OldLoggingV1_1_13> for LoggingV1_1_20 {
fn from(_value: OldLoggingV1_1_13) -> Self {
LoggingV1_1_20 {}
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct DebugConfigV1 {
pub struct OldDebugConfigV1_1_13 {
#[serde(with = "humantime_serde")]
pub average_packet_delay: Duration,
@@ -119,10 +114,10 @@ pub struct DebugConfigV1 {
pub maximum_reply_key_age: Duration,
}
impl From<DebugConfigV1> for DebugConfigV2 {
fn from(value: DebugConfigV1) -> Self {
DebugConfigV2 {
traffic: TrafficV2 {
impl From<OldDebugConfigV1_1_13> for DebugConfigV1_1_20 {
fn from(value: OldDebugConfigV1_1_13) -> Self {
DebugConfigV1_1_20 {
traffic: TrafficV1_1_20 {
average_packet_delay: value.average_packet_delay,
message_sending_average_delay: value.message_sending_average_delay,
disable_main_poisson_packet_distribution: value
@@ -130,25 +125,25 @@ impl From<DebugConfigV1> for DebugConfigV2 {
primary_packet_size: PacketSize::RegularPacket,
secondary_packet_size: value.use_extended_packet_size.map(Into::into),
},
cover_traffic: CoverTrafficV2 {
cover_traffic: CoverTrafficV1_1_20 {
loop_cover_traffic_average_delay: value.loop_cover_traffic_average_delay,
disable_loop_cover_traffic_stream: value.disable_loop_cover_traffic_stream,
..CoverTrafficV2::default()
..CoverTrafficV1_1_20::default()
},
gateway_connection: GatewayConnectionV2 {
gateway_connection: GatewayConnectionV1_1_20 {
gateway_response_timeout: value.gateway_response_timeout,
},
acknowledgements: AcknowledgementsV2 {
acknowledgements: AcknowledgementsV1_1_20 {
average_ack_delay: value.average_ack_delay,
ack_wait_multiplier: value.ack_wait_multiplier,
ack_wait_addition: value.ack_wait_addition,
},
topology: TopologyV2 {
topology: TopologyV1_1_20 {
topology_refresh_rate: value.topology_refresh_rate,
topology_resolution_timeout: value.topology_resolution_timeout,
disable_refreshing: false,
},
reply_surbs: ReplySurbsV2 {
reply_surbs: ReplySurbsV1_1_20 {
minimum_reply_surb_storage_threshold: value.minimum_reply_surb_storage_threshold,
maximum_reply_surb_storage_threshold: value.maximum_reply_surb_storage_threshold,
minimum_reply_surb_request_size: value.minimum_reply_surb_request_size,
@@ -166,9 +161,9 @@ impl From<DebugConfigV1> for DebugConfigV2 {
}
}
impl Default for DebugConfigV1 {
impl Default for OldDebugConfigV1_1_13 {
fn default() -> Self {
DebugConfigV1 {
OldDebugConfigV1_1_13 {
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
@@ -195,10 +190,10 @@ impl Default for DebugConfigV1 {
}
}
impl<T, U> From<ConfigV1<T>> for ConfigV2<U> {
fn from(value: ConfigV1<T>) -> Self {
ConfigV2 {
client: ClientV2 {
impl<T, U> From<OldConfigV1_1_13<T>> for ConfigV1_1_20<U> {
fn from(value: OldConfigV1_1_13<T>) -> Self {
ConfigV1_1_20 {
client: ClientV1_1_20 {
version: value.client.version,
id: value.client.id,
disabled_credentials_mode: value.client.disabled_credentials_mode,
@@ -1,11 +1,12 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::old::v3::{
AcknowledgementsV3, CoverTrafficV3, DebugConfigV3, GatewayConnectionV3,
GatewayEndpointConfigV3, ReplySurbsV3, TopologyV3, TrafficV3,
use crate::config::old_config_v1_1_20_2::{
AcknowledgementsV1_1_20_2, CoverTrafficV1_1_20_2, DebugConfigV1_1_20_2,
GatewayConnectionV1_1_20_2, GatewayEndpointConfigV1_1_20_2, ReplySurbsV1_1_20_2,
TopologyV1_1_20_2, TrafficV1_1_20_2,
};
use nym_sphinx_params::{PacketSize, PacketType};
use nym_sphinx::params::{PacketSize, PacketType};
use serde::{Deserialize, Serialize};
use std::marker::PhantomData;
use std::path::PathBuf;
@@ -53,41 +54,27 @@ pub(crate) const DEFAULT_MAXIMUM_REPLY_SURB_AGE: Duration = Duration::from_secs(
// 24 hours
pub(crate) const DEFAULT_MAXIMUM_REPLY_KEY_AGE: Duration = Duration::from_secs(24 * 60 * 60);
// aliases for backwards compatibility
pub type ConfigV1_1_20<T> = ConfigV2<T>;
pub type ClientV1_1_20<T> = ClientV2<T>;
pub type LoggingV1_1_20 = LoggingV2;
pub type DebugConfigV1_1_20 = DebugConfigV2;
pub type GatewayEndpointConfigV1_1_20 = GatewayEndpointConfigV2;
pub type TrafficV1_1_20 = TrafficV2;
pub type CoverTrafficV1_1_20 = CoverTrafficV2;
pub type GatewayConnectionV1_1_20 = GatewayConnectionV2;
pub type AcknowledgementsV1_1_20 = AcknowledgementsV2;
pub type TopologyV1_1_20 = TopologyV2;
pub type ReplySurbsV1_1_20 = ReplySurbsV2;
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV2<T> {
pub client: ClientV2<T>,
pub struct ConfigV1_1_20<T> {
pub client: ClientV1_1_20<T>,
#[serde(default)]
pub logging: LoggingV2,
pub logging: LoggingV1_1_20,
#[serde(default)]
pub debug: DebugConfigV2,
pub debug: DebugConfigV1_1_20,
}
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
pub struct GatewayEndpointConfigV2 {
pub struct GatewayEndpointConfigV1_1_20 {
pub gateway_id: String,
pub gateway_owner: String,
pub gateway_listener: String,
}
impl From<GatewayEndpointConfigV2> for GatewayEndpointConfigV3 {
fn from(value: GatewayEndpointConfigV2) -> Self {
GatewayEndpointConfigV3 {
impl From<GatewayEndpointConfigV1_1_20> for GatewayEndpointConfigV1_1_20_2 {
fn from(value: GatewayEndpointConfigV1_1_20) -> Self {
GatewayEndpointConfigV1_1_20_2 {
gateway_id: value.gateway_id,
gateway_owner: value.gateway_owner,
gateway_listener: value.gateway_listener,
@@ -96,7 +83,7 @@ impl From<GatewayEndpointConfigV2> for GatewayEndpointConfigV3 {
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
pub struct ClientV2<T> {
pub struct ClientV1_1_20<T> {
pub version: String,
pub id: String,
#[serde(default)]
@@ -111,7 +98,7 @@ pub struct ClientV2<T> {
pub public_encryption_key_file: PathBuf,
pub gateway_shared_key_file: PathBuf,
pub ack_key_file: PathBuf,
pub gateway_endpoint: GatewayEndpointConfigV2,
pub gateway_endpoint: GatewayEndpointConfigV1_1_20,
pub database_path: PathBuf,
#[serde(default)]
pub reply_surb_database_path: PathBuf,
@@ -123,11 +110,11 @@ pub struct ClientV2<T> {
#[derive(Debug, Clone, Default, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct LoggingV2 {}
pub struct LoggingV1_1_20 {}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default)]
pub struct TrafficV2 {
pub struct TrafficV1_1_20 {
#[serde(with = "humantime_serde")]
pub average_packet_delay: Duration,
#[serde(with = "humantime_serde")]
@@ -137,9 +124,9 @@ pub struct TrafficV2 {
pub secondary_packet_size: Option<PacketSize>,
}
impl From<TrafficV2> for TrafficV3 {
fn from(value: TrafficV2) -> Self {
TrafficV3 {
impl From<TrafficV1_1_20> for TrafficV1_1_20_2 {
fn from(value: TrafficV1_1_20) -> Self {
TrafficV1_1_20_2 {
average_packet_delay: value.average_packet_delay,
message_sending_average_delay: value.message_sending_average_delay,
disable_main_poisson_packet_distribution: value
@@ -151,9 +138,9 @@ impl From<TrafficV2> for TrafficV3 {
}
}
impl Default for TrafficV2 {
impl Default for TrafficV1_1_20 {
fn default() -> Self {
TrafficV2 {
TrafficV1_1_20 {
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
disable_main_poisson_packet_distribution: false,
@@ -165,16 +152,16 @@ impl Default for TrafficV2 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct CoverTrafficV2 {
pub struct CoverTrafficV1_1_20 {
#[serde(with = "humantime_serde")]
pub loop_cover_traffic_average_delay: Duration,
pub cover_traffic_primary_size_ratio: f64,
pub disable_loop_cover_traffic_stream: bool,
}
impl From<CoverTrafficV2> for CoverTrafficV3 {
fn from(value: CoverTrafficV2) -> Self {
CoverTrafficV3 {
impl From<CoverTrafficV1_1_20> for CoverTrafficV1_1_20_2 {
fn from(value: CoverTrafficV1_1_20) -> Self {
CoverTrafficV1_1_20_2 {
loop_cover_traffic_average_delay: value.loop_cover_traffic_average_delay,
cover_traffic_primary_size_ratio: value.cover_traffic_primary_size_ratio,
disable_loop_cover_traffic_stream: value.disable_loop_cover_traffic_stream,
@@ -182,9 +169,9 @@ impl From<CoverTrafficV2> for CoverTrafficV3 {
}
}
impl Default for CoverTrafficV2 {
impl Default for CoverTrafficV1_1_20 {
fn default() -> Self {
CoverTrafficV2 {
CoverTrafficV1_1_20 {
loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
cover_traffic_primary_size_ratio: DEFAULT_COVER_TRAFFIC_PRIMARY_SIZE_RATIO,
disable_loop_cover_traffic_stream: false,
@@ -194,22 +181,22 @@ impl Default for CoverTrafficV2 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct GatewayConnectionV2 {
pub struct GatewayConnectionV1_1_20 {
#[serde(with = "humantime_serde")]
pub gateway_response_timeout: Duration,
}
impl From<GatewayConnectionV2> for GatewayConnectionV3 {
fn from(value: GatewayConnectionV2) -> Self {
GatewayConnectionV3 {
impl From<GatewayConnectionV1_1_20> for GatewayConnectionV1_1_20_2 {
fn from(value: GatewayConnectionV1_1_20) -> Self {
GatewayConnectionV1_1_20_2 {
gateway_response_timeout: value.gateway_response_timeout,
}
}
}
impl Default for GatewayConnectionV2 {
impl Default for GatewayConnectionV1_1_20 {
fn default() -> Self {
GatewayConnectionV2 {
GatewayConnectionV1_1_20 {
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
}
}
@@ -217,7 +204,7 @@ impl Default for GatewayConnectionV2 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct AcknowledgementsV2 {
pub struct AcknowledgementsV1_1_20 {
#[serde(with = "humantime_serde")]
pub average_ack_delay: Duration,
pub ack_wait_multiplier: f64,
@@ -225,9 +212,9 @@ pub struct AcknowledgementsV2 {
pub ack_wait_addition: Duration,
}
impl From<AcknowledgementsV2> for AcknowledgementsV3 {
fn from(value: AcknowledgementsV2) -> Self {
AcknowledgementsV3 {
impl From<AcknowledgementsV1_1_20> for AcknowledgementsV1_1_20_2 {
fn from(value: AcknowledgementsV1_1_20) -> Self {
AcknowledgementsV1_1_20_2 {
average_ack_delay: value.average_ack_delay,
ack_wait_multiplier: value.ack_wait_multiplier,
ack_wait_addition: value.ack_wait_addition,
@@ -235,9 +222,9 @@ impl From<AcknowledgementsV2> for AcknowledgementsV3 {
}
}
impl Default for AcknowledgementsV2 {
impl Default for AcknowledgementsV1_1_20 {
fn default() -> Self {
AcknowledgementsV2 {
AcknowledgementsV1_1_20 {
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
ack_wait_addition: DEFAULT_ACK_WAIT_ADDITION,
@@ -247,7 +234,7 @@ impl Default for AcknowledgementsV2 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct TopologyV2 {
pub struct TopologyV1_1_20 {
#[serde(with = "humantime_serde")]
pub topology_refresh_rate: Duration,
#[serde(with = "humantime_serde")]
@@ -255,9 +242,9 @@ pub struct TopologyV2 {
pub disable_refreshing: bool,
}
impl From<TopologyV2> for TopologyV3 {
fn from(value: TopologyV2) -> Self {
TopologyV3 {
impl From<TopologyV1_1_20> for TopologyV1_1_20_2 {
fn from(value: TopologyV1_1_20) -> Self {
TopologyV1_1_20_2 {
topology_refresh_rate: value.topology_refresh_rate,
topology_resolution_timeout: value.topology_resolution_timeout,
disable_refreshing: value.disable_refreshing,
@@ -265,9 +252,9 @@ impl From<TopologyV2> for TopologyV3 {
}
}
impl Default for TopologyV2 {
impl Default for TopologyV1_1_20 {
fn default() -> Self {
TopologyV2 {
TopologyV1_1_20 {
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
disable_refreshing: false,
@@ -277,7 +264,7 @@ impl Default for TopologyV2 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct ReplySurbsV2 {
pub struct ReplySurbsV1_1_20 {
pub minimum_reply_surb_storage_threshold: usize,
pub maximum_reply_surb_storage_threshold: usize,
pub minimum_reply_surb_request_size: u32,
@@ -293,9 +280,9 @@ pub struct ReplySurbsV2 {
pub maximum_reply_key_age: Duration,
}
impl From<ReplySurbsV2> for ReplySurbsV3 {
fn from(value: ReplySurbsV2) -> Self {
ReplySurbsV3 {
impl From<ReplySurbsV1_1_20> for ReplySurbsV1_1_20_2 {
fn from(value: ReplySurbsV1_1_20) -> Self {
ReplySurbsV1_1_20_2 {
minimum_reply_surb_storage_threshold: value.minimum_reply_surb_storage_threshold,
maximum_reply_surb_storage_threshold: value.maximum_reply_surb_storage_threshold,
minimum_reply_surb_request_size: value.minimum_reply_surb_request_size,
@@ -310,9 +297,9 @@ impl From<ReplySurbsV2> for ReplySurbsV3 {
}
}
impl Default for ReplySurbsV2 {
impl Default for ReplySurbsV1_1_20 {
fn default() -> Self {
ReplySurbsV2 {
ReplySurbsV1_1_20 {
minimum_reply_surb_storage_threshold: DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
maximum_reply_surb_storage_threshold: DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD,
minimum_reply_surb_request_size: DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE,
@@ -329,18 +316,18 @@ impl Default for ReplySurbsV2 {
#[derive(Debug, Default, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct DebugConfigV2 {
pub traffic: TrafficV2,
pub cover_traffic: CoverTrafficV2,
pub gateway_connection: GatewayConnectionV2,
pub acknowledgements: AcknowledgementsV2,
pub topology: TopologyV2,
pub reply_surbs: ReplySurbsV2,
pub struct DebugConfigV1_1_20 {
pub traffic: TrafficV1_1_20,
pub cover_traffic: CoverTrafficV1_1_20,
pub gateway_connection: GatewayConnectionV1_1_20,
pub acknowledgements: AcknowledgementsV1_1_20,
pub topology: TopologyV1_1_20,
pub reply_surbs: ReplySurbsV1_1_20,
}
impl From<DebugConfigV2> for DebugConfigV3 {
fn from(value: DebugConfigV2) -> Self {
DebugConfigV3 {
impl From<DebugConfigV1_1_20> for DebugConfigV1_1_20_2 {
fn from(value: DebugConfigV1_1_20) -> Self {
DebugConfigV1_1_20_2 {
traffic: value.traffic.into(),
cover_traffic: value.cover_traffic.into(),
gateway_connection: value.gateway_connection.into(),
@@ -1,12 +1,12 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::old::v4::{
AcknowledgementsV4, ClientV4, ConfigV4, CoverTrafficV4, DebugConfigV4, GatewayConnectionV4,
ReplySurbsV4, TopologyV4, TrafficV4,
use crate::config::old_config_v1_1_30::{
AcknowledgementsV1_1_30, ClientV1_1_30, ConfigV1_1_30, CoverTrafficV1_1_30, DebugConfigV1_1_30,
GatewayConnectionV1_1_30, ReplySurbsV1_1_30, TopologyV1_1_30, TrafficV1_1_30,
};
use crate::old::v5::GatewayEndpointConfigV5;
use nym_sphinx_params::{PacketSize, PacketType};
use crate::config::old_config_v1_1_33::OldGatewayEndpointConfigV1_1_33;
use nym_sphinx::params::{PacketSize, PacketType};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use url::Url;
@@ -50,31 +50,18 @@ const DEFAULT_MAXIMUM_REPLY_SURB_AGE: Duration = Duration::from_secs(12 * 60 * 6
// 24 hours
const DEFAULT_MAXIMUM_REPLY_KEY_AGE: Duration = Duration::from_secs(24 * 60 * 60);
// aliases for backwards compatibility
pub type ConfigV1_1_20_2 = ConfigV3;
pub type ClientV1_1_20_2 = ClientV3;
pub type DebugConfigV1_1_20_2 = DebugConfigV3;
pub type GatewayEndpointConfigV1_1_20_2 = GatewayEndpointConfigV3;
pub type TrafficV1_1_20_2 = TrafficV3;
pub type CoverTrafficV1_1_20_2 = CoverTrafficV3;
pub type GatewayConnectionV1_1_20_2 = GatewayConnectionV3;
pub type AcknowledgementsV1_1_20_2 = AcknowledgementsV3;
pub type TopologyV1_1_20_2 = TopologyV3;
pub type ReplySurbsV1_1_20_2 = ReplySurbsV3;
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV3 {
pub client: ClientV3,
pub struct ConfigV1_1_20_2 {
pub client: ClientV1_1_20_2,
#[serde(default)]
pub debug: DebugConfigV3,
pub debug: DebugConfigV1_1_20_2,
}
impl From<ConfigV3> for ConfigV4 {
fn from(value: ConfigV3) -> Self {
ConfigV4 {
impl From<ConfigV1_1_20_2> for ConfigV1_1_30 {
fn from(value: ConfigV1_1_20_2) -> Self {
ConfigV1_1_30 {
client: value.client.into(),
debug: value.debug.into(),
}
@@ -82,7 +69,7 @@ impl From<ConfigV3> for ConfigV4 {
}
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
pub struct GatewayEndpointConfigV3 {
pub struct GatewayEndpointConfigV1_1_20_2 {
/// gateway_id specifies ID of the gateway to which the client should send messages.
/// If initially omitted, a random gateway will be chosen from the available topology.
pub gateway_id: String,
@@ -94,9 +81,9 @@ pub struct GatewayEndpointConfigV3 {
pub gateway_listener: String,
}
impl From<GatewayEndpointConfigV3> for GatewayEndpointConfigV5 {
fn from(value: GatewayEndpointConfigV3) -> Self {
GatewayEndpointConfigV5 {
impl From<GatewayEndpointConfigV1_1_20_2> for OldGatewayEndpointConfigV1_1_33 {
fn from(value: GatewayEndpointConfigV1_1_20_2) -> Self {
OldGatewayEndpointConfigV1_1_33 {
gateway_id: value.gateway_id,
gateway_owner: value.gateway_owner,
gateway_listener: value.gateway_listener,
@@ -105,7 +92,7 @@ impl From<GatewayEndpointConfigV3> for GatewayEndpointConfigV5 {
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
pub struct ClientV3 {
pub struct ClientV1_1_20_2 {
pub version: String,
pub id: String,
@@ -118,12 +105,12 @@ pub struct ClientV3 {
#[serde(alias = "validator_api_urls")]
pub nym_api_urls: Vec<Url>,
pub gateway_endpoint: GatewayEndpointConfigV3,
pub gateway_endpoint: GatewayEndpointConfigV1_1_20_2,
}
impl From<ClientV3> for ClientV4 {
fn from(value: ClientV3) -> Self {
ClientV4 {
impl From<ClientV1_1_20_2> for ClientV1_1_30 {
fn from(value: ClientV1_1_20_2) -> Self {
ClientV1_1_30 {
version: value.version,
id: value.id,
disabled_credentials_mode: value.disabled_credentials_mode,
@@ -135,7 +122,7 @@ impl From<ClientV3> for ClientV4 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default)]
pub struct TrafficV3 {
pub struct TrafficV1_1_20_2 {
#[serde(with = "humantime_serde")]
pub average_packet_delay: Duration,
#[serde(with = "humantime_serde")]
@@ -146,9 +133,9 @@ pub struct TrafficV3 {
pub packet_type: PacketType,
}
impl From<TrafficV3> for TrafficV4 {
fn from(value: TrafficV3) -> Self {
TrafficV4 {
impl From<TrafficV1_1_20_2> for TrafficV1_1_30 {
fn from(value: TrafficV1_1_20_2) -> Self {
TrafficV1_1_30 {
average_packet_delay: value.average_packet_delay,
message_sending_average_delay: value.message_sending_average_delay,
disable_main_poisson_packet_distribution: value
@@ -160,9 +147,9 @@ impl From<TrafficV3> for TrafficV4 {
}
}
impl Default for TrafficV3 {
impl Default for TrafficV1_1_20_2 {
fn default() -> Self {
TrafficV3 {
TrafficV1_1_20_2 {
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
disable_main_poisson_packet_distribution: false,
@@ -175,16 +162,16 @@ impl Default for TrafficV3 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct CoverTrafficV3 {
pub struct CoverTrafficV1_1_20_2 {
#[serde(with = "humantime_serde")]
pub loop_cover_traffic_average_delay: Duration,
pub cover_traffic_primary_size_ratio: f64,
pub disable_loop_cover_traffic_stream: bool,
}
impl From<CoverTrafficV3> for CoverTrafficV4 {
fn from(value: CoverTrafficV3) -> Self {
CoverTrafficV4 {
impl From<CoverTrafficV1_1_20_2> for CoverTrafficV1_1_30 {
fn from(value: CoverTrafficV1_1_20_2) -> Self {
CoverTrafficV1_1_30 {
loop_cover_traffic_average_delay: value.loop_cover_traffic_average_delay,
cover_traffic_primary_size_ratio: value.cover_traffic_primary_size_ratio,
disable_loop_cover_traffic_stream: value.disable_loop_cover_traffic_stream,
@@ -192,9 +179,9 @@ impl From<CoverTrafficV3> for CoverTrafficV4 {
}
}
impl Default for CoverTrafficV3 {
impl Default for CoverTrafficV1_1_20_2 {
fn default() -> Self {
CoverTrafficV3 {
CoverTrafficV1_1_20_2 {
loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
cover_traffic_primary_size_ratio: DEFAULT_COVER_TRAFFIC_PRIMARY_SIZE_RATIO,
disable_loop_cover_traffic_stream: false,
@@ -204,22 +191,22 @@ impl Default for CoverTrafficV3 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct GatewayConnectionV3 {
pub struct GatewayConnectionV1_1_20_2 {
#[serde(with = "humantime_serde")]
pub gateway_response_timeout: Duration,
}
impl From<GatewayConnectionV3> for GatewayConnectionV4 {
fn from(value: GatewayConnectionV3) -> Self {
GatewayConnectionV4 {
impl From<GatewayConnectionV1_1_20_2> for GatewayConnectionV1_1_30 {
fn from(value: GatewayConnectionV1_1_20_2) -> Self {
GatewayConnectionV1_1_30 {
gateway_response_timeout: value.gateway_response_timeout,
}
}
}
impl Default for GatewayConnectionV3 {
impl Default for GatewayConnectionV1_1_20_2 {
fn default() -> Self {
GatewayConnectionV3 {
GatewayConnectionV1_1_20_2 {
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
}
}
@@ -227,7 +214,7 @@ impl Default for GatewayConnectionV3 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct AcknowledgementsV3 {
pub struct AcknowledgementsV1_1_20_2 {
#[serde(with = "humantime_serde")]
pub average_ack_delay: Duration,
pub ack_wait_multiplier: f64,
@@ -235,9 +222,9 @@ pub struct AcknowledgementsV3 {
pub ack_wait_addition: Duration,
}
impl From<AcknowledgementsV3> for AcknowledgementsV4 {
fn from(value: AcknowledgementsV3) -> Self {
AcknowledgementsV4 {
impl From<AcknowledgementsV1_1_20_2> for AcknowledgementsV1_1_30 {
fn from(value: AcknowledgementsV1_1_20_2) -> Self {
AcknowledgementsV1_1_30 {
average_ack_delay: value.average_ack_delay,
ack_wait_multiplier: value.ack_wait_multiplier,
ack_wait_addition: value.ack_wait_addition,
@@ -245,9 +232,9 @@ impl From<AcknowledgementsV3> for AcknowledgementsV4 {
}
}
impl Default for AcknowledgementsV3 {
impl Default for AcknowledgementsV1_1_20_2 {
fn default() -> Self {
AcknowledgementsV3 {
AcknowledgementsV1_1_20_2 {
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
ack_wait_addition: DEFAULT_ACK_WAIT_ADDITION,
@@ -257,7 +244,7 @@ impl Default for AcknowledgementsV3 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct TopologyV3 {
pub struct TopologyV1_1_20_2 {
#[serde(with = "humantime_serde")]
pub topology_refresh_rate: Duration,
#[serde(with = "humantime_serde")]
@@ -265,9 +252,9 @@ pub struct TopologyV3 {
pub disable_refreshing: bool,
}
impl Default for TopologyV3 {
impl Default for TopologyV1_1_20_2 {
fn default() -> Self {
TopologyV3 {
TopologyV1_1_20_2 {
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
disable_refreshing: false,
@@ -275,9 +262,9 @@ impl Default for TopologyV3 {
}
}
impl From<TopologyV3> for TopologyV4 {
fn from(value: TopologyV3) -> Self {
TopologyV4 {
impl From<TopologyV1_1_20_2> for TopologyV1_1_30 {
fn from(value: TopologyV1_1_20_2) -> Self {
TopologyV1_1_30 {
topology_refresh_rate: value.topology_refresh_rate,
topology_resolution_timeout: value.topology_resolution_timeout,
disable_refreshing: value.disable_refreshing,
@@ -288,7 +275,7 @@ impl From<TopologyV3> for TopologyV4 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct ReplySurbsV3 {
pub struct ReplySurbsV1_1_20_2 {
pub minimum_reply_surb_storage_threshold: usize,
pub maximum_reply_surb_storage_threshold: usize,
pub minimum_reply_surb_request_size: u32,
@@ -304,9 +291,9 @@ pub struct ReplySurbsV3 {
pub maximum_reply_key_age: Duration,
}
impl Default for ReplySurbsV3 {
impl Default for ReplySurbsV1_1_20_2 {
fn default() -> Self {
ReplySurbsV3 {
ReplySurbsV1_1_20_2 {
minimum_reply_surb_storage_threshold: DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
maximum_reply_surb_storage_threshold: DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD,
minimum_reply_surb_request_size: DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE,
@@ -321,9 +308,9 @@ impl Default for ReplySurbsV3 {
}
}
impl From<ReplySurbsV3> for ReplySurbsV4 {
fn from(value: ReplySurbsV3) -> Self {
ReplySurbsV4 {
impl From<ReplySurbsV1_1_20_2> for ReplySurbsV1_1_30 {
fn from(value: ReplySurbsV1_1_20_2) -> Self {
ReplySurbsV1_1_30 {
minimum_reply_surb_storage_threshold: value.minimum_reply_surb_storage_threshold,
maximum_reply_surb_storage_threshold: value.maximum_reply_surb_storage_threshold,
minimum_reply_surb_request_size: value.minimum_reply_surb_request_size,
@@ -340,18 +327,18 @@ impl From<ReplySurbsV3> for ReplySurbsV4 {
#[derive(Debug, Default, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct DebugConfigV3 {
pub traffic: TrafficV3,
pub cover_traffic: CoverTrafficV3,
pub gateway_connection: GatewayConnectionV3,
pub acknowledgements: AcknowledgementsV3,
pub topology: TopologyV3,
pub reply_surbs: ReplySurbsV3,
pub struct DebugConfigV1_1_20_2 {
pub traffic: TrafficV1_1_20_2,
pub cover_traffic: CoverTrafficV1_1_20_2,
pub gateway_connection: GatewayConnectionV1_1_20_2,
pub acknowledgements: AcknowledgementsV1_1_20_2,
pub topology: TopologyV1_1_20_2,
pub reply_surbs: ReplySurbsV1_1_20_2,
}
impl From<DebugConfigV3> for DebugConfigV4 {
fn from(value: DebugConfigV3) -> Self {
DebugConfigV4 {
impl From<DebugConfigV1_1_20_2> for DebugConfigV1_1_30 {
fn from(value: DebugConfigV1_1_20_2) -> Self {
DebugConfigV1_1_30 {
traffic: value.traffic.into(),
cover_traffic: value.cover_traffic.into(),
gateway_connection: value.gateway_connection.into(),
@@ -1,13 +1,16 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::old::v5::{
AcknowledgementsV5, ClientV5, ConfigV5, CoverTrafficV5, DebugConfigV5, GatewayConnectionV5,
GroupByV5, ReplySurbsV5, TopologyStructureV5, TopologyV5, TrafficV5,
use crate::client::topology_control::geo_aware_provider::CountryGroup;
use crate::config::old_config_v1_1_33::{
AcknowledgementsV1_1_33, ClientV1_1_33, ConfigV1_1_33, CoverTrafficV1_1_33, DebugConfigV1_1_33,
GatewayConnectionV1_1_33, GroupByV1_1_33, ReplySurbsV1_1_33, TopologyStructureV1_1_33,
TopologyV1_1_33, TrafficV1_1_33,
};
use nym_sphinx::{
addressing::clients::Recipient,
params::{PacketSize, PacketType},
};
use crate::CountryGroup;
use nym_sphinx_addressing::Recipient;
use nym_sphinx_params::{PacketSize, PacketType};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use url::Url;
@@ -53,39 +56,27 @@ const DEFAULT_MAXIMUM_REPLY_SURB_AGE: Duration = Duration::from_secs(12 * 60 * 6
// 24 hours
const DEFAULT_MAXIMUM_REPLY_KEY_AGE: Duration = Duration::from_secs(24 * 60 * 60);
// aliases for backwards compatibility
pub type ConfigV1_1_30 = ConfigV4;
pub type ClientV1_1_30 = ClientV4;
pub type DebugConfigV1_1_30 = DebugConfigV4;
pub type TrafficV1_1_30 = TrafficV4;
pub type CoverTrafficV1_1_30 = CoverTrafficV4;
pub type GatewayConnectionV1_1_30 = GatewayConnectionV4;
pub type AcknowledgementsV1_1_30 = AcknowledgementsV4;
pub type TopologyV1_1_30 = TopologyV4;
pub type ReplySurbsV1_1_30 = ReplySurbsV4;
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV4 {
pub client: ClientV4,
pub struct ConfigV1_1_30 {
pub client: ClientV1_1_30,
#[serde(default)]
pub debug: DebugConfigV4,
pub debug: DebugConfigV1_1_30,
}
impl From<ConfigV4> for ConfigV5 {
fn from(value: ConfigV4) -> Self {
ConfigV5 {
client: ClientV5 {
impl From<ConfigV1_1_30> for ConfigV1_1_33 {
fn from(value: ConfigV1_1_30) -> Self {
ConfigV1_1_33 {
client: ClientV1_1_33 {
version: value.client.version,
id: value.client.id,
disabled_credentials_mode: value.client.disabled_credentials_mode,
nyxd_urls: value.client.nyxd_urls,
nym_api_urls: value.client.nym_api_urls,
},
debug: DebugConfigV5 {
traffic: TrafficV5 {
debug: DebugConfigV1_1_33 {
traffic: TrafficV1_1_33 {
average_packet_delay: value.debug.traffic.average_packet_delay,
message_sending_average_delay: value
.debug
@@ -99,7 +90,7 @@ impl From<ConfigV4> for ConfigV5 {
secondary_packet_size: value.debug.traffic.secondary_packet_size,
packet_type: value.debug.traffic.packet_type,
},
cover_traffic: CoverTrafficV5 {
cover_traffic: CoverTrafficV1_1_33 {
loop_cover_traffic_average_delay: value
.debug
.cover_traffic
@@ -113,18 +104,18 @@ impl From<ConfigV4> for ConfigV5 {
.cover_traffic
.disable_loop_cover_traffic_stream,
},
gateway_connection: GatewayConnectionV5 {
gateway_connection: GatewayConnectionV1_1_33 {
gateway_response_timeout: value
.debug
.gateway_connection
.gateway_response_timeout,
},
acknowledgements: AcknowledgementsV5 {
acknowledgements: AcknowledgementsV1_1_33 {
average_ack_delay: value.debug.acknowledgements.average_ack_delay,
ack_wait_multiplier: value.debug.acknowledgements.ack_wait_multiplier,
ack_wait_addition: value.debug.acknowledgements.ack_wait_addition,
},
topology: TopologyV5 {
topology: TopologyV1_1_33 {
topology_refresh_rate: value.debug.topology.topology_refresh_rate,
topology_resolution_timeout: value.debug.topology.topology_resolution_timeout,
disable_refreshing: value.debug.topology.disable_refreshing,
@@ -134,7 +125,7 @@ impl From<ConfigV4> for ConfigV5 {
.max_startup_gateway_waiting_period,
topology_structure: value.debug.topology.topology_structure.into(),
},
reply_surbs: ReplySurbsV5 {
reply_surbs: ReplySurbsV1_1_33 {
minimum_reply_surb_storage_threshold: value
.debug
.reply_surbs
@@ -178,7 +169,7 @@ impl From<ConfigV4> for ConfigV5 {
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
// note: the deny_unknown_fields is VITAL here to allow upgrades from v1.1.20_2
#[serde(deny_unknown_fields)]
pub struct ClientV4 {
pub struct ClientV1_1_30 {
/// Version of the client for which this configuration was created.
pub version: String,
@@ -202,7 +193,7 @@ pub struct ClientV4 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct TrafficV4 {
pub struct TrafficV1_1_30 {
/// The parameter of Poisson distribution determining how long, on average,
/// sent packet is going to be delayed at any given mix node.
/// So for a packet going through three mix nodes, on average, it will take three times this value
@@ -233,9 +224,9 @@ pub struct TrafficV4 {
pub packet_type: PacketType,
}
impl Default for TrafficV4 {
impl Default for TrafficV1_1_30 {
fn default() -> Self {
TrafficV4 {
TrafficV1_1_30 {
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
disable_main_poisson_packet_distribution: false,
@@ -248,7 +239,7 @@ impl Default for TrafficV4 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct CoverTrafficV4 {
pub struct CoverTrafficV1_1_30 {
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take for another loop cover traffic message to be sent.
#[serde(with = "humantime_serde")]
@@ -263,9 +254,9 @@ pub struct CoverTrafficV4 {
pub disable_loop_cover_traffic_stream: bool,
}
impl Default for CoverTrafficV4 {
impl Default for CoverTrafficV1_1_30 {
fn default() -> Self {
CoverTrafficV4 {
CoverTrafficV1_1_30 {
loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
cover_traffic_primary_size_ratio: DEFAULT_COVER_TRAFFIC_PRIMARY_SIZE_RATIO,
disable_loop_cover_traffic_stream: false,
@@ -275,16 +266,16 @@ impl Default for CoverTrafficV4 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct GatewayConnectionV4 {
pub struct GatewayConnectionV1_1_30 {
/// How long we're willing to wait for a response to a message sent to the gateway,
/// before giving up on it.
#[serde(with = "humantime_serde")]
pub gateway_response_timeout: Duration,
}
impl Default for GatewayConnectionV4 {
impl Default for GatewayConnectionV1_1_30 {
fn default() -> Self {
GatewayConnectionV4 {
GatewayConnectionV1_1_30 {
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
}
}
@@ -292,7 +283,7 @@ impl Default for GatewayConnectionV4 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct AcknowledgementsV4 {
pub struct AcknowledgementsV1_1_30 {
/// The parameter of Poisson distribution determining how long, on average,
/// sent acknowledgement is going to be delayed at any given mix node.
/// So for an ack going through three mix nodes, on average, it will take three times this value
@@ -312,9 +303,9 @@ pub struct AcknowledgementsV4 {
pub ack_wait_addition: Duration,
}
impl Default for AcknowledgementsV4 {
impl Default for AcknowledgementsV1_1_30 {
fn default() -> Self {
AcknowledgementsV4 {
AcknowledgementsV1_1_30 {
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
ack_wait_addition: DEFAULT_ACK_WAIT_ADDITION,
@@ -324,7 +315,7 @@ impl Default for AcknowledgementsV4 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct TopologyV4 {
pub struct TopologyV1_1_30 {
/// The uniform delay every which clients are querying the directory server
/// to try to obtain a compatible network topology to send sphinx packets through.
#[serde(with = "humantime_serde")]
@@ -347,23 +338,23 @@ pub struct TopologyV4 {
pub max_startup_gateway_waiting_period: Duration,
/// Specifies the mixnode topology to be used for sending packets.
pub topology_structure: TopologyStructureV4,
pub topology_structure: TopologyStructureV1_1_30,
}
#[allow(clippy::large_enum_variant)]
#[derive(Default, Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum TopologyStructureV4 {
pub enum TopologyStructureV1_1_30 {
#[default]
NymApi,
GeoAware(GroupByV4),
GeoAware(GroupByV1_1_30),
}
impl From<TopologyStructureV4> for TopologyStructureV5 {
fn from(value: TopologyStructureV4) -> Self {
impl From<TopologyStructureV1_1_30> for TopologyStructureV1_1_33 {
fn from(value: TopologyStructureV1_1_30) -> Self {
match value {
TopologyStructureV4::NymApi => TopologyStructureV5::NymApi,
TopologyStructureV4::GeoAware(group_by) => {
TopologyStructureV5::GeoAware(group_by.into())
TopologyStructureV1_1_30::NymApi => TopologyStructureV1_1_33::NymApi,
TopologyStructureV1_1_30::GeoAware(group_by) => {
TopologyStructureV1_1_33::GeoAware(group_by.into())
}
}
}
@@ -371,44 +362,44 @@ impl From<TopologyStructureV4> for TopologyStructureV5 {
#[allow(clippy::large_enum_variant)]
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum GroupByV4 {
pub enum GroupByV1_1_30 {
CountryGroup(CountryGroup),
NymAddress(Recipient),
}
impl From<GroupByV4> for GroupByV5 {
fn from(value: GroupByV4) -> Self {
impl From<GroupByV1_1_30> for GroupByV1_1_33 {
fn from(value: GroupByV1_1_30) -> Self {
match value {
GroupByV4::CountryGroup(country) => GroupByV5::CountryGroup(country),
GroupByV4::NymAddress(addr) => GroupByV5::NymAddress(addr),
GroupByV1_1_30::CountryGroup(country) => GroupByV1_1_33::CountryGroup(country),
GroupByV1_1_30::NymAddress(addr) => GroupByV1_1_33::NymAddress(addr),
}
}
}
impl std::fmt::Display for GroupByV4 {
impl std::fmt::Display for GroupByV1_1_30 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GroupByV4::CountryGroup(group) => write!(f, "group: {}", group),
GroupByV4::NymAddress(address) => write!(f, "address: {}", address),
GroupByV1_1_30::CountryGroup(group) => write!(f, "group: {}", group),
GroupByV1_1_30::NymAddress(address) => write!(f, "address: {}", address),
}
}
}
impl Default for TopologyV4 {
impl Default for TopologyV1_1_30 {
fn default() -> Self {
TopologyV4 {
TopologyV1_1_30 {
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
disable_refreshing: false,
max_startup_gateway_waiting_period: DEFAULT_MAX_STARTUP_GATEWAY_WAITING_PERIOD,
topology_structure: TopologyStructureV4::default(),
topology_structure: TopologyStructureV1_1_30::default(),
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct ReplySurbsV4 {
pub struct ReplySurbsV1_1_30 {
/// Defines the minimum number of reply surbs the client wants to keep in its storage at all times.
/// It can only allow to go below that value if its to request additional reply surbs.
pub minimum_reply_surb_storage_threshold: usize,
@@ -446,9 +437,9 @@ pub struct ReplySurbsV4 {
pub maximum_reply_key_age: Duration,
}
impl Default for ReplySurbsV4 {
impl Default for ReplySurbsV1_1_30 {
fn default() -> Self {
ReplySurbsV4 {
ReplySurbsV1_1_30 {
minimum_reply_surb_storage_threshold: DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
maximum_reply_surb_storage_threshold: DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD,
minimum_reply_surb_request_size: DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE,
@@ -465,22 +456,22 @@ impl Default for ReplySurbsV4 {
#[derive(Debug, Default, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct DebugConfigV4 {
pub struct DebugConfigV1_1_30 {
/// Defines all configuration options related to traffic streams.
pub traffic: TrafficV4,
pub traffic: TrafficV1_1_30,
/// Defines all configuration options related to cover traffic stream(s).
pub cover_traffic: CoverTrafficV4,
pub cover_traffic: CoverTrafficV1_1_30,
/// Defines all configuration options related to the gateway connection.
pub gateway_connection: GatewayConnectionV4,
pub gateway_connection: GatewayConnectionV1_1_30,
/// Defines all configuration options related to acknowledgements, such as delays or wait timeouts.
pub acknowledgements: AcknowledgementsV4,
pub acknowledgements: AcknowledgementsV1_1_30,
/// Defines all configuration options related topology, such as refresh rates or timeouts.
pub topology: TopologyV4,
pub topology: TopologyV1_1_30,
/// Defines all configuration options related to reply SURBs.
pub reply_surbs: ReplySurbsV4,
pub reply_surbs: ReplySurbsV1_1_30,
}
@@ -1,12 +1,15 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{
Acknowledgements, Client, Config, CountryGroup, CoverTraffic, DebugConfig, GatewayConnection,
GroupBy, ReplySurbs, Topology, TopologyStructure, Traffic,
use crate::client::topology_control::geo_aware_provider::CountryGroup;
use crate::config::{
Acknowledgements, Client, Config, CoverTraffic, DebugConfig, GatewayConnection, GroupBy,
ReplySurbs, Topology, TopologyStructure, Traffic,
};
use nym_sphinx::{
addressing::clients::Recipient,
params::{PacketSize, PacketType},
};
use nym_sphinx_addressing::Recipient;
use nym_sphinx_params::{PacketSize, PacketType};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use url::Url;
@@ -52,21 +55,8 @@ const DEFAULT_MAXIMUM_REPLY_SURB_AGE: Duration = Duration::from_secs(12 * 60 * 6
// 24 hours
const DEFAULT_MAXIMUM_REPLY_KEY_AGE: Duration = Duration::from_secs(24 * 60 * 60);
// aliases for backwards compatibility
pub type ConfigV1_1_33 = ConfigV5;
pub type ClientV1_1_33 = ClientV5;
pub type DebugConfigV1_1_33 = DebugConfigV5;
pub type OldGatewayEndpointConfigV1_1_33 = GatewayEndpointConfigV5;
pub type TrafficV1_1_33 = TrafficV5;
pub type CoverTrafficV1_1_33 = CoverTrafficV5;
pub type GatewayConnectionV1_1_33 = GatewayConnectionV5;
pub type AcknowledgementsV1_1_33 = AcknowledgementsV5;
pub type TopologyV1_1_33 = TopologyV5;
pub type ReplySurbsV1_1_33 = ReplySurbsV5;
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
pub struct GatewayEndpointConfigV5 {
pub struct OldGatewayEndpointConfigV1_1_33 {
/// gateway_id specifies ID of the gateway to which the client should send messages.
/// If initially omitted, a random gateway will be chosen from the available topology.
pub gateway_id: String,
@@ -80,15 +70,15 @@ pub struct GatewayEndpointConfigV5 {
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV5 {
pub client: ClientV5,
pub struct ConfigV1_1_33 {
pub client: ClientV1_1_33,
#[serde(default)]
pub debug: DebugConfigV5,
pub debug: DebugConfigV1_1_33,
}
impl From<ConfigV5> for Config {
fn from(value: ConfigV5) -> Self {
impl From<ConfigV1_1_33> for Config {
fn from(value: ConfigV1_1_33) -> Self {
Config {
client: Client {
version: value.client.version,
@@ -188,7 +178,7 @@ impl From<ConfigV5> for Config {
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
// note: the deny_unknown_fields is VITAL here to allow upgrades from v1.1.20_2
#[serde(deny_unknown_fields)]
pub struct ClientV5 {
pub struct ClientV1_1_33 {
/// Version of the client for which this configuration was created.
pub version: String,
@@ -212,7 +202,7 @@ pub struct ClientV5 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct TrafficV5 {
pub struct TrafficV1_1_33 {
/// The parameter of Poisson distribution determining how long, on average,
/// sent packet is going to be delayed at any given mix node.
/// So for a packet going through three mix nodes, on average, it will take three times this value
@@ -243,9 +233,9 @@ pub struct TrafficV5 {
pub packet_type: PacketType,
}
impl Default for TrafficV5 {
impl Default for TrafficV1_1_33 {
fn default() -> Self {
TrafficV5 {
TrafficV1_1_33 {
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
disable_main_poisson_packet_distribution: false,
@@ -258,7 +248,7 @@ impl Default for TrafficV5 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct CoverTrafficV5 {
pub struct CoverTrafficV1_1_33 {
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take for another loop cover traffic message to be sent.
#[serde(with = "humantime_serde")]
@@ -273,9 +263,9 @@ pub struct CoverTrafficV5 {
pub disable_loop_cover_traffic_stream: bool,
}
impl Default for CoverTrafficV5 {
impl Default for CoverTrafficV1_1_33 {
fn default() -> Self {
CoverTrafficV5 {
CoverTrafficV1_1_33 {
loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
cover_traffic_primary_size_ratio: DEFAULT_COVER_TRAFFIC_PRIMARY_SIZE_RATIO,
disable_loop_cover_traffic_stream: false,
@@ -285,16 +275,16 @@ impl Default for CoverTrafficV5 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct GatewayConnectionV5 {
pub struct GatewayConnectionV1_1_33 {
/// How long we're willing to wait for a response to a message sent to the gateway,
/// before giving up on it.
#[serde(with = "humantime_serde")]
pub gateway_response_timeout: Duration,
}
impl Default for GatewayConnectionV5 {
impl Default for GatewayConnectionV1_1_33 {
fn default() -> Self {
GatewayConnectionV5 {
GatewayConnectionV1_1_33 {
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
}
}
@@ -302,7 +292,7 @@ impl Default for GatewayConnectionV5 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct AcknowledgementsV5 {
pub struct AcknowledgementsV1_1_33 {
/// The parameter of Poisson distribution determining how long, on average,
/// sent acknowledgement is going to be delayed at any given mix node.
/// So for an ack going through three mix nodes, on average, it will take three times this value
@@ -322,9 +312,9 @@ pub struct AcknowledgementsV5 {
pub ack_wait_addition: Duration,
}
impl Default for AcknowledgementsV5 {
impl Default for AcknowledgementsV1_1_33 {
fn default() -> Self {
AcknowledgementsV5 {
AcknowledgementsV1_1_33 {
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
ack_wait_addition: DEFAULT_ACK_WAIT_ADDITION,
@@ -334,7 +324,7 @@ impl Default for AcknowledgementsV5 {
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct TopologyV5 {
pub struct TopologyV1_1_33 {
/// The uniform delay every which clients are querying the directory server
/// to try to obtain a compatible network topology to send sphinx packets through.
#[serde(with = "humantime_serde")]
@@ -357,66 +347,68 @@ pub struct TopologyV5 {
pub max_startup_gateway_waiting_period: Duration,
/// Specifies the mixnode topology to be used for sending packets.
pub topology_structure: TopologyStructureV5,
pub topology_structure: TopologyStructureV1_1_33,
}
#[allow(clippy::large_enum_variant)]
#[derive(Default, Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum TopologyStructureV5 {
pub enum TopologyStructureV1_1_33 {
#[default]
NymApi,
GeoAware(GroupByV5),
GeoAware(GroupByV1_1_33),
}
impl From<TopologyStructureV5> for TopologyStructure {
fn from(value: TopologyStructureV5) -> Self {
impl From<TopologyStructureV1_1_33> for TopologyStructure {
fn from(value: TopologyStructureV1_1_33) -> Self {
match value {
TopologyStructureV5::NymApi => TopologyStructure::NymApi,
TopologyStructureV5::GeoAware(group_by) => TopologyStructure::GeoAware(group_by.into()),
TopologyStructureV1_1_33::NymApi => TopologyStructure::NymApi,
TopologyStructureV1_1_33::GeoAware(group_by) => {
TopologyStructure::GeoAware(group_by.into())
}
}
}
}
#[allow(clippy::large_enum_variant)]
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum GroupByV5 {
pub enum GroupByV1_1_33 {
CountryGroup(CountryGroup),
NymAddress(Recipient),
}
impl From<GroupByV5> for GroupBy {
fn from(value: GroupByV5) -> Self {
impl From<GroupByV1_1_33> for GroupBy {
fn from(value: GroupByV1_1_33) -> Self {
match value {
GroupByV5::CountryGroup(country) => GroupBy::CountryGroup(country),
GroupByV5::NymAddress(addr) => GroupBy::NymAddress(addr),
GroupByV1_1_33::CountryGroup(country) => GroupBy::CountryGroup(country),
GroupByV1_1_33::NymAddress(addr) => GroupBy::NymAddress(addr),
}
}
}
impl std::fmt::Display for GroupByV5 {
impl std::fmt::Display for GroupByV1_1_33 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GroupByV5::CountryGroup(group) => write!(f, "group: {}", group),
GroupByV5::NymAddress(address) => write!(f, "address: {}", address),
GroupByV1_1_33::CountryGroup(group) => write!(f, "group: {}", group),
GroupByV1_1_33::NymAddress(address) => write!(f, "address: {}", address),
}
}
}
impl Default for TopologyV5 {
impl Default for TopologyV1_1_33 {
fn default() -> Self {
TopologyV5 {
TopologyV1_1_33 {
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
disable_refreshing: false,
max_startup_gateway_waiting_period: DEFAULT_MAX_STARTUP_GATEWAY_WAITING_PERIOD,
topology_structure: TopologyStructureV5::default(),
topology_structure: TopologyStructureV1_1_33::default(),
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct ReplySurbsV5 {
pub struct ReplySurbsV1_1_33 {
/// Defines the minimum number of reply surbs the client wants to keep in its storage at all times.
/// It can only allow to go below that value if its to request additional reply surbs.
pub minimum_reply_surb_storage_threshold: usize,
@@ -458,9 +450,9 @@ pub struct ReplySurbsV5 {
pub surb_mix_hops: Option<u8>,
}
impl Default for ReplySurbsV5 {
impl Default for ReplySurbsV1_1_33 {
fn default() -> Self {
ReplySurbsV5 {
ReplySurbsV1_1_33 {
minimum_reply_surb_storage_threshold: DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
maximum_reply_surb_storage_threshold: DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD,
minimum_reply_surb_request_size: DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE,
@@ -478,22 +470,22 @@ impl Default for ReplySurbsV5 {
#[derive(Debug, Default, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct DebugConfigV5 {
pub struct DebugConfigV1_1_33 {
/// Defines all configuration options related to traffic streams.
pub traffic: TrafficV5,
pub traffic: TrafficV1_1_33,
/// Defines all configuration options related to cover traffic stream(s).
pub cover_traffic: CoverTrafficV5,
pub cover_traffic: CoverTrafficV1_1_33,
/// Defines all configuration options related to the gateway connection.
pub gateway_connection: GatewayConnectionV5,
pub gateway_connection: GatewayConnectionV1_1_33,
/// Defines all configuration options related to acknowledgements, such as delays or wait timeouts.
pub acknowledgements: AcknowledgementsV5,
pub acknowledgements: AcknowledgementsV1_1_33,
/// Defines all configuration options related topology, such as refresh rates or timeouts.
pub topology: TopologyV5,
pub topology: TopologyV1_1_33,
/// Defines all configuration options related to reply SURBs.
pub reply_surbs: ReplySurbsV5,
pub reply_surbs: ReplySurbsV1_1_33,
}
@@ -16,8 +16,6 @@ thiserror = { workspace = true }
url = { workspace = true }
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
tokio = { version = "1.24.1", features = ["macros"] }
si-scale = "0.2.2"
time.workspace = true
# internal
nym-bandwidth-controller = { path = "../../bandwidth-controller" }
@@ -10,14 +10,9 @@ use futures::stream::{SplitSink, SplitStream};
use futures::{SinkExt, StreamExt};
use log::*;
use nym_gateway_requests::registration::handshake::SharedKeys;
use nym_gateway_requests::ServerResponse;
use nym_task::TaskClient;
use si_scale::helpers::bibytes2;
use std::os::raw::c_int as RawFd;
use std::sync::atomic::{AtomicI64, Ordering};
use std::sync::Arc;
use std::time::Duration;
use time::OffsetDateTime;
use tungstenite::Message;
#[cfg(unix)]
@@ -53,21 +48,6 @@ pub(crate) fn ws_fd(_conn: &WsConn) -> Option<RawFd> {
None
}
// disgusting? absolutely, but does the trick for now
static LAST_LOGGED_BANDWIDTH_TS: AtomicI64 = AtomicI64::new(0);
fn maybe_log_bandwidth(remaining: i64) {
// SAFETY: this value is always populated with valid timestamps
let last =
OffsetDateTime::from_unix_timestamp(LAST_LOGGED_BANDWIDTH_TS.load(Ordering::Relaxed))
.unwrap();
let now = OffsetDateTime::now_utc();
if last + Duration::from_secs(10) < now {
log::info!("remaining bandwidth: {}", bibytes2(remaining as f64));
LAST_LOGGED_BANDWIDTH_TS.store(now.unix_timestamp(), Ordering::Relaxed)
}
}
#[derive(Debug)]
pub(crate) struct PartiallyDelegated {
sink_half: SplitSink<WsConn, Message>,
@@ -76,10 +56,7 @@ pub(crate) struct PartiallyDelegated {
}
impl PartiallyDelegated {
fn recover_received_plaintexts(
ws_msgs: Vec<Message>,
shared_key: &SharedKeys,
) -> Result<Vec<Vec<u8>>, GatewayClientError> {
fn recover_received_plaintexts(ws_msgs: Vec<Message>, shared_key: &SharedKeys) -> Vec<Vec<u8>> {
let mut plaintexts = Vec::with_capacity(ws_msgs.len());
for ws_msg in ws_msgs {
match ws_msg {
@@ -97,32 +74,15 @@ impl PartiallyDelegated {
// TODO: those can return the "send confirmations" - perhaps it should be somehow worked around?
Message::Text(text) => {
trace!(
"received a text message - probably a response to some previous query! - {text}",
"received a text message - probably a response to some previous query! - {}",
text
);
match ServerResponse::try_from(text)
.map_err(|_| GatewayClientError::MalformedResponse)?
{
ServerResponse::Send {
remaining_bandwidth,
} => maybe_log_bandwidth(remaining_bandwidth),
ServerResponse::Error { message } => {
error!("gateway failure: {message}");
return Err(GatewayClientError::GatewayError(message));
}
other => {
warn!(
"received illegal message of type {} in an authenticated client",
other.name()
)
}
}
continue;
}
_ => continue,
}
}
Ok(plaintexts)
plaintexts
}
fn route_socket_messages(
@@ -130,7 +90,7 @@ impl PartiallyDelegated {
packet_router: &PacketRouter,
shared_key: &SharedKeys,
) -> Result<(), GatewayClientError> {
let plaintexts = Self::recover_received_plaintexts(ws_msgs, shared_key)?;
let plaintexts = Self::recover_received_plaintexts(ws_msgs, shared_key);
packet_router.route_received(plaintexts)
}
@@ -170,8 +130,7 @@ impl PartiallyDelegated {
};
if let Err(err) = Self::route_socket_messages(ws_msgs, &packet_router, shared_key.as_ref()) {
log::error!("Route socket messages failed: {err}");
break Err(err)
log::warn!("Route socket messages failed: {err}");
}
}
};
+1 -1
View File
@@ -17,7 +17,7 @@ csv = "1.3.0"
cw-utils = { workspace = true }
futures = { workspace = true }
handlebars = "3.0.1"
humantime-serde = { workspace = true }
humantime-serde = "1.0"
inquire = "0.6.2"
k256 = { workspace = true, features = ["ecdsa", "sha256"] }
log = { workspace = true }
@@ -148,10 +148,6 @@ pub async fn execute(args: Args, client: SigningClient) -> anyhow::Result<()> {
bail!("the provided free pass request has too long expiry (expiry is set to on {expiration_date})")
}
if expiration_date < now {
bail!("the provided free pass expiry is set in the past!")
}
// issuance start
block_until_coconut_is_available(&client).await?;
@@ -1,354 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::cmp::Ordering;
use std::collections::{HashMap, HashSet};
use std::fs;
use std::fs::OpenOptions;
use clap::Parser;
use comfy_table::Table;
use csv::WriterBuilder;
use log::info;
use nym_mixnet_contract_common::ExecuteMsg;
use nym_mixnet_contract_common::ExecuteMsg::{DelegateToMixnode, UndelegateFromMixnode};
use nym_mixnet_contract_common::PendingEpochEventKind::{Delegate, Undelegate};
use nym_validator_client::nyxd::contract_traits::{NymContractsProvider, PagedMixnetQueryClient};
use nym_validator_client::nyxd::Coin;
use crate::context::SigningClient;
use crate::utils::pretty_coin;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub memo: Option<String>,
#[clap(
long,
help = "Input csv files with delegation amounts. Format: (mixID, amount(in NYM))"
)]
pub input: String,
#[clap(
long,
help = "An output file path (CSV format) to create or append a log of results to"
)]
pub output: Option<String>,
}
#[derive(Debug)]
pub struct InputFileRow {
pub mix_id: String,
pub amount: Coin,
}
#[derive(Debug)]
pub struct InputFileReader {
pub rows: Vec<InputFileRow>,
}
impl InputFileReader {
pub fn new(path: &str) -> Result<InputFileReader, anyhow::Error> {
let file_contents = fs::read_to_string(path)?;
let mut rows = Vec::new();
let mut mix_id_set = HashSet::new();
for line in file_contents
.lines()
.map(str::trim)
.filter(|line| !line.is_empty())
{
let tokens: Vec<_> = line.split(',').collect();
if tokens.len() != 2 {
anyhow::bail!("Incorrect format: {}", line);
}
let mix_id = tokens[0].trim().to_string();
let input_amount = tokens[1]
.trim()
.parse::<u128>()
.map_err(|_| anyhow::anyhow!("'{}' has an invalid amount", line))?;
let micro_nym_amount = input_amount * 1_000_000;
if !mix_id_set.insert(mix_id.clone()) {
anyhow::bail!("Duplicate mix_id found: {}", mix_id);
}
rows.push(InputFileRow {
mix_id,
amount: Coin {
amount: micro_nym_amount,
denom: "unym".to_string(),
},
});
}
Ok(InputFileReader { rows })
}
}
fn write_to_csv(
output_details: Vec<[String; 3]>,
output_file: Option<String>,
) -> Result<(), anyhow::Error> {
if let Some(file_path) = output_file {
// Determine if the file exists and is not empty
let file_exists = fs::metadata(&file_path)
.map(|metadata| metadata.len() > 0)
.unwrap_or(false);
// Open the file for appending or creation
let file = OpenOptions::new()
.append(true)
.create(true)
.open(&file_path)?;
if !file_exists {
let mut wtr = csv::Writer::from_writer(&file);
wtr.write_record(["Operation", "Transaction Hash", "Timestamp"])?;
wtr.flush()?;
}
let mut wtr = WriterBuilder::new()
.has_headers(!file_exists)
.from_writer(file);
// Write the details to the CSV file
for detail in output_details {
wtr.write_record(&detail)?;
}
wtr.flush()?;
info!("All operations saved to output file");
}
Ok(())
}
async fn fetch_delegation_data(
client: &SigningClient,
) -> Result<HashMap<String, Coin>, anyhow::Error> {
let address = client.address();
// Fetch all delegations for the user
let delegations = match client.get_all_delegator_delegations(&address).await {
Ok(delegations) => delegations,
Err(e) => {
anyhow::bail!("Error fetching delegations: {}", e)
}
};
// Build a map to make it easier to handle delegation data
let mut existing_delegation_map: HashMap<String, Coin> = HashMap::new();
let mut pending_delegation_map: HashMap<String, Coin> = HashMap::new();
for delegation in delegations {
existing_delegation_map
.insert(delegation.mix_id.to_string(), Coin::from(delegation.amount));
}
// Look for pending delegate / undelegate events which might be of interest to us
let pending_events = match client.get_all_pending_epoch_events().await {
Ok(events) => events,
Err(e) => {
anyhow::bail!("Error fetching pending epoch events: {}", e);
}
};
for event in pending_events {
match event.event.kind {
// If a pending undelegate tx is found, remove it from delegation map
Undelegate { owner, mix_id, .. } => {
if owner == address.as_ref()
&& existing_delegation_map.get(&mix_id.to_string()).is_some()
{
existing_delegation_map.remove(&mix_id.to_string());
}
}
// If a pending delegation event is found, gather them to consolidate later
Delegate {
owner,
mix_id,
amount,
..
} => {
if owner == address.as_ref() {
let mut amount = Coin::from(amount);
if let Some(pending_record) = pending_delegation_map.get(&mix_id.to_string()) {
amount.amount += pending_record.amount;
}
pending_delegation_map.insert(mix_id.to_string(), amount);
}
}
_ => {}
};
}
// Consolidate pending events into delegation map
for (mix_id, amount) in pending_delegation_map {
existing_delegation_map
.entry(mix_id)
.and_modify(|e| e.amount += amount.amount)
.or_insert(amount);
}
Ok(existing_delegation_map)
}
pub async fn delegate_to_multiple_mixnodes(args: Args, client: SigningClient) {
let records = match InputFileReader::new(&args.input) {
Ok(records) => records,
Err(e) => {
println!("Error reading input file: {}", e);
return;
}
};
let existing_delegation_map = fetch_delegation_data(&client)
.await
.expect("Could not fetch existing delegations");
let mut delegation_table = Table::new();
let mut undelegation_table = Table::new();
let mut delegation_msgs: Vec<(ExecuteMsg, Vec<Coin>)> = Vec::new();
let mut undelegation_msgs: Vec<(ExecuteMsg, Vec<Coin>)> = Vec::new();
delegation_table.set_header(["Mix ID", "Input Amount", "Adjusted Amount"]);
undelegation_table.set_header(["Mix ID"]);
for row in &records.rows {
let input_amount = row.amount.amount;
let existing_delegation_amount = existing_delegation_map
.get(&row.mix_id)
.map_or(0, |coin| coin.amount);
match existing_delegation_amount.cmp(&input_amount) {
Ordering::Equal => continue, // No action needed if amounts are equal
Ordering::Less => {
// Delegate the difference if the existing delegation is less
let difference = Coin {
amount: input_amount - existing_delegation_amount,
denom: row.amount.denom.clone(),
};
let mix_id = row.mix_id.clone().parse::<u32>().unwrap();
delegation_msgs.push((DelegateToMixnode { mix_id }, vec![difference.clone()]));
delegation_table.add_row(&[
row.mix_id.clone(),
pretty_coin(&row.amount),
pretty_coin(&difference),
]);
}
Ordering::Greater => {
let mix_id = row.mix_id.clone().parse::<u32>().unwrap();
let coins: Vec<Coin> = vec![];
undelegation_msgs.push((UndelegateFromMixnode { mix_id }, coins));
undelegation_table.add_row(&[row.mix_id.clone()]);
if row.amount.amount > 0 {
delegation_msgs.push((DelegateToMixnode { mix_id }, vec![row.amount.clone()]));
delegation_table.add_row(&[
row.mix_id.clone(),
pretty_coin(&row.amount),
pretty_coin(&row.amount),
]);
}
}
}
}
if delegation_msgs.is_empty() && undelegation_msgs.is_empty() {
println!("Nothing to do. Delegations are up-to-date!");
return;
}
if !undelegation_msgs.is_empty() {
println!("Undelegation records : \n{}\n\n", undelegation_table);
}
if !delegation_msgs.is_empty() {
println!("Delegation records : \n{}\n\n", delegation_table);
}
let ans = inquire::Confirm::new("Do you want to continue with the shown operations?")
.with_default(false)
.with_help_message("You must confirm before the transactions are signed")
.prompt();
if let Err(e) = ans {
info!("Aborting, {}...", e);
return;
}
if let Ok(false) = ans {
info!("Aborting:: User denied proceeding with signing!");
return;
}
let mut output_details: Vec<[String; 3]> = Vec::new();
let now = time::OffsetDateTime::now_utc();
let now = now
.format(&time::format_description::well_known::Rfc3339)
.unwrap();
let mixnet_contract = client
.mixnet_contract_address()
.expect("mixnet contract address is not available");
// Execute all undelegation transactions
if !undelegation_msgs.is_empty() {
let res = client
.execute_multiple(
mixnet_contract,
undelegation_msgs.clone(),
None,
format!(
"Undelegate from {} nodes via nym-cli",
undelegation_msgs.len()
),
)
.await
.expect("Could not undelegate!");
println!(
"Undelegation transaction successful : {}",
res.transaction_hash
);
output_details.push([
"Undelegate".to_string(),
res.transaction_hash.to_string(),
now.clone(),
]);
}
// Execute all delegation delegations
if !delegation_msgs.is_empty() {
let res = client
.execute_multiple(
mixnet_contract,
delegation_msgs,
None,
format!(
"Delegatation to {} nodes via nym-cli",
undelegation_msgs.len()
),
)
.await
.expect("Could not delegate");
println!(
"Delegation transaction successful : {}",
res.transaction_hash
);
output_details.push([
"Delegate".to_string(),
res.transaction_hash.to_string(),
now.clone(),
]);
}
if args.output.is_some() {
if let Err(e) = write_to_csv(output_details, args.output) {
info!("Failed to write to CSV, {}", e);
}
}
}
@@ -6,7 +6,6 @@ use clap::{Args, Subcommand};
pub mod rewards;
pub mod delegate_to_mixnode;
pub mod delegate_to_multiple_mixnodes;
pub mod query_for_delegations;
pub mod undelegate_from_mixnode;
pub mod vesting_delegate_to_mixnode;
@@ -27,8 +26,6 @@ pub enum MixnetDelegatorsCommands {
Rewards(rewards::MixnetDelegatorsReward),
/// Delegate to a mixnode
Delegate(delegate_to_mixnode::Args),
/// Perform bulk delegations from an input file
DelegateMulti(delegate_to_multiple_mixnodes::Args),
/// Undelegate from a mixnode
Undelegate(undelegate_from_mixnode::Args),
/// Delegate to a mixnode with locked tokens
-15
View File
@@ -1,15 +0,0 @@
[package]
name = "nym-country-group"
version = "0.1.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
serde = { workspace = true, features = ["derive"] }
tracing.workspace = true
-158
View File
@@ -1,158 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use serde::{Deserialize, Serialize};
use std::fmt;
use tracing::info;
#[derive(Copy, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub enum CountryGroup {
Europe,
NorthAmerica,
SouthAmerica,
Oceania,
Asia,
Africa,
Unknown,
}
impl CountryGroup {
// We map country codes into group, which initially are continent codes to a first approximation,
// but we do it manually to reserve the right to tweak this distribution for our purposes.
// NOTE: I did this quickly, and it's not a complete list of all countries, but only those that
// were present in the network at the time. Please add more as needed.
pub fn new(country_code: &str) -> Self {
let country_code = country_code.to_uppercase();
use CountryGroup::*;
match country_code.as_ref() {
// Europe
"AT" => Europe,
"BG" => Europe,
"CH" => Europe,
"CY" => Europe,
"CZ" => Europe,
"DE" => Europe,
"DK" => Europe,
"ES" => Europe,
"FI" => Europe,
"FR" => Europe,
"GB" => Europe,
"GR" => Europe,
"IE" => Europe,
"IT" => Europe,
"LT" => Europe,
"LU" => Europe,
"LV" => Europe,
"MD" => Europe,
"MT" => Europe,
"NL" => Europe,
"NO" => Europe,
"PL" => Europe,
"RO" => Europe,
"SE" => Europe,
"SK" => Europe,
"TR" => Europe,
"UA" => Europe,
// North America
"CA" => NorthAmerica,
"MX" => NorthAmerica,
"US" => NorthAmerica,
// South America
"AR" => SouthAmerica,
"BR" => SouthAmerica,
"CL" => SouthAmerica,
"CO" => SouthAmerica,
"CR" => SouthAmerica,
"GT" => SouthAmerica,
// Oceania
"AU" => Oceania,
// Asia
"AM" => Asia,
"BH" => Asia,
"CN" => Asia,
"GE" => Asia,
"HK" => Asia,
"ID" => Asia,
"IL" => Asia,
"IN" => Asia,
"JP" => Asia,
"KH" => Asia,
"KR" => Asia,
"KZ" => Asia,
"MY" => Asia,
"RU" => Asia,
"SG" => Asia,
"TH" => Asia,
"VN" => Asia,
// Africa
"SC" => Africa,
"UG" => Africa,
"ZA" => Africa,
// And group level codes work too
"EU" => Europe,
"NA" => NorthAmerica,
"SA" => SouthAmerica,
"OC" => Oceania,
"AS" => Asia,
"AF" => Africa,
// And some aliases
"EUROPE" => Europe,
"NORTHAMERICA" => NorthAmerica,
"SOUTHAMERICA" => SouthAmerica,
"OCEANIA" => Oceania,
"ASIA" => Asia,
"AFRICA" => Africa,
_ => {
info!("Unknown country code: {country_code}");
Unknown
}
}
}
}
impl fmt::Display for CountryGroup {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use CountryGroup::*;
match self {
Europe => write!(f, "EU"),
NorthAmerica => write!(f, "NA"),
SouthAmerica => write!(f, "SA"),
Oceania => write!(f, "OC"),
Asia => write!(f, "AS"),
Africa => write!(f, "AF"),
Unknown => write!(f, "Unknown"),
}
}
}
impl std::str::FromStr for CountryGroup {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let group = CountryGroup::new(s);
if group == CountryGroup::Unknown {
Err(())
} else {
Ok(group)
}
}
}
impl CountryGroup {
#[allow(unused)]
fn known(self) -> Option<CountryGroup> {
use CountryGroup::*;
match self {
Europe | NorthAmerica | SouthAmerica | Oceania | Asia | Africa => Some(self),
Unknown => None,
}
}
}
@@ -1,48 +0,0 @@
/*
* Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
* SPDX-License-Identifier: Apache-2.0
*/
ALTER TABLE coconut_credentials
RENAME TO old_coconut_credentials;
CREATE TABLE coconut_credentials
(
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
-- introduce a way for us to introduce breaking changes in serialization
serialization_revision INTEGER NOT NULL,
-- the best we can do without enums
credential_type TEXT CHECK ( credential_type IN ('BandwidthVoucher', 'FreeBandwidthPass') ) NOT NULL,
credential_data BLOB NOT NULL UNIQUE,
epoch_id INTEGER NOT NULL,
-- this field is only really applicable to free passes
expired BOOLEAN NOT NULL
);
ALTER TABLE credential_usage
RENAME TO old_credential_usage;
-- for bandwidth vouchers there's going to be only a single entry; for freepasses there can be as many as there are gateways
CREATE TABLE credential_usage
(
credential_id INTEGER NOT NULL REFERENCES coconut_credentials (id),
gateway_id_bs58 TEXT NOT NULL,
-- no matter credential type, we can't spend the same credential with the same gateway multiple times
UNIQUE (credential_id, gateway_id_bs58)
);
INSERT INTO coconut_credentials
SELECT *
FROM old_coconut_credentials;
INSERT INTO credential_usage
SELECT *
FROM old_credential_usage;
DROP TABLE old_coconut_credentials;
DROP TABLE old_credential_usage;
@@ -44,7 +44,7 @@ impl CoconutCredentialManager {
r#"
SELECT *
FROM coconut_credentials
WHERE coconut_credentials.credential_type == "FreeBandwidthPass" AND coconut_credentials.expired = false
WHERE coconut_credentials.credential_type == "FreeBandwidthPass"
AND NOT EXISTS (SELECT 1
FROM credential_usage
WHERE credential_usage.credential_id = coconut_credentials.id
@@ -10,9 +10,6 @@ use std::str::FromStr;
use thiserror::Error;
use zeroize::{Zeroize, ZeroizeOnDrop};
#[cfg(feature = "serde")]
pub mod serde_helpers;
#[cfg(feature = "sphinx")]
use nym_sphinx_types::{DestinationAddressBytes, DESTINATION_ADDRESS_LENGTH};
@@ -1,18 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::PublicKey;
pub mod bs58_pubkey {
use super::*;
use serde_crate::{Deserialize, Deserializer, Serializer};
pub fn serialize<S: Serializer>(key: &PublicKey, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&key.to_base58_string())
}
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<PublicKey, D::Error> {
let s = String::deserialize(deserializer)?;
PublicKey::from_base58_string(s).map_err(serde::de::Error::custom)
}
}
-3
View File
@@ -3,6 +3,3 @@
pub mod encryption;
pub mod identity;
pub use encryption as x25519;
pub use identity as ed25519;
+1 -1
View File
@@ -836,7 +836,7 @@ mod tests {
let share3 = chunks3.clone().try_into().unwrap();
let shares = vec![share1, share2, share3];
let chunks = [chunks1, chunks2, chunks3];
let chunks = &[chunks1, chunks2, chunks3];
for (i, pk_i) in pks.iter().enumerate() {
let mut ciphertext_chunk_i = Vec::with_capacity(NUM_CHUNKS);
-20
View File
@@ -1,20 +0,0 @@
[package]
name = "nym-http-api-common"
version = "0.1.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
axum.workspace = true
bytes = "1.5.0"
mime = "0.3.17"
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
serde_yaml = "0.9.25"
utoipa = { workspace = true }
+3 -5
View File
@@ -10,11 +10,11 @@ license.workspace = true
[dependencies]
bytes = "1.0"
futures = { workspace = true }
humantime-serde = { workspace = true }
humantime-serde = "1.0"
log = { workspace = true }
rand = "0.8"
serde = { workspace = true, features = ["derive"] }
tokio = { workspace = true, features = [
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.24.1", features = [
"time",
"macros",
"rt",
@@ -23,7 +23,6 @@ tokio = { workspace = true, features = [
] }
tokio-util = { workspace = true, features = ["codec"] }
url = { workspace = true }
time.workspace = true
thiserror = { workspace = true }
nym-crypto = { path = "../crypto" }
@@ -38,4 +37,3 @@ nym-task = { path = "../task" }
nym-validator-client = { path = "../client-libs/validator-client" }
nym-bin-common = { path = "../bin-common" }
nym-metrics = { path = "../nym-metrics" }
nym-node-http-api = { path = "../../nym-node/nym-node-http-api" }
+245 -21
View File
@@ -1,35 +1,259 @@
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_node_http_api::state::metrics::{SharedVerlocStats, VerlocNodeResult};
use std::mem;
use time::OffsetDateTime;
use nym_crypto::asymmetric::identity;
use serde::{Serialize, Serializer};
use std::cmp::Ordering;
use std::fmt::{self, Display, Formatter};
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::RwLock;
pub(crate) trait VerlocStatsUpdateExt {
async fn start_new_measurements(&self, nodes_to_test: usize);
async fn append_measurement_results(&self, new_data: Vec<VerlocNodeResult>);
async fn finish_measurements(&self);
#[derive(Clone, Default)]
pub struct AtomicVerlocResult {
inner: Arc<RwLock<VerlocResult>>,
}
impl VerlocStatsUpdateExt for SharedVerlocStats {
async fn start_new_measurements(&self, nodes_to_test: usize) {
let mut guard = self.write().await;
guard.previous_run_data = mem::take(&mut guard.current_run_data);
guard.current_run_data.nodes_tested = nodes_to_test;
#[derive(Debug, Clone, Serialize, Default)]
pub struct VerlocResult {
total_tested: usize,
#[serde(with = "humantime_serde")]
run_started: Option<std::time::SystemTime>,
#[serde(with = "humantime_serde")]
run_finished: Option<std::time::SystemTime>,
results: Vec<Verloc>,
}
impl AtomicVerlocResult {
pub(crate) fn new() -> Self {
AtomicVerlocResult {
inner: Arc::new(RwLock::new(VerlocResult {
total_tested: 0,
run_started: None,
run_finished: None,
results: Vec::new(),
})),
}
}
async fn append_measurement_results(&self, mut new_data: Vec<VerlocNodeResult>) {
let mut write_permit = self.write().await;
write_permit.current_run_data.results.append(&mut new_data);
pub(crate) async fn reset_results(&self, new_tested: usize) {
let mut write_permit = self.inner.write().await;
write_permit.total_tested = new_tested;
write_permit.run_started = Some(std::time::SystemTime::now());
write_permit.run_finished = None;
write_permit.results = Vec::new()
}
pub(crate) async fn append_results(&self, mut new_data: Vec<Verloc>) {
let mut write_permit = self.inner.write().await;
write_permit.results.append(&mut new_data);
// make sure the data always stays in order.
// TODO: considering the front of the results is guaranteed to be sorted, should perhaps
// a non-default sorting algorithm be used?
write_permit.current_run_data.results.sort()
write_permit.results.sort()
}
async fn finish_measurements(&self) {
self.write().await.current_run_data.run_finished = Some(OffsetDateTime::now_utc())
pub(crate) async fn finish_measurements(&self) {
self.inner.write().await.run_finished = Some(std::time::SystemTime::now());
}
// Considering that on every read we will need to clone data regardless, let's make our
// lives simpler and clone it here rather than deal with lifetime of the permit
pub async fn clone_data(&self) -> VerlocResult {
self.inner.read().await.clone()
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize)]
pub struct Verloc {
#[serde(serialize_with = "serialize_identity_as_string")]
pub identity: identity::PublicKey,
pub latest_measurement: Option<Measurement>,
}
fn serialize_identity_as_string<S>(
identity: &identity::PublicKey,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&identity.to_base58_string())
}
impl Display for Verloc {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
if let Some(measurement) = self.latest_measurement {
write!(f, "{} - {}", self.identity, measurement)
} else {
write!(f, "{} - COULD NOT MEASURE", self.identity)
}
}
}
impl PartialOrd for Verloc {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Verloc {
fn cmp(&self, other: &Self) -> Ordering {
// if both have measurement, compare measurements
// then if only one have measurement, prefer that one
// completely ignore identity as it makes no sense to order by it
if let Some(self_measurement) = &self.latest_measurement {
if let Some(other_measurement) = &other.latest_measurement {
self_measurement.cmp(other_measurement)
} else {
Ordering::Less
}
} else if other.latest_measurement.is_some() {
Ordering::Greater
} else {
Ordering::Equal
}
}
}
impl Verloc {
pub(crate) fn new(
identity: identity::PublicKey,
latest_measurement: Option<Measurement>,
) -> Self {
Verloc {
identity,
latest_measurement,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize)]
pub struct Measurement {
#[serde(serialize_with = "humantime_serde::serialize")]
pub minimum: Duration,
#[serde(serialize_with = "humantime_serde::serialize")]
pub mean: Duration,
#[serde(serialize_with = "humantime_serde::serialize")]
pub maximum: Duration,
#[serde(serialize_with = "humantime_serde::serialize")]
pub standard_deviation: Duration,
}
impl Measurement {
pub(crate) fn new(raw_results: &[Duration]) -> Self {
let minimum = *raw_results.iter().min().expect("didn't get any results!");
let maximum = *raw_results.iter().max().expect("didn't get any results!");
let mean = Self::duration_mean(raw_results);
let standard_deviation = Self::duration_standard_deviation(raw_results, mean);
Measurement {
minimum,
mean,
maximum,
standard_deviation,
}
}
fn duration_mean(data: &[Duration]) -> Duration {
let sum = data.iter().sum::<Duration>();
let count = data.len() as u32;
sum / count
}
fn duration_standard_deviation(data: &[Duration], mean: Duration) -> Duration {
let variance_micros = data
.iter()
.map(|&value| {
// make sure we don't underflow
let diff = if mean > value {
mean - value
} else {
value - mean
};
// we don't need nanos precision
let diff_micros = diff.as_micros();
diff_micros * diff_micros
})
.sum::<u128>()
/ data.len() as u128;
// we shouldn't really overflow as our differences shouldn't be larger than couple seconds at the worst possible case scenario
let std_deviation_micros = (variance_micros as f64).sqrt() as u64;
Duration::from_micros(std_deviation_micros)
}
}
impl Display for Measurement {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"rtt min/avg/max/mdev = {:?} / {:?} / {:?} / {:?}",
self.minimum, self.mean, self.maximum, self.standard_deviation
)
}
}
impl PartialOrd for Measurement {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Measurement {
fn cmp(&self, other: &Self) -> Ordering {
// minimum value is most important, then look at standard deviation, then mean and finally maximum
let min_cmp = self.minimum.cmp(&other.minimum);
if min_cmp != Ordering::Equal {
return min_cmp;
}
let std_dev_cmp = self.standard_deviation.cmp(&other.standard_deviation);
if std_dev_cmp != Ordering::Equal {
return std_dev_cmp;
}
let std_dev_cmp = self.mean.cmp(&other.mean);
if std_dev_cmp != Ordering::Equal {
return std_dev_cmp;
}
self.maximum.cmp(&other.maximum)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sorting_vec_of_verlocs() {
let some_identity =
identity::PublicKey::from_base58_string("Be9wH7xuXBRJAuV1pC7MALZv6a61RvWQ3SypsNarqTt")
.unwrap();
let no_measurement = Verloc::new(some_identity, None);
let low_min = Verloc::new(
some_identity,
Some(Measurement {
minimum: Duration::from_millis(42),
mean: Duration::from_millis(43),
maximum: Duration::from_millis(44),
standard_deviation: Duration::from_millis(45),
}),
);
let higher_min = Verloc::new(
some_identity,
Some(Measurement {
minimum: Duration::from_millis(420),
mean: Duration::from_millis(430),
maximum: Duration::from_millis(440),
standard_deviation: Duration::from_millis(450),
}),
);
let mut vec_verloc = vec![no_measurement, low_min, no_measurement, higher_min];
vec_verloc.sort();
let expected_sorted = vec![low_min, higher_min, no_measurement, no_measurement];
assert_eq!(expected_sorted, vec_verloc);
}
}
+18 -19
View File
@@ -1,4 +1,4 @@
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::verloc::listener::PacketListener;
@@ -9,7 +9,6 @@ use log::*;
use nym_bin_common::version_checker::{self, parse_version};
use nym_crypto::asymmetric::identity;
use nym_network_defaults::mainnet::NYM_API;
use nym_node_http_api::state::metrics::{SharedVerlocStats, VerlocNodeResult};
use nym_task::TaskClient;
use rand::seq::SliceRandom;
use rand::thread_rng;
@@ -21,9 +20,7 @@ use tokio::task::JoinHandle;
use tokio::time::sleep;
use url::Url;
use measurement::VerlocStatsUpdateExt;
// pub use crate::verloc::measurement::{AtomicVerlocResult, Verloc, VerlocResult};
pub use crate::verloc::measurement::{AtomicVerlocResult, Verloc, VerlocResult};
pub mod error;
pub(crate) mod listener;
@@ -182,7 +179,7 @@ pub struct VerlocMeasurer {
// then it definitely cannot be constructed here and probably will need to be passed from outside,
// as mixnodes/gateways would already be using an instance of said client.
validator_client: nym_validator_client::NymApiClient,
state: SharedVerlocStats,
results: AtomicVerlocResult,
}
impl VerlocMeasurer {
@@ -213,14 +210,10 @@ impl VerlocMeasurer {
config.nym_api_urls[0].clone(),
),
config,
state: SharedVerlocStats::default(),
results: AtomicVerlocResult::new(),
}
}
pub fn set_shared_state(&mut self, state: SharedVerlocStats) {
self.state = state;
}
fn use_next_nym_api(&mut self) {
if self.config.nym_api_urls.len() == 1 {
warn!("There's only a single validator API available - it won't be possible to use a different one");
@@ -232,6 +225,10 @@ impl VerlocMeasurer {
.change_nym_api(self.config.nym_api_urls[self.currently_used_api].clone())
}
pub fn get_verloc_results_pointer(&self) -> AtomicVerlocResult {
self.results.clone()
}
fn start_listening(&self) -> JoinHandle<()> {
let packet_listener = Arc::clone(&self.packet_listener);
tokio::spawn(packet_listener.run())
@@ -273,7 +270,7 @@ impl VerlocMeasurer {
while !shutdown_listener.is_shutdown() {
tokio::select! {
measurement_result = measurement_chunk.next() => {
let Some(result) = measurement_result else {
let Some(result) = measurement_result else {
// if the stream has finished, it means we got everything we could have gotten
break
};
@@ -281,16 +278,18 @@ impl VerlocMeasurer {
// if we receive JoinError it means the task failed to get executed, so either there's a bigger issue with tokio
// or there was a panic inside the task itself. In either case, we should just terminate ourselves.
let execution_result = result.expect("the measurement task panicked!");
let identity = execution_result.1;
let measurement_result = match execution_result.0 {
Err(err) => {
debug!("Failed to perform measurement for {identity}: {err}");
debug!(
"Failed to perform measurement for {} - {}",
execution_result.1.to_base58_string(),
err
);
None
}
Ok(result) => Some(result),
};
chunk_results.push(VerlocNodeResult::new(identity, measurement_result));
chunk_results.push(Verloc::new(execution_result.1, measurement_result));
},
_ = shutdown_listener.recv() => {
trace!("Shutdown received while measuring");
@@ -300,7 +299,7 @@ impl VerlocMeasurer {
}
// update the results vector with chunks as they become available (by default every 50 nodes)
self.state.append_measurement_results(chunk_results).await;
self.results.append_results(chunk_results).await;
}
MeasurementOutcome::Done
@@ -359,7 +358,7 @@ impl VerlocMeasurer {
.collect::<Vec<_>>();
// on start of each run remove old results
self.state.start_new_measurements(tested_nodes.len()).await;
self.results.reset_results(tested_nodes.len()).await;
if let MeasurementOutcome::Shutdown = self.perform_measurement(tested_nodes).await {
log::trace!("Shutting down after aborting measurements");
@@ -367,7 +366,7 @@ impl VerlocMeasurer {
}
// write current time to "run finished" field
self.state.finish_measurements().await;
self.results.finish_measurements().await;
info!(
"Finished performing verloc measurements. The next one will happen in {:?}",
+4 -4
View File
@@ -1,11 +1,11 @@
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::verloc::error::RttError;
use crate::verloc::measurement::Measurement;
use crate::verloc::packet::{EchoPacket, ReplyPacket};
use log::*;
use nym_crypto::asymmetric::identity;
use nym_node_http_api::state::metrics::VerlocMeasurement;
use nym_task::TaskClient;
use rand::{thread_rng, Rng};
use std::net::SocketAddr;
@@ -82,7 +82,7 @@ impl PacketSender {
pub(super) async fn send_packets_to_node(
self: Arc<Self>,
tested_node: TestedNode,
) -> Result<VerlocMeasurement, RttError> {
) -> Result<Measurement, RttError> {
let mut shutdown_listener = self.shutdown_listener.fork(tested_node.address.to_string());
shutdown_listener.mark_as_success();
@@ -208,6 +208,6 @@ impl PacketSender {
sleep(self.delay_between_packets).await;
}
Ok(VerlocMeasurement::new(&results))
Ok(Measurement::new(&results))
}
}
-3
View File
@@ -3,6 +3,3 @@
pub mod clients;
pub mod nodes;
pub use clients::Recipient;
pub use nodes::NodeIdentity;
+1 -5
View File
@@ -348,7 +348,7 @@ impl TaskClient {
"unnamed-TaskClient".to_string()
};
log!(target: target, level, "{}", format!("[{target}] {msg}"))
log!(target: target, level, "{msg}")
}
#[must_use]
@@ -474,10 +474,6 @@ impl TaskClient {
self.mode.set_should_not_signal_on_drop();
}
pub fn disarm(&mut self) {
self.mark_as_success();
}
pub fn send_we_stopped(&mut self, err: SentError) {
if self.mode.is_dummy() {
return;
+2 -1
View File
@@ -1,4 +1,3 @@
#
# Summary
- [Introduction](introduction.md)
@@ -24,6 +23,8 @@
- [Linux](nymvpn/gui-linux.md)
- [MacOS](nymvpn/gui-mac.md)
- [CLI](nymvpn/cli.md)
- [Linux](nymvpn/cli-linux.md)
- [MacOS](nymvpn/cli-mac.md)
- [Troubleshooting](nymvpn/troubleshooting.md)
- [NymVPN FAQ](nymvpn/faq.md)
- [NymConnect X Monero](tutorials/monero.md)
@@ -1,4 +1,4 @@
# NymVPN alpha CLI Guide
# NymVPN alpha CLI: Guide for GNU/Linux
```admonish info
NymVPN is an experimental software and it's for testing purposes only. All users testing the client are expected to sign GDPR Information Sheet and Consent Form (shared at the workshop) so we use their results to improve the client, and submit the form [*NymVPN User research*]({{nym_vpn_form_url}}) with the testing results.
@@ -8,7 +8,7 @@ NymVPN is an experimental software and it's for testing purposes only. All users
> Any syntax in `<>` brackets is a user's/version unique variable. Exchange with a corresponding name without the `<>` brackets.
1. Open Github [releases page]({{nym_vpn_releases}}) and download the CLI latest binary for your system
1. Open Github [releases page]({{nym_vpn_releases}}) and download the binary for Debian based Linux
2. Verify sha hash of your downloaded binary with the one listed on the [releases page]({{nym_vpn_releases}}). You can use a simple `shasum` command and compare strings (ie with Python) or run in the same directory the following command, exchanging `<SHA_STRING>` with the one of your binary, like in the example:
```sh
@@ -25,12 +25,17 @@ tar -xvf <BINARY>.tar.gz
# tar -xvf nym-vpn-cli_<!-- cmdrun scripts/nym_vpn_cli_version.sh -->_ubuntu-22.04_x86_64.tar.gz
```
4. Make executable:
4. Make executable by running:
```sh
# make sure you are in the right sub-directory
chmod u+x ./nym-vpn-cli
```
5. Create Sandbox environment config file by saving [this](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) as `sandbox.env` in the same directory as your NymVPN binaries by running:
```sh
curl -o sandbox.env -L https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env
```
## Run NymVPN
**For NymVPN to work, all other VPNs must be switched off!** At this alpha stage of NymVPN, the network connection (wifi) must be reconnected after or in between the testing rounds.
@@ -42,7 +47,7 @@ Make sure your terminal is open in the same directory as your `nym-vpn-cli` bina
```sh
sudo ./nym-vpn-cli -c ./sandbox.env --entry-gateway-id <ENTRY_GATEWAY_ID> --exit-router-address <EXIT_ROUTER_ADDRESS> --enable-wireguard --private-key <PRIVATE_KEY> --wg-ip <WIREGUARD_IP>
```
3. To choose different Gateways, visit [explorer.nymtech.net/network-components/gateways](https://explorer.nymtech.net/network-components/gateways) and copy-paste an identity key of your choice
3. To choose different Gateways, visit [nymvpn.com/en/alpha/api/gateways](https://nymvpn.com/en/alpha/api/gateways) and pick one
4. See all possibilities in [command explanation](#cli-commands-and-options) section below
In case of errors, see [troubleshooting section](troubleshooting.md).
@@ -51,16 +56,16 @@ In case of errors, see [troubleshooting section](troubleshooting.md).
The basic syntax of `nym-vpn-cli` is:
```sh
sudo ./nym-vpn-cli <--exit-router-address <EXIT_ROUTER_ADDRESS>|--exit-gateway-id <EXIT_GATEWAY_ID>|--exit-gateway-country <EXIT_GATEWAY_COUNTRY>>
sudo ./nym-vpn-cli -c ./sandbox.env --entry-gateway-id <ENTRY_GATEWAY_ID> --exit-router-address <EXIT_ROUTER_ADDRESS> --enable-wireguard --private-key <PRIVATE_KEY> --wg-ip <WG_IP>
```
* To choose different Gateways, visit [nymvpn.com/en/alpha/api/gateways](https://explorer.nymtech.net/network-components/gateways)
* To choose different Gateways, visit [nymvpn.com/en/alpha/api/gateways](https://nymvpn.com/en/alpha/api/gateways)
* To see all possibilities run with `--help` flag:
```sh
./nym-vpn-cli --help
```
~~~admonish example collapsible=true title="Console output"
```sh
Usage: nym-vpn-cli [OPTIONS] <--exit-router-address <EXIT_ROUTER_ADDRESS>|--exit-gateway-id <EXIT_GATEWAY_ID>|--exit-gateway-country <EXIT_GATEWAY_COUNTRY>>
Usage: nym-vpn-cli [OPTIONS]
Options:
-c, --config-env-file <CONFIG_ENV_FILE>
@@ -71,13 +76,11 @@ Options:
Mixnet public ID of the entry gateway
--entry-gateway-country <ENTRY_GATEWAY_COUNTRY>
Auto-select entry gateway by country ISO
--entry-gateway-low-latency
Auto-select entry gateway by latency
--exit-router-address <EXIT_ROUTER_ADDRESS>
Mixnet recipient address
--exit-gateway-id <EXIT_GATEWAY_ID>
--exit-gateway-country <EXIT_GATEWAY_COUNTRY>
--exit-router-country <EXIT_ROUTER_COUNTRY>
Mixnet recipient address
--enable-wireguard
Enable the wireguard traffic between the client and the entry gateway
@@ -85,10 +88,8 @@ Options:
Associated private key
--wg-ip <WG_IP>
The IP address of the wireguard interface used for the first hop to the entry gateway
--nym-ipv4 <NYM_IPV4>
The IPv4 address of the nym TUN device that wraps IP packets in sphinx packets
--nym-ipv6 <NYM_IPV6>
The IPv6 address of the nym TUN device that wraps IP packets in sphinx packets
--nym-ip <NYM_IP>
The IP address of the nym TUN device that wraps IP packets in sphinx packets
--nym-mtu <NYM_MTU>
The MTU of the nym TUN device that wraps IP packets in sphinx packets
--disable-routing
@@ -124,19 +125,3 @@ Here is a list of the options and their descriptions. Some are essential, some a
- `--ip` is the IP address of the TUN device. That is the IP address of the local private network that is set up between local client and the Exit Gateway.
- `--mtu`: The MTU of the TUN device. That is the max IP packet size of the local private network that is set up between local client and the Exit Gateway.
- `--disable-routing`: Disable routing all traffic through the VPN TUN device.
## Testnet environment
If you want to run NymVPN CLI in Nym Sandbox environment, there are a few adjustments to be done:
1. Create Sandbox environment config file by saving [this](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) as `sandbox.env` in the same directory as your NymVPN binaries by running:
```sh
curl -o sandbox.env -L https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env
```
1. Check available Gateways at [nymvpn.com/en/alpha/api/gateways](https://nymvpn.com/en/alpha/api/gateways)
2. Run with a flag `-c`
```sh
sudo ./nym-vpn-cli -c <PATH_TO>/sandbox.env <--exit-router-address <EXIT_ROUTER_ADDRESS>|--exit-gateway-id <EXIT_GATEWAY_ID>|--exit-gateway-country <EXIT_GATEWAY_COUNTRY>>
```
@@ -1,35 +0,0 @@
# NymVPN Command Line Interface (CLI)
```admonish info
Our alpha testing round is done with participants at live workshop events. This guide will not work for everyone, as the NymVPN source code is not yet publicly accessible. The alpha testing is done on Nym testnet Sandbox environment, this configuration is limited and will not work in the future.
**If you commit to test NymVPN alpha, please start with the [user research form]({{nym_vpn_form_url}}) where all the steps will be provided**. If you disagree with any of the conditions listed, please leave this page.
```
Follow the simple [automated script](#automated-script-for-cli-installation) below to install and run NymVPN CLI. If you prefer to do a manual setup follow the steps in the guide for [Linux](cli-linux.md) or [MacOS](cli-mac.md).
Visit NymVPN alpha latest [release page]({{nym_vpn_releases}}) to check sha sums or download the binaries directly.
## Automated Script for CLI Installation
We wrote a [script](https://gist.github.com/serinko/d65450653d6bbafacbcee71c9cb8fb31) which does download of the CLI, sha256 verification, extraction, installation and configuration for Linux and MacOS users automatically following the steps below:
1. Open a terminal window in a directory where you want the script and NymVPN CLI binary be downloaded and run
```sh
curl -o execute-nym-vpn-cli-binary.sh -L https://gist.githubusercontent.com/tommyv1987/87267ded27e1eb7651aa9cc745ddf4af/raw/d39f98dbb36ccff761a7e940073388a6fe7b73fe/execute-nym-vpn-cli-binary.sh && chmod u+x execute-nym-vpn-cli-binary.sh && sudo -E ./execute-nym-vpn-cli-binary.sh
```
2. Follow the prompts in the program
3. The script will automatically start the client. Make sure to **turn off any other VPNs** and follow the prompts:
* It prints a JSON view of existing Gateways and prompt you to:
- *Make sure to use two different Gateways for entry and exit!*
- `enter a gateway ID:` paste one of the values labeled with a key `"identityKey"` printed above (without `" "`)
- `enter an exit address:` paste one of the values labeled with a key `"address"` printed above (without `" "`)
- `do you want five hop or two hop?`: type `five` or `two`
- `enable WireGuard? (yes/no):` if you chose yes, find your private key and wireguard IP [here](https://nymvpn.com/en/alpha)
To run `nym-vpn-cli` again, reconnect your wifi, move to the directory of your CLI binary `cd ~/nym-vpn-cli-dir` and follow the guide for [Linux](cli-linux.md#run-nymvpn) or [MacOS](cli-mac.md#run-nymvpn). If you find it too difficult, just run this script again - like in step \#3 above.
In case of errors check out the [troubleshooting](troubleshooting.md) section.
+21 -137
View File
@@ -1,151 +1,35 @@
# NymVPN alpha CLI Guide
# NymVPN Command Line Interface (CLI)
```admonish info
NymVPN is an experimental software and it's for testing purposes only. All users testing the client are expected to sign GDPR Information Sheet and Consent Form (shared at the workshop) so we use their results to improve the client, and submit the form [*NymVPN User research*]({{nym_vpn_form_url}}) with the testing results.
Our alpha testing round is done with participants at live workshop events. This guide will not work for everyone, as the NymVPN source code is not yet publicly accessible. The alpha testing is done on Nym testnet Sandbox environment, this configuration is limited and will not work in the future.
**If you commit to test NymVPN alpha, please start with the [user research form]({{nym_vpn_form_url}}) where all the steps will be provided**. If you disagree with any of the conditions listed, please leave this page.
```
## Installation
Follow the simple [automated script](#automated-script-for-cli-installation) below to install and run NymVPN CLI. If you prefer to do a manual setup follow the steps in the guide for [Linux](cli-linux.md) or [MacOS](cli-mac.md).
> Any syntax in `<>` brackets is a user's/version unique variable. Exchange with a corresponding name without the `<>` brackets.
Visit NymVPN alpha latest [release page]({{nym_vpn_releases}}) to check sha sums or download the binaries directly.
1. Open Github [releases page]({{nym_vpn_releases}}) and download the CLI latest binary for your system
## Automated Script for CLI Installation
2. Verify sha hash of your downloaded binary with the one listed on the [releases page]({{nym_vpn_releases}}). You can use a simple `shasum` command and compare strings (ie with Python) or run in the same directory the following command, exchanging `<SHA_STRING>` with the one of your binary, like in the example:
We wrote a [script](https://gist.github.com/serinko/d65450653d6bbafacbcee71c9cb8fb31) which does download of the CLI, sha256 verification, extraction, installation and configuration for Linux and MacOS users automatically following the steps below:
1. Open a terminal window in a directory where you want the script and NymVPN CLI binary be downloaded and run
```sh
echo "<SHA_STRING>" | shasum -a 256 -c
# choose a correct one according to your binary, this is just an example
# echo "0e4abb461e86b2c168577e0294112a3bacd3a24bf8565b49783bfebd9b530e23 nym-vpn-cli_<!-- cmdrun scripts/nym_vpn_cli_version.sh -->_ubuntu-22.04_amd64.tar.gz" | shasum -a 256 -c
curl -o execute-nym-vpn-cli-binary.sh -L https://gist.githubusercontent.com/tommyv1987/87267ded27e1eb7651aa9cc745ddf4af/raw/d39f98dbb36ccff761a7e940073388a6fe7b73fe/execute-nym-vpn-cli-binary.sh && chmod u+x execute-nym-vpn-cli-binary.sh && sudo -E ./execute-nym-vpn-cli-binary.sh
```
3. Extract files:
```sh
tar -xvf <BINARY>.tar.gz
# for example
# tar -xvf nym-vpn-cli_<!-- cmdrun scripts/nym_vpn_cli_version.sh -->_ubuntu-22.04_x86_64.tar.gz
```
2. Follow the prompts in the program
4. Make executable:
```sh
# make sure you are in the right sub-directory
chmod u+x nym-vpn-cli
```
3. The script will automatically start the client. Make sure to **turn off any other VPNs** and follow the prompts:
## Run NymVPN
* It prints a JSON view of existing Gateways and prompt you to:
- *Make sure to use two different Gateways for entry and exit!*
- `enter a gateway ID:` paste one of the values labeled with a key `"identityKey"` printed above (without `" "`)
- `enter an exit address:` paste one of the values labeled with a key `"address"` printed above (without `" "`)
- `do you want five hop or two hop?`: type `five` or `two`
- `enable WireGuard? (yes/no):` if you chose yes, find your private key and wireguard IP [here](https://nymvpn.com/en/alpha)
**For NymVPN to work, all other VPNs must be switched off!** At this alpha stage of NymVPN, the network connection (wifi) must be reconnected after or in between the testing rounds.
To run `nym-vpn-cli` again, reconnect your wifi, move to the directory of your CLI binary `cd ~/nym-vpn-cli-dir` and follow the guide for [Linux](cli-linux.md#run-nymvpn) or [MacOS](cli-mac.md#run-nymvpn). If you find it too difficult, just run this script again - like in step \#3 above.
Make sure your terminal is open in the same directory as your `nym-vpn-cli` binary.
1. Run it as root with `sudo` - the command will look like this with specified arguments:
```sh
# choose only one conditional --argument listed in {brackets}
sudo ./nym-vpn-cli { --exit-router-address <EXIT_ROUTER_ADDRESS>|--exit-gateway-id <EXIT_GATEWAY_ID>|--exit-gateway-country <EXIT_GATEWAY_COUNTRY> }
```
2. To choose different Gateways, visit [explorer.nymtech.net/network-components/gateways](https://explorer.nymtech.net/network-components/gateways) and copy-paste an identity key of your choice
```admonish note
Nym Exit Gateway functionality was implemented just recently and not all the Gateways are upgraded and ready to handle the VPN connections. If you want to make sure you are connecting to a Gateway with an embedded Network Requester, IP Packet Router and applied Nym exit policy, visit [this page](https://nymtech.net/events/fast-and-furious), scroll down to the list and search Gateways with all the functionalities enabled.
```
3. See all possibilities in [command explanation](#cli-commands-and-options) section below
4. In case of errors, see [troubleshooting section](troubleshooting.md)
### CLI Commands and Options
The basic syntax of `nym-vpn-cli` is:
```sh
# choose only one conditional --argument listed in {brackets}
sudo ./nym-vpn-cli { --exit-router-address <EXIT_ROUTER_ADDRESS>|--exit-gateway-id <EXIT_GATEWAY_ID>|--exit-gateway-country <EXIT_GATEWAY_COUNTRY> }
```
To see all the possibilities run with `--help` flag:
```sh
./nym-vpn-cli --help
```
~~~admonish example collapsible=true title="Console output"
```sh
Usage: nym-vpn-cli [OPTIONS] <--exit-router-address <EXIT_ROUTER_ADDRESS>|--exit-gateway-id <EXIT_GATEWAY_ID>|--exit-gateway-country <EXIT_GATEWAY_COUNTRY>>
Options:
-c, --config-env-file <CONFIG_ENV_FILE>
Path pointing to an env file describing the network
--mixnet-client-path <MIXNET_CLIENT_PATH>
Path to the data directory of a previously initialised mixnet client, where the keys reside
--entry-gateway-id <ENTRY_GATEWAY_ID>
Mixnet public ID of the entry gateway
--entry-gateway-country <ENTRY_GATEWAY_COUNTRY>
Auto-select entry gateway by country ISO
--entry-gateway-low-latency
Auto-select entry gateway by latency
--exit-router-address <EXIT_ROUTER_ADDRESS>
Mixnet recipient address
--exit-gateway-id <EXIT_GATEWAY_ID>
--exit-gateway-country <EXIT_GATEWAY_COUNTRY>
Mixnet recipient address
--enable-wireguard
Enable the wireguard traffic between the client and the entry gateway
--private-key <PRIVATE_KEY>
Associated private key
--wg-ip <WG_IP>
The IP address of the wireguard interface used for the first hop to the entry gateway
--nym-ipv4 <NYM_IPV4>
The IPv4 address of the nym TUN device that wraps IP packets in sphinx packets
--nym-ipv6 <NYM_IPV6>
The IPv6 address of the nym TUN device that wraps IP packets in sphinx packets
--nym-mtu <NYM_MTU>
The MTU of the nym TUN device that wraps IP packets in sphinx packets
--disable-routing
Disable routing all traffic through the nym TUN device. When the flag is set, the nym TUN device will be created, but to route traffic through it you will need to do it manually, e.g. ping -Itun0
--enable-two-hop
Enable two-hop mixnet traffic. This means that traffic jumps directly from entry gateway to exit gateway
--enable-poisson-rate
Enable Poisson process rate limiting of outbound traffic
--disable-background-cover-traffic
Disable constant rate background loop cover traffic
-h, --help
Print help
-V, --version
Print version
```
~~~
Here is a list of the options and their descriptions. Some are essential, some are more technical and not needed to be adjusted by users.
**Fundamental commands and arguments**
- `--entry-gateway-id`: paste one of the values labeled with a key `"identityKey"` (without `" "`)
- `--exit-gateway-id`: paste one of the values labeled with a key `"identityKey"` (without `" "`)
- `--exit-router-address`: paste one of the values labeled with a key `"address"` (without `" "`)
- `--enable-wireguard`: Enable the wireguard traffic between the client and the entry gateway. NymVPN uses Mullvad libraries for wrapping `wireguard-go` and to setup local routing rules to route all traffic to the TUN virtual network device
- `--wg-ip`: The address of the wireguard interface, you can get it [here](https://nymvpn.com/en/alpha)
- `--private-key`: get your private key for testing purposes [here](https://nymvpn.com/en/alpha)
- `--enable-two-hop` is a faster setup where the traffic is routed from the client to Entry Gateway and directly to Exit Gateway (default is 5-hops)
**Advanced options**
- `-c` is a path to an enviroment config, like [`sandbox.env`](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env)
- `--enable-poisson`: Enables process rate limiting of outbound traffic (disabled by default). It means that NymVPN client will send packets at a steady stream to the Entry Gateway. By default it's on average one sphinx packet per 20ms, but there is some randomness (poisson distribution). When there are no real data to fill the sphinx packets with, cover packets are generated instead.
- `--ip` is the IP address of the TUN device. That is the IP address of the local private network that is set up between local client and the Exit Gateway.
- `--mtu`: The MTU of the TUN device. That is the max IP packet size of the local private network that is set up between local client and the Exit Gateway.
- `--disable-routing`: Disable routing all traffic through the VPN TUN device.
## Testnet environment
If you want to run NymVPN CLI in Nym Sandbox environment, there are a few adjustments to be done:
1. Create Sandbox environment config file by saving [this](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) as `sandbox.env` in the same directory as your NymVPN binaries:
```sh
curl -o sandbox.env -L https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env
```
1. Check available Gateways at [nymvpn.com/en/alpha/api/gateways](https://nymvpn.com/en/alpha/api/gateways)
2. Run with a flag `-c`
```sh
sudo ./nym-vpn-cli -c <PATH_TO>/sandbox.env <--exit-router-address <EXIT_ROUTER_ADDRESS>|--exit-gateway-id <EXIT_GATEWAY_ID>|--exit-gateway-country <EXIT_GATEWAY_COUNTRY>>
```
In case of errors check out the [troubleshooting](troubleshooting.md) section.
+4 -4
View File
@@ -1,12 +1,12 @@
# NymVPN - Desktop (GUI)
```admonish info
Our alpha testing round is done with participants at live workshop events and the application in this stage may not work for everyone.
Our alpha testing round is done with participants at live workshop events. This guide will not work for everyone, as the NymVPN source code is not yet publicly accessible. The alpha testing is done on Nym testnet Sandbox environment, this configuration is limited and will not work in the future.
**If you commit to test NymVPN alpha, please start with the [user research form]({{nym_vpn_form_url}}) where all the steps will be provided**. If you disagree with any of the conditions listed, please leave this page.
```
This is a desktop (GUI) version of NymVPN client. A demo of how the application will look like for majority of day-to-day users.
This is the alpha version of NymVPN desktop application (GUI). A demo of how the client will look like for majority of day-to-day users.
Follow the simple [automated script](#automated-script-for-gui-installation) below to install and run NymVPN GUI. If the script didn't work for your distribution or you prefer to do a manual setup follow the steps in the guide for [Linux](gui-linux.md) or [MacOS](gui-mac.md) .
@@ -14,12 +14,12 @@ Visit NymVPN alpha latest [release page]({{nym_vpn_releases}}) to check sha sums
## Linux AppImage Automated Installation Method
The latest releases contain `appimage.sh` script. This method makes the installation simple for Linux users who want to run NymVPN from AppImmage. Executing the command below will download the binary to `~/.local/bin` and verify the checksum:
The latest releases contain `appimage.sh` script. This method makes the installation simple for Linux users who want to run NymVPN from AppImmage. Executing the command below will download the binary to `~/.local/bin` and verify the checksum.
```sh
curl -fsSL https://github.com/nymtech/nym-vpn-client/releases/download/nym-vpn-desktop-v<!-- cmdrun scripts/nym_vpn_desktop_version.sh -->/appimage.sh | bash
```
Run with the command:
Run with the command
```sh
sudo -E ~/.local/bin/nym-vpn.appimage
```
+4 -3
View File
@@ -5,15 +5,16 @@
**Nym proudly presents NymVPN alpha** - a client that uses [Nym Mixnet](https://nymtech.net) to anonymise all of a user's internet traffic through either a 5-hop mixnet (for a full network privacy) or the faster 2-hop decentralised VPN (with some extra features).
**You are invited to take part in the alpha testing** of this new application. The following pages provide a how-to guide, explaining steps to install and run NymVPN [CLI](cli.md) and [GUI](gui.md).
**You are invited to take part in the alpha testing** of this new application. The following pages provide a how-to guide, explaining steps to install and run NymVPN [CLI](cli.md) and [GUI](gui.md) on the Sandbox testnet environment.
**Here is how**
1. Go to the NymVPN [testers form]({{nym_vpn_form_url}})
2. Fill and submit the [form!]({{nym_vpn_form_url}})
2. Please consent to the GDPR so we can use the results
3. To test the GUI, [go here](gui.md)
4. To test the CLI, [go here](cli.md)
5. Join the [NymVPN matrix channel](https://matrix.to/#/#NymVPN:nymtech.chat) if you have any questions, comments or blockers
5. Fill and submit the [form!]({{nym_vpn_form_url}})
6. Join the [NymVPN matrix channel](https://matrix.to/#/#NymVPN:nymtech.chat) if you have any questions, comments or blockers
***NymVPN alpha testing will last from 15th of January - 15th of February.***
@@ -2,14 +2,6 @@
Below are listed some points which may need to be addressed when testing NymVPN alpha. If you crashed into any errors which are not listed, please contact us at the testing workshop or in the NymVPN [Matrix channel](https://matrix.to/#/#NymVPN:nymtech.chat).
#### NymVPN attempts to connect to sandbox testnet
If you testing the latest versions and you correctly expect the client to run over the mainnet, but it listens to `https://sandbox-nym-api1.nymtech.net`, it's probably because of your previous configuration.
Check your `config.toml` either in the directory from which you run your client or in `~/.config/nym-vpn/` and remove `sandbox.env` from the config file and folder.
If the problem persists (probably due to some locally cache) download [`mainnet.env`](https://github.com/nymtech/nym/blob/master/envs/mainnet.env) and save it to the same directory.
#### Running GUI failed due to `TOML parse error`
If you see this error when running NymVPN alpha desktop, it's because the older versions needed entry location in `config.toml` configuration file. From `v0.0.3` the entry location is selected directly by the user in the application. This error is due to an old `app-data.toml` config in your computer.
+2 -3
View File
@@ -1,4 +1,3 @@
#
# Summary
[Introduction](introduction.md)
@@ -15,7 +14,7 @@
<!-- - [Version Compatibility Table](binaries/version-compatiblity.md) -->
# Nodes
- [Node Types (Previously Setup Guides)](nodes/overview.md)
- [Node Types (Previously Setup Guides)](nodes/overview.md)
- [Mix Node](nodes/mixnode.md)
- [Gateway](nodes/gateway.md)
- [Network Requester](nodes/network-requester.md)
@@ -57,7 +56,7 @@
- [Mixnet Explorer](explorers/mixnet-explorer.md)
# Nyx Blockchain
- [Interacting with Nyx Chain and Smart Contracts](nyx/interacting-with-chain.md)
- [Interacting with Nyx Chain and Smart Contracts](nyx/interacting-with-chain.md)
- [Smart Contracts](nyx/smart-contracts.md)
- [Mixnet Contract](nyx/mixnet-contract.md)
- [Vesting Contract](nyx/vesting-contract.md)
-6
View File
@@ -1,4 +1,3 @@
#
# Summary
- [Introduction](introduction.md)
@@ -29,11 +28,6 @@
- [ExploreNYM scripts](testing/explorenym-scripts.md)
<!-- - [Run in a Docker](testing/docker-monitor.md) -->
- [Troubleshooting](nodes/troubleshooting.md)
<!--
- [Nym Nodes]()
- [Validators]
- [Binary]
-->
# Token Economics
+9 -64
View File
@@ -7,7 +7,7 @@
## What is the Nym API?
The Nym API is a binary that will be operated by the Nyx validator set. This binary can be run in several different modes, and has two main bits of functionality:
* network monitoring (calculating the routing score of Mixnet nodes)
* generation and validation of [zk-nyms](https://blog.nymtech.net/zk-nyms-are-here-a-major-milestone-towards-a-market-ready-mixnet-a3470c9ab10a), our implementation of the Coconut Selective Disclosure Credential Scheme.
* generation and validation of [zk-Nyms](https://blog.nymtech.net/zk-nyms-are-here-a-major-milestone-towards-a-market-ready-mixnet-a3470c9ab10a), our implementation of the Coconut Selective Disclosure Credential Scheme.
This is important for both the proper decentralisation of the network uptime calculation and, more pressingly, enabling the NymVPN to utilise privacy preserving payments.
@@ -77,18 +77,18 @@ pruning-interval = "100"
The example value of `100` for `pruning-interval` can be customised as per your requirement.
### Credential Generation
Validators that take part in the DKG ceremony will become part of the 'quorum' generating and verifying zk-nym credentials. These will initially be used for private proof of payment for NymVPN (see our blogposts [here](https://blog.nymtech.net/nymvpn-an-invitation-for-privacy-experts-and-enthusiasts-63644139d09d) and [here](https://blog.nymtech.net/zk-nyms-are-here-a-major-milestone-towards-a-market-ready-mixnet-a3470c9ab10a) for more on this), and in the future will be expanded into more general usecases such as [offline ecash](https://arxiv.org/abs/2303.08221).
### (Coming Soon) Credential Generation
Validators that take part in the DKG ceremony will become part of the 'quorum' generating and verifying zk-Nym credentials. These will initially be used for private proof of payment for NymVPN (see our blogposts [here](https://blog.nymtech.net/nymvpn-an-invitation-for-privacy-experts-and-enthusiasts-63644139d09d) and [here](https://blog.nymtech.net/zk-nyms-are-here-a-major-milestone-towards-a-market-ready-mixnet-a3470c9ab10a) for more on this), and in the future will be expanded into more general usecases such as [offline ecash](https://arxiv.org/abs/2303.08221).
The DKG ceremony will be used to create a subset of existing validators who run `nym-api` alongside a Nyx full-node. As outlined above, they will be the ones taking part in the generation and verification of zk-nym credentials. The size of the 'minimum viable quorum' is 10 - we are aiming for a larger number than this for the initial quorum in order to have some redundancy in the case of a validator dropping or going offline.
The DKG ceremony will be used to create a subset of existing validators - referred to as the quorum. As outlined above, they will be the ones taking part in the generation and verification of zk-Nym credentials. The size of the 'minimum viable quorum' is 10 - we are aiming for a larger number than this for the initial quorum in order to have some redundancy in the case of a Validator dropping or going offline.
We will be releasing more detailed step-by-step documentation for involved validators nearer to the ceremony itself, but at a high level it will involve:
* the deployment and initialisation of [`group`](https://github.com/nymtech/nym/tree/develop/contracts/multisig/cw4-group) and [`multisig`](https://github.com/nymtech/nym/tree/develop/contracts/multisig) contracts by Nym. Validators that are members of the `group` contract are the only ones that will be able to take part in the ceremony.
* the deployment and initialisation of an instance of the [DKG contract](https://github.com/nymtech/nym/tree/develop/contracts/coconut-dkg) by Nym.
* Validators will update their `nym-api` configs with the address of the deployed contracts. They will also stop running their API instance in caching only mode, instead switching over run with the `--enabled-credentials-mode`.
* From the perspective of operators, this is all they have to do. Under the hood, each `nym-api` instance will then take part in several rounds of key submission, verification, and derivation. This will continue until quorum is acheived.
* From the perspective of operators, this is all they have to do. Under the hood, each `nym-api` instance will then take part in several rounds of key submission, verification, and derivation. This will continue until quorum is acheived. More information on this will be released closer to the time of the ceremony.
**We will be communicating individually with members of the existing validator set who have expressed interest in joining the quorum concerning the timing and specifics of the ceremony**.
**We will be communicating individually with members of the existing Validator set who have expressed interest in joining the quorum concerning the timing and specifics of the ceremony**.
## Current version
```
@@ -117,7 +117,7 @@ You can also check the various arguments required for individual commands with:
./nym-api <COMMAND> --help
```
### Initialising your Nym API Instance in caching mode
### Initialising your Nym API Instance
Initialise your API instance with:
```
@@ -126,70 +126,17 @@ Initialise your API instance with:
You can optionally pass a local identifier for this instance with the `--id` flag. Otherwise the ID of your instance defaults to `default`.
### Enabling credential signing on your Nym API instance
To engage in the Distributed Key Generation (DKG) ceremony, it's essential to transition your `nym-api` instance from its default caching mode to the active credential signing mode. This section guides you through the process of enabling credential signing
#### Generate a new wallet
Begin by generating a new wallet address specifically for your instance to use in credential signing mode. Utilize the `nyxd` command-line tool with the following command:
```
nyxd keys add signer
```
~~~admonish warning title="Backup your keys!"
It's critical to securely back up the mnemonic phrase generated during this process. This mnemonic is your key to recovering the wallet in the future, so store it in a secure, offline location.
~~~
#### Fund the address
Next, deposit NYM tokens into the newly created wallet address to ensure it can cover transaction fees incurred during the credential signing process. `nym-api` will not operate if the wallet's balance falls below 10 NYM tokens, displaying an error message upon startup.
We recommand beginning with an initial deposit of 100 NYM tokens and monitoring the balance regularly, topping it up as necessary to maintain operational readiness.
#### Update API configuration
With your new wallet ready and funded, proceed to update your `nym-api` configuration to enable credential signing:
Update your `config.toml` located in `<HOME_DIR>/.nym/nym-api/foo/config/config.toml` as below:
Enable the coconut signer:
```
[coconut_signer]
# Specifies whether coconut signing protocol is enabled in this process.
enabled = true # This was previously false
```
Set your announce address if it is empty. This is the URL you previously configured for your `nym-api` instance
```
# This is the address you previously configured for the nym-api
# Not to be confused with the Cosmos REST API URL
announce_address = 'https://nym-api.your.tld/'
```
Finally, input the mnemonic phrase generated during the wallet creation step into the mnemonic field
```
mnemonic = '<YOUR_MNEMONIC_HERE>'
```
After completing these steps, your `nym-api` instance is configured to participate in credential signing and the DKG ceremony.
### Running your Nym API Instance
The API binary currently defaults to running in caching mode. You can run your API with:
```
./nym-api run --id <YOUR_ID>
./nym-api run
```
By default the API will be trying to query your full node running locally on `localhost:26657`. If your node is hosted elsewhere, you can specify the RPC location by using the `--nyxd-validator ` flag on `run`:
```
./nym-api run --id <YOUR_ID> --nyxd-validator https://rpc-nym.yourcorp.tld:443
./nym-api run --nyxd-validator https://rpc-nym.yourcorp.tld:443
```
> You can also change the value of `local_validator` in the config file found by default in `$HOME/.nym/nym-api/<ID>/config/config.toml`.
@@ -279,8 +226,6 @@ Starting nym api...
## Automation
You will most likely want to automate your validator restarting if your server reboots. Checkout the [maintenance page](./maintenance.md) for an example `service` file.
You can also use `nymvisor` to automatically update the `nym-api` node. The steps to install Nymvisor can be found [here](nymvisor-upgrade.md).
## Exposing web endpoint using HTTPS
It is recommended to expose the webserver over HTTPS by using a webserver like Nginx. An example configuration for configuring Nginx is listed [on the maintenance page](maintenance.md#nym-api-configuration). If you're using a custom solution, ensure to allow requests from anywhere by setting a permissive CORS policy.
-3
View File
@@ -1,3 +0,0 @@
# cmdrun scripts
A place for all scripts used for `mdbook-cmdrun` to auto-fill the pages with up todate info, syntax and command outputs.
-182
View File
@@ -1,182 +0,0 @@
import argparse
import os
import requests
import json
import sys
import pandas as pd
from collections import namedtuple
############################################
############## GENERAL FNs #################
############################################
def get_url(args):
config_file = "./api_targets_config.json"
with open(config_file, "r") as f:
config = json.load(f)
env = args.api
endpoint = args.endpoint
if env == "github":
url = f"{config[env]}/{endpoint}"
else:
url = f"{config[env]}/api/v1/{endpoint}"
return url
def subparser_read(args):
url = get_url(args)
r = requests.get(url)
response = r.json()
return response
############################################
########### NYX RELATED FNs ################
############################################
def convert_u_nym(unym):
unym = int(unym)
nym = unym / 1000000
nym = int(nym)
return nym
def thousand_separator(n):
n = f'{n:_}'
return n
def remove_underscore(arg):
string = arg.replace("_", " ")
string = string.title()
return string
def display_supply_table(response, args):
df = pd.DataFrame(response)
df = df.T
del df['denom']
# df.set_axis(['**Item**', '**Amount in NYM**'], axis=1, inplace=True)
df = df.rename_axis('index1').reset_index()
df = df.rename(columns={'index1': '**Item**', 'amount': '**Amount in NYM**'})
df['**Item**'] = df['**Item**'].apply(remove_underscore)
df['**Amount in NYM**'] = df['**Amount in NYM**'].apply(convert_u_nym)
df['**Amount in NYM**'] = df['**Amount in NYM**'].apply(thousand_separator)
table = df.to_markdown(index=False)
print(table)
def read_supply(args):
response = subparser_read(args)
if args.endpoint == "circulating-supply":
if args.format:
display_supply_table(response, args)
else:
print(response)
elif args.endpoint == "foo":
# placeholder for other endpoint args
pass
else:
# placeholder for other endpoint args
pass
###########################################
############ GH RELATED FNs ###############
###########################################
def get_nym_vpn_version(args):
response = subparser_read(args)
if args.client == "desktop":
version = current_desktop_version(args, response)
elif args.client == "cli":
version = current_cli_version(args, response)
else:
print("Incorrect argument for -c, --client")
sys.exit(-1)
def current_cli_version(args, response):
df = pd.DataFrame(response)
print(df)
# NEEDS THIS IN PYTHON:
# current_cli_version=$(curl -s $release_url | jq -r '.[].tag_name' | grep '^nym-vpn-cli-v' | sort -Vr | head -n 1 | awk -F'-v' '{print $NF}')
def current_desktop_version(args, response):
# NEEDS THIS IN PYTHON:
df = pd.DataFrame(response)
print(df)
# version=$(curl -s $release_url | jq -r '.[].tag_name' | grep '^nym-vpn-desktop-v' | sort -Vr | head -n 1 | awk -F'-v' '{print $NF}')
###########################################
############### MAIN PARSER ###############
###########################################
def parser_main():
parser = argparse.ArgumentParser(
prog="Nym API scraper",
description='''Get any live data from Nyx validator''',
epilog=''
)
subparsers = parser.add_subparsers(help="")
parser_supply = subparsers.add_parser('supply',
help='reads API on supply',
aliases=['s','S']
)
parser_supply.add_argument(
"-a","--api",
type=str,
default="mainnet",
help="choose: mainnet, perf, sandbox"
)
parser_supply.add_argument(
"-e","--endpoint",
type=str,
help="choose from: https://validator.nymtech.net/api/swagger/index.html"
)
parser_supply.add_argument(
"-f","--format",
action="store_true",
help="format the output for documentation purpose (.md) - default: False (raw output)",
)
parser_supply.set_defaults(func=read_supply)
parser_nym_vpn = subparsers.add_parser('nym_vpn',
help='reads NymVPN latest version',
aliases=['n','N']
)
parser_nym_vpn.add_argument(
"-c","--client",
type=str,
default="desktop",
help="choose: desktop, cli - default: desktop"
)
parser_nym_vpn.add_argument(
"-a","--api",
type=str,
default="github",
help="choose: mainnet, perf, sandbox"
)
parser_nym_vpn.add_argument(
"-e","--endpoint",
type=str,
help="add the url suffix",
default="repos/nymtech/nym-vpn-client/releases"
)
parser_nym_vpn.set_defaults(func=get_nym_vpn_version)
args = parser.parse_args()
try:
args.func(args)
except AttributeError as e:
print("Error on argparser")
sys.exit(-1)
if __name__ == "__main__":
parser_main()
@@ -1,6 +0,0 @@
{
"sandbox": "https://sandbox-nym-api1.nymtech.net",
"perf": "https://api.performance.nymte.ch",
"mainnet": "https://validator.nymtech.net",
"github": "https://api.github.com"
}
+4 -4
View File
@@ -13,10 +13,10 @@ DENOMS_EXPONENT=6
REWARDING_VALIDATOR_ADDRESS=n1pefc2utwpy5w78p2kqdsfmpjxfwmn9d39k5mqa
MIXNET_CONTRACT_ADDRESS=n1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3sjkxkav
VESTING_CONTRACT_ADDRESS=n1unyuj8qnmygvzuex3dwmg9yzt9alhvyeat0uu0jedg2wj33efl5qackslz
COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n1ljlwey4xdj0zs7zueepc48nkr033fca6fjgvurfvttqegm8dvsrswsul70
GROUP_CONTRACT_ADDRESS=n10v3rjnq4cjyccfykyams68ztce337gksuu6f0lvtl4meuwvkewaqru4uav
MULTISIG_CONTRACT_ADDRESS=n1cemnu8as0ls45v3caunpesl8jlsfw2ff9rlwnltlecp7zrxct4dsqc2y42
COCONUT_DKG_CONTRACT_ADDRESS=n1zx96qgd88vqlzcxkpwzks7kqs5ctrx36xtzfc58p7q6c4ng9anlqzc4nh8
COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n13902g92xfefeyzuyed49snlm5fxv5ms6mdq5kvrut27hasdw5a9q9vyw6c
GROUP_CONTRACT_ADDRESS=n18nczmqw6adwxg2wnlef3hf0etf8anccafp2pjpul5rrtmv96umyq5mv7t5
MULTISIG_CONTRACT_ADDRESS=n1q3zzxl78rlmxv3vn0uf4vkyz285lk8q2xzne299yt9x6mpfgk90qukuzmv
COCONUT_DKG_CONTRACT_ADDRESS=n1jsz20ggp5a6v76j060erkzvxmeus8htlpl77yxp878f0gf95cyaq6p2pee
NAME_SERVICE_CONTRACT_ADDRESS=n12ne7qtmdwd0j03t9t5es8md66wq4e5xg9neladrsag8fx3y89rcs36asfp
SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS=n1ps5yutd7sufwg058qd7ac7ldnlazsvmhzqwucsfxmm445d70u8asqxpur4
EPHEMERA_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "explorer-api"
version = "1.1.34"
version = "1.1.33"
edition = "2021"
license.workspace = true
@@ -10,7 +10,7 @@ license.workspace = true
chrono = { version = "0.4.31", features = ["serde"] }
clap = { workspace = true, features = ["cargo", "derive"] }
dotenvy = { workspace = true }
humantime-serde = { workspace = true }
humantime-serde = "1.0"
isocountry = "0.3.2"
itertools = "0.10.3"
log = { workspace = true }
+11 -8
View File
@@ -4,7 +4,7 @@
[package]
name = "nym-gateway"
license = "GPL-3.0"
version = "1.1.35"
version = "1.1.33"
authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
"Jędrzej Stuczyński <andrew@nymtech.net>",
@@ -18,6 +18,7 @@ rust-version = "1.70"
[dependencies]
anyhow = { workspace = true }
async-trait = { workspace = true }
atty = "0.2"
bip39 = { workspace = true }
bs58 = { workspace = true }
clap = { workspace = true, features = ["cargo", "derive"] }
@@ -26,10 +27,11 @@ dashmap = { workspace = true }
dirs = "4.0"
dotenvy = { workspace = true }
futures = { workspace = true }
humantime-serde = { workspace = true }
humantime-serde = "1.0.1"
ipnetwork = "0.16"
log = { workspace = true }
once_cell = "1.7.2"
pretty_env_logger = "0.4"
rand = "0.7"
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
@@ -38,7 +40,6 @@ sqlx = { workspace = true, features = [
"sqlite",
"macros",
"migrate",
"time"
] }
subtle-encoding = { version = "0.5", features = ["bech32-preview"] }
thiserror = { workspace = true }
@@ -50,13 +51,14 @@ tokio = { workspace = true, features = [
"time",
] }
tokio-stream = { version = "0.1.11", features = ["fs"] }
tokio-tungstenite = { workspace = true }
tokio-tungstenite = { version = "0.20.1" }
tokio-util = { workspace = true, features = ["codec"] }
url = { workspace = true, features = ["serde"] }
time = { workspace = true }
zeroize = { workspace = true }
# internal
nym-node = { path = "../nym-node" }
nym-api-requests = { path = "../nym-api/nym-api-requests" }
nym-bin-common = { path = "../common/bin-common", features = ["output_format"] }
@@ -69,7 +71,6 @@ nym-mixnet-client = { path = "../common/client-libs/mixnet-client" }
nym-mixnode-common = { path = "../common/mixnode-common" }
nym-network-defaults = { path = "../common/network-defaults" }
nym-network-requester = { path = "../service-providers/network-requester" }
nym-node-http-api = { path = "../nym-node/nym-node-http-api" }
nym-pemstore = { path = "../common/pemstore" }
nym-sphinx = { path = "../common/nymsphinx" }
nym-statistics-common = { path = "../common/statistics" }
@@ -79,13 +80,15 @@ nym-validator-client = { path = "../common/client-libs/validator-client" }
nym-ip-packet-router = { path = "../service-providers/ip-packet-router" }
nym-wireguard = { path = "../common/wireguard", optional = true }
nym-wireguard-types = { path = "../common/wireguard-types", default-features = false }
defguard_wireguard_rs = { git = "https://github.com/neacsu/wireguard-rs.git", rev = "c2cd0c1119f699f4bc43f5e6ffd6fc242caa42ed", optional = true }
[dev-dependencies]
tower = "0.4.13"
rand = "0.8.5"
hyper = "0.14.27"
[build-dependencies]
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
tokio = { version = "1.24.1", features = ["rt-multi-thread", "macros"] }
sqlx = { workspace = true, features = [
"runtime-tokio-rustls",
"sqlite",
@@ -4,7 +4,7 @@
use nym_crypto::asymmetric::identity;
use thiserror::Error;
#[derive(Debug, Clone, Error)]
#[derive(Debug, Error)]
pub enum HandshakeError {
#[error(
"received key material of invalid length - {0}. Expected: {}",
-25
View File
@@ -190,22 +190,6 @@ impl ClientControlRequest {
}
}
pub fn name(&self) -> String {
match self {
ClientControlRequest::Authenticate { .. } => "Authenticate".to_string(),
ClientControlRequest::RegisterHandshakeInitRequest { .. } => {
"RegisterHandshakeInitRequest".to_string()
}
ClientControlRequest::BandwidthCredential { .. } => "BandwidthCredential".to_string(),
ClientControlRequest::BandwidthCredentialV2 { .. } => {
"BandwidthCredentialV2".to_string()
}
ClientControlRequest::ClaimFreeTestnetBandwidth => {
"ClaimFreeTestnetBandwidth".to_string()
}
}
}
pub fn new_enc_coconut_bandwidth_credential_v1(
credential: &OldV1Credential,
shared_key: &SharedKeys,
@@ -307,15 +291,6 @@ pub enum ServerResponse {
}
impl ServerResponse {
pub fn name(&self) -> String {
match self {
ServerResponse::Authenticate { .. } => "Authenticate".to_string(),
ServerResponse::Register { .. } => "Register".to_string(),
ServerResponse::Bandwidth { .. } => "Bandwidth".to_string(),
ServerResponse::Send { .. } => "Send".to_string(),
ServerResponse::Error { .. } => "Error".to_string(),
}
}
pub fn new_error<S: Into<String>>(msg: S) -> Self {
ServerResponse::Error {
message: msg.into(),
@@ -1,7 +0,0 @@
/*
* Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
* SPDX-License-Identifier: Apache-2.0
*/
ALTER TABLE available_bandwidth
ADD COLUMN freepass_expiration TIMESTAMP WITHOUT TIME ZONE;
+119 -17
View File
@@ -2,23 +2,20 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::commands::upgrade_helpers;
use log::{error, info};
use nym_config::{save_formatted_config_to_file, OptionalSet};
use nym_crypto::asymmetric::identity;
use nym_gateway::config::default_config_filepath;
use nym_gateway::config::persistence::paths::{
use crate::config::default_config_filepath;
use crate::config::persistence::paths::{
default_ip_packet_router_data_dir, default_network_requester_data_dir,
};
use nym_gateway::config::Config;
use nym_gateway::error::GatewayError;
use nym_gateway::helpers::{
override_ip_packet_router_config, override_network_requester_config,
OverrideIpPacketRouterConfig, OverrideNetworkRequesterConfig,
};
use crate::config::Config;
use crate::error::GatewayError;
use log::{error, info};
use nym_bin_common::version_checker;
use nym_config::{save_formatted_config_to_file, OptionalSet};
use nym_crypto::asymmetric::identity;
use nym_network_defaults::mainnet;
use nym_network_defaults::var_names::NYXD;
use nym_network_defaults::var_names::{BECH32_PREFIX, NYM_API, STATISTICS_SERVICE_DOMAIN_ADDRESS};
use nym_network_requester::config::BaseClientConfig;
use nym_network_requester::{
generate_new_client_keys, set_active_gateway, setup_fs_gateways_storage, setup_gateway,
GatewaySetup, OnDiskKeys,
@@ -105,11 +102,21 @@ impl OverrideConfig {
}
}
pub(crate) fn try_override_config<O: Into<OverrideConfig>>(
config: Config,
override_args: O,
) -> Result<Config, GatewayError> {
override_args.into().do_override(config)
#[derive(Default, Debug)]
pub(crate) struct OverrideNetworkRequesterConfig {
pub(crate) fastmode: bool,
pub(crate) no_cover: bool,
pub(crate) medium_toggle: bool,
pub(crate) open_proxy: Option<bool>,
pub(crate) enable_statistics: Option<bool>,
pub(crate) statistics_recipient: Option<String>,
}
#[derive(Default, Debug)]
pub(crate) struct OverrideIpPacketRouterConfig {
// TODO
}
/// Ensures that a given bech32 address is valid
@@ -129,6 +136,30 @@ pub(crate) fn ensure_correct_bech32_prefix(address: &AccountId) -> Result<(), Ga
Ok(())
}
// this only checks compatibility between config the binary. It does not take into consideration
// network version. It might do so in the future.
pub(crate) fn ensure_config_version_compatibility(cfg: &Config) -> Result<(), GatewayError> {
let binary_version = env!("CARGO_PKG_VERSION");
let config_version = &cfg.gateway.version;
if binary_version == config_version {
Ok(())
} else if version_checker::is_minor_version_compatible(binary_version, config_version) {
log::warn!(
"The gateway binary has different version than what is specified in config file! {binary_version} and {config_version}. \
But, they are still semver compatible. However, consider running the `upgrade` command.");
Ok(())
} else {
log::error!(
"The gateway binary has different version than what is specified in config file! {binary_version} and {config_version}. \
And they are semver incompatible! - please run the `upgrade` command before attempting `run` again");
Err(GatewayError::LocalVersionCheckFailure {
binary_version: binary_version.to_owned(),
config_version: config_version.to_owned(),
})
}
}
pub(crate) fn try_load_current_config(id: &str) -> Result<Config, GatewayError> {
upgrade_helpers::try_upgrade_config(id)?;
@@ -152,6 +183,77 @@ fn make_ip_id(gateway_id: &str) -> String {
format!("{gateway_id}-ip-packet-router")
}
// NOTE: make sure this is in sync with service-providers/network-requester/src/cli/mod.rs::override_config
pub(crate) fn override_network_requester_config(
mut cfg: nym_network_requester::Config,
opts: Option<OverrideNetworkRequesterConfig>,
) -> nym_network_requester::Config {
let Some(opts) = opts else { return cfg };
// as of 12.09.23 the below is true (not sure how this comment will rot in the future)
// medium_toggle:
// - sets secondary packet size to 16kb
// - disables poisson distribution of the main traffic stream
// - sets the cover traffic stream to 1 packet / 5s (on average)
// - disables per hop delay
//
// fastmode (to be renamed to `fast-poisson`):
// - sets average per hop delay to 10ms
// - sets the cover traffic stream to 1 packet / 2000s (on average); for all intents and purposes it disables the stream
// - sets the poisson distribution of the main traffic stream to 4ms, i.e. 250 packets / s on average
//
// no_cover:
// - disables poisson distribution of the main traffic stream
// - disables the secondary cover traffic stream
// disable poisson rate in the BASE client if the NR option is enabled
if cfg.network_requester.disable_poisson_rate {
cfg.set_no_poisson_process();
}
// those should be enforced by `clap` when parsing the arguments
if opts.medium_toggle {
assert!(!opts.fastmode);
assert!(!opts.no_cover);
cfg.set_medium_toggle();
}
cfg.with_base(
BaseClientConfig::with_high_default_traffic_volume,
opts.fastmode,
)
.with_base(BaseClientConfig::with_disabled_cover_traffic, opts.no_cover)
.with_optional(
nym_network_requester::Config::with_open_proxy,
opts.open_proxy,
)
.with_optional(
nym_network_requester::Config::with_enabled_statistics,
opts.enable_statistics,
)
.with_optional(
nym_network_requester::Config::with_statistics_recipient,
opts.statistics_recipient,
)
}
// NOTE: make sure this is in sync with service-providers/ip-packet-router/src/cli/mod.rs::override_config
pub(crate) fn override_ip_packet_router_config(
mut cfg: nym_ip_packet_router::Config,
opts: Option<OverrideIpPacketRouterConfig>,
) -> nym_ip_packet_router::Config {
let Some(_opts) = opts else { return cfg };
// disable poisson rate in the BASE client if the IPR option is enabled
if cfg.ip_packet_router.disable_poisson_rate {
info!("Disabling poisson rate for ip packet router");
cfg.set_no_poisson_process();
}
cfg
}
pub(crate) async fn initialise_local_network_requester(
gateway_config: &Config,
opts: OverrideNetworkRequesterConfig,
+314 -10
View File
@@ -1,22 +1,326 @@
// Copyright 2020-2024 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::commands::helpers::{
initialise_local_ip_packet_router, initialise_local_network_requester,
OverrideNetworkRequesterConfig,
};
use crate::config::{default_config_directory, default_config_filepath, default_data_directory};
use crate::node::helpers::node_details;
use crate::{commands::helpers::OverrideConfig, config::Config, OutputFormat};
use anyhow::bail;
use clap::Args;
use colored::Colorize;
use nym_crypto::asymmetric::{encryption, identity};
use std::net::IpAddr;
use std::path::PathBuf;
use std::{fs, io};
use super::helpers::OverrideIpPacketRouterConfig;
#[allow(dead_code)]
#[derive(Args, Clone, Debug)]
pub struct Init {
/// Id of the gateway we want to create config for
#[clap(long)]
id: Option<String>,
id: String,
/// The listening address on which the gateway will be receiving sphinx packets and listening for client data
#[clap(long, alias = "host")]
listening_address: IpAddr,
/// Comma separated list of public ip addresses that will announced to the nym-api and subsequently to the clients.
/// In nearly all circumstances, it's going to be identical to the address you're going to use for bonding.
#[clap(long, value_delimiter = ',')]
public_ips: Option<Vec<IpAddr>>,
/// Optional hostname associated with this gateway that will announced to the nym-api and subsequently to the clients
#[clap(long)]
hostname: Option<String>,
/// The port on which the gateway will be listening for sphinx packets
#[clap(long)]
mix_port: Option<u16>,
/// The port on which the gateway will be listening for clients gateway-requests
#[clap(long)]
clients_port: Option<u16>,
/// Path to sqlite database containing all gateway persistent data
#[clap(long)]
datastore: Option<PathBuf>,
/// Comma separated list of endpoints of nym APIs
#[clap(long, alias = "validator_apis", value_delimiter = ',')]
// the alias here is included for backwards compatibility (1.1.4 and before)
nym_apis: Option<Vec<url::Url>>,
/// Comma separated list of endpoints of the validator
#[clap(
long,
alias = "validators",
alias = "nyxd_validators",
value_delimiter = ',',
hide = true
)]
// the alias here is included for backwards compatibility (1.1.4 and before)
nyxd_urls: Option<Vec<url::Url>>,
/// Cosmos wallet mnemonic needed for double spending protection
#[clap(long)]
mnemonic: Option<bip39::Mnemonic>,
/// Set this gateway to work only with coconut credentials; that would disallow clients to
/// bypass bandwidth credential requirement
#[clap(long, hide = true)]
only_coconut_credentials: Option<bool>,
/// Enable/disable gateway anonymized statistics that get sent to a statistics aggregator server
#[clap(long)]
enabled_statistics: Option<bool>,
/// URL where a statistics aggregator is running. The default value is a Nym aggregator server
#[clap(long)]
statistics_service_url: Option<url::Url>,
/// Allows this gateway to run an embedded network requester for minimal network overhead
#[clap(long)]
with_network_requester: bool,
/// Allows this gateway to run an embedded network requester for minimal network overhead
#[clap(long, hide = true)]
with_ip_packet_router: bool,
// ##### NETWORK REQUESTER FLAGS #####
/// Specifies whether this network requester should run in 'open-proxy' mode
#[clap(long, requires = "with_network_requester")]
open_proxy: Option<bool>,
/// Enable service anonymized statistics that get sent to a statistics aggregator server
#[clap(long, requires = "with_network_requester")]
enable_statistics: Option<bool>,
/// Mixnet client address where a statistics aggregator is running. The default value is a Nym
/// aggregator client
#[clap(long, requires = "with_network_requester")]
statistics_recipient: Option<String>,
/// Mostly debug-related option to increase default traffic rate so that you would not need to
/// modify config post init
#[clap(
long,
hide = true,
conflicts_with = "medium_toggle",
requires = "with_network_requester"
)]
fastmode: bool,
/// Disable loop cover traffic and the Poisson rate limiter (for debugging only)
#[clap(
long,
hide = true,
conflicts_with = "medium_toggle",
requires = "with_network_requester"
)]
no_cover: bool,
/// Enable medium mixnet traffic, for experiments only.
/// This includes things like disabling cover traffic, no per hop delays, etc.
#[clap(
long,
hide = true,
conflicts_with = "no_cover",
conflicts_with = "fastmode",
requires = "with_network_requester"
)]
medium_toggle: bool,
#[clap(short, long, default_value_t = OutputFormat::default())]
output: OutputFormat,
}
pub async fn execute(_args: Init) -> anyhow::Result<()> {
bail!(
"standalone mixnode initialisation has been removed - please initialise a `nym-node` instead"
.red()
.bold()
)
impl From<Init> for OverrideConfig {
fn from(init_config: Init) -> Self {
OverrideConfig {
listening_address: Some(init_config.listening_address),
public_ips: init_config.public_ips,
hostname: init_config.hostname,
mix_port: init_config.mix_port,
clients_port: init_config.clients_port,
datastore: init_config.datastore,
nym_apis: init_config.nym_apis,
mnemonic: init_config.mnemonic,
enabled_statistics: init_config.enabled_statistics,
statistics_service_url: init_config.statistics_service_url,
nyxd_urls: init_config.nyxd_urls,
only_coconut_credentials: init_config.only_coconut_credentials,
with_network_requester: Some(init_config.with_network_requester),
with_ip_packet_router: Some(init_config.with_ip_packet_router),
}
}
}
impl<'a> From<&'a Init> for OverrideNetworkRequesterConfig {
fn from(value: &'a Init) -> Self {
OverrideNetworkRequesterConfig {
fastmode: value.fastmode,
no_cover: value.no_cover,
medium_toggle: value.medium_toggle,
open_proxy: value.open_proxy,
enable_statistics: value.enable_statistics,
statistics_recipient: value.statistics_recipient.clone(),
}
}
}
impl From<&Init> for OverrideIpPacketRouterConfig {
fn from(_value: &Init) -> Self {
OverrideIpPacketRouterConfig {}
}
}
fn init_paths(id: &str) -> io::Result<()> {
fs::create_dir_all(default_data_directory(id))?;
fs::create_dir_all(default_config_directory(id))
}
pub async fn execute(args: Init) -> anyhow::Result<()> {
eprintln!("Initialising gateway {}...", args.id);
let output = args.output;
let already_init = if default_config_filepath(&args.id).exists() {
eprintln!(
"Gateway \"{}\" was already initialised before! Config information will be \
overwritten (but keys will be kept)!",
args.id
);
true
} else {
init_paths(&args.id)?;
false
};
// Initialising the config structure is just overriding a default constructed one
let fresh_config = Config::new(&args.id);
let nr_opts = (&args).into();
let ip_opts = (&args).into();
let mut config = OverrideConfig::from(args).do_override(fresh_config)?;
// if gateway was already initialised, don't generate new keys, et al.
if !already_init {
let mut rng = rand::rngs::OsRng;
let identity_keys = identity::KeyPair::new(&mut rng);
let sphinx_keys = encryption::KeyPair::new(&mut rng);
if let Err(err) = nym_pemstore::store_keypair(
&identity_keys,
&nym_pemstore::KeyPairPath::new(
config.storage_paths.private_identity_key(),
config.storage_paths.public_identity_key(),
),
) {
bail!("failed to save the identity keys: {err}")
}
if let Err(err) = nym_pemstore::store_keypair(
&sphinx_keys,
&nym_pemstore::KeyPairPath::new(
config.storage_paths.private_encryption_key(),
config.storage_paths.public_encryption_key(),
),
) {
bail!("failed to save the sphinx keys: {err}")
}
if config.network_requester.enabled {
initialise_local_network_requester(&config, nr_opts, *identity_keys.public_key())
.await?;
}
if config.ip_packet_router.enabled {
initialise_local_ip_packet_router(&config, ip_opts, *identity_keys.public_key())
.await?;
}
eprintln!("Saved identity and mixnet sphinx keypairs");
}
let config_save_location = config.default_location();
if let Err(err) = config.save_to_default_location() {
bail!("failed to save the config file: {err}")
}
config.save_path = Some(config_save_location.clone());
eprintln!(
"Saved configuration file to {}",
config_save_location.display()
);
eprintln!("Gateway configuration completed.\n\n\n");
output.to_stdout(&node_details(&config).await?);
Ok(())
}
#[cfg(test)]
mod tests {
use nym_network_defaults::var_names::BECH32_PREFIX;
use crate::node::{storage::InMemStorage, Gateway};
use super::*;
#[tokio::test]
async fn create_gateway_with_in_mem_storage() {
let args = Init {
id: "foo-id".to_string(),
listening_address: "1.1.1.1".parse().unwrap(),
public_ips: None,
hostname: None,
mix_port: Some(42),
clients_port: Some(43),
datastore: Some("/foo-datastore".parse().unwrap()),
nym_apis: None,
mnemonic: None,
statistics_service_url: None,
enabled_statistics: None,
nyxd_urls: None,
only_coconut_credentials: None,
output: Default::default(),
with_network_requester: false,
with_ip_packet_router: false,
open_proxy: None,
enable_statistics: None,
statistics_recipient: None,
fastmode: false,
no_cover: false,
medium_toggle: false,
};
std::env::set_var(BECH32_PREFIX, "n");
let fresh_config = Config::new(&args.id);
let config = OverrideConfig::from(args)
.do_override(fresh_config)
.unwrap();
let (identity_keys, sphinx_keys) = {
let mut rng = rand::rngs::OsRng;
(
identity::KeyPair::new(&mut rng),
encryption::KeyPair::new(&mut rng),
)
};
// The test is really if this instantiates with InMemStorage without panics
let _gateway = Gateway::new_from_keys_and_storage(
config,
None,
None,
identity_keys,
sphinx_keys,
InMemStorage,
)
.await;
}
}
+2 -12
View File
@@ -4,10 +4,7 @@
use crate::Cli;
use clap::CommandFactory;
use clap::Subcommand;
use log::warn;
use nym_bin_common::completions::{fig_generate, ArgShell};
use std::io::IsTerminal;
use std::time::Duration;
pub(crate) mod build_info;
pub(crate) mod helpers;
@@ -28,7 +25,7 @@ pub(crate) enum Commands {
NodeDetails(node_details::NodeDetails),
/// Starts the gateway
Run(Box<run::Run>),
Run(run::Run),
/// Add network requester support to this gateway
// essentially an option to include NR without having to setup fresh gateway
@@ -55,17 +52,10 @@ pub(crate) enum Commands {
pub(crate) async fn execute(args: Cli) -> anyhow::Result<()> {
let bin_name = "nym-gateway";
warn!("standalone gateways have been deprecated - please consider migrating it to a `nym-node` via `nym-node migrate gateway` command");
if std::io::stdout().is_terminal() {
// if user is running it in terminal session,
// introduce the delay, so they'd notice the message
tokio::time::sleep(Duration::from_secs(1)).await
}
match args.command {
Commands::Init(m) => init::execute(m).await?,
Commands::NodeDetails(m) => node_details::execute(m).await?,
Commands::Run(m) => run::execute(*m).await?,
Commands::Run(m) => run::execute(m).await?,
Commands::SetupNetworkRequester(m) => setup_network_requester::execute(m).await?,
Commands::SetupIpPacketRouter(m) => setup_ip_packet_router::execute(m).await?,
Commands::Sign(m) => sign::execute(m)?,
+1 -1
View File
@@ -2,9 +2,9 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::commands::helpers::try_load_current_config;
use crate::node::helpers::node_details;
use clap::Args;
use nym_bin_common::output_format::OutputFormat;
use nym_gateway::helpers::node_details;
#[derive(Args, Clone)]
pub struct NodeDetails {
+17 -13
View File
@@ -1,17 +1,22 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::commands::helpers::{try_load_current_config, try_override_config, OverrideConfig};
use crate::commands::helpers::{
ensure_config_version_compatibility, OverrideConfig, OverrideNetworkRequesterConfig,
};
use crate::node::helpers::node_details;
use crate::support::config::build_config;
use anyhow::bail;
use clap::Args;
use log::warn;
use nym_bin_common::output_format::OutputFormat;
use nym_config::helpers::SPECIAL_ADDRESSES;
use nym_gateway::helpers::{OverrideIpPacketRouterConfig, OverrideNetworkRequesterConfig};
use nym_gateway::GatewayError;
use nym_node::error::NymNodeError;
use std::net::IpAddr;
use std::path::PathBuf;
use super::helpers::OverrideIpPacketRouterConfig;
#[derive(Args, Clone)]
pub struct Run {
/// Id of the gateway we want to run
@@ -22,12 +27,12 @@ pub struct Run {
#[arg(long, alias = "host")]
listening_address: Option<IpAddr>,
/// Comma separated list of public ip addresses that will be announced to the nym-api and subsequently to the clients.
/// Comma separated list of public ip addresses that will announced to the nym-api and subsequently to the clients.
/// In nearly all circumstances, it's going to be identical to the address you're going to use for bonding.
#[arg(long, value_delimiter = ',')]
public_ips: Option<Vec<IpAddr>>,
/// Optional hostname associated with this gateway that will be announced to the nym-api and subsequently to the clients
/// Optional hostname associated with this gateway that will announced to the nym-api and subsequently to the clients
#[arg(long)]
hostname: Option<String>,
@@ -191,7 +196,7 @@ fn check_public_ips(ips: &[IpAddr], local: bool) -> anyhow::Result<()> {
for ip in ips {
if SPECIAL_ADDRESSES.contains(ip) {
if !local {
return Err(GatewayError::InvalidPublicIp { address: *ip }.into());
return Err(NymNodeError::InvalidPublicIp { address: *ip }.into());
}
suspicious_ip.push(ip);
}
@@ -220,12 +225,12 @@ pub async fn execute(args: Run) -> anyhow::Result<()> {
let nr_opts = (&args).into();
let ip_opts = (&args).into();
let mut config = try_load_current_config(&args.id)?;
config = try_override_config(config, args)?;
let config = build_config(id, args)?;
ensure_config_version_compatibility(&config)?;
let public_ips = &config.host.public_ips;
if public_ips.is_empty() {
return Err(GatewayError::NoPublicIps.into());
return Err(NymNodeError::NoPublicIps.into());
}
check_public_ips(public_ips, local)?;
if config.gateway.clients_wss_port.is_some() && config.host.hostname.is_none() {
@@ -236,14 +241,13 @@ pub async fn execute(args: Run) -> anyhow::Result<()> {
show_binding_warning(config.gateway.listening_address);
}
let node_details = node_details(&config).await?;
let gateway =
nym_gateway::create_gateway(config, Some(nr_opts), Some(ip_opts), custom_mixnet).await?;
let node_details = gateway.node_details().await?;
crate::node::create_gateway(config, Some(nr_opts), Some(ip_opts), custom_mixnet).await?;
eprintln!(
"\nTo bond your gateway you will need to install the Nym wallet, go to https://nymtech.net/get-involved and select the Download button.\n\
Select the correct version and install it to your machine. You will need to provide some of the following: \n ");
output.to_stdout(&node_details);
gateway.run().await?;
Ok(())
gateway.run().await
}
@@ -1,10 +1,12 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::commands::helpers::{initialise_local_ip_packet_router, try_load_current_config};
use crate::commands::helpers::{
initialise_local_ip_packet_router, try_load_current_config, OverrideIpPacketRouterConfig,
};
use crate::node::helpers::load_public_key;
use clap::Args;
use nym_bin_common::output_format::OutputFormat;
use nym_gateway::helpers::{load_public_key, OverrideIpPacketRouterConfig};
use std::path::PathBuf;
#[derive(Args, Clone)]
@@ -1,14 +1,13 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::commands::helpers::{initialise_local_network_requester, try_load_current_config};
use crate::commands::helpers::{
initialise_local_network_requester, try_load_current_config, OverrideNetworkRequesterConfig,
};
use crate::node::helpers::load_public_key;
use clap::Args;
use log::warn;
use nym_bin_common::output_format::OutputFormat;
use nym_gateway::helpers::{load_public_key, OverrideNetworkRequesterConfig};
use std::io::IsTerminal;
use std::path::PathBuf;
use std::time::Duration;
#[derive(Args, Clone)]
pub struct CmdArgs {
@@ -78,13 +77,6 @@ impl<'a> From<&'a CmdArgs> for OverrideNetworkRequesterConfig {
}
pub async fn execute(args: CmdArgs) -> anyhow::Result<()> {
warn!("standalone gateways have been deprecated - please consider migrating it to a `nym-node` via `nym-node migrate gateway` command");
if std::io::stdout().is_terminal() {
// if user is running it in terminal session,
// introduce the delay, so they'd notice the message
tokio::time::sleep(Duration::from_secs(1)).await
}
let mut config = try_load_current_config(&args.id)?;
let opts = (&args).into();
+6 -5
View File
@@ -2,14 +2,15 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::commands::helpers::{
ensure_correct_bech32_prefix, try_load_current_config, try_override_config, OverrideConfig,
ensure_config_version_compatibility, ensure_correct_bech32_prefix, OverrideConfig,
};
use crate::error::GatewayError;
use crate::node::helpers::load_identity_keys;
use crate::support::config::build_config;
use anyhow::{bail, Result};
use clap::{ArgGroup, Args};
use nym_bin_common::output_format::OutputFormat;
use nym_crypto::asymmetric::identity;
use nym_gateway::error::GatewayError;
use nym_gateway::helpers::load_identity_keys;
use nym_types::helpers::ConsoleSigningOutput;
use nym_validator_client::nyxd;
@@ -119,8 +120,8 @@ fn print_signed_contract_msg(
}
pub fn execute(args: Sign) -> anyhow::Result<()> {
let mut config = try_load_current_config(&args.id)?;
config = try_override_config(config, OverrideConfig::default())?;
let config = build_config(args.id.clone(), OverrideConfig::default())?;
ensure_config_version_compatibility(&config)?;
let output = args.output;
let signed_target = SignedTarget::try_from(args)?;
+6 -6
View File
@@ -1,13 +1,13 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::config::old_config_v1_1_20::ConfigV1_1_20;
use crate::config::old_config_v1_1_28::ConfigV1_1_28;
use crate::config::old_config_v1_1_29::ConfigV1_1_29;
use crate::config::old_config_v1_1_31::ConfigV1_1_31;
use crate::config::{default_config_filepath, Config};
use crate::error::GatewayError;
use log::info;
use nym_gateway::config::old_config_v1_1_20::ConfigV1_1_20;
use nym_gateway::config::old_config_v1_1_28::ConfigV1_1_28;
use nym_gateway::config::old_config_v1_1_29::ConfigV1_1_29;
use nym_gateway::config::old_config_v1_1_31::ConfigV1_1_31;
use nym_gateway::config::{default_config_filepath, Config};
use nym_gateway::error::GatewayError;
fn try_upgrade_v1_1_20_config(id: &str) -> Result<bool, GatewayError> {
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
+28 -136
View File
@@ -1,31 +1,30 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::config::persistence::paths::GatewayPaths;
use crate::config::template::CONFIG_TEMPLATE;
use log::{debug, warn};
use nym_bin_common::logging::LoggingSettings;
use nym_config::defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT};
use nym_config::helpers::inaddr_any;
use nym_config::serde_helpers::{de_maybe_port, de_maybe_stringified};
use nym_config::{
must_get_home, read_config_from_toml_file, save_formatted_config_to_file, NymConfigTemplate,
DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, DEFAULT_DATA_DIR, NYM_DIR,
};
use nym_network_defaults::{mainnet, DEFAULT_NYM_NODE_HTTP_PORT, WG_PORT};
use serde::{Deserialize, Serialize};
use nym_network_defaults::mainnet;
use nym_node::config;
use serde::{Deserialize, Deserializer, Serialize};
use std::io;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::net::{IpAddr, SocketAddr};
use std::path::{Path, PathBuf};
use std::time::Duration;
use url::Url;
use zeroize::{Zeroize, ZeroizeOnDrop};
pub use crate::config::persistence::paths::{GatewayPaths, WireguardPaths};
pub mod old_config_v1_1_20;
pub mod old_config_v1_1_28;
pub mod old_config_v1_1_29;
pub mod old_config_v1_1_31;
pub(crate) mod old_config_v1_1_20;
pub(crate) mod old_config_v1_1_28;
pub(crate) mod old_config_v1_1_29;
pub(crate) mod old_config_v1_1_31;
pub mod persistence;
mod template;
@@ -42,8 +41,17 @@ const DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE: usize = 2000;
const DEFAULT_STORED_MESSAGE_FILENAME_LENGTH: u16 = 16;
const DEFAULT_MESSAGE_RETRIEVAL_LIMIT: i64 = 100;
const DEFAULT_CLIENT_BANDWIDTH_MAX_FLUSHING_RATE: Duration = Duration::from_millis(5);
const DEFAULT_CLIENT_BANDWIDTH_MAX_DELTA_FLUSHING_AMOUNT: i64 = 512 * 1024; // 512kB
fn de_maybe_port<'de, D>(deserializer: D) -> Result<Option<u16>, D::Error>
where
D: Deserializer<'de>,
{
let port = u16::deserialize(deserializer)?;
if port == 0 {
Ok(None)
} else {
Ok(Some(port))
}
}
/// Derive default path to gateway's config directory.
/// It should get resolved to `$HOME/.nym/gateways/<id>/config`
@@ -78,16 +86,16 @@ pub struct Config {
#[serde(skip)]
pub(crate) save_path: Option<PathBuf>,
pub host: Host,
pub host: config::Host,
#[serde(default)]
pub http: Http,
pub http: config::Http,
pub gateway: Gateway,
// currently not really used for anything useful
#[serde(default)]
pub wireguard: Wireguard,
// currently not really used for anything useful
pub wireguard: config::Wireguard,
pub storage_paths: GatewayPaths,
@@ -114,7 +122,7 @@ impl Config {
let default_gateway = Gateway::new_default(id.as_ref());
Config {
save_path: None,
host: Host {
host: config::Host {
// this is a very bad default!
public_ips: vec![default_gateway.listening_address],
hostname: None,
@@ -130,32 +138,6 @@ impl Config {
}
}
#[allow(clippy::too_many_arguments)]
pub fn externally_loaded(
host: impl Into<Host>,
http: impl Into<Http>,
gateway: impl Into<Gateway>,
wireguard: impl Into<Wireguard>,
storage_paths: impl Into<GatewayPaths>,
network_requester: impl Into<NetworkRequester>,
ip_packet_router: impl Into<IpPacketRouter>,
logging: impl Into<LoggingSettings>,
debug: impl Into<Debug>,
) -> Self {
Config {
save_path: None,
host: host.into(),
http: http.into(),
gateway: gateway.into(),
wireguard: wireguard.into(),
storage_paths: storage_paths.into(),
network_requester: network_requester.into(),
ip_packet_router: ip_packet_router.into(),
logging: logging.into(),
debug: debug.into(),
}
}
// simple wrapper that reads config file and assigns path location
fn read_from_path<P: AsRef<Path>>(path: P) -> io::Result<Self> {
let path = path.as_ref();
@@ -165,6 +147,9 @@ impl Config {
Ok(loaded)
}
// currently this is dead code, but once we allow loading configs from custom paths
// well, we will have to be using it
#[allow(dead_code)]
pub fn read_from_toml_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
Self::read_from_path(path)
}
@@ -300,87 +285,6 @@ impl Config {
}
}
// TODO: this is very much a WIP. we need proper ssl certificate support here
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Host {
/// Ip address(es) of this host, such as 1.1.1.1 that external clients will use for connections.
pub public_ips: Vec<IpAddr>,
/// Optional hostname of this node, for example nymtech.net.
// TODO: this is temporary. to be replaced by pulling the data directly from the certs.
#[serde(deserialize_with = "de_maybe_stringified")]
pub hostname: Option<String>,
}
impl Host {
pub fn validate(&self) -> bool {
if self.public_ips.is_empty() {
return false;
}
true
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Http {
/// Socket address this node will use for binding its http API.
/// default: `0.0.0.0:8000`
pub bind_address: SocketAddr,
/// Path to assets directory of custom landing page of this node.
#[serde(deserialize_with = "de_maybe_stringified")]
pub landing_page_assets_path: Option<PathBuf>,
}
impl Default for Http {
fn default() -> Self {
Http {
bind_address: SocketAddr::new(
IpAddr::V4(Ipv4Addr::UNSPECIFIED),
DEFAULT_NYM_NODE_HTTP_PORT,
),
landing_page_assets_path: None,
}
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Wireguard {
/// Specifies whether the wireguard service is enabled on this node.
pub enabled: bool,
/// Socket address this node will use for binding its wireguard interface.
/// default: `0.0.0.0:51822`
pub bind_address: SocketAddr,
/// Port announced to external clients wishing to connect to the wireguard interface.
/// Useful in the instances where the node is behind a proxy.
pub announced_port: u16,
/// The prefix denoting the maximum number of the clients that can be connected via Wireguard.
/// The maximum value for IPv4 is 32 and for IPv6 is 128
pub private_network_prefix: u8,
/// Paths for wireguard keys, client registries, etc.
pub storage_paths: WireguardPaths,
}
impl Default for Wireguard {
fn default() -> Self {
Wireguard {
enabled: false,
bind_address: SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), WG_PORT),
announced_port: WG_PORT,
private_network_prefix: 16,
storage_paths: WireguardPaths {},
}
}
}
// we only really care about the mnemonic being zeroized
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Zeroize, ZeroizeOnDrop)]
pub struct Gateway {
@@ -509,23 +413,14 @@ pub struct Debug {
/// Delay between each subsequent presence data being sent.
#[serde(with = "humantime_serde")]
// DEAD FIELD
pub presence_sending_delay: Duration,
/// Length of filenames for new client messages.
// DEAD FIELD
pub stored_messages_filename_length: u16,
/// Number of messages from offline client that can be pulled at once from the storage.
pub message_retrieval_limit: i64,
/// Defines maximum delay between client bandwidth information being flushed to the persistent storage.
#[serde(with = "humantime_serde")]
pub client_bandwidth_max_flushing_rate: Duration,
/// Defines a maximum change in client bandwidth before it gets flushed to the persistent storage.
pub client_bandwidth_max_delta_flushing_amount: i64,
/// Specifies whether the mixnode should be using the legacy framing for the sphinx packets.
// it's set to true by default. The reason for that decision is to preserve compatibility with the
// existing nodes whilst everyone else is upgrading and getting the code for handling the new field.
@@ -543,9 +438,6 @@ impl Default for Debug {
maximum_connection_buffer_size: DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE,
stored_messages_filename_length: DEFAULT_STORED_MESSAGE_FILENAME_LENGTH,
message_retrieval_limit: DEFAULT_MESSAGE_RETRIEVAL_LIMIT,
client_bandwidth_max_flushing_rate: DEFAULT_CLIENT_BANDWIDTH_MAX_FLUSHING_RATE,
client_bandwidth_max_delta_flushing_amount:
DEFAULT_CLIENT_BANDWIDTH_MAX_DELTA_FLUSHING_AMOUNT,
use_legacy_framed_packet_version: false,
}
}
+1 -2
View File
@@ -1,7 +1,6 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::config::Host;
use nym_config::{
must_get_home, read_config_from_toml_file, DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, NYM_DIR,
};
@@ -114,7 +113,7 @@ impl From<ConfigV1_1_29> for ConfigV1_1_31 {
save_path: value.save_path,
// \/ ADDED
host: Host {
host: nym_node::config::Host {
// this is a very bad default!
public_ips: vec![value.gateway.listening_address],
hostname: None,
+11 -10
View File
@@ -1,12 +1,11 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::config::persistence::paths::{GatewayPaths, WireguardPaths};
use crate::config::persistence::paths::GatewayPaths;
use nym_bin_common::logging::LoggingSettings;
use nym_config::{
must_get_home, read_config_from_toml_file, DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, NYM_DIR,
};
use nym_network_defaults::WG_PORT;
use serde::{Deserialize, Deserializer, Serialize};
use std::io;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
@@ -15,7 +14,7 @@ use std::time::Duration;
use url::Url;
use super::persistence::paths::KeysPaths;
use super::{Config, Debug, Gateway, Host, Http, NetworkRequester, Wireguard};
use super::{Config, Debug, Gateway, NetworkRequester};
const DEFAULT_GATEWAYS_DIR: &str = "gateways";
@@ -77,10 +76,10 @@ pub struct ConfigV1_1_31 {
#[serde(skip)]
pub(crate) save_path: Option<PathBuf>,
pub host: Host,
pub host: nym_node::config::Host,
#[serde(default)]
pub http: Http,
pub http: nym_node::config::Http,
pub gateway: GatewayV1_1_31,
@@ -125,12 +124,12 @@ impl From<ConfigV1_1_31> for Config {
nyxd_urls: value.gateway.nyxd_urls,
cosmos_mnemonic: value.gateway.cosmos_mnemonic,
},
wireguard: Wireguard {
wireguard: nym_node::config::Wireguard {
enabled: value.wireguard.enabled,
bind_address: value.wireguard.bind_address,
announced_port: value.wireguard.announced_port,
private_network_prefix: Default::default(),
storage_paths: WireguardPaths {
storage_paths: nym_node::config::persistence::WireguardPaths {
// no fields (yet)
},
},
@@ -165,7 +164,6 @@ impl From<ConfigV1_1_31> for Config {
stored_messages_filename_length: value.debug.stored_messages_filename_length,
message_retrieval_limit: value.debug.message_retrieval_limit,
use_legacy_framed_packet_version: value.debug.use_legacy_framed_packet_version,
..Default::default()
},
}
}
@@ -243,8 +241,11 @@ impl Default for WireguardV1_1_31 {
fn default() -> Self {
Self {
enabled: false,
bind_address: SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), WG_PORT),
announced_port: WG_PORT,
bind_address: SocketAddr::new(
IpAddr::V4(Ipv4Addr::UNSPECIFIED),
nym_node::config::DEFAULT_WIREGUARD_PORT,
),
announced_port: nym_node::config::DEFAULT_WIREGUARD_PORT,
storage_paths: WireguardPathsV1_1_31 {},
}
}
+16 -30
View File
@@ -2,8 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::config::{default_config_directory, default_data_directory};
use nym_config::serde_helpers::de_maybe_stringified;
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};
use std::path::{Path, PathBuf};
pub const DEFAULT_PRIVATE_IDENTITY_KEY_FILENAME: &str = "private_identity.pem";
@@ -29,6 +28,19 @@ pub fn default_ip_packet_router_data_dir<P: AsRef<Path>>(id: P) -> PathBuf {
default_data_directory(id).join(DEFAULT_IP_PACKET_ROUTER_DATA_DIR)
}
/// makes sure that an empty path is converted into a `None` as opposed to `Some("")`
fn de_maybe_path<'de, D>(deserializer: D) -> Result<Option<PathBuf>, D::Error>
where
D: Deserializer<'de>,
{
let path = PathBuf::deserialize(deserializer)?;
if path.as_os_str().is_empty() {
Ok(None)
} else {
Ok(Some(path))
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct GatewayPaths {
@@ -40,13 +52,13 @@ pub struct GatewayPaths {
pub clients_storage: PathBuf,
/// Path to the configuration of the embedded network requester.
#[serde(deserialize_with = "de_maybe_stringified")]
#[serde(deserialize_with = "de_maybe_path")]
pub network_requester_config: Option<PathBuf>,
// pub node_description: PathBuf,
// pub cosmos_bip39_mnemonic: PathBuf,
/// Path to the configuration of the embedded ip packet router.
#[serde(deserialize_with = "de_maybe_stringified")]
#[serde(deserialize_with = "de_maybe_path")]
pub ip_packet_router_config: Option<PathBuf>,
}
@@ -61,20 +73,6 @@ impl GatewayPaths {
}
}
pub fn new_empty() -> Self {
GatewayPaths {
keys: KeysPaths {
private_identity_key_file: Default::default(),
public_identity_key_file: Default::default(),
private_sphinx_key_file: Default::default(),
public_sphinx_key_file: Default::default(),
},
clients_storage: Default::default(),
network_requester_config: None,
ip_packet_router_config: None,
}
}
#[must_use]
pub fn with_network_requester_config<P: AsRef<Path>>(mut self, path: P) -> Self {
self.network_requester_config = Some(path.as_ref().into());
@@ -169,15 +167,3 @@ impl KeysPaths {
&self.public_sphinx_key_file
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct WireguardPaths {
// pub keys:
}
impl WireguardPaths {
pub fn new_empty() -> Self {
WireguardPaths {}
}
}
+4 -31
View File
@@ -5,17 +5,14 @@ use crate::node::storage::error::StorageError;
use nym_ip_packet_router::error::IpPacketRouterError;
use nym_network_requester::error::{ClientCoreError, NetworkRequesterError};
use nym_validator_client::nyxd::error::NyxdError;
use nym_validator_client::nyxd::{AccountId, Coin};
use nym_validator_client::nyxd::AccountId;
use nym_validator_client::ValidatorClientError;
use std::io;
use std::net::IpAddr;
use std::path::PathBuf;
use thiserror::Error;
pub use crate::node::client_handling::websocket::connection_handler::authenticated::RequestHandlingError;
#[derive(Debug, Error)]
pub enum GatewayError {
pub(crate) enum GatewayError {
#[error("failed to load {keys} keys from '{}' (private key) and '{}' (public key): {err}", .paths.private_key_path.display(), .paths.public_key_path.display())]
KeyPairLoadFailure {
keys: String,
@@ -92,9 +89,6 @@ pub enum GatewayError {
actual_prefix: String,
},
#[error("this node has insufficient balance to run as zk-nym entry node since it won't be capable of redeeming received credentials. it's account ({account}) has a balance of only {balance}")]
InsufficientNodeBalance { account: AccountId, balance: Coin },
#[error("storage failure: {source}")]
StorageError {
#[from]
@@ -137,36 +131,15 @@ pub enum GatewayError {
source: NyxdError,
},
// TODO: in the future this should work the other way, i.e. NymNode depending on Gateway errors
#[error(transparent)]
ClientRequestFailure {
#[from]
source: RequestHandlingError,
},
#[error("failed to catch an interrupt: {source}")]
ShutdownFailure {
#[source]
source: Box<dyn std::error::Error + Send + Sync>,
},
#[error("this node hasn't set any valid public addresses to announce. Please modify [host.public_ips] section of your config")]
NoPublicIps,
#[error("this node attempted to announce an invalid public address: {address}. Please modify [host.public_ips] section of your config. Alternatively, if you wanted to use it in the local setting, run the node with the '--local' flag.")]
InvalidPublicIp { address: IpAddr },
#[error(transparent)]
NymNodeHttpError(#[from] nym_node_http_api::NymNodeHttpError),
NymNodeError(#[from] nym_node::error::NymNodeError),
#[error("there was an issue with wireguard IP network: {source}")]
IpNetworkError {
#[from]
source: ipnetwork::IpNetworkError,
},
#[cfg(all(feature = "wireguard", target_os = "linux"))]
#[error("failed to remove wireguard interface: {0}")]
WireguardInterfaceError(#[from] defguard_wireguard_rs::error::WireguardInterfaceError),
}
impl From<ClientCoreError> for GatewayError {
-231
View File
@@ -1,231 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::config::Config;
use crate::node::helpers::load_keypair;
use crate::GatewayError;
use nym_config::OptionalSet;
use nym_crypto::asymmetric::{encryption, identity};
use nym_ip_packet_router::config::BaseClientConfig;
use nym_pemstore::traits::PemStorableKey;
use nym_pemstore::KeyPairPath;
use nym_sphinx::addressing::clients::Recipient;
use nym_types::gateway::{
GatewayIpPacketRouterDetails, GatewayNetworkRequesterDetails, GatewayNodeDetailsResponse,
};
use std::path::Path;
pub use crate::node::helpers::{load_ip_packet_router_config, load_network_requester_config};
fn display_maybe_path<P: AsRef<Path>>(path: Option<P>) -> String {
path.as_ref()
.map(|p| p.as_ref().display().to_string())
.unwrap_or_default()
}
fn display_path<P: AsRef<Path>>(path: P) -> String {
path.as_ref().display().to_string()
}
#[derive(Default)]
pub struct OverrideNetworkRequesterConfig {
pub fastmode: bool,
pub no_cover: bool,
pub medium_toggle: bool,
pub open_proxy: Option<bool>,
pub enable_statistics: Option<bool>,
pub statistics_recipient: Option<String>,
}
#[derive(Default)]
pub struct OverrideIpPacketRouterConfig {
// TODO
}
// NOTE: make sure this is in sync with service-providers/network-requester/src/cli/mod.rs::override_config
pub fn override_network_requester_config(
mut cfg: nym_network_requester::Config,
opts: Option<OverrideNetworkRequesterConfig>,
) -> nym_network_requester::Config {
let Some(opts) = opts else { return cfg };
// as of 12.09.23 the below is true (not sure how this comment will rot in the future)
// medium_toggle:
// - sets secondary packet size to 16kb
// - disables poisson distribution of the main traffic stream
// - sets the cover traffic stream to 1 packet / 5s (on average)
// - disables per hop delay
//
// fastmode (to be renamed to `fast-poisson`):
// - sets average per hop delay to 10ms
// - sets the cover traffic stream to 1 packet / 2000s (on average); for all intents and purposes it disables the stream
// - sets the poisson distribution of the main traffic stream to 4ms, i.e. 250 packets / s on average
//
// no_cover:
// - disables poisson distribution of the main traffic stream
// - disables the secondary cover traffic stream
// disable poisson rate in the BASE client if the NR option is enabled
if cfg.network_requester.disable_poisson_rate {
cfg.set_no_poisson_process();
}
// those should be enforced by `clap` when parsing the arguments
if opts.medium_toggle {
assert!(!opts.fastmode);
assert!(!opts.no_cover);
cfg.set_medium_toggle();
}
cfg.with_base(
BaseClientConfig::with_high_default_traffic_volume,
opts.fastmode,
)
.with_base(BaseClientConfig::with_disabled_cover_traffic, opts.no_cover)
.with_optional(
nym_network_requester::Config::with_open_proxy,
opts.open_proxy,
)
.with_optional(
nym_network_requester::Config::with_enabled_statistics,
opts.enable_statistics,
)
.with_optional(
nym_network_requester::Config::with_statistics_recipient,
opts.statistics_recipient,
)
}
// NOTE: make sure this is in sync with service-providers/ip-packet-router/src/cli/mod.rs::override_config
pub fn override_ip_packet_router_config(
mut cfg: nym_ip_packet_router::Config,
opts: Option<OverrideIpPacketRouterConfig>,
) -> nym_ip_packet_router::Config {
let Some(_opts) = opts else { return cfg };
// disable poisson rate in the BASE client if the IPR option is enabled
if cfg.ip_packet_router.disable_poisson_rate {
log::info!("Disabling poisson rate for ip packet router");
cfg.set_no_poisson_process();
}
cfg
}
pub fn load_public_key<T, P, S>(path: P, name: S) -> Result<T, GatewayError>
where
T: PemStorableKey,
P: AsRef<Path>,
S: Into<String>,
{
nym_pemstore::load_key(path.as_ref()).map_err(|err| GatewayError::PublicKeyLoadFailure {
key: name.into(),
path: path.as_ref().to_path_buf(),
err,
})
}
/// Loads identity keys stored on disk
pub fn load_identity_keys(config: &Config) -> Result<identity::KeyPair, GatewayError> {
let identity_paths = KeyPairPath::new(
config.storage_paths.keys.private_identity_key(),
config.storage_paths.keys.public_identity_key(),
);
load_keypair(identity_paths, "gateway identity")
}
pub async fn node_details(config: &Config) -> Result<GatewayNodeDetailsResponse, GatewayError> {
let gateway_identity_public_key: identity::PublicKey = load_public_key(
&config.storage_paths.keys.public_identity_key_file,
"gateway identity",
)?;
let gateway_sphinx_public_key: encryption::PublicKey = load_public_key(
&config.storage_paths.keys.public_sphinx_key_file,
"gateway sphinx",
)?;
let network_requester =
if let Some(nr_cfg_path) = &config.storage_paths.network_requester_config {
let cfg = load_network_requester_config(&config.gateway.id, nr_cfg_path).await?;
let nr_identity_public_key: identity::PublicKey = load_public_key(
&cfg.storage_paths.common_paths.keys.public_identity_key_file,
"network requester identity",
)?;
let nr_encryption_key: encryption::PublicKey = load_public_key(
&cfg.storage_paths
.common_paths
.keys
.public_encryption_key_file,
"network requester encryption",
)?;
let address = Recipient::new(
nr_identity_public_key,
nr_encryption_key,
gateway_identity_public_key,
);
Some(GatewayNetworkRequesterDetails {
enabled: config.network_requester.enabled,
identity_key: nr_identity_public_key.to_base58_string(),
encryption_key: nr_encryption_key.to_base58_string(),
open_proxy: cfg.network_requester.open_proxy,
enabled_statistics: cfg.network_requester.enabled_statistics,
address: address.to_string(),
config_path: display_path(nr_cfg_path),
})
} else {
None
};
let ip_packet_router = if let Some(nr_cfg_path) = &config.storage_paths.ip_packet_router_config
{
let cfg = load_ip_packet_router_config(&config.gateway.id, nr_cfg_path).await?;
let nr_identity_public_key: identity::PublicKey = load_public_key(
&cfg.storage_paths.common_paths.keys.public_identity_key_file,
"ip packet router identity",
)?;
let nr_encryption_key: encryption::PublicKey = load_public_key(
&cfg.storage_paths
.common_paths
.keys
.public_encryption_key_file,
"ip packet router encryption",
)?;
let address = Recipient::new(
nr_identity_public_key,
nr_encryption_key,
gateway_identity_public_key,
);
Some(GatewayIpPacketRouterDetails {
enabled: config.ip_packet_router.enabled,
identity_key: nr_identity_public_key.to_base58_string(),
encryption_key: nr_encryption_key.to_base58_string(),
address: address.to_string(),
config_path: display_path(nr_cfg_path),
})
} else {
None
};
Ok(GatewayNodeDetailsResponse {
identity_key: gateway_identity_public_key.to_base58_string(),
sphinx_key: gateway_sphinx_public_key.to_base58_string(),
bind_address: config.gateway.listening_address.to_string(),
mix_port: config.gateway.mix_port,
clients_port: config.gateway.clients_port,
config_path: display_maybe_path(config.save_path.as_ref()),
data_store: display_path(&config.storage_paths.clients_storage),
network_requester,
ip_packet_router,
})
}
+14 -18
View File
@@ -1,22 +1,22 @@
// Copyright 2023-2024 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::config::Config;
use crate::error::GatewayError;
use crate::helpers::load_public_key;
use crate::node::helpers::load_public_key;
use ipnetwork::IpNetwork;
use log::{debug, warn};
use log::warn;
use nym_bin_common::bin_info_owned;
use nym_crypto::asymmetric::{encryption, identity};
use nym_network_requester::RequestFilter;
use nym_node_http_api::api::api_requests;
use nym_node_http_api::api::api_requests::v1::network_requester::exit_policy::models::UsedExitPolicy;
use nym_node_http_api::api::api_requests::SignedHostInformation;
use nym_node_http_api::router::WireguardAppState;
use nym_node_http_api::NymNodeHttpError;
use nym_node::error::NymNodeError;
use nym_node::http::api::api_requests;
use nym_node::http::api::api_requests::v1::network_requester::exit_policy::models::UsedExitPolicy;
use nym_node::http::api::api_requests::SignedHostInformation;
use nym_node::http::router::WireguardAppState;
use nym_node::wireguard::types::GatewayClientRegistry;
use nym_sphinx::addressing::clients::Recipient;
use nym_task::TaskClient;
use nym_wireguard_types::registration::GatewayClientRegistry;
use std::net::{IpAddr, Ipv4Addr};
use std::sync::Arc;
@@ -33,7 +33,6 @@ fn load_gateway_details(
};
Ok(api_requests::v1::gateway::models::Gateway {
enforces_zk_nyms: config.gateway.only_coconut_credentials,
client_interfaces: api_requests::v1::gateway::models::ClientInterfaces {
wireguard,
mixnet_websockets: Some(api_requests::v1::gateway::models::WebSockets {
@@ -53,14 +52,13 @@ fn load_host_details(
ip_address: config.host.public_ips.clone(),
hostname: config.host.hostname.clone(),
keys: api_requests::v1::node::models::HostKeys {
ed25519_identity: identity_keypair.public_key().to_base58_string(),
x25519_sphinx: sphinx_key.to_base58_string(),
x25519_noise: "".to_string(),
ed25519: identity_keypair.public_key().to_base58_string(),
x25519: sphinx_key.to_base58_string(),
},
};
let signed_info = SignedHostInformation::new(host_info, identity_keypair.private_key())
.map_err(NymNodeHttpError::from)?;
.map_err(NymNodeError::from)?;
Ok(signed_info)
}
@@ -244,15 +242,13 @@ impl<'a> HttpApiBuilder<'a> {
}
pub(crate) fn start(self, task_client: TaskClient) -> Result<(), GatewayError> {
debug!("starting http API");
// is it suboptimal to load all the keys, etc for the second time after they've already been
// retrieved during startup of the rest of the components?
// yes, a bit.
// but in the grand scheme of things performance penalty is negligible since it's only happening on startup
// and makes the code a bit nicer to manage. on top of it, all of it will refactored anyway at some point
// (famous last words, eh? - 22.09.23)
let mut config = nym_node_http_api::Config::new(
let mut config = nym_node::http::Config::new(
bin_info_owned!(),
load_host_details(
self.gateway_config,
@@ -295,7 +291,7 @@ impl<'a> HttpApiBuilder<'a> {
.ok()
});
let router = nym_node_http_api::NymNodeRouter::new(config, None, wg_state);
let router = nym_node::http::NymNodeRouter::new(config, wg_state);
let server = router
.build_server(&self.gateway_config.http.bind_address)?
-14
View File
@@ -1,14 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
#![warn(clippy::expect_used)]
#![warn(clippy::unwrap_used)]
pub mod config;
pub mod error;
pub mod helpers;
pub(crate) mod http;
pub mod node;
pub use error::GatewayError;
pub use node::{create_gateway, Gateway};
+7 -3
View File
@@ -9,11 +9,16 @@ use colored::Colorize;
use log::error;
use nym_bin_common::bin_info;
use nym_bin_common::logging::{maybe_print_banner, setup_logging};
use nym_bin_common::output_format::OutputFormat;
use nym_network_defaults::setup_env;
use std::io::IsTerminal;
use std::sync::OnceLock;
mod commands;
mod config;
pub(crate) mod error;
mod http;
mod node;
pub(crate) mod support;
fn pretty_build_info_static() -> &'static str {
static PRETTY_BUILD_INFORMATION: OnceLock<String> = OnceLock::new();
@@ -47,12 +52,11 @@ async fn main() -> anyhow::Result<()> {
}
commands::execute(args).await.map_err(|err| {
if std::io::stdout().is_terminal() {
if atty::is(atty::Stream::Stdout) {
let error_message = format!("{err}").red();
error!("{error_message}");
error!("Exiting...");
}
err
})
}
+17 -23
View File
@@ -42,35 +42,18 @@ pub struct Bandwidth {
}
impl Bandwidth {
pub const fn new_unchecked(value: u64) -> Bandwidth {
pub const fn new(value: u64) -> Bandwidth {
Bandwidth { value }
}
pub fn new(bandwidth_value: u64) -> Result<Bandwidth, BandwidthError> {
if bandwidth_value > i64::MAX as u64 {
// note that this would have represented more than 1 exabyte,
// which is like 125,000 worth of hard drives, so I don't think we have
// to worry about it for now...
warn!("Somehow we received bandwidth value higher than 9223372036854775807. We don't really want to deal with this now");
return Err(BandwidthError::UnsupportedBandwidthValue(bandwidth_value));
}
Ok(Bandwidth {
value: bandwidth_value,
})
}
pub(crate) fn parse_raw_bandwidth(
value: &str,
typ: CredentialType,
) -> Result<(u64, Option<OffsetDateTime>), BandwidthError> {
let (bandwidth_value, freepass_expiration) =
pub fn try_from_raw_value(value: &str, typ: CredentialType) -> Result<Self, BandwidthError> {
let bandwidth_value =
match typ {
CredentialType::Voucher => {
let token_value: u64 = value
.parse()
.map_err(|source| BandwidthError::VoucherValueParsingFailure { source })?;
(token_value * nym_network_defaults::BYTES_PER_UTOKEN, None)
token_value * nym_network_defaults::BYTES_PER_UTOKEN
}
CredentialType::FreePass => {
let expiry_timestamp: i64 = value
@@ -87,10 +70,21 @@ impl Bandwidth {
if expiry_date < now {
return Err(BandwidthError::ExpiredFreePass { expiry_date });
}
(nym_network_defaults::BYTES_PER_FREEPASS, Some(expiry_date))
nym_network_defaults::BYTES_PER_FREEPASS
}
};
Ok((bandwidth_value, freepass_expiration))
if bandwidth_value > i64::MAX as u64 {
// note that this would have represented more than 1 exabyte,
// which is like 125,000 worth of hard drives, so I don't think we have
// to worry about it for now...
warn!("Somehow we received bandwidth value higher than 9223372036854775807. We don't really want to deal with this now");
return Err(BandwidthError::UnsupportedBandwidthValue(bandwidth_value));
}
Ok(Bandwidth {
value: bandwidth_value,
})
}
pub fn value(&self) -> u64 {
+1 -2
View File
@@ -8,5 +8,4 @@ mod bandwidth;
pub(crate) mod embedded_clients;
pub(crate) mod websocket;
pub(crate) const FREE_TESTNET_BANDWIDTH_VALUE: Bandwidth =
Bandwidth::new_unchecked(64 * 1024 * 1024 * 1024); // 64GB
pub(crate) const FREE_TESTNET_BANDWIDTH_VALUE: Bandwidth = Bandwidth::new(64 * 1024 * 1024 * 1024); // 64GB
@@ -1,16 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::node::client_handling::websocket::connection_handler::coconut::CoconutVerifier;
use crate::node::client_handling::websocket::connection_handler::BandwidthFlushingBehaviourConfig;
use nym_crypto::asymmetric::identity;
use std::sync::Arc;
// I can see this being possible expanded with say storage or client store
#[derive(Clone)]
pub(crate) struct CommonHandlerState {
pub(crate) coconut_verifier: Arc<CoconutVerifier>,
pub(crate) local_identity: Arc<identity::KeyPair>,
pub(crate) only_coconut_credentials: bool,
pub(crate) bandwidth_cfg: BandwidthFlushingBehaviourConfig,
}
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::node::client_handling::bandwidth::BandwidthError;
use crate::node::client_handling::websocket::connection_handler::ClientBandwidth;
use crate::node::{
client_handling::{
bandwidth::Bandwidth,
@@ -35,20 +34,14 @@ use nym_validator_client::coconut::CoconutApiError;
use rand::{CryptoRng, Rng};
use std::{process, time::Duration};
use thiserror::Error;
use time::OffsetDateTime;
use tokio::io::{AsyncRead, AsyncWrite};
use tokio_tungstenite::tungstenite::{protocol::Message, Error as WsError};
#[derive(Debug, Error)]
pub enum RequestHandlingError {
pub(crate) enum RequestHandlingError {
#[error("Internal gateway storage error")]
StorageError(#[from] StorageError),
#[error(
"the database entry for bandwidth of the registered client {client_address} is missing!"
)]
MissingClientBandwidthEntry { client_address: String },
#[error("Provided bandwidth IV is malformed - {0}")]
MalformedIV(#[from] IVConversionError),
@@ -58,8 +51,8 @@ pub enum RequestHandlingError {
#[error("Provided binary request was malformed - {0}")]
InvalidTextRequest(<ClientControlRequest as TryFrom<String>>::Error),
#[error("The received request is not valid in the current context: {additional_context}")]
IllegalRequest { additional_context: String },
#[error("The received request is not valid in the current context")]
IllegalRequest,
#[error("Provided bandwidth credential did not verify correctly on {0}")]
InvalidBandwidthCredential(String),
@@ -97,18 +90,9 @@ pub enum RequestHandlingError {
#[error("the provided credential did not contain a valid type attribute")]
InvalidTypeAttribute,
#[error("insufficient bandwidth available to process the request. required: {required}B, available: {available}B")]
OutOfBandwidth { required: i64, available: i64 },
#[error("the provided credential did not have a bandwidth attribute")]
MissingBandwidthAttribute,
#[error("attempted to claim a bandwidth voucher for an account using a free pass (it expires on {expiration})")]
BandwidthVoucherForFreePassAccount { expiration: OffsetDateTime },
#[error("attempted to claim another free pass for the account while another free pass is still active (it expires on {expiration})")]
PreexistingFreePass { expiration: OffsetDateTime },
#[error("the DKG contract is unavailable")]
UnavailableDkgContract,
}
@@ -137,7 +121,6 @@ impl IntoWSMessage for Result<ServerResponse, RequestHandlingError> {
pub(crate) struct AuthenticatedHandler<R, S, St> {
inner: FreshHandler<R, S, St>,
client: ClientDetails,
client_bandwidth: ClientBandwidth,
mix_receiver: MixMessageReceiver,
// Occasionally the handler is requested to ping the connected client for confirm that it's
// active, such as when a duplicate connection is detected. This hashmap stores the oneshot
@@ -170,32 +153,19 @@ where
/// * `fresh`: fresh, unauthenticated, connection handler.
/// * `client`: details (i.e. address and shared keys) of the registered client
/// * `mix_receiver`: channel used for receiving messages from the mixnet destined for this client.
pub(crate) async fn upgrade(
pub(crate) fn upgrade(
fresh: FreshHandler<R, S, St>,
client: ClientDetails,
mix_receiver: MixMessageReceiver,
is_active_request_receiver: IsActiveRequestReceiver,
) -> Result<Self, RequestHandlingError> {
// note: the `upgrade` function can only be called after registering or authenticating the client,
// meaning the appropriate database rows must have been created
// so in theory we could just unwrap the value here, but since we're returning a Result anyway,
// we might as well return a failure response instead
let bandwidth = fresh
.storage
.get_available_bandwidth(client.address)
.await?
.ok_or(RequestHandlingError::MissingClientBandwidthEntry {
client_address: client.address.as_base58_string(),
})?;
Ok(AuthenticatedHandler {
) -> Self {
AuthenticatedHandler {
inner: fresh,
client,
client_bandwidth: ClientBandwidth::new(bandwidth.into()),
mix_receiver,
is_active_request_receiver,
is_active_ping_pending_reply: None,
})
}
}
/// Explicitly removes handle from the global store.
@@ -205,10 +175,15 @@ where
.disconnect(self.client.address)
}
async fn expire_freepass(&mut self) -> Result<(), RequestHandlingError> {
self.client_bandwidth.bandwidth = Default::default();
self.client_bandwidth.update_flush_data();
Ok(self.inner.expire_freepass(self.client.address).await?)
/// Checks the amount of bandwidth available for the connected client.
async fn get_available_bandwidth(&self) -> Result<i64, RequestHandlingError> {
let bandwidth = self
.inner
.storage
.get_available_bandwidth(self.client.address)
.await?
.unwrap_or_default();
Ok(bandwidth)
}
/// Increases the amount of available bandwidth of the connected client by the specified value.
@@ -216,27 +191,11 @@ where
/// # Arguments
///
/// * `amount`: amount to increase the available bandwidth by.
async fn increase_bandwidth(
&mut self,
bandwidth: Bandwidth,
) -> Result<(), RequestHandlingError> {
self.client_bandwidth.bandwidth.bytes += bandwidth.value() as i64;
// any increases to bandwidth should get flushed immediately
// (we don't want to accidentally miss somebody claiming a gigabyte voucher)
self.flush_bandwidth().await
}
async fn set_freepass_expiration(
&mut self,
expiration: OffsetDateTime,
) -> Result<(), RequestHandlingError> {
self.client_bandwidth.bandwidth.freepass_expiration = Some(expiration);
async fn increase_bandwidth(&self, bandwidth: Bandwidth) -> Result<(), RequestHandlingError> {
self.inner
.storage
.set_freepass_expiration(self.client.address, expiration)
.increase_bandwidth(self.client.address, bandwidth.value() as i64)
.await?;
self.client_bandwidth.update_flush_data();
Ok(())
}
@@ -245,18 +204,11 @@ where
/// # Arguments
///
/// * `amount`: amount to decrease the available bandwidth by.
async fn consume_bandwidth(&mut self, amount: i64) -> Result<(), RequestHandlingError> {
self.client_bandwidth.bandwidth.bytes -= amount;
// since we're going to be operating on a fair use policy anyway, even if we crash and let extra few packets
// through, that's completely fine
if self
.client_bandwidth
.should_flush(self.inner.shared_state.bandwidth_cfg)
{
self.flush_bandwidth().await?;
}
async fn consume_bandwidth(&self, amount: i64) -> Result<(), RequestHandlingError> {
self.inner
.storage
.consume_bandwidth(self.client.address, amount)
.await?;
Ok(())
}
@@ -280,22 +232,6 @@ where
let serial_number = credential.data.blinded_serial_number();
trace!("processing credential {}", serial_number.to_bs58());
// if we already have had received a free pass (that's not expired, don't accept any additional bandwidth)
if self.client_bandwidth.bandwidth.freepass_expired() {
// the free pass we used before has expired -> reset our state and handle the request as normal
self.expire_freepass().await?;
} else if let Some(expiration) = self.client_bandwidth.bandwidth.freepass_expiration {
// the free pass is still valid -> return error
return match credential.data.typ {
CredentialType::Voucher => {
Err(RequestHandlingError::BandwidthVoucherForFreePassAccount { expiration })
}
CredentialType::FreePass => {
Err(RequestHandlingError::PreexistingFreePass { expiration })
}
};
}
let already_spent = self
.inner
.storage
@@ -311,6 +247,12 @@ where
credential.data.epoch_id
);
let aggregated_verification_key = self
.inner
.coconut_verifier
.verification_key(credential.data.epoch_id)
.await?;
if !credential.data.validate_type_attribute() {
trace!("mismatch in the type attribute");
return Err(RequestHandlingError::InvalidTypeAttribute);
@@ -322,52 +264,41 @@ where
};
// this will extract token amounts out of bandwidth vouchers and validate expiry of free passes
let (raw_bandwidth, freepass_expiration) =
Bandwidth::parse_raw_bandwidth(bandwidth_attribute, credential.data.typ)?;
let bandwidth = Bandwidth::new(raw_bandwidth)?;
let bandwidth = Bandwidth::try_from_raw_value(bandwidth_attribute, credential.data.typ)?;
trace!("embedded bandwidth: {bandwidth:?}");
// locally verify the credential
{
let aggregated_verification_key = self
.inner
.shared_state
.coconut_verifier
.verification_key(credential.data.epoch_id)
.await?;
let params = bandwidth_credential_params();
if !credential.data.verify(params, &aggregated_verification_key) {
trace!("the credential did not verify correctly");
return Err(RequestHandlingError::InvalidBandwidthCredential(
String::from("local credential verification has failed"),
));
}
let params = bandwidth_credential_params();
if !credential.data.verify(params, &aggregated_verification_key) {
trace!("the credential did not verify correctly");
return Err(RequestHandlingError::InvalidBandwidthCredential(
String::from("local credential verification has failed"),
));
}
match credential.data.typ {
let was_freepass = match credential.data.typ {
CredentialType::Voucher => {
trace!("the credential is a bandwidth voucher. attempting to release the funds");
let api_clients = self
.inner
.shared_state
.coconut_verifier
.api_clients(credential.data.epoch_id)
.await?;
self.inner
.shared_state
.coconut_verifier
.release_bandwidth_voucher_funds(&api_clients, credential)
.await?;
false
}
CredentialType::FreePass => {
// no need to do anything special here, we already extracted the bandwidth amount and checked expiry
info!("received a free pass credential");
true
}
}
};
// technically this is not atomic, i.e. checking for the spending and then marking as spent,
// but because we have the `UNIQUE` constraint on the database table
@@ -380,21 +311,12 @@ where
trace!("storing serial number information");
self.inner
.storage
.insert_spent_credential(
serial_number,
freepass_expiration.is_some(),
self.client.address,
)
.insert_spent_credential(serial_number, was_freepass, self.client.address)
.await?;
trace!("increasing client bandwidth");
self.increase_bandwidth(bandwidth).await?;
// set free pass expiration
if let Some(expiration) = freepass_expiration {
self.set_freepass_expiration(expiration).await?;
}
let available_total = self.client_bandwidth.bandwidth.bytes;
let available_total = self.get_available_bandwidth().await?;
Ok(ServerResponse::Bandwidth { available_total })
}
@@ -445,47 +367,17 @@ where
) -> Result<ServerResponse, RequestHandlingError> {
debug!("handling testnet bandwidth request");
if self.inner.shared_state.only_coconut_credentials {
if self.inner.only_coconut_credentials {
return Err(RequestHandlingError::OnlyCoconutCredentials);
}
self.increase_bandwidth(FREE_TESTNET_BANDWIDTH_VALUE)
.await?;
let available_total = self.client_bandwidth.bandwidth.bytes;
let available_total = self.get_available_bandwidth().await?;
Ok(ServerResponse::Bandwidth { available_total })
}
async fn flush_bandwidth(&mut self) -> Result<(), RequestHandlingError> {
trace!("flushing client bandwidth to the underlying storage");
self.inner
.storage
.set_bandwidth(self.client.address, self.client_bandwidth.bandwidth.bytes)
.await?;
self.client_bandwidth.update_flush_data();
Ok(())
}
async fn try_use_bandwidth(
&mut self,
required_bandwidth: i64,
) -> Result<i64, RequestHandlingError> {
if self.client_bandwidth.bandwidth.freepass_expired() {
self.expire_freepass().await?;
}
let available_bandwidth = self.client_bandwidth.bandwidth.bytes;
if available_bandwidth < required_bandwidth {
return Err(RequestHandlingError::OutOfBandwidth {
required: required_bandwidth,
available: available_bandwidth,
});
}
self.consume_bandwidth(required_bandwidth).await?;
Ok(self.client_bandwidth.bandwidth.bytes)
}
/// Tries to handle request to forward sphinx packet into the network. The request can only succeed
/// if the client has enough available bandwidth.
///
@@ -495,16 +387,24 @@ where
///
/// * `mix_packet`: packet received from the client that should get forwarded into the network.
async fn handle_forward_sphinx(
&mut self,
&self,
mix_packet: MixPacket,
) -> Result<ServerResponse, RequestHandlingError> {
let required_bandwidth = mix_packet.packet().len() as i64;
let consumed_bandwidth = mix_packet.packet().len() as i64;
let remaining_bandwidth = self.try_use_bandwidth(required_bandwidth).await?;
let available_bandwidth = self.get_available_bandwidth().await?;
if available_bandwidth < consumed_bandwidth {
return Ok(ServerResponse::new_error(
"Insufficient bandwidth available",
));
}
self.consume_bandwidth(consumed_bandwidth).await?;
self.forward_packet(mix_packet);
Ok(ServerResponse::Send {
remaining_bandwidth,
remaining_bandwidth: available_bandwidth - consumed_bandwidth,
})
}
@@ -513,7 +413,7 @@ where
/// # Arguments
///
/// * `bin_msg`: raw message to handle.
async fn handle_binary(&mut self, bin_msg: Vec<u8>) -> Message {
async fn handle_binary(&self, bin_msg: Vec<u8>) -> Message {
trace!("binary request");
// this function decrypts the request and checks the MAC
match BinaryRequest::try_from_encrypted_tagged_bytes(bin_msg, &self.client.shared_keys) {
@@ -555,13 +455,7 @@ where
.handle_claim_testnet_bandwidth()
.await
.into_ws_message(),
other => RequestHandlingError::IllegalRequest {
additional_context: format!(
"received illegal message of type {} in an authenticated client",
other.name()
),
}
.into_error_message(),
_ => RequestHandlingError::IllegalRequest.into_error_message(),
},
}
}
@@ -1,7 +1,6 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::error::RequestHandlingError;
use futures::{
channel::{mpsc, oneshot},
SinkExt, StreamExt,
@@ -21,19 +20,18 @@ use nym_gateway_requests::{
use nym_mixnet_client::forwarder::MixForwardingSender;
use nym_sphinx::DestinationAddressBytes;
use rand::{CryptoRng, Rng};
use std::time::Duration;
use std::{sync::Arc, time::Duration};
use thiserror::Error;
use tokio::io::{AsyncRead, AsyncWrite};
use tokio_tungstenite::tungstenite::{protocol::Message, Error as WsError};
use crate::node::client_handling::websocket::common_state::CommonHandlerState;
use crate::node::client_handling::websocket::connection_handler::AvailableBandwidth;
use crate::node::{
client_handling::{
active_clients::ActiveClientsStore,
websocket::{
connection_handler::{
AuthenticatedHandler, ClientDetails, InitialAuthResult, SocketStream,
coconut::CoconutVerifier, AuthenticatedHandler, ClientDetails, InitialAuthResult,
SocketStream,
},
message_receiver::{IsActive, IsActiveRequestSender},
},
@@ -79,55 +77,24 @@ pub(crate) enum InitialAuthenticationError {
#[error("Attempted to negotiate connection with client using incompatible protocol version. Ours is {current} and the client reports {client:?}")]
IncompatibleProtocol { client: Option<u8>, current: u8 },
#[error("failed to send authentication error response: {source}")]
ErrorResponseSendFailure {
#[source]
source: WsError,
},
#[error("failed to send authentication response: {source}")]
ResponseSendFailure {
#[source]
source: WsError,
},
#[error("possibly received a sphinx packet without prior authentication. Request is going to be ignored")]
BinaryRequestWithoutAuthentication,
#[error("received a connection close message")]
CloseMessage,
#[error("the connection has unexpectedly closed")]
ClosedConnection,
#[error("failed to obtain message from websocket stream: {source}")]
FailedToReadMessage {
#[source]
source: WsError,
},
#[error("could not establish client details")]
EmptyClientDetails,
#[error("failed to upgrade the client handler: {source}")]
HandlerUpgradeFailure { source: RequestHandlingError },
}
impl InitialAuthenticationError {
/// Converts this Error into an appropriate websocket Message.
fn to_error_message(&self) -> Message {
fn into_error_message(self) -> Message {
ServerResponse::new_error(self.to_string()).into()
}
}
pub(crate) struct FreshHandler<R, S, St> {
rng: R,
pub(crate) shared_state: CommonHandlerState,
local_identity: Arc<identity::KeyPair>,
pub(crate) only_coconut_credentials: bool,
pub(crate) active_clients_store: ActiveClientsStore,
pub(crate) outbound_mix_sender: MixForwardingSender,
pub(crate) socket_connection: SocketStream<S>,
pub(crate) storage: St,
pub(crate) coconut_verifier: Arc<CoconutVerifier>,
// currently unused (but populated)
pub(crate) negotiated_protocol: Option<u8>,
@@ -146,19 +113,23 @@ where
pub(crate) fn new(
rng: R,
conn: S,
only_coconut_credentials: bool,
outbound_mix_sender: MixForwardingSender,
local_identity: Arc<identity::KeyPair>,
storage: St,
active_clients_store: ActiveClientsStore,
shared_state: CommonHandlerState,
coconut_verifier: Arc<CoconutVerifier>,
) -> Self {
FreshHandler {
rng,
active_clients_store,
only_coconut_credentials,
outbound_mix_sender,
socket_connection: SocketStream::RawTcp(conn),
local_identity,
storage,
coconut_verifier,
negotiated_protocol: None,
shared_state,
}
}
@@ -200,7 +171,7 @@ where
gateway_handshake(
&mut self.rng,
ws_stream,
self.shared_state.local_identity.as_ref(),
self.local_identity.as_ref(),
init_msg,
)
.await
@@ -465,7 +436,7 @@ where
// Ask the other connection to ping if they are still active.
// Use a oneshot channel to return the result to us
let (ping_result_sender, ping_result_receiver) = oneshot::channel();
debug!("Asking other connection handler to ping the connected client to see if it is still active");
log::debug!("Asking other connection handler to ping the connected client to see if it is still active");
if let Err(err) = is_active_request_tx.send(ping_result_sender).await {
warn!("Failed to send ping request to other handler: {err}");
}
@@ -477,31 +448,31 @@ where
IsActive::NotActive => {
// The other handler reported that the client is not active, so we can
// disconnect the other client and continue with this connection.
debug!("Other handler reports it is not active");
log::debug!("Other handler reports it is not active");
self.active_clients_store.disconnect(address);
}
IsActive::Active => {
// The other handled reported a positive reply, so we have to assume it's
// still active and disconnect this connection.
info!("Other handler reports it is active");
log::info!("Other handler reports it is active");
return Err(InitialAuthenticationError::DuplicateConnection);
}
IsActive::BusyPinging => {
// The other handler is already busy pinging the client, so we have to
// assume it's still active and disconnect this connection.
debug!("Other handler reports it is already busy pinging the client");
log::debug!("Other handler reports it is already busy pinging the client");
return Err(InitialAuthenticationError::DuplicateConnection);
}
}
}
Ok(Err(_)) => {
// Other channel failed to reply (the channel sender probably dropped)
info!("Other connection failed to reply, disconnecting it in favour of this new connection");
log::info!("Other connection failed to reply, disconnecting it in favour of this new connection");
self.active_clients_store.disconnect(address);
}
Err(_) => {
// Timeout waiting for reply
warn!(
log::warn!(
"Other connection timed out, disconnecting it in favour of this new connection"
);
self.active_clients_store.disconnect(address);
@@ -538,7 +509,7 @@ where
// Check for duplicate clients
if let Some(client_tx) = self.active_clients_store.get_remote_client(address) {
warn!("Detected duplicate connection for client: {address}");
log::warn!("Detected duplicate connection for client: {address}");
self.handle_duplicate_client(address, client_tx.is_active_request_sender)
.await?;
}
@@ -547,17 +518,11 @@ where
.authenticate_client(address, encrypted_address, iv)
.await?;
let status = shared_keys.is_some();
let available_bandwidth: AvailableBandwidth =
self.storage.get_available_bandwidth(address).await?.into();
let bandwidth_remaining = if available_bandwidth.freepass_expired() {
self.expire_freepass(address).await?;
0
} else {
available_bandwidth.bytes
};
let bandwidth_remaining = self
.storage
.get_available_bandwidth(address)
.await?
.unwrap_or(0);
let client_details =
shared_keys.map(|shared_keys| ClientDetails::new(address, shared_keys));
@@ -571,13 +536,6 @@ where
))
}
pub(crate) async fn expire_freepass(
&self,
client: DestinationAddressBytes,
) -> Result<(), StorageError> {
self.storage.reset_freepass_bandwidth(client).await
}
/// Attempts to finalize registration of the client by storing the derived shared keys in the
/// persistent store as well as creating entry for its bandwidth allocation.
///
@@ -699,7 +657,7 @@ where
// TODO: somehow cleanup this method
pub(crate) async fn perform_initial_authentication(
mut self,
) -> Result<AuthenticatedHandler<R, S, St>, InitialAuthenticationError>
) -> Option<AuthenticatedHandler<R, S, St>>
where
S: AsyncRead + AsyncWrite + Unpin + Send,
{
@@ -708,51 +666,40 @@ where
while let Some(msg) = self.read_websocket_message().await {
let msg = match msg {
Ok(msg) => msg,
Err(source) => {
debug!("failed to obtain message from websocket stream! stopping connection handler: {source}");
return Err(InitialAuthenticationError::FailedToReadMessage { source });
Err(err) => {
debug!("failed to obtain message from websocket stream! stopping connection handler: {err}");
break;
}
};
if msg.is_close() {
return Err(InitialAuthenticationError::CloseMessage);
break;
}
// ONLY handle 'Authenticate' or 'Register' requests, ignore everything else
match msg {
// we have explicitly checked for close message
Message::Close(_) => unreachable!(),
Message::Close(_) => break,
Message::Text(text_msg) => {
let (mix_sender, mix_receiver) = mpsc::unbounded();
return match self.handle_initial_authentication_request(text_msg).await {
match self.handle_initial_authentication_request(text_msg).await {
Err(err) => {
debug!("authentication failure: {err}");
// try to send error to the client
if let Err(source) =
self.send_websocket_message(err.to_error_message()).await
if let Err(err) =
self.send_websocket_message(err.into_error_message()).await
{
debug!("Failed to send authentication error response: {source}");
return Err(InitialAuthenticationError::ErrorResponseSendFailure {
source,
});
debug!("Failed to send authentication error response - {err}");
return None;
}
// return the underlying error
Err(err)
}
Ok(auth_result) => {
// try to send auth response back to the client
if let Err(source) = self
if let Err(err) = self
.send_websocket_message(auth_result.server_response.into())
.await
{
debug!("Failed to send authentication response: {source}");
return Err(InitialAuthenticationError::ResponseSendFailure {
source,
});
debug!("Failed to send authentication response - {err}");
return None;
}
if let Some(client_details) = auth_result.client_details {
return if let Some(client_details) = auth_result.client_details {
// Channel for handlers to ask other handlers if they are still active.
let (is_active_request_sender, is_active_request_receiver) =
mpsc::unbounded();
@@ -761,29 +708,23 @@ where
mix_sender,
is_active_request_sender,
);
AuthenticatedHandler::upgrade(
Some(AuthenticatedHandler::upgrade(
self,
client_details,
mix_receiver,
is_active_request_receiver,
)
.await
.map_err(|source| {
InitialAuthenticationError::HandlerUpgradeFailure { source }
})
))
} else {
// honestly, it's been so long I don't remember under what conditions its possible (if at all)
// to have empty client details
Err(InitialAuthenticationError::EmptyClientDetails)
}
None
};
}
};
}
}
Message::Binary(_) => {
// perhaps logging level should be reduced here, let's leave it for now and see what happens
// if client is working correctly, this should have never happened
debug!("possibly received a sphinx packet without prior authentication. Request is going to be ignored");
if let Err(source) = self
if let Err(err) = self
.send_websocket_message(
ServerResponse::new_error(
"binary request without prior authentication",
@@ -792,18 +733,15 @@ where
)
.await
{
return Err(InitialAuthenticationError::ErrorResponseSendFailure {
source,
});
debug!("Failed to send error response during authentication: {err}",)
}
return Err(InitialAuthenticationError::BinaryRequestWithoutAuthentication);
return None;
}
_ => continue,
};
}
Err(InitialAuthenticationError::ClosedConnection)
None
}
pub(crate) async fn start_handling(self, shutdown: nym_task::TaskClient)
@@ -1,7 +1,6 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::config::Config;
use crate::node::storage::Storage;
use log::{trace, warn};
use nym_gateway_requests::registration::handshake::SharedKeys;
@@ -9,8 +8,6 @@ use nym_gateway_requests::ServerResponse;
use nym_sphinx::DestinationAddressBytes;
use nym_task::TaskClient;
use rand::{CryptoRng, Rng};
use std::time::Duration;
use time::OffsetDateTime;
use tokio::io::{AsyncRead, AsyncWrite};
use tokio_tungstenite::WebSocketStream;
use zeroize::{Zeroize, ZeroizeOnDrop};
@@ -18,7 +15,7 @@ use zeroize::{Zeroize, ZeroizeOnDrop};
pub(crate) use self::authenticated::AuthenticatedHandler;
pub(crate) use self::fresh::FreshHandler;
pub(crate) mod authenticated;
mod authenticated;
pub(crate) mod coconut;
mod fresh;
@@ -106,84 +103,12 @@ pub(crate) async fn handle_connection<R, S, St>(
trace!("received shutdown signal while performing initial authentication");
return;
}
Some(Err(err)) => {
warn!("authentication has failed: {err}");
Some(None) => {
warn!("authentication has failed");
return;
}
Some(Ok(auth_handle)) => auth_handle.listen_for_requests(shutdown).await,
Some(Some(auth_handle)) => auth_handle.listen_for_requests(shutdown).await,
}
trace!("The handler is done!");
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct BandwidthFlushingBehaviourConfig {
/// Defines maximum delay between client bandwidth information being flushed to the persistent storage.
pub client_bandwidth_max_flushing_rate: Duration,
/// Defines a maximum change in client bandwidth before it gets flushed to the persistent storage.
pub client_bandwidth_max_delta_flushing_amount: i64,
}
impl<'a> From<&'a Config> for BandwidthFlushingBehaviourConfig {
fn from(value: &'a Config) -> Self {
BandwidthFlushingBehaviourConfig {
client_bandwidth_max_flushing_rate: value.debug.client_bandwidth_max_flushing_rate,
client_bandwidth_max_delta_flushing_amount: value
.debug
.client_bandwidth_max_delta_flushing_amount,
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub(crate) struct AvailableBandwidth {
pub(crate) bytes: i64,
pub(crate) freepass_expiration: Option<OffsetDateTime>,
}
impl AvailableBandwidth {
pub(crate) fn freepass_expired(&self) -> bool {
if let Some(expiration) = self.freepass_expiration {
if expiration < OffsetDateTime::now_utc() {
return true;
}
}
false
}
}
pub(crate) struct ClientBandwidth {
pub(crate) bandwidth: AvailableBandwidth,
pub(crate) last_flushed: OffsetDateTime,
pub(crate) bytes_at_last_flush: i64,
}
impl ClientBandwidth {
pub(crate) fn new(bandwidth: AvailableBandwidth) -> ClientBandwidth {
ClientBandwidth {
bandwidth,
last_flushed: OffsetDateTime::now_utc(),
bytes_at_last_flush: bandwidth.bytes,
}
}
pub(crate) fn should_flush(&self, cfg: BandwidthFlushingBehaviourConfig) -> bool {
if (self.bytes_at_last_flush - self.bandwidth.bytes).abs()
>= cfg.client_bandwidth_max_delta_flushing_amount
{
return true;
}
if self.last_flushed + cfg.client_bandwidth_max_flushing_rate < OffsetDateTime::now_utc() {
return true;
}
false
}
pub(crate) fn update_flush_data(&mut self) {
self.last_flushed = OffsetDateTime::now_utc();
self.bytes_at_last_flush = self.bandwidth.bytes;
}
}
@@ -2,26 +2,37 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::node::client_handling::active_clients::ActiveClientsStore;
use crate::node::client_handling::websocket::common_state::CommonHandlerState;
use crate::node::client_handling::websocket::connection_handler::coconut::CoconutVerifier;
use crate::node::client_handling::websocket::connection_handler::FreshHandler;
use crate::node::storage::Storage;
use log::*;
use nym_crypto::asymmetric::identity;
use nym_mixnet_client::forwarder::MixForwardingSender;
use rand::rngs::OsRng;
use std::net::SocketAddr;
use std::process;
use std::sync::Arc;
use tokio::task::JoinHandle;
pub(crate) struct Listener {
address: SocketAddr,
shared_state: CommonHandlerState,
local_identity: Arc<identity::KeyPair>,
only_coconut_credentials: bool,
pub(crate) coconut_verifier: Arc<CoconutVerifier>,
}
impl Listener {
pub(crate) fn new(address: SocketAddr, shared_state: CommonHandlerState) -> Self {
pub(crate) fn new(
address: SocketAddr,
local_identity: Arc<identity::KeyPair>,
only_coconut_credentials: bool,
coconut_verifier: Arc<CoconutVerifier>,
) -> Self {
Listener {
address,
shared_state,
local_identity,
only_coconut_credentials,
coconut_verifier,
}
}
@@ -60,10 +71,12 @@ impl Listener {
let handle = FreshHandler::new(
OsRng,
socket,
self.only_coconut_credentials,
outbound_mix_sender.clone(),
Arc::clone(&self.local_identity),
storage.clone(),
active_clients_store.clone(),
self.shared_state.clone(),
Arc::clone(&self.coconut_verifier),
);
let shutdown = shutdown.clone().named(format!("ClientConnectionHandler_{remote_addr}"));
tokio::spawn(async move { handle.start_handling(shutdown).await });
@@ -3,9 +3,6 @@
pub(crate) use listener::Listener;
pub(crate) mod common_state;
pub(crate) mod connection_handler;
pub(crate) mod listener;
pub(crate) mod message_receiver;
pub(crate) use common_state::CommonHandlerState;
+139 -9
View File
@@ -3,15 +3,123 @@
use crate::config::Config;
use crate::error::GatewayError;
use crate::node::storage::PersistentStorage;
use nym_crypto::asymmetric::encryption;
use nym_pemstore::traits::PemStorableKeyPair;
use nym_crypto::asymmetric::{encryption, identity};
use nym_pemstore::traits::{PemStorableKey, PemStorableKeyPair};
use nym_pemstore::KeyPairPath;
use nym_sphinx::addressing::clients::Recipient;
use nym_types::gateway::{
GatewayIpPacketRouterDetails, GatewayNetworkRequesterDetails, GatewayNodeDetailsResponse,
};
use std::path::Path;
pub async fn load_network_requester_config<P: AsRef<Path>>(
fn display_maybe_path<P: AsRef<Path>>(path: Option<P>) -> String {
path.as_ref()
.map(|p| p.as_ref().display().to_string())
.unwrap_or_default()
}
fn display_path<P: AsRef<Path>>(path: P) -> String {
path.as_ref().display().to_string()
}
pub(crate) async fn node_details(
config: &Config,
) -> Result<GatewayNodeDetailsResponse, GatewayError> {
let gateway_identity_public_key: identity::PublicKey = load_public_key(
&config.storage_paths.keys.public_identity_key_file,
"gateway identity",
)?;
let gateway_sphinx_public_key: encryption::PublicKey = load_public_key(
&config.storage_paths.keys.public_sphinx_key_file,
"gateway sphinx",
)?;
let network_requester =
if let Some(nr_cfg_path) = &config.storage_paths.network_requester_config {
let cfg = load_network_requester_config(&config.gateway.id, nr_cfg_path).await?;
let nr_identity_public_key: identity::PublicKey = load_public_key(
&cfg.storage_paths.common_paths.keys.public_identity_key_file,
"network requester identity",
)?;
let nr_encryption_key: encryption::PublicKey = load_public_key(
&cfg.storage_paths
.common_paths
.keys
.public_encryption_key_file,
"network requester encryption",
)?;
let address = Recipient::new(
nr_identity_public_key,
nr_encryption_key,
gateway_identity_public_key,
);
Some(GatewayNetworkRequesterDetails {
enabled: config.network_requester.enabled,
identity_key: nr_identity_public_key.to_base58_string(),
encryption_key: nr_encryption_key.to_base58_string(),
open_proxy: cfg.network_requester.open_proxy,
enabled_statistics: cfg.network_requester.enabled_statistics,
address: address.to_string(),
config_path: display_path(nr_cfg_path),
})
} else {
None
};
let ip_packet_router = if let Some(nr_cfg_path) = &config.storage_paths.ip_packet_router_config
{
let cfg = load_ip_packet_router_config(&config.gateway.id, nr_cfg_path).await?;
let nr_identity_public_key: identity::PublicKey = load_public_key(
&cfg.storage_paths.common_paths.keys.public_identity_key_file,
"ip packet router identity",
)?;
let nr_encryption_key: encryption::PublicKey = load_public_key(
&cfg.storage_paths
.common_paths
.keys
.public_encryption_key_file,
"ip packet router encryption",
)?;
let address = Recipient::new(
nr_identity_public_key,
nr_encryption_key,
gateway_identity_public_key,
);
Some(GatewayIpPacketRouterDetails {
enabled: config.ip_packet_router.enabled,
identity_key: nr_identity_public_key.to_base58_string(),
encryption_key: nr_encryption_key.to_base58_string(),
address: address.to_string(),
config_path: display_path(nr_cfg_path),
})
} else {
None
};
Ok(GatewayNodeDetailsResponse {
identity_key: gateway_identity_public_key.to_base58_string(),
sphinx_key: gateway_sphinx_public_key.to_base58_string(),
bind_address: config.gateway.listening_address.to_string(),
mix_port: config.gateway.mix_port,
clients_port: config.gateway.clients_port,
config_path: display_maybe_path(config.save_path.as_ref()),
data_store: display_path(&config.storage_paths.clients_storage),
network_requester,
ip_packet_router,
})
}
pub(crate) async fn load_network_requester_config<P: AsRef<Path>>(
id: &str,
path: P,
) -> Result<nym_network_requester::Config, GatewayError> {
@@ -24,7 +132,7 @@ pub async fn load_network_requester_config<P: AsRef<Path>>(
read_network_requester_config(id, path)
}
pub async fn load_ip_packet_router_config<P: AsRef<Path>>(
pub(crate) async fn load_ip_packet_router_config<P: AsRef<Path>>(
id: &str,
path: P,
) -> Result<nym_ip_packet_router::Config, GatewayError> {
@@ -37,7 +145,7 @@ pub async fn load_ip_packet_router_config<P: AsRef<Path>>(
read_ip_packet_router_config(id, path)
}
pub fn read_network_requester_config<P: AsRef<Path>>(
fn read_network_requester_config<P: AsRef<Path>>(
id: &str,
path: P,
) -> Result<nym_network_requester::Config, GatewayError> {
@@ -51,7 +159,7 @@ pub fn read_network_requester_config<P: AsRef<Path>>(
})
}
pub fn read_ip_packet_router_config<P: AsRef<Path>>(
fn read_ip_packet_router_config<P: AsRef<Path>>(
id: &str,
path: P,
) -> Result<nym_ip_packet_router::Config, GatewayError> {
@@ -74,7 +182,7 @@ pub(crate) async fn initialise_main_storage(
Ok(PersistentStorage::init(path, retrieval_limit).await?)
}
pub fn load_keypair<T: PemStorableKeyPair>(
pub(crate) fn load_keypair<T: PemStorableKeyPair>(
paths: KeyPairPath,
name: impl Into<String>,
) -> Result<T, GatewayError> {
@@ -85,6 +193,28 @@ pub fn load_keypair<T: PemStorableKeyPair>(
})
}
pub(crate) fn load_public_key<T, P, S>(path: P, name: S) -> Result<T, GatewayError>
where
T: PemStorableKey,
P: AsRef<Path>,
S: Into<String>,
{
nym_pemstore::load_key(path.as_ref()).map_err(|err| GatewayError::PublicKeyLoadFailure {
key: name.into(),
path: path.as_ref().to_path_buf(),
err,
})
}
/// Loads identity keys stored on disk
pub(crate) fn load_identity_keys(config: &Config) -> Result<identity::KeyPair, GatewayError> {
let identity_paths = KeyPairPath::new(
config.storage_paths.keys.private_identity_key(),
config.storage_paths.keys.public_identity_key(),
);
load_keypair(identity_paths, "gateway identity")
}
/// Loads Sphinx keys stored on disk
pub(crate) fn load_sphinx_keys(config: &Config) -> Result<encryption::KeyPair, GatewayError> {
let sphinx_paths = KeyPairPath::new(

Some files were not shown because too many files have changed in this diff Show More