Compare commits

..

1 Commits

Author SHA1 Message Date
Rachyandco 2bc7448b08 correct link 2023-06-07 13:45:24 +02:00
272 changed files with 7142 additions and 13298 deletions
+6 -15
View File
@@ -49,7 +49,7 @@ jobs:
"build-tools;$SDK_BUILDTOOLS_VERSION" "build-tools;$SDK_BUILDTOOLS_VERSION"
- name: Install Rust toolchain - name: Install Rust toolchain
uses: dtolnay/rust-toolchain@1.70.0 uses: dtolnay/rust-toolchain@stable
- name: Install rust android targets - name: Install rust android targets
run: | run: |
@@ -60,7 +60,6 @@ jobs:
working-directory: sdk/lib/socks5-listener/ working-directory: sdk/lib/socks5-listener/
env: env:
RELEASE: true RELEASE: true
RUSTFLAGS: "-C link-args=-Wl,--hash-style=gnu"
# build for arm64 and x86_64 # build for arm64 and x86_64
run: ./build-android.sh aarch64 x86_64 run: ./build-android.sh aarch64 x86_64
@@ -68,27 +67,20 @@ jobs:
working-directory: nym-connect/native/android working-directory: nym-connect/native/android
env: env:
ANDROID_SDK_ROOT: ${{ env.ANDROID_HOME }} ANDROID_SDK_ROOT: ${{ env.ANDROID_HOME }}
SENTRY_AUTH_TOKEN: ${{ secrets.NYMS5_ANDROID_SENTRY_AUTH_TOKEN }}
# build for arm64 and x86_64 # build for arm64 and x86_64
run: | run: ./gradlew :app:assembleArch64Release
echo "auth.token=$SENTRY_AUTH_TOKEN" | tee -a sentry.properties
./gradlew :app:assembleArch64Debug
./gradlew :app:assembleArch64Release
- name: Prepare APKs - name: Prepare APKs
run: | run: |
mkdir apk mkdir apk
mv nym-connect/native/android/app/build/outputs/apk/arch64/debug/app-arch64-debug.apk \
apk/nyms5-arch64-debug.apk
mv nym-connect/native/android/app/build/outputs/apk/arch64/release/app-arch64-release-unsigned.apk \ mv nym-connect/native/android/app/build/outputs/apk/arch64/release/app-arch64-release-unsigned.apk \
apk/nyms5-arch64-release.apk apk/nyms5-arch64-release.apk
- name: Upload APKs - name: Upload APKs
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: nyms5-apk-arch64 name: nyms5-apk-arch64-release
path: | path: |
apk/nyms5-arch64-debug.apk
apk/nyms5-arch64-release.apk apk/nyms5-arch64-release.apk
gh-release: gh-release:
@@ -101,11 +93,10 @@ jobs:
- name: Download binary artifact - name: Download binary artifact
uses: actions/download-artifact@v3 uses: actions/download-artifact@v3
with: with:
name: nyms5-apk-arch64 name: nyms5-apk-arch64-release
path: apk path: apk
- name: Release - name: Release
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
with: with:
files: | files: apk/nyms5-arch64-release.apk
apk/nyms5-arch64-debug.apk
apk/nyms5-arch64-release.apk
+1 -2
View File
@@ -43,5 +43,4 @@ envs/qwerty.env
.parcel-cache .parcel-cache
**/.DS_Store **/.DS_Store
cpu-cycles/libcpucycles/build cpu-cycles/libcpucycles/build
foxyfox.env foxyfox.env
gateway/deploy.sh
-33
View File
@@ -4,39 +4,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased] ## [Unreleased]
## [v1.1.22] (2023-06-20)
- CLI tool for querying network-requesters ([#3539])
- Statically link OpenSSL ([#3510])
- NymConnect - add sentry.io reporting ([#3421])
- init command does not change version number in config.toml ([#3336])
- [Bug] Config version does not correspond to binary version ([#3434])
[#3539]: https://github.com/nymtech/nym/issues/3539
[#3510]: https://github.com/nymtech/nym/issues/3510
[#3421]: https://github.com/nymtech/nym/issues/3421
[#3336]: https://github.com/nymtech/nym/issues/3336
[#3434]: https://github.com/nymtech/nym/issues/3434
## [v1.1.21] (2023-06-13)
- mixFetch: Change socks5 `SendRequest` to include OrderedMessage index as a field rather than making it serialized inside the `data` field
([#3534])
- Explorer - add more data columns to the Service Provider section: ([#3474])
- network-requester: support report if they run an open proxy using `ControlRequest` API ([#3461])
- Refactor client configs (London discussion) ([#3444])
- Increase `DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE` to 2000 to improve reliability ([#3433])
- socks5: sender waits for lanes to clear even though the connection is closed ([#3366])
- version bump for variables ([#3545])
[#3534]: https://github.com/nymtech/nym/issues/3534
[#3474]: https://github.com/nymtech/nym/issues/3474
[#3461]: https://github.com/nymtech/nym/issues/3461
[#3444]: https://github.com/nymtech/nym/issues/3444
[#3433]: https://github.com/nymtech/nym/issues/3433
[#3366]: https://github.com/nymtech/nym/issues/3366
[#3545]: https://github.com/nymtech/nym/pull/3545
## [v1.1.20] (2023-06-06) ## [v1.1.20] (2023-06-06)
- Explorer - Fix SP supported apps list ([#3458]) - Explorer - Fix SP supported apps list ([#3458])
Generated
+302 -1471
View File
File diff suppressed because it is too large Load Diff
+1 -3
View File
@@ -87,7 +87,6 @@ members = [
"nym-api/nym-api-requests", "nym-api/nym-api-requests",
"nym-outfox", "nym-outfox",
"tools/nym-cli", "tools/nym-cli",
"tools/nym-nr-query",
"tools/ts-rs-cli" "tools/ts-rs-cli"
] ]
@@ -113,8 +112,8 @@ edition = "2021"
license = "Apache-2.0" license = "Apache-2.0"
[workspace.dependencies] [workspace.dependencies]
anyhow = "1.0.71"
async-trait = "0.1.64" async-trait = "0.1.64"
anyhow = "1.0.71"
bip39 = { version = "2.0.0", features = ["zeroize"] } bip39 = { version = "2.0.0", features = ["zeroize"] }
cfg-if = "1.0.0" cfg-if = "1.0.0"
cosmwasm-derive = "=1.0.0" cosmwasm-derive = "=1.0.0"
@@ -130,7 +129,6 @@ cw3-fixed-multisig = { version = "=0.13.4" }
cw4 = { version = "=0.13.4" } cw4 = { version = "=0.13.4" }
dotenvy = "0.15.6" dotenvy = "0.15.6"
generic-array = "0.14.7" generic-array = "0.14.7"
getrandom = "0.2.10"
lazy_static = "1.4.0" lazy_static = "1.4.0"
log = "0.4" log = "0.4"
once_cell = "1.7.2" once_cell = "1.7.2"
+1 -1
View File
@@ -21,7 +21,7 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
### Building ### Building
Platform build instructions are available on [our docs site](https://nymtech.net/docs/binaries/build-nym.html). Platform build instructions are available on [our docs site](https://nymtech.net/docs/binaries/building-nym.html).
Wallet build instructions are also available on [our docs site](https://nymtech.net/docs/wallet/desktop-wallet.html). Wallet build instructions are also available on [our docs site](https://nymtech.net/docs/wallet/desktop-wallet.html).
### Developing ### Developing
-2
View File
@@ -12,9 +12,7 @@ serde = { workspace = true, features = ["derive"] }
thiserror = "1.0" thiserror = "1.0"
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal", "macros"] } # async runtime tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal", "macros"] } # async runtime
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" } nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
nym-client-core = { path = "../../common/client-core" }
nym-config = { path = "../../common/config" } nym-config = { path = "../../common/config" }
nym-credentials = { path = "../../common/credentials" } nym-credentials = { path = "../../common/credentials" }
nym-credential-storage = { path = "../../common/credential-storage" } nym-credential-storage = { path = "../../common/credential-storage" }
+6 -8
View File
@@ -1,4 +1,4 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
mod commands; mod commands;
@@ -9,14 +9,13 @@ use commands::*;
use error::Result; use error::Result;
use log::*; use log::*;
use nym_bin_common::completions::fig_generate; use nym_bin_common::completions::fig_generate;
use nym_config::DEFAULT_DATA_DIR; use nym_config::{CRED_DB_FILE_NAME, DATA_DIR};
use nym_network_defaults::{setup_env, NymNetworkDetails}; use nym_network_defaults::{setup_env, NymNetworkDetails};
use std::process::exit; use std::process::exit;
use std::time::{Duration, SystemTime}; use std::time::{Duration, SystemTime};
use clap::{CommandFactory, Parser}; use clap::{CommandFactory, Parser};
use nym_bin_common::logging::setup_logging; use nym_bin_common::logging::setup_logging;
use nym_client_core::config::disk_persistence::CommonClientPaths;
use nym_validator_client::nyxd::traits::DkgQueryClient; use nym_validator_client::nyxd::traits::DkgQueryClient;
use nym_validator_client::nyxd::{Coin, CosmWasmClient}; use nym_validator_client::nyxd::{Coin, CosmWasmClient};
use nym_validator_client::Config; use nym_validator_client::Config;
@@ -72,11 +71,10 @@ async fn main() -> Result<()> {
match args.command { match args.command {
Command::Run(r) => { Command::Run(r) => {
// we assume the structure of <home-dir>/data let db_path = r
let data_dir = r.client_home_directory.join(DEFAULT_DATA_DIR); .client_home_directory
let paths = CommonClientPaths::new_default(data_dir); .join(DATA_DIR)
let db_path = paths.credentials_database; .join(CRED_DB_FILE_NAME);
let shared_storage = let shared_storage =
nym_credential_storage::initialise_persistent_storage(db_path).await; nym_credential_storage::initialise_persistent_storage(db_path).await;
let recovery_storage = recovery_storage::RecoveryStorage::new(r.recovery_dir)?; let recovery_storage = recovery_storage::RecoveryStorage::new(r.recovery_dir)?;
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-client" name = "nym-client"
version = "1.1.22" version = "1.1.20"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"] authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client" description = "Implementation of the Nym Client"
edition = "2021" edition = "2021"
+101 -92
View File
@@ -1,104 +1,99 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::client::config::persistence::ClientPaths; use crate::client::config::template::config_template;
use crate::client::config::template::CONFIG_TEMPLATE; use nym_client_core::config::ClientCoreConfigTrait;
use nym_bin_common::logging::LoggingSettings;
use nym_config::defaults::DEFAULT_WEBSOCKET_LISTENING_PORT; use nym_config::defaults::DEFAULT_WEBSOCKET_LISTENING_PORT;
use nym_config::{ use nym_config::{NymConfig, OptionalSet};
must_get_home, read_config_from_toml_file, save_formatted_config_to_file, NymConfigTemplate,
OptionalSet, DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, DEFAULT_DATA_DIR, NYM_DIR,
};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt::Debug; use std::fmt::Debug;
use std::io;
use std::net::{IpAddr, Ipv4Addr}; use std::net::{IpAddr, Ipv4Addr};
use std::path::{Path, PathBuf}; use std::path::PathBuf;
use std::str::FromStr; use std::str::FromStr;
pub use nym_client_core::config::Config as BaseClientConfig; pub use nym_client_core::config::Config as BaseConfig;
pub use nym_client_core::config::MISSING_VALUE;
pub use nym_client_core::config::{DebugConfig, GatewayEndpointConfig}; pub use nym_client_core::config::{DebugConfig, GatewayEndpointConfig};
pub mod old_config_v1_1_13; pub mod old_config_v1_1_13;
pub mod old_config_v1_1_20;
pub mod old_config_v1_1_20_2;
mod persistence;
mod template; mod template;
const DEFAULT_CLIENTS_DIR: &str = "clients"; #[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone, Copy)]
#[serde(deny_unknown_fields)]
/// Derive default path to clients's config directory. pub enum SocketType {
/// It should get resolved to `$HOME/.nym/mixnodes/<id>/config` WebSocket,
pub fn default_config_directory<P: AsRef<Path>>(id: P) -> PathBuf { None,
must_get_home()
.join(NYM_DIR)
.join(DEFAULT_CLIENTS_DIR)
.join(id)
.join(DEFAULT_CONFIG_DIR)
} }
/// Derive default path to client's config file. impl SocketType {
/// It should get resolved to `$HOME/.nym/clients/<id>/config/config.toml` pub fn from_string<S: Into<String>>(val: S) -> Self {
pub fn default_config_filepath<P: AsRef<Path>>(id: P) -> PathBuf { let mut upper = val.into();
default_config_directory(id).join(DEFAULT_CONFIG_FILENAME) upper.make_ascii_uppercase();
match upper.as_ref() {
"WEBSOCKET" | "WS" => SocketType::WebSocket,
_ => SocketType::None,
}
}
pub fn is_websocket(&self) -> bool {
matches!(self, SocketType::WebSocket)
}
} }
/// Derive default path to client's data directory where files, such as keys, are stored. #[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
/// It should get resolved to `$HOME/.nym/clients/<id>/data` #[serde(deny_unknown_fields)]
pub fn default_data_directory<P: AsRef<Path>>(id: P) -> PathBuf {
must_get_home()
.join(NYM_DIR)
.join(DEFAULT_CLIENTS_DIR)
.join(id)
.join(DEFAULT_DATA_DIR)
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
pub struct Config { pub struct Config {
#[serde(flatten)] #[serde(flatten)]
pub base: BaseClientConfig, base: BaseConfig<Config>,
pub socket: Socket, socket: Socket,
// pub paths: CommonClientPathfinder,
pub storage_paths: ClientPaths,
pub logging: LoggingSettings,
} }
impl NymConfigTemplate for Config { impl NymConfig for Config {
fn template() -> &'static str { fn template() -> &'static str {
CONFIG_TEMPLATE config_template()
}
fn default_root_directory() -> PathBuf {
dirs::home_dir()
.expect("Failed to evaluate $HOME value")
.join(".nym")
.join("clients")
}
fn try_default_root_directory() -> Option<PathBuf> {
dirs::home_dir().map(|path| path.join(".nym").join("clients"))
}
fn root_directory(&self) -> PathBuf {
self.base.get_nym_root_directory()
}
fn config_directory(&self) -> PathBuf {
self.root_directory()
.join(self.base.get_id())
.join("config")
}
fn data_directory(&self) -> PathBuf {
self.root_directory().join(self.base.get_id()).join("data")
}
}
impl ClientCoreConfigTrait for Config {
fn get_gateway_endpoint(&self) -> &nym_client_core::config::GatewayEndpointConfig {
self.base.get_gateway_endpoint()
} }
} }
impl Config { impl Config {
pub fn new<S: AsRef<str>>(id: S) -> Self { pub fn new<S: Into<String>>(id: S) -> Self {
Config { Config {
base: BaseClientConfig::new(id.as_ref(), env!("CARGO_PKG_VERSION")), base: BaseConfig::new(id),
storage_paths: ClientPaths::new_default(default_data_directory(id.as_ref())),
logging: Default::default(),
socket: Default::default(), socket: Default::default(),
} }
} }
pub fn read_from_toml_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
read_config_from_toml_file(path)
}
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
Self::read_from_toml_file(default_config_filepath(id))
}
pub fn default_location(&self) -> PathBuf {
default_config_filepath(&self.base.client.id)
}
pub fn save_to_default_location(&self) -> io::Result<()> {
let config_save_location: PathBuf = self.default_location();
save_formatted_config_to_file(self, config_save_location)
}
pub fn validate(&self) -> bool { pub fn validate(&self) -> bool {
// no other sections have explicit requirements (yet) // no other sections have explicit requirements (yet)
self.base.validate() self.base.validate()
@@ -128,10 +123,39 @@ impl Config {
self self
} }
// getters
pub fn get_config_file_save_location(&self) -> PathBuf {
self.config_directory().join(Self::config_file_name())
}
pub fn get_base(&self) -> &BaseConfig<Self> {
&self.base
}
pub fn get_base_mut(&mut self) -> &mut BaseConfig<Self> {
&mut self.base
}
pub fn get_debug_settings(&self) -> &DebugConfig {
self.get_base().get_debug_config()
}
pub fn get_socket_type(&self) -> SocketType {
self.socket.socket_type
}
pub fn get_listening_ip(&self) -> IpAddr {
self.socket.host
}
pub fn get_listening_port(&self) -> u16 {
self.socket.listening_port
}
// poor man's 'builder' method // poor man's 'builder' method
pub fn with_base<F, T>(mut self, f: F, val: T) -> Self pub fn with_base<F, T>(mut self, f: F, val: T) -> Self
where where
F: Fn(BaseClientConfig, T) -> BaseClientConfig, F: Fn(BaseConfig<Self>, T) -> BaseConfig<Self>,
{ {
self.base = f(self.base, val); self.base = f(self.base, val);
self self
@@ -141,7 +165,7 @@ impl Config {
// (plz, lets refactor it) // (plz, lets refactor it)
pub fn with_optional_ext<F, T>(mut self, f: F, val: Option<T>) -> Self pub fn with_optional_ext<F, T>(mut self, f: F, val: Option<T>) -> Self
where where
F: Fn(BaseClientConfig, T) -> BaseClientConfig, F: Fn(BaseConfig<Self>, T) -> BaseConfig<Self>,
{ {
self.base = self.base.with_optional(f, val); self.base = self.base.with_optional(f, val);
self self
@@ -149,7 +173,7 @@ impl Config {
pub fn with_optional_env_ext<F, T>(mut self, f: F, val: Option<T>, env_var: &str) -> Self pub fn with_optional_env_ext<F, T>(mut self, f: F, val: Option<T>, env_var: &str) -> Self
where where
F: Fn(BaseClientConfig, T) -> BaseClientConfig, F: Fn(BaseConfig<Self>, T) -> BaseConfig<Self>,
T: FromStr, T: FromStr,
<T as FromStr>::Err: Debug, <T as FromStr>::Err: Debug,
{ {
@@ -165,7 +189,7 @@ impl Config {
parser: G, parser: G,
) -> Self ) -> Self
where where
F: Fn(BaseClientConfig, T) -> BaseClientConfig, F: Fn(BaseConfig<Self>, T) -> BaseConfig<Self>,
G: Fn(&str) -> T, G: Fn(&str) -> T,
{ {
self.base = self.base.with_optional_custom_env(f, val, env_var, parser); self.base = self.base.with_optional_custom_env(f, val, env_var, parser);
@@ -173,34 +197,19 @@ impl Config {
} }
} }
// define_optional_set_inner!(Config, base, BaseClientConfig);
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone, Copy)]
#[serde(deny_unknown_fields)]
pub enum SocketType {
WebSocket,
None,
}
impl SocketType {
pub fn is_websocket(&self) -> bool {
matches!(self, SocketType::WebSocket)
}
}
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)] #[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(default, deny_unknown_fields)] #[serde(default, deny_unknown_fields)]
pub struct Socket { pub struct Socket {
pub socket_type: SocketType, socket_type: SocketType,
pub host: IpAddr, host: IpAddr,
pub listening_port: u16, listening_port: u16,
} }
impl Default for Socket { impl Default for Socket {
fn default() -> Self { fn default() -> Self {
Socket { Socket {
socket_type: SocketType::WebSocket, socket_type: SocketType::WebSocket,
host: IpAddr::V4(Ipv4Addr::LOCALHOST), host: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
listening_port: DEFAULT_WEBSOCKET_LISTENING_PORT, listening_port: DEFAULT_WEBSOCKET_LISTENING_PORT,
} }
} }
@@ -1,33 +1,58 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::client::config::old_config_v1_1_20::{ConfigV1_1_20, SocketV1_1_20}; use crate::client::config::{Config, Socket};
use nym_client_core::config::old_config_v1_1_13::OldConfigV1_1_13 as OldBaseConfigV1_1_13; use nym_client_core::config::old_config_v1_1_13::OldConfigV1_1_13 as OldBaseConfigV1_1_13;
use nym_config::legacy_helpers::nym_config::MigrationNymConfig; use nym_config::NymConfig;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::path::PathBuf; use std::path::PathBuf;
#[derive(Debug, Deserialize, PartialEq, Serialize)] #[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct OldConfigV1_1_13 { pub struct OldConfigV1_1_13 {
#[serde(flatten)] #[serde(flatten)]
pub base: OldBaseConfigV1_1_13<OldConfigV1_1_13>, base: OldBaseConfigV1_1_13<OldConfigV1_1_13>,
pub socket: SocketV1_1_20, socket: Socket,
} }
impl MigrationNymConfig for OldConfigV1_1_13 { impl NymConfig for OldConfigV1_1_13 {
fn template() -> &'static str {
// not intended to be used
unimplemented!()
}
fn default_root_directory() -> PathBuf { fn default_root_directory() -> PathBuf {
dirs::home_dir() dirs::home_dir()
.expect("Failed to evaluate $HOME value") .expect("Failed to evaluate $HOME value")
.join(".nym") .join(".nym")
.join("clients") .join("clients")
} }
fn try_default_root_directory() -> Option<PathBuf> {
dirs::home_dir().map(|path| path.join(".nym").join("clients"))
}
fn root_directory(&self) -> PathBuf {
self.base.client.nym_root_directory.clone()
}
fn config_directory(&self) -> PathBuf {
self.root_directory()
.join(&self.base.client.id)
.join("config")
}
fn data_directory(&self) -> PathBuf {
self.root_directory()
.join(&self.base.client.id)
.join("data")
}
} }
impl From<OldConfigV1_1_13> for ConfigV1_1_20 { impl From<OldConfigV1_1_13> for Config {
fn from(value: OldConfigV1_1_13) -> Self { fn from(value: OldConfigV1_1_13) -> Self {
ConfigV1_1_20 { Config {
base: value.base.into(), base: value.base.into(),
socket: value.socket, socket: value.socket,
} }
@@ -1,115 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::config::old_config_v1_1_20_2::{
ClientPathsV1_1_20_2, ConfigV1_1_20_2, SocketTypeV1_1_20_2, SocketV1_1_20_2,
};
use nym_bin_common::logging::LoggingSettings;
use nym_client_core::config::disk_persistence::keys_paths::ClientKeysPaths;
use nym_client_core::config::disk_persistence::old_v1_1_20_2::CommonClientPathsV1_1_20_2;
use nym_client_core::config::old_config_v1_1_20::ConfigV1_1_20 as BaseConfigV1_1_20;
use nym_client_core::config::old_config_v1_1_20_2::{
ClientV1_1_20_2, ConfigV1_1_20_2 as BaseConfigV1_1_20_2,
};
use nym_config::defaults::DEFAULT_WEBSOCKET_LISTENING_PORT;
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use std::net::{IpAddr, Ipv4Addr};
use std::path::PathBuf;
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone, Copy)]
#[serde(deny_unknown_fields)]
pub enum SocketTypeV1_1_20 {
WebSocket,
None,
}
impl From<SocketTypeV1_1_20> for SocketTypeV1_1_20_2 {
fn from(value: SocketTypeV1_1_20) -> Self {
match value {
SocketTypeV1_1_20::WebSocket => SocketTypeV1_1_20_2::WebSocket,
SocketTypeV1_1_20::None => SocketTypeV1_1_20_2::None,
}
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_20 {
#[serde(flatten)]
pub base: BaseConfigV1_1_20<ConfigV1_1_20>,
pub socket: SocketV1_1_20,
}
impl From<ConfigV1_1_20> for ConfigV1_1_20_2 {
fn from(value: ConfigV1_1_20) -> Self {
ConfigV1_1_20_2 {
base: BaseConfigV1_1_20_2 {
client: ClientV1_1_20_2 {
version: value.base.client.version,
id: value.base.client.id,
disabled_credentials_mode: value.base.client.disabled_credentials_mode,
nyxd_urls: value.base.client.nyxd_urls,
nym_api_urls: value.base.client.nym_api_urls,
gateway_endpoint: value.base.client.gateway_endpoint.into(),
},
debug: value.base.debug.into(),
},
socket: value.socket.into(),
storage_paths: ClientPathsV1_1_20_2 {
common_paths: CommonClientPathsV1_1_20_2 {
keys: ClientKeysPaths {
private_identity_key_file: value.base.client.private_identity_key_file,
public_identity_key_file: value.base.client.public_identity_key_file,
private_encryption_key_file: value.base.client.private_encryption_key_file,
public_encryption_key_file: value.base.client.public_encryption_key_file,
gateway_shared_key_file: value.base.client.gateway_shared_key_file,
ack_key_file: value.base.client.ack_key_file,
},
credentials_database: value.base.client.database_path,
reply_surb_database: value.base.client.reply_surb_database_path,
},
},
logging: LoggingSettings::default(),
}
}
}
impl MigrationNymConfig for ConfigV1_1_20 {
fn default_root_directory() -> PathBuf {
dirs::home_dir()
.expect("Failed to evaluate $HOME value")
.join(".nym")
.join("clients")
}
}
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct SocketV1_1_20 {
socket_type: SocketTypeV1_1_20,
host: IpAddr,
listening_port: u16,
}
impl From<SocketV1_1_20> for SocketV1_1_20_2 {
fn from(value: SocketV1_1_20) -> Self {
SocketV1_1_20_2 {
socket_type: value.socket_type.into(),
host: value.host,
listening_port: value.listening_port,
}
}
}
impl Default for SocketV1_1_20 {
fn default() -> Self {
SocketV1_1_20 {
socket_type: SocketTypeV1_1_20::WebSocket,
host: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
listening_port: DEFAULT_WEBSOCKET_LISTENING_PORT,
}
}
}
@@ -1,103 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::config::persistence::ClientPaths;
use crate::client::config::{default_config_filepath, Config, Socket, SocketType};
use nym_bin_common::logging::LoggingSettings;
use nym_client_core::config::disk_persistence::old_v1_1_20_2::CommonClientPathsV1_1_20_2;
use nym_client_core::config::old_config_v1_1_20_2::ConfigV1_1_20_2 as BaseConfigV1_1_20_2;
use nym_client_core::config::GatewayEndpointConfig;
use nym_config::read_config_from_toml_file;
use nym_network_defaults::DEFAULT_WEBSOCKET_LISTENING_PORT;
use serde::{Deserialize, Serialize};
use std::io;
use std::net::{IpAddr, Ipv4Addr};
use std::path::Path;
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone)]
pub struct ClientPathsV1_1_20_2 {
#[serde(flatten)]
pub common_paths: CommonClientPathsV1_1_20_2,
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
pub struct ConfigV1_1_20_2 {
#[serde(flatten)]
pub base: BaseConfigV1_1_20_2,
pub socket: SocketV1_1_20_2,
pub storage_paths: ClientPathsV1_1_20_2,
pub logging: LoggingSettings,
}
impl ConfigV1_1_20_2 {
pub fn read_from_toml_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
read_config_from_toml_file(path)
}
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
Self::read_from_toml_file(default_config_filepath(id))
}
// in this upgrade, gateway endpoint configuration was moved out of the config file,
// so its returned to be stored elsewhere.
pub fn upgrade(self) -> (Config, GatewayEndpointConfig) {
let gateway_details = self.base.client.gateway_endpoint.clone().into();
let config = Config {
base: self.base.into(),
socket: self.socket.into(),
storage_paths: ClientPaths {
common_paths: self.storage_paths.common_paths.upgrade_default(),
},
logging: self.logging,
};
(config, gateway_details)
}
}
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone, Copy)]
#[serde(deny_unknown_fields)]
pub enum SocketTypeV1_1_20_2 {
WebSocket,
None,
}
impl From<SocketTypeV1_1_20_2> for SocketType {
fn from(value: SocketTypeV1_1_20_2) -> Self {
match value {
SocketTypeV1_1_20_2::WebSocket => SocketType::WebSocket,
SocketTypeV1_1_20_2::None => SocketType::None,
}
}
}
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct SocketV1_1_20_2 {
pub socket_type: SocketTypeV1_1_20_2,
pub host: IpAddr,
pub listening_port: u16,
}
impl From<SocketV1_1_20_2> for Socket {
fn from(value: SocketV1_1_20_2) -> Self {
Socket {
socket_type: value.socket_type.into(),
host: value.host,
listening_port: value.listening_port,
}
}
}
impl Default for SocketV1_1_20_2 {
fn default() -> Self {
SocketV1_1_20_2 {
socket_type: SocketTypeV1_1_20_2::WebSocket,
host: IpAddr::V4(Ipv4Addr::LOCALHOST),
listening_port: DEFAULT_WEBSOCKET_LISTENING_PORT,
}
}
}
@@ -1,20 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_client_core::config::disk_persistence::CommonClientPaths;
use serde::{Deserialize, Serialize};
use std::path::Path;
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone)]
pub struct ClientPaths {
#[serde(flatten)]
pub common_paths: CommonClientPaths,
}
impl ClientPaths {
pub fn new_default<P: AsRef<Path>>(base_data_directory: P) -> Self {
ClientPaths {
common_paths: CommonClientPaths::new_default(base_data_directory),
}
}
}
+36 -22
View File
@@ -1,11 +1,12 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// While using normal toml marshalling would have been way simpler with less overhead, pub(crate) fn config_template() -> &'static str {
// I think it's useful to have comments attached to the saved config file to explain behaviour of // While using normal toml marshalling would have been way simpler with less overhead,
// particular fields. // I think it's useful to have comments attached to the saved config file to explain behaviour of
// Note: any changes to the template must be reflected in the appropriate structs. // particular fields.
pub(crate) const CONFIG_TEMPLATE: &str = r#" // Note: any changes to the template must be reflected in the appropriate structs.
r#"
# This is a TOML config file. # This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml # For more information, see https://github.com/toml-lang/toml
@@ -36,37 +37,50 @@ nym_api_urls = [
{{/each}} {{/each}}
] ]
[storage_paths]
# Path to file containing private identity key. # Path to file containing private identity key.
keys.private_identity_key_file = '{{ storage_paths.keys.private_identity_key_file }}' private_identity_key_file = '{{ client.private_identity_key_file }}'
# Path to file containing public identity key. # Path to file containing public identity key.
keys.public_identity_key_file = '{{ storage_paths.keys.public_identity_key_file }}' public_identity_key_file = '{{ client.public_identity_key_file }}'
# Path to file containing private encryption key. # Path to file containing private encryption key.
keys.private_encryption_key_file = '{{ storage_paths.keys.private_encryption_key_file }}' private_encryption_key_file = '{{ client.private_encryption_key_file }}'
# Path to file containing public encryption key. # Path to file containing public encryption key.
keys.public_encryption_key_file = '{{ storage_paths.keys.public_encryption_key_file }}' public_encryption_key_file = '{{ client.public_encryption_key_file }}'
# Path to the database containing bandwidth credentials
database_path = '{{ client.database_path }}'
# Path to the persistent store for received reply surbs, unused encryption keys and used sender tags.
reply_surb_database_path = '{{ client.reply_surb_database_path }}'
##### additional client config options #####
# A gateway specific, optional, base58 stringified shared key used for # A gateway specific, optional, base58 stringified shared key used for
# communication with particular gateway. # communication with particular gateway.
keys.gateway_shared_key_file = '{{ storage_paths.keys.gateway_shared_key_file }}' gateway_shared_key_file = '{{ client.gateway_shared_key_file }}'
# Path to file containing key used for encrypting and decrypting the content of an # Path to file containing key used for encrypting and decrypting the content of an
# acknowledgement so that nobody besides the client knows which packet it refers to. # acknowledgement so that nobody besides the client knows which packet it refers to.
keys.ack_key_file = '{{ storage_paths.keys.ack_key_file }}' ack_key_file = '{{ client.ack_key_file }}'
##### advanced configuration options #####
# Path to the database containing bandwidth credentials # Absolute path to the home Nym Clients directory.
credentials_database = '{{ storage_paths.credentials_database }}' nym_root_directory = '{{ client.nym_root_directory }}'
[client.gateway_endpoint]
# ID of the gateway from which the client should be fetching messages.
gateway_id = '{{ client.gateway_endpoint.gateway_id }}'
# Address of the gateway owner to which the client should send messages.
gateway_owner = '{{ client.gateway_endpoint.gateway_owner }}'
# Address of the gateway listener to which all client requests should be sent.
gateway_listener = '{{ client.gateway_endpoint.gateway_listener }}'
# Path to the persistent store for received reply surbs, unused encryption keys and used sender tags.
reply_surb_database = '{{ storage_paths.reply_surb_database }}'
# 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.
gateway_details = '{{ storage_paths.gateway_details }}'
##### socket config options ##### ##### socket config options #####
@@ -106,5 +120,5 @@ average_ack_delay = '{{ debug.acknowledgements.average_ack_delay }}'
[debug.cover_traffic] [debug.cover_traffic]
loop_cover_traffic_average_delay = '{{ debug.cover_traffic.loop_cover_traffic_average_delay }}' loop_cover_traffic_average_delay = '{{ debug.cover_traffic.loop_cover_traffic_average_delay }}'
"#
"#; }
+41 -22
View File
@@ -1,4 +1,4 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::client::config::Config; use crate::client::config::Config;
@@ -6,15 +6,19 @@ use crate::error::ClientError;
use crate::websocket; use crate::websocket;
use futures::channel::mpsc; use futures::channel::mpsc;
use log::*; use log::*;
use nym_client_core::client::base_client::non_wasm_helpers::default_query_dkg_client_from_config; use nym_bandwidth_controller::BandwidthController;
use nym_client_core::client::base_client::non_wasm_helpers::create_bandwidth_controller;
use nym_client_core::client::base_client::storage::OnDiskPersistent; use nym_client_core::client::base_client::storage::OnDiskPersistent;
use nym_client_core::client::base_client::{ use nym_client_core::client::base_client::{
BaseClientBuilder, ClientInput, ClientOutput, ClientState, non_wasm_helpers, BaseClientBuilder, ClientInput, ClientOutput, ClientState,
}; };
use nym_client_core::client::inbound_messages::InputMessage; use nym_client_core::client::inbound_messages::InputMessage;
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
use nym_client_core::client::received_buffer::{ use nym_client_core::client::received_buffer::{
ReceivedBufferMessage, ReceivedBufferRequestSender, ReconstructedMessagesReceiver, ReceivedBufferMessage, ReceivedBufferRequestSender, ReconstructedMessagesReceiver,
}; };
use nym_client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
use nym_credential_storage::persistent_storage::PersistentStorage;
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag; use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
use nym_sphinx::params::PacketType; use nym_sphinx::params::PacketType;
use nym_task::connections::TransmissionLane; use nym_task::connections::TransmissionLane;
@@ -42,6 +46,17 @@ impl SocketClient {
SocketClient { config } SocketClient { config }
} }
async fn create_bandwidth_controller(
config: &Config,
) -> BandwidthController<Client<QueryNyxdClient>, PersistentStorage> {
let storage = nym_credential_storage::initialise_persistent_storage(
config.get_base().get_database_path(),
)
.await;
create_bandwidth_controller(config.get_base(), storage)
}
fn start_websocket_listener( fn start_websocket_listener(
config: &Config, config: &Config,
client_input: ClientInput, client_input: ClientInput,
@@ -78,7 +93,7 @@ impl SocketClient {
Some(packet_type), Some(packet_type),
); );
websocket::Listener::new(config.socket.host, config.socket.listening_port) websocket::Listener::new(config.get_listening_ip(), config.get_listening_port())
.start(websocket_handler, shutdown); .start(websocket_handler, shutdown);
} }
@@ -91,38 +106,42 @@ impl SocketClient {
res res
} }
async fn initialise_storage(&self) -> Result<OnDiskPersistent, ClientError> { fn key_store(&self) -> OnDiskKeys {
Ok(OnDiskPersistent::from_paths( let pathfinder = ClientKeyPathfinder::new_from_config(self.config.get_base());
self.config.storage_paths.common_paths.clone(), OnDiskKeys::new(pathfinder)
&self.config.base.debug,
)
.await?)
} }
// TODO: see if this could also be shared with socks5 client / nym-sdk maybe // TODO: see if this could also be shared with socks5 client / nym-sdk maybe
async fn create_base_client_builder(&self) -> Result<NativeClientBuilder, ClientError> { async fn create_base_client_builder(&self) -> Result<NativeClientBuilder, ClientError> {
// don't create dkg client for the bandwidth controller if credentials are disabled // don't create bandwidth controller if credentials are disabled
let dkg_query_client = if self.config.base.client.disabled_credentials_mode { let bandwidth_controller = if self.config.get_base().get_disabled_credentials_mode() {
None None
} else { } else {
Some(default_query_dkg_client_from_config(&self.config.base)) Some(Self::create_bandwidth_controller(&self.config).await)
}; };
let storage = self.initialise_storage().await?; let base_client = BaseClientBuilder::new_from_base_config(
self.config.get_base(),
let base_client = BaseClientBuilder::new(&self.config.base, storage, dkg_query_client); self.key_store(),
bandwidth_controller,
non_wasm_helpers::setup_fs_reply_surb_backend(
self.config.get_base().get_reply_surb_database_path(),
&self.config.get_debug_settings().reply_surbs,
)
.await?,
);
Ok(base_client) Ok(base_client)
} }
pub async fn start_socket(self) -> Result<TaskManager, ClientError> { pub async fn start_socket(self) -> Result<TaskManager, ClientError> {
if !self.config.socket.socket_type.is_websocket() { if !self.config.get_socket_type().is_websocket() {
return Err(ClientError::InvalidSocketMode); return Err(ClientError::InvalidSocketMode);
} }
let base_builder = self.create_base_client_builder().await?; let base_builder = self.create_base_client_builder().await?;
let packet_type = self.config.base.debug.traffic.packet_type; let packet_type = self.config.get_base().get_packet_type();
let mut started_client = base_builder.start_base().await?; let mut started_client = base_builder.start_base(packet_type).await?;
let self_address = started_client.address; let self_address = started_client.address;
let client_input = started_client.client_input.register_producer(); let client_input = started_client.client_input.register_producer();
let client_output = started_client.client_output.register_consumer(); let client_output = started_client.client_output.register_consumer();
@@ -145,13 +164,13 @@ impl SocketClient {
} }
pub async fn start_direct(self) -> Result<DirectClient, ClientError> { pub async fn start_direct(self) -> Result<DirectClient, ClientError> {
if self.config.socket.socket_type.is_websocket() { if self.config.get_socket_type().is_websocket() {
return Err(ClientError::InvalidSocketMode); return Err(ClientError::InvalidSocketMode);
} }
let base_builder = self.create_base_client_builder().await?; let base_builder = self.create_base_client_builder().await?;
let packet_type = self.config.base.debug.traffic.packet_type; let packet_type = self.config.get_base().get_packet_type();
let mut started_client = base_builder.start_base().await?; let mut started_client = base_builder.start_base(packet_type).await?;
let address = started_client.address; let address = started_client.address;
let client_input = started_client.client_input.register_producer(); let client_input = started_client.client_input.register_producer();
let client_output = started_client.client_output.register_consumer(); let client_output = started_client.client_output.register_consumer();
+37 -47
View File
@@ -1,10 +1,7 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::client::config::{ use crate::commands::try_upgrade_v1_1_13_config;
default_config_directory, default_config_filepath, default_data_directory,
};
use crate::commands::try_upgrade_config;
use crate::{ use crate::{
client::config::Config, client::config::Config,
commands::{override_config, OverrideConfig}, commands::{override_config, OverrideConfig},
@@ -12,16 +9,13 @@ use crate::{
}; };
use clap::Args; use clap::Args;
use nym_bin_common::output_format::OutputFormat; use nym_bin_common::output_format::OutputFormat;
use nym_client_core::client::base_client::storage::gateway_details::OnDiskGatewayDetails;
use nym_client_core::client::key_manager::persistence::OnDiskKeys; use nym_client_core::client::key_manager::persistence::OnDiskKeys;
use nym_client_core::config::GatewayEndpointConfig; use nym_config::NymConfig;
use nym_client_core::init::GatewaySetup;
use nym_crypto::asymmetric::identity; use nym_crypto::asymmetric::identity;
use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::addressing::clients::Recipient;
use serde::Serialize; use serde::Serialize;
use std::fmt::Display; use std::fmt::Display;
use std::net::IpAddr; use std::net::IpAddr;
use std::{fs, io};
use tap::TapFallible; use tap::TapFallible;
#[derive(Args, Clone)] #[derive(Args, Clone)]
@@ -103,15 +97,15 @@ impl From<Init> for OverrideConfig {
pub struct InitResults { pub struct InitResults {
#[serde(flatten)] #[serde(flatten)]
client_core: nym_client_core::init::InitResults, client_core: nym_client_core::init::InitResults,
client_listening_port: u16, client_listening_port: String,
client_address: String, client_address: String,
} }
impl InitResults { impl InitResults {
fn new(config: &Config, address: &Recipient, gateway: &GatewayEndpointConfig) -> Self { fn new(config: &Config, address: &Recipient) -> Self {
Self { Self {
client_core: nym_client_core::init::InitResults::new(&config.base, address, gateway), client_core: nym_client_core::init::InitResults::new(config.get_base(), address),
client_listening_port: config.socket.listening_port, client_listening_port: config.get_listening_port().to_string(),
client_address: address.to_string(), client_address: address.to_string(),
} }
} }
@@ -125,26 +119,18 @@ impl Display for InitResults {
} }
} }
fn init_paths(id: &str) -> io::Result<()> {
fs::create_dir_all(default_data_directory(id))?;
fs::create_dir_all(default_config_directory(id))
}
pub(crate) async fn execute(args: &Init) -> Result<(), ClientError> { pub(crate) async fn execute(args: &Init) -> Result<(), ClientError> {
eprintln!("Initialising client..."); eprintln!("Initialising client...");
let id = &args.id; let id = &args.id;
let already_init = if default_config_filepath(id).exists() { let already_init = Config::default_config_file_path(id).exists();
if already_init {
// in case we're using old config, try to upgrade it // in case we're using old config, try to upgrade it
// (if we're using the current version, it's a no-op) // (if we're using the current version, it's a no-op)
try_upgrade_config(id)?; try_upgrade_v1_1_13_config(id)?;
eprintln!("Client \"{id}\" was already initialised before"); eprintln!("Client \"{id}\" was already initialised before");
true }
} else {
init_paths(id)?;
false
};
// Usually you only register with the gateway on the first init, however you can force // Usually you only register with the gateway on the first init, however you can force
// re-registering if wanted. // re-registering if wanted.
@@ -160,44 +146,48 @@ pub(crate) async fn execute(args: &Init) -> Result<(), ClientError> {
// Attempt to use a user-provided gateway, if possible // Attempt to use a user-provided gateway, if possible
let user_chosen_gateway_id = args.gateway; let user_chosen_gateway_id = args.gateway;
let gateway_setup = GatewaySetup::new_fresh(
user_chosen_gateway_id.map(|id| id.to_base58_string()),
Some(args.latency_based_selection),
);
// Load and potentially override config // Load and potentially override config
let config = override_config(Config::new(id), OverrideConfig::from(args.clone())); let mut config = override_config(Config::new(id), OverrideConfig::from(args.clone()));
// Setup gateway by either registering a new one, or creating a new config from the selected // Setup gateway by either registering a new one, or creating a new config from the selected
// one but with keys kept, or reusing the gateway configuration. // one but with keys kept, or reusing the gateway configuration.
let key_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone()); let key_store = OnDiskKeys::from_config(config.get_base());
let details_store = let gateway = nym_client_core::init::setup_gateway_from_config::<Config, _, _>(
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
let init_details = nym_client_core::init::setup_gateway(
&gateway_setup,
&key_store, &key_store,
&details_store,
register_gateway, register_gateway,
Some(&config.base.client.nym_api_urls), user_chosen_gateway_id,
config.get_base(),
args.latency_based_selection,
) )
.await .await
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?; .tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?;
let config_save_location = config.default_location(); config.get_base_mut().set_gateway_endpoint(gateway);
config.save_to_default_location().tap_err(|_| {
config.save_to_file(None).tap_err(|_| {
log::error!("Failed to save the config file"); log::error!("Failed to save the config file");
})?; })?;
eprintln!(
"Saved configuration file to {}",
config_save_location.display()
);
let address = init_details.client_address()?; print_saved_config(&config);
eprintln!("Client configuration completed.\n"); let address =
nym_client_core::init::get_client_address_from_stored_ondisk_keys(config.get_base())?;
let init_results = InitResults::new(&config, &address, &init_details.gateway_details); let init_results = InitResults::new(&config, &address);
println!("{}", args.output.format(&init_results)); println!("{}", args.output.format(&init_results));
Ok(()) Ok(())
} }
fn print_saved_config(config: &Config) {
let config_save_location = config.get_config_file_save_location();
eprintln!("Saved configuration file to {config_save_location:?}");
eprintln!("Using gateway: {}", config.get_base().get_gateway_id());
log::debug!("Gateway id: {}", config.get_base().get_gateway_id());
log::debug!("Gateway owner: {}", config.get_base().get_gateway_owner());
log::debug!(
"Gateway listener: {}",
config.get_base().get_gateway_listener()
);
eprintln!("Client configuration completed.\n");
}
+13 -133
View File
@@ -2,23 +2,14 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::client::config::old_config_v1_1_13::OldConfigV1_1_13; use crate::client::config::old_config_v1_1_13::OldConfigV1_1_13;
use crate::client::config::old_config_v1_1_20::ConfigV1_1_20; use crate::client::config::{BaseConfig, Config};
use crate::client::config::old_config_v1_1_20_2::ConfigV1_1_20_2;
use crate::client::config::{BaseClientConfig, Config};
use crate::error::ClientError;
use clap::CommandFactory; use clap::CommandFactory;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use log::{error, info}; use log::info;
use nym_bin_common::build_information::BinaryBuildInformation; use nym_bin_common::build_information::BinaryBuildInformation;
use nym_bin_common::completions::{fig_generate, ArgShell}; use nym_bin_common::completions::{fig_generate, ArgShell};
use nym_client_core::client::base_client::storage::gateway_details::{ use nym_config::{NymConfig, OptionalSet};
OnDiskGatewayDetails, PersistedGatewayDetails,
};
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
use nym_client_core::config::GatewayEndpointConfig;
use nym_client_core::error::ClientCoreError;
use nym_config::OptionalSet;
use std::error::Error; use std::error::Error;
use std::net::IpAddr; use std::net::IpAddr;
@@ -91,151 +82,40 @@ pub(crate) async fn execute(args: &Cli) -> Result<(), Box<dyn Error + Send + Syn
pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config { pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
config config
.with_optional(Config::with_disabled_socket, args.disable_socket) .with_optional(Config::with_disabled_socket, args.disable_socket)
.with_base( .with_base(BaseConfig::with_high_default_traffic_volume, args.fastmode)
BaseClientConfig::with_high_default_traffic_volume, .with_base(BaseConfig::with_disabled_cover_traffic, args.no_cover)
args.fastmode,
)
.with_base(BaseClientConfig::with_disabled_cover_traffic, args.no_cover)
.with_optional(Config::with_port, args.port) .with_optional(Config::with_port, args.port)
.with_optional(Config::with_host, args.host) .with_optional(Config::with_host, args.host)
.with_optional_custom_env_ext( .with_optional_custom_env_ext(
BaseClientConfig::with_custom_nym_apis, BaseConfig::with_custom_nym_apis,
args.nym_apis, args.nym_apis,
nym_network_defaults::var_names::NYM_API, nym_network_defaults::var_names::NYM_API,
nym_config::parse_urls, nym_config::parse_urls,
) )
.with_optional_custom_env_ext( .with_optional_custom_env_ext(
BaseClientConfig::with_custom_nyxd, BaseConfig::with_custom_nyxd,
args.nyxd_urls, args.nyxd_urls,
nym_network_defaults::var_names::NYXD, nym_network_defaults::var_names::NYXD,
nym_config::parse_urls, nym_config::parse_urls,
) )
.with_optional_ext( .with_optional_ext(
BaseClientConfig::with_disabled_credentials, BaseConfig::with_disabled_credentials,
args.enabled_credentials_mode.map(|b| !b), args.enabled_credentials_mode.map(|b| !b),
) )
} }
fn persist_gateway_details( fn try_upgrade_v1_1_13_config(id: &str) -> std::io::Result<()> {
config: &Config, // explicitly load it as v1.1.13 (which is incompatible with the current, i.e. 1.1.14+)
details: GatewayEndpointConfig,
) -> Result<(), ClientError> {
let details_store =
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
let keys_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone());
let shared_keys = keys_store.ephemeral_load_gateway_keys().map_err(|source| {
ClientError::ClientCoreError(ClientCoreError::KeyStoreError {
source: Box::new(source),
})
})?;
let persisted_details = PersistedGatewayDetails::new(details, &shared_keys);
details_store
.store_to_disk(&persisted_details)
.map_err(|source| {
ClientError::ClientCoreError(ClientCoreError::GatewayDetailsStoreError {
source: Box::new(source),
})
})
}
fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, ClientError> {
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
// explicitly load it as v1.1.13 (which is incompatible with the next step, i.e. 1.1.19)
let Ok(old_config) = OldConfigV1_1_13::load_from_file(id) else { let Ok(old_config) = OldConfigV1_1_13::load_from_file(id) else {
// if we failed to load it, there might have been nothing to upgrade // if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day // or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(false); return Ok(());
}; };
info!("It seems the client is using <= v1.1.13 config template."); info!("It seems the client is using <= v1.1.13 config template.");
info!("It is going to get updated to the current specification."); info!("It is going to get updated to the current specification.");
let updated_step1: ConfigV1_1_20 = old_config.into(); let updated: Config = old_config.into();
let updated_step2: ConfigV1_1_20_2 = updated_step1.into(); updated.save_to_file(None)
let (updated, gateway_config) = updated_step2.upgrade();
persist_gateway_details(&updated, gateway_config)?;
updated.save_to_default_location()?;
Ok(true)
}
fn try_upgrade_v1_1_20_config(id: &str) -> Result<bool, ClientError> {
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
// explicitly load it as v1.1.20 (which is incompatible with the current one, i.e. +1.1.21)
let Ok(old_config) = ConfigV1_1_20::load_from_file(id) else {
// if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(false);
};
info!("It seems the client is using <= v1.1.20 config template.");
info!("It is going to get updated to the current specification.");
let updated_step1: ConfigV1_1_20_2 = old_config.into();
let (updated, gateway_config) = updated_step1.upgrade();
persist_gateway_details(&updated, gateway_config)?;
updated.save_to_default_location()?;
Ok(true)
}
fn try_upgrade_v1_1_20_2_config(id: &str) -> Result<bool, ClientError> {
// explicitly load it as v1.1.20_2 (which is incompatible with the current one, i.e. +1.1.21)
let Ok(old_config) = ConfigV1_1_20_2::read_from_default_path(id) else {
// if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(false);
};
info!("It seems the client is using <= v1.1.20_2 config template.");
info!("It is going to get updated to the current specification.");
let (updated, gateway_config) = old_config.upgrade();
persist_gateway_details(&updated, gateway_config)?;
updated.save_to_default_location()?;
Ok(true)
}
fn try_upgrade_config(id: &str) -> Result<(), ClientError> {
if try_upgrade_v1_1_13_config(id)? {
return Ok(());
}
if try_upgrade_v1_1_20_config(id)? {
return Ok(());
}
if try_upgrade_v1_1_20_2_config(id)? {
return Ok(());
}
Ok(())
}
fn try_load_current_config(id: &str) -> Result<Config, ClientError> {
// try to load the config as is
if let Ok(cfg) = Config::read_from_default_path(id) {
return if !cfg.validate() {
Err(ClientError::ConfigValidationFailure)
} else {
Ok(cfg)
};
}
// we couldn't load it - try upgrading it from older revisions
try_upgrade_config(id)?;
let config = match Config::read_from_default_path(id) {
Ok(cfg) => cfg,
Err(err) => {
error!("Failed to load config for {id}. Are you sure you have run `init` before? (Error was: {err})");
return Err(ClientError::FailedToLoadConfig(id.to_string()));
}
};
if !config.validate() {
return Err(ClientError::ConfigValidationFailure);
}
Ok(config)
} }
#[cfg(test)] #[cfg(test)]
+29 -7
View File
@@ -1,7 +1,10 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::commands::try_load_current_config; use std::error::Error;
use std::net::IpAddr;
use crate::commands::try_upgrade_v1_1_13_config;
use crate::{ use crate::{
client::{config::Config, SocketClient}, client::{config::Config, SocketClient},
commands::{override_config, OverrideConfig}, commands::{override_config, OverrideConfig},
@@ -10,9 +13,8 @@ use crate::{
use clap::Args; use clap::Args;
use log::*; use log::*;
use nym_bin_common::version_checker::is_minor_version_compatible; use nym_bin_common::version_checker::is_minor_version_compatible;
use nym_config::NymConfig;
use nym_crypto::asymmetric::identity; use nym_crypto::asymmetric::identity;
use std::error::Error;
use std::net::IpAddr;
#[derive(Args, Clone)] #[derive(Args, Clone)]
pub(crate) struct Run { pub(crate) struct Run {
@@ -80,7 +82,7 @@ impl From<Run> for OverrideConfig {
// network version. It might do so in the future. // network version. It might do so in the future.
fn version_check(cfg: &Config) -> bool { fn version_check(cfg: &Config) -> bool {
let binary_version = env!("CARGO_PKG_VERSION"); let binary_version = env!("CARGO_PKG_VERSION");
let config_version = &cfg.base.client.version; let config_version = cfg.get_base().get_version();
if binary_version == config_version { if binary_version == config_version {
true true
} else { } else {
@@ -96,10 +98,30 @@ fn version_check(cfg: &Config) -> bool {
} }
pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn Error + Send + Sync>> { pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn Error + Send + Sync>> {
eprintln!("Starting client {}...", args.id); let id = &args.id;
let mut config = try_load_current_config(&args.id)?; // in case we're using old config, try to upgrade it
config = override_config(config, OverrideConfig::from(args.clone())); // (if we're using the current version, it's a no-op)
try_upgrade_v1_1_13_config(id)?;
let mut config = match Config::load_from_file(id) {
Ok(cfg) => cfg,
Err(err) => {
error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {err})", id);
return Err(Box::new(ClientError::FailedToLoadConfig(id.to_string())));
}
};
if !config.validate() {
return Err(Box::new(ClientError::ConfigValidationFailure));
}
let override_config_fields = OverrideConfig::from(args.clone());
config = override_config(config, override_config_fields);
if config.get_base_mut().set_empty_fields_to_defaults() {
warn!("some of the core config options were left unset. the default values are going to get used instead.");
}
if !version_check(&config) { if !version_check(&config) {
error!("failed the local version check"); error!("failed the local version check");
+84 -16
View File
@@ -1,14 +1,42 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::client::config::Config; use crate::client::config::{Config, MISSING_VALUE};
use crate::commands::try_load_current_config;
use clap::Args;
use nym_bin_common::version_checker::Version; use nym_bin_common::version_checker::Version;
use nym_config::NymConfig;
use clap::Args;
use std::fmt::Display;
use std::process; use std::process;
fn unimplemented_upgrade(current_version: &Version, config_version: &Version) -> ! { #[allow(dead_code)]
eprintln!("Cannot perform upgrade from {config_version} to {current_version} as it hasn't been implemented yet"); fn fail_upgrade<D1: Display, D2: Display>(from_version: D1, to_version: D2) -> ! {
print_failed_upgrade(from_version, to_version);
process::exit(1)
}
fn print_start_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
println!("\n==================\nTrying to upgrade client from {from} to {to} ...");
}
fn print_failed_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
eprintln!("Upgrade from {from} to {to} failed!\n==================\n");
}
fn print_successful_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
println!("Upgrade from {from} to {to} was successful!\n==================\n");
}
fn outdated_upgrade(config_version: &Version, package_version: &Version) -> ! {
eprintln!(
"Cannot perform upgrade from {config_version} to {package_version}. Your version is too old to perform the upgrade.!"
);
process::exit(1)
}
fn unsupported_upgrade(current_version: &Version, config_version: &Version) -> ! {
eprintln!("Cannot perform upgrade from {config_version} to {current_version}. Please let the developers know about this issue if you expected it to work!");
process::exit(1) process::exit(1)
} }
@@ -20,7 +48,7 @@ pub(crate) struct Upgrade {
} }
fn parse_config_version(config: &Config) -> Version { fn parse_config_version(config: &Config) -> Version {
let version = Version::parse(&config.base.client.version).unwrap_or_else(|err| { let version = Version::parse(config.get_base().get_version()).unwrap_or_else(|err| {
eprintln!("failed to parse client version! - {err}"); eprintln!("failed to parse client version! - {err}");
process::exit(1) process::exit(1)
}); });
@@ -49,14 +77,53 @@ fn parse_package_version() -> Version {
version version
} }
fn do_upgrade(config: Config, _args: &Upgrade, package_version: &Version) { fn minor_0_12_upgrade(
let config_version = parse_config_version(&config); mut config: Config,
if &config_version == package_version { _matches: &Upgrade,
println!("You're using the most recent version!"); config_version: &Version,
return; package_version: &Version,
} ) -> Config {
let to_version = if package_version.major == 0 && package_version.minor == 12 {
package_version.clone()
} else {
Version::new(0, 12, 0)
};
unimplemented_upgrade(package_version, &config_version) print_start_upgrade(config_version, &to_version);
config
.get_base_mut()
.set_custom_version(to_version.to_string().as_ref());
config.save_to_file(None).unwrap_or_else(|err| {
eprintln!("failed to overwrite config file! - {err}");
print_failed_upgrade(config_version, &to_version);
process::exit(1);
});
print_successful_upgrade(config_version, to_version);
config
}
fn do_upgrade(mut config: Config, args: &Upgrade, package_version: &Version) {
loop {
let config_version = parse_config_version(&config);
if &config_version == package_version {
println!("You're using the most recent version!");
return;
}
config = match config_version.major {
0 => match config_version.minor {
9 | 10 => outdated_upgrade(&config_version, package_version),
11 => minor_0_12_upgrade(config, args, &config_version, package_version),
_ => unsupported_upgrade(&config_version, package_version),
},
_ => unsupported_upgrade(&config_version, package_version),
}
}
} }
pub(crate) fn execute(args: &Upgrade) { pub(crate) fn execute(args: &Upgrade) {
@@ -64,15 +131,16 @@ pub(crate) fn execute(args: &Upgrade) {
let id = &args.id; let id = &args.id;
let existing_config = try_load_current_config(id).unwrap_or_else(|err| { let existing_config = Config::load_from_file(id).unwrap_or_else(|err| {
eprintln!("failed to load existing config file! - {err}"); eprintln!("failed to load existing config file! - {err}");
process::exit(1) process::exit(1)
}); });
if existing_config.base.client.version.is_empty() { if existing_config.get_base().get_version() == MISSING_VALUE {
eprintln!("the existing configuration file does not seem to contain version number."); eprintln!("the existing configuration file does not seem to contain version number.");
process::exit(1); process::exit(1);
} }
// here be upgrade path to 0.9.X and beyond based on version number from config
do_upgrade(existing_config, args, &package_version) do_upgrade(existing_config, args, &package_version)
} }
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-socks5-client" name = "nym-socks5-client"
version = "1.1.22" version = "1.1.20"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"] 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" description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021" edition = "2021"
+37 -48
View File
@@ -1,25 +1,20 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::commands::try_upgrade_config; use crate::commands::try_upgrade_v1_1_13_config;
use crate::config::{
default_config_directory, default_config_filepath, default_data_directory, Config,
};
use crate::{ use crate::{
commands::{override_config, OverrideConfig}, commands::{override_config, OverrideConfig},
error::Socks5ClientError, error::Socks5ClientError,
}; };
use clap::Args; use clap::Args;
use nym_bin_common::output_format::OutputFormat; use nym_bin_common::output_format::OutputFormat;
use nym_client_core::client::base_client::storage::gateway_details::OnDiskGatewayDetails;
use nym_client_core::client::key_manager::persistence::OnDiskKeys; use nym_client_core::client::key_manager::persistence::OnDiskKeys;
use nym_client_core::config::GatewayEndpointConfig; use nym_config::NymConfig;
use nym_client_core::init::GatewaySetup;
use nym_crypto::asymmetric::identity; use nym_crypto::asymmetric::identity;
use nym_socks5_client_core::config::Config;
use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::addressing::clients::Recipient;
use serde::Serialize; use serde::Serialize;
use std::fmt::Display; use std::fmt::Display;
use std::{fs, io};
use tap::TapFallible; use tap::TapFallible;
#[derive(Args, Clone)] #[derive(Args, Clone)]
@@ -105,19 +100,15 @@ impl From<Init> for OverrideConfig {
pub struct InitResults { pub struct InitResults {
#[serde(flatten)] #[serde(flatten)]
client_core: nym_client_core::init::InitResults, client_core: nym_client_core::init::InitResults,
socks5_listening_port: u16, socks5_listening_port: String,
client_address: String, client_address: String,
} }
impl InitResults { impl InitResults {
fn new(config: &Config, address: &Recipient, gateway: &GatewayEndpointConfig) -> Self { fn new(config: &Config, address: &Recipient) -> Self {
Self { Self {
client_core: nym_client_core::init::InitResults::new( client_core: nym_client_core::init::InitResults::new(config.get_base(), address),
&config.core.base, socks5_listening_port: config.get_socks5().get_listening_port().to_string(),
address,
gateway,
),
socks5_listening_port: config.core.socks5.listening_port,
client_address: address.to_string(), client_address: address.to_string(),
} }
} }
@@ -131,27 +122,19 @@ impl Display for InitResults {
} }
} }
fn init_paths(id: &str) -> io::Result<()> {
fs::create_dir_all(default_data_directory(id))?;
fs::create_dir_all(default_config_directory(id))
}
pub(crate) async fn execute(args: &Init) -> Result<(), Socks5ClientError> { pub(crate) async fn execute(args: &Init) -> Result<(), Socks5ClientError> {
eprintln!("Initialising client..."); eprintln!("Initialising client...");
let id = &args.id; let id = &args.id;
let provider_address = &args.provider; let provider_address = &args.provider;
let already_init = if default_config_filepath(id).exists() { let already_init = Config::default_config_file_path(id).exists();
if already_init {
// in case we're using old config, try to upgrade it // in case we're using old config, try to upgrade it
// (if we're using the current version, it's a no-op) // (if we're using the current version, it's a no-op)
try_upgrade_config(id)?; try_upgrade_v1_1_13_config(id)?;
eprintln!("SOCKS5 client \"{id}\" was already initialised before"); eprintln!("SOCKS5 client \"{id}\" was already initialised before");
true }
} else {
init_paths(id)?;
false
};
// Usually you only register with the gateway on the first init, however you can force // Usually you only register with the gateway on the first init, however you can force
// re-registering if wanted. // re-registering if wanted.
@@ -167,47 +150,53 @@ pub(crate) async fn execute(args: &Init) -> Result<(), Socks5ClientError> {
// Attempt to use a user-provided gateway, if possible // Attempt to use a user-provided gateway, if possible
let user_chosen_gateway_id = args.gateway; let user_chosen_gateway_id = args.gateway;
let gateway_setup = GatewaySetup::new_fresh(
user_chosen_gateway_id.map(|id| id.to_base58_string()),
Some(args.latency_based_selection),
);
// Load and potentially override config // Load and potentially override config
let config = override_config( let mut config = override_config(
Config::new(id, &provider_address.to_string()), Config::new(id, &provider_address.to_string()),
OverrideConfig::from(args.clone()), OverrideConfig::from(args.clone()),
); );
// Setup gateway by either registering a new one, or creating a new config from the selected // Setup gateway by either registering a new one, or creating a new config from the selected
// one but with keys kept, or reusing the gateway configuration. // one but with keys kept, or reusing the gateway configuration.
let key_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone()); let key_store = OnDiskKeys::from_config(config.get_base());
let details_store = let gateway = nym_client_core::init::setup_gateway_from_config::<Config, _, _>(
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
let init_details = nym_client_core::init::setup_gateway(
&gateway_setup,
&key_store, &key_store,
&details_store,
register_gateway, register_gateway,
Some(&config.core.base.client.nym_api_urls), user_chosen_gateway_id,
config.get_base(),
args.latency_based_selection,
) )
.await .await
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?; .tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?;
config.get_base_mut().set_gateway_endpoint(gateway);
// TODO: ask the service provider we specified for its interface version and set it in the config // TODO: ask the service provider we specified for its interface version and set it in the config
let config_save_location = config.default_location(); config.save_to_file(None).tap_err(|_| {
config.save_to_default_location().tap_err(|_| {
log::error!("Failed to save the config file"); log::error!("Failed to save the config file");
})?; })?;
eprintln!(
"Saved configuration file to {}",
config_save_location.display()
);
let address = init_details.client_address()?; print_saved_config(&config);
let init_results = InitResults::new(&config, &address, &init_details.gateway_details); let address =
nym_client_core::init::get_client_address_from_stored_ondisk_keys(config.get_base())?;
let init_results = InitResults::new(&config, &address);
println!("{}", args.output.format(&init_results)); println!("{}", args.output.format(&init_results));
Ok(()) Ok(())
} }
fn print_saved_config(config: &Config) {
let config_save_location = config.get_config_file_save_location();
eprintln!("Saved configuration file to {:?}", config_save_location);
eprintln!("Using gateway: {}", config.get_base().get_gateway_id());
log::debug!("Gateway id: {}", config.get_base().get_gateway_id());
log::debug!("Gateway owner: {}", config.get_base().get_gateway_owner());
log::debug!(
"Gateway listener: {}",
config.get_base().get_gateway_listener()
);
eprintln!("Client configuration completed.\n");
}
+18 -138
View File
@@ -1,24 +1,15 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::config::old_config_v1_1_13::OldConfigV1_1_13;
use crate::config::old_config_v1_1_20::ConfigV1_1_20;
use crate::config::old_config_v1_1_20_2::ConfigV1_1_20_2;
use crate::config::{BaseClientConfig, Config};
use crate::error::Socks5ClientError;
use clap::CommandFactory; use clap::CommandFactory;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use log::{error, info}; use log::info;
use nym_bin_common::build_information::BinaryBuildInformation; use nym_bin_common::build_information::BinaryBuildInformation;
use nym_bin_common::completions::{fig_generate, ArgShell}; use nym_bin_common::completions::{fig_generate, ArgShell};
use nym_client_core::client::base_client::storage::gateway_details::{ use nym_config::{NymConfig, OptionalSet};
OnDiskGatewayDetails, PersistedGatewayDetails, use nym_socks5_client_core::config::old_config_v1_1_13::OldConfigV1_1_13;
}; use nym_socks5_client_core::config::{BaseConfig, Config};
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
use nym_client_core::config::GatewayEndpointConfig;
use nym_client_core::error::ClientCoreError;
use nym_config::OptionalSet;
use nym_sphinx::params::PacketType; use nym_sphinx::params::PacketType;
use std::error::Error; use std::error::Error;
@@ -97,152 +88,41 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
PacketType::Mix PacketType::Mix
}; };
config config
.with_base( .with_base(BaseConfig::with_high_default_traffic_volume, args.fastmode)
BaseClientConfig::with_high_default_traffic_volume, .with_base(BaseConfig::with_disabled_cover_traffic, args.no_cover)
args.fastmode, .with_base(BaseConfig::with_packet_type, packet_type)
)
.with_base(BaseClientConfig::with_disabled_cover_traffic, args.no_cover)
.with_base(BaseClientConfig::with_packet_type, packet_type)
.with_optional(Config::with_anonymous_replies, args.use_anonymous_replies) .with_optional(Config::with_anonymous_replies, args.use_anonymous_replies)
.with_optional(Config::with_port, args.port) .with_optional(Config::with_port, args.port)
.with_optional_base_custom_env( .with_optional_custom_env_ext(
BaseClientConfig::with_custom_nym_apis, BaseConfig::with_custom_nym_apis,
args.nym_apis, args.nym_apis,
nym_network_defaults::var_names::NYM_API, nym_network_defaults::var_names::NYM_API,
nym_config::parse_urls, nym_config::parse_urls,
) )
.with_optional_base_custom_env( .with_optional_custom_env_ext(
BaseClientConfig::with_custom_nyxd, BaseConfig::with_custom_nyxd,
args.nyxd_urls, args.nyxd_urls,
nym_network_defaults::var_names::NYXD, nym_network_defaults::var_names::NYXD,
nym_config::parse_urls, nym_config::parse_urls,
) )
.with_optional_base( .with_optional_ext(
BaseClientConfig::with_disabled_credentials, BaseConfig::with_disabled_credentials,
args.enabled_credentials_mode.map(|b| !b), args.enabled_credentials_mode.map(|b| !b),
) )
} }
fn persist_gateway_details( fn try_upgrade_v1_1_13_config(id: &str) -> std::io::Result<()> {
config: &Config, // explicitly load it as v1.1.13 (which is incompatible with the current, i.e. 1.1.14+)
details: GatewayEndpointConfig,
) -> Result<(), Socks5ClientError> {
let details_store =
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
let keys_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone());
let shared_keys = keys_store.ephemeral_load_gateway_keys().map_err(|source| {
Socks5ClientError::ClientCoreError(ClientCoreError::KeyStoreError {
source: Box::new(source),
})
})?;
let persisted_details = PersistedGatewayDetails::new(details, &shared_keys);
details_store
.store_to_disk(&persisted_details)
.map_err(|source| {
Socks5ClientError::ClientCoreError(ClientCoreError::GatewayDetailsStoreError {
source: Box::new(source),
})
})
}
fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, Socks5ClientError> {
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
// explicitly load it as v1.1.13 (which is incompatible with the next step, i.e. 1.1.19)
let Ok(old_config) = OldConfigV1_1_13::load_from_file(id) else { let Ok(old_config) = OldConfigV1_1_13::load_from_file(id) else {
// if we failed to load it, there might have been nothing to upgrade // if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day // or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(false); return Ok(());
}; };
info!("It seems the client is using <= v1.1.13 config template."); info!("It seems the client is using <= v1.1.13 config template.");
info!("It is going to get updated to the current specification."); info!("It is going to get updated to the current specification.");
let updated_step1: ConfigV1_1_20 = old_config.into(); let updated: Config = old_config.into();
let updated_step2: ConfigV1_1_20_2 = updated_step1.into(); updated.save_to_file(None)
let (updated, gateway_config) = updated_step2.upgrade();
persist_gateway_details(&updated, gateway_config)?;
updated.save_to_default_location()?;
Ok(true)
}
fn try_upgrade_v1_1_20_config(id: &str) -> Result<bool, Socks5ClientError> {
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
// explicitly load it as v1.1.20 (which is incompatible with the current one, i.e. +1.1.21)
let Ok(old_config) = ConfigV1_1_20::load_from_file(id) else {
// if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(false);
};
info!("It seems the client is using <= v1.1.20 config template.");
info!("It is going to get updated to the current specification.");
let updated_step1: ConfigV1_1_20_2 = old_config.into();
let (updated, gateway_config) = updated_step1.upgrade();
persist_gateway_details(&updated, gateway_config)?;
updated.save_to_default_location()?;
Ok(true)
}
fn try_upgrade_v1_1_20_2_config(id: &str) -> Result<bool, Socks5ClientError> {
// explicitly load it as v1.1.20_2 (which is incompatible with the current one, i.e. +1.1.21)
let Ok(old_config) = ConfigV1_1_20_2::read_from_default_path(id) else {
// if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(false);
};
info!("It seems the client is using <= v1.1.20_2 config template.");
info!("It is going to get updated to the current specification.");
let (updated, gateway_config) = old_config.upgrade();
persist_gateway_details(&updated, gateway_config)?;
updated.save_to_default_location()?;
Ok(true)
}
fn try_upgrade_config(id: &str) -> Result<(), Socks5ClientError> {
if try_upgrade_v1_1_13_config(id)? {
return Ok(());
}
if try_upgrade_v1_1_20_config(id)? {
return Ok(());
}
if try_upgrade_v1_1_20_2_config(id)? {
return Ok(());
}
Ok(())
}
fn try_load_current_config(id: &str) -> Result<Config, Socks5ClientError> {
// try to load the config as is
if let Ok(cfg) = Config::read_from_default_path(id) {
return if !cfg.validate() {
Err(Socks5ClientError::ConfigValidationFailure)
} else {
Ok(cfg)
};
}
// we couldn't load it - try upgrading it from older revisions
try_upgrade_config(id)?;
let config = match Config::read_from_default_path(id) {
Ok(cfg) => cfg,
Err(err) => {
error!("Failed to load config for {id}. Are you sure you have run `init` before? (Error was: {err})");
return Err(Socks5ClientError::FailedToLoadConfig(id.to_string()));
}
};
if !config.validate() {
return Err(Socks5ClientError::ConfigValidationFailure);
}
Ok(config)
} }
#[cfg(test)] #[cfg(test)]
+38 -13
View File
@@ -1,8 +1,7 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::commands::try_load_current_config; use crate::commands::try_upgrade_v1_1_13_config;
use crate::config::Config;
use crate::{ use crate::{
commands::{override_config, OverrideConfig}, commands::{override_config, OverrideConfig},
error::Socks5ClientError, error::Socks5ClientError,
@@ -11,8 +10,9 @@ use clap::Args;
use log::*; use log::*;
use nym_bin_common::version_checker::is_minor_version_compatible; use nym_bin_common::version_checker::is_minor_version_compatible;
use nym_client_core::client::base_client::storage::OnDiskPersistent; use nym_client_core::client::base_client::storage::OnDiskPersistent;
use nym_config::NymConfig;
use nym_crypto::asymmetric::identity; use nym_crypto::asymmetric::identity;
use nym_socks5_client_core::NymClient; use nym_socks5_client_core::{config::Config, NymClient};
use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::addressing::clients::Recipient;
#[derive(Args, Clone)] #[derive(Args, Clone)]
@@ -21,6 +21,10 @@ pub(crate) struct Run {
#[clap(long)] #[clap(long)]
id: String, id: String,
/// Custom path to the nym-mixnet-client configuration file
#[clap(long)]
config: Option<String>,
/// Specifies whether this client is going to use an anonymous sender tag for communication with the service provider. /// Specifies whether this client is going to use an anonymous sender tag for communication with the service provider.
/// While this is going to hide its actual address information, it will make the actual communication /// While this is going to hide its actual address information, it will make the actual communication
/// slower and consume nearly double the bandwidth as it will require sending reply SURBs. /// slower and consume nearly double the bandwidth as it will require sending reply SURBs.
@@ -88,12 +92,13 @@ impl From<Run> for OverrideConfig {
// network version. It might do so in the future. // network version. It might do so in the future.
fn version_check(cfg: &Config) -> bool { fn version_check(cfg: &Config) -> bool {
let binary_version = env!("CARGO_PKG_VERSION"); let binary_version = env!("CARGO_PKG_VERSION");
let config_version = &cfg.core.base.client.version; let config_version = cfg.get_base().get_version();
if binary_version == config_version { if binary_version == config_version {
true true
} else { } else {
warn!( warn!(
"The socks5-client binary has different version than what is specified in config file! {binary_version} and {config_version}", "The mixnode binary has different version than what is specified in config file! {} and {}",
binary_version, config_version
); );
if is_minor_version_compatible(binary_version, config_version) { if is_minor_version_compatible(binary_version, config_version) {
info!("but they are still semver compatible. However, consider running the `upgrade` command"); info!("but they are still semver compatible. However, consider running the `upgrade` command");
@@ -106,18 +111,38 @@ fn version_check(cfg: &Config) -> bool {
} }
pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn std::error::Error + Send + Sync>> { pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
eprintln!("Starting client {}...", args.id); let id = &args.id;
let mut config = try_load_current_config(&args.id)?; // in case we're using old config, try to upgrade it
config = override_config(config, OverrideConfig::from(args.clone())); // (if we're using the current version, it's a no-op)
try_upgrade_v1_1_13_config(id)?;
let mut config = match Config::load_from_file(id) {
Ok(cfg) => cfg,
Err(err) => {
error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {err})", id);
return Err(Box::new(Socks5ClientError::FailedToLoadConfig(
id.to_string(),
)));
}
};
if !config.validate() {
return Err(Box::new(Socks5ClientError::ConfigValidationFailure));
}
let override_config_fields = OverrideConfig::from(args.clone());
config = override_config(config, override_config_fields);
if config.get_base_mut().set_empty_fields_to_defaults() {
warn!("some of the core config options were left unset. the default values are going to get used instead.");
}
if !version_check(&config) { if !version_check(&config) {
error!("failed the local version check"); error!("failed the local version check");
return Err(Box::new(Socks5ClientError::FailedLocalVersionCheck)); return Err(Box::new(Socks5ClientError::FailedLocalVersionCheck));
} }
let storage = let storage = OnDiskPersistent::from_config(config.get_base()).await?;
OnDiskPersistent::from_paths(config.storage_paths.common_paths, &config.core.base.debug) NymClient::new(config, storage).run_forever().await
.await?;
NymClient::new(config.core, storage).run_forever().await
} }
+99 -19
View File
@@ -1,14 +1,50 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::commands::try_load_current_config;
use crate::config::Config;
use clap::Args;
use nym_bin_common::version_checker::Version; use nym_bin_common::version_checker::Version;
use std::process; use nym_config::NymConfig;
use nym_socks5_client_core::config::{Config, MISSING_VALUE};
fn unimplemented_upgrade(current_version: &Version, config_version: &Version) -> ! { use clap::Args;
eprintln!("Cannot perform upgrade from {config_version} to {current_version} as it hasn't been implemented yet"); use std::{fmt::Display, process};
#[allow(dead_code)]
fn fail_upgrade<D1: Display, D2: Display>(from_version: D1, to_version: D2) -> ! {
print_failed_upgrade(from_version, to_version);
process::exit(1)
}
fn print_start_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
println!(
"\n==================\nTrying to upgrade client from {} to {} ...",
from, to
);
}
fn print_failed_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
eprintln!(
"Upgrade from {} to {} failed!\n==================\n",
from, to
);
}
fn print_successful_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
println!(
"Upgrade from {} to {} was successful!\n==================\n",
from, to
);
}
fn outdated_upgrade(config_version: &Version, package_version: &Version) -> ! {
eprintln!(
"Cannot perform upgrade from {} to {}. Your version is too old to perform the upgrade.!",
config_version, package_version
);
process::exit(1)
}
fn unsupported_upgrade(current_version: &Version, config_version: &Version) -> ! {
eprintln!("Cannot perform upgrade from {} to {}. Please let the developers know about this issue if you expected it to work!", config_version, current_version);
process::exit(1) process::exit(1)
} }
@@ -20,14 +56,15 @@ pub(crate) struct Upgrade {
} }
fn parse_config_version(config: &Config) -> Version { fn parse_config_version(config: &Config) -> Version {
let version = Version::parse(&config.core.base.client.version).unwrap_or_else(|err| { let version = Version::parse(config.get_base().get_version()).unwrap_or_else(|err| {
eprintln!("failed to parse client version! - {err}"); eprintln!("failed to parse client version! - {err}");
process::exit(1) process::exit(1)
}); });
if version.is_prerelease() || !version.build.is_empty() { if version.is_prerelease() || !version.build.is_empty() {
eprintln!( eprintln!(
"Trying to upgrade from a non-released version {version}. This is not supported!" "Trying to upgrade from a non-released version {}. This is not supported!",
version
); );
process::exit(1) process::exit(1)
} }
@@ -42,21 +79,63 @@ fn parse_package_version() -> Version {
// however, we are not using them ourselves at the moment and hence it should be fine. // however, we are not using them ourselves at the moment and hence it should be fine.
// if we change our mind, we could easily tweak this code // if we change our mind, we could easily tweak this code
if version.is_prerelease() || !version.build.is_empty() { if version.is_prerelease() || !version.build.is_empty() {
eprintln!("Trying to upgrade to a non-released version {version}. This is not supported!"); eprintln!(
"Trying to upgrade to a non-released version {}. This is not supported!",
version
);
process::exit(1) process::exit(1)
} }
version version
} }
fn do_upgrade(config: Config, _args: &Upgrade, package_version: &Version) { fn minor_0_12_upgrade(
let config_version = parse_config_version(&config); mut config: Config,
if &config_version == package_version { _args: &Upgrade,
println!("You're using the most recent version!"); config_version: &Version,
return; package_version: &Version,
} ) -> Config {
let to_version = if package_version.major == 0 && package_version.minor == 12 {
package_version.clone()
} else {
Version::new(0, 12, 0)
};
unimplemented_upgrade(package_version, &config_version) print_start_upgrade(config_version, &to_version);
config
.get_base_mut()
.set_custom_version(to_version.to_string().as_ref());
config.save_to_file(None).unwrap_or_else(|err| {
eprintln!("failed to overwrite config file! - {err}");
print_failed_upgrade(config_version, &to_version);
process::exit(1);
});
print_successful_upgrade(config_version, to_version);
config
}
fn do_upgrade(mut config: Config, args: &Upgrade, package_version: &Version) {
loop {
let config_version = parse_config_version(&config);
if &config_version == package_version {
println!("You're using the most recent version!");
return;
}
config = match config_version.major {
0 => match config_version.minor {
9 | 10 => outdated_upgrade(&config_version, package_version),
11 => minor_0_12_upgrade(config, args, &config_version, package_version),
_ => unsupported_upgrade(&config_version, package_version),
},
_ => unsupported_upgrade(&config_version, package_version),
}
}
} }
pub(crate) fn execute(args: &Upgrade) { pub(crate) fn execute(args: &Upgrade) {
@@ -64,15 +143,16 @@ pub(crate) fn execute(args: &Upgrade) {
let id = &args.id; let id = &args.id;
let existing_config = try_load_current_config(id).unwrap_or_else(|err| { let existing_config = Config::load_from_file(id).unwrap_or_else(|err| {
eprintln!("failed to load existing config file! - {err}"); eprintln!("failed to load existing config file! - {err}");
process::exit(1) process::exit(1)
}); });
if existing_config.core.base.client.version.is_empty() { if existing_config.get_base().get_version() == MISSING_VALUE {
eprintln!("the existing configuration file does not seem to contain version number."); eprintln!("the existing configuration file does not seem to contain version number.");
process::exit(1); process::exit(1);
} }
// here be upgrade path to 0.9.X and beyond based on version number from config
do_upgrade(existing_config, args, &package_version) do_upgrade(existing_config, args, &package_version)
} }
-160
View File
@@ -1,160 +0,0 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::persistence::SocksClientPaths;
use crate::config::template::CONFIG_TEMPLATE;
use nym_bin_common::logging::LoggingSettings;
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 serde::{Deserialize, Serialize};
use std::fmt::Debug;
use std::io;
use std::path::{Path, PathBuf};
use std::str::FromStr;
pub use nym_client_core::config::Config as BaseClientConfig;
pub use nym_socks5_client_core::config::Config as CoreConfig;
pub mod old_config_v1_1_13;
pub mod old_config_v1_1_20;
pub mod old_config_v1_1_20_2;
mod persistence;
mod template;
const DEFAULT_SOCKS5_CLIENTS_DIR: &str = "socks5-clients";
/// Derive default path to clients's config directory.
/// It should get resolved to `$HOME/.nym/socks5-clients/<id>/config`
pub fn default_config_directory<P: AsRef<Path>>(id: P) -> PathBuf {
must_get_home()
.join(NYM_DIR)
.join(DEFAULT_SOCKS5_CLIENTS_DIR)
.join(id)
.join(DEFAULT_CONFIG_DIR)
}
/// Derive default path to client's config file.
/// It should get resolved to `$HOME/.nym/socks5-clients/<id>/config/config.toml`
pub fn default_config_filepath<P: AsRef<Path>>(id: P) -> PathBuf {
default_config_directory(id).join(DEFAULT_CONFIG_FILENAME)
}
/// Derive default path to client's data directory where files, such as keys, are stored.
/// It should get resolved to `$HOME/.nym/socks5-clients/<id>/data`
pub fn default_data_directory<P: AsRef<Path>>(id: P) -> PathBuf {
must_get_home()
.join(NYM_DIR)
.join(DEFAULT_SOCKS5_CLIENTS_DIR)
.join(id)
.join(DEFAULT_DATA_DIR)
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Config {
pub core: CoreConfig,
pub storage_paths: SocksClientPaths,
pub logging: LoggingSettings,
}
impl NymConfigTemplate for Config {
fn template() -> &'static str {
CONFIG_TEMPLATE
}
}
impl Config {
pub fn new<S: AsRef<str>>(id: S, provider_mix_address: S) -> Self {
Config {
core: CoreConfig::new(
id.as_ref(),
env!("CARGO_PKG_VERSION"),
provider_mix_address.as_ref(),
),
storage_paths: SocksClientPaths::new_default(default_data_directory(id.as_ref())),
logging: Default::default(),
}
}
pub fn read_from_toml_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
read_config_from_toml_file(path)
}
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
Self::read_from_toml_file(default_config_filepath(id))
}
pub fn default_location(&self) -> PathBuf {
default_config_filepath(&self.core.base.client.id)
}
pub fn save_to_default_location(&self) -> io::Result<()> {
let config_save_location: PathBuf = self.default_location();
save_formatted_config_to_file(self, config_save_location)
}
pub fn validate(&self) -> bool {
// no other sections have explicit requirements (yet)
self.core.validate()
}
pub fn with_port(mut self, port: u16) -> Self {
self.core.socks5.listening_port = port;
self
}
pub fn with_anonymous_replies(mut self, anonymous_replies: bool) -> Self {
self.core.socks5.send_anonymously = anonymous_replies;
self
}
// poor man's 'builder' method
pub fn with_base<F, T>(mut self, f: F, val: T) -> Self
where
F: Fn(BaseClientConfig, T) -> BaseClientConfig,
{
self.core = self.core.with_base(f, val);
self
}
pub fn with_optional_base<F, T>(mut self, f: F, val: Option<T>) -> Self
where
F: Fn(BaseClientConfig, T) -> BaseClientConfig,
{
self.core = self.core.with_optional_base(f, val);
self
}
#[allow(unused)]
pub fn with_optional_base_env<F, T>(mut self, f: F, val: Option<T>, env_var: &str) -> Self
where
F: Fn(BaseClientConfig, T) -> BaseClientConfig,
T: FromStr,
<T as FromStr>::Err: Debug,
{
self.core = self.core.with_optional_base_env(f, val, env_var);
self
}
pub fn with_optional_base_custom_env<F, T, G>(
mut self,
f: F,
val: Option<T>,
env_var: &str,
parser: G,
) -> Self
where
F: Fn(BaseClientConfig, T) -> BaseClientConfig,
G: Fn(&str) -> T,
{
self.core = self
.core
.with_optional_base_custom_env(f, val, env_var, parser);
self
}
}
@@ -1,38 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::old_config_v1_1_20::{ConfigV1_1_20, Socks5V1_1_20};
use nym_client_core::config::old_config_v1_1_13::OldConfigV1_1_13 as OldBaseConfigV1_1_13;
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
use nym_config::must_get_home;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct OldConfigV1_1_13 {
#[serde(flatten)]
pub base: OldBaseConfigV1_1_13<OldConfigV1_1_13>,
pub socks5: Socks5V1_1_20,
}
impl MigrationNymConfig for OldConfigV1_1_13 {
fn default_root_directory() -> PathBuf {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
let base_dir = must_get_home();
#[cfg(any(target_os = "android", target_os = "ios"))]
let base_dir = PathBuf::from("/tmp");
base_dir.join(".nym").join("socks5-clients")
}
}
impl From<OldConfigV1_1_13> for ConfigV1_1_20 {
fn from(value: OldConfigV1_1_13) -> Self {
ConfigV1_1_20 {
base: value.base.into(),
socks5: value.socks5,
}
}
}
@@ -1,137 +0,0 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::old_config_v1_1_20_2::{
ConfigV1_1_20_2, CoreConfigV1_1_20_2, SocksClientPathsV1_1_20_2,
};
use nym_bin_common::logging::LoggingSettings;
use nym_client_core::config::disk_persistence::keys_paths::ClientKeysPaths;
use nym_client_core::config::disk_persistence::old_v1_1_20_2::CommonClientPathsV1_1_20_2;
use nym_client_core::config::old_config_v1_1_20::ConfigV1_1_20 as BaseConfigV1_1_20;
use nym_client_core::config::old_config_v1_1_20_2::ClientV1_1_20_2;
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
use nym_config::must_get_home;
use nym_socks5_client_core::config::old_config_v1_1_20_2::{
BaseClientConfigV1_1_20_2, Socks5DebugV1_1_20_2, Socks5V1_1_20_2,
};
use nym_socks5_client_core::config::{ProviderInterfaceVersion, Socks5ProtocolVersion};
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use std::path::PathBuf;
const DEFAULT_CONNECTION_START_SURBS: u32 = 20;
const DEFAULT_PER_REQUEST_SURBS: u32 = 3;
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_20 {
#[serde(flatten)]
pub base: BaseConfigV1_1_20<ConfigV1_1_20>,
pub socks5: Socks5V1_1_20,
}
impl From<ConfigV1_1_20> for ConfigV1_1_20_2 {
fn from(value: ConfigV1_1_20) -> Self {
ConfigV1_1_20_2 {
core: CoreConfigV1_1_20_2 {
base: BaseClientConfigV1_1_20_2 {
client: ClientV1_1_20_2 {
version: value.base.client.version,
id: value.base.client.id,
disabled_credentials_mode: value.base.client.disabled_credentials_mode,
nyxd_urls: value.base.client.nyxd_urls,
nym_api_urls: value.base.client.nym_api_urls,
gateway_endpoint: value.base.client.gateway_endpoint.into(),
},
debug: value.base.debug.into(),
},
socks5: value.socks5.into(),
},
storage_paths: SocksClientPathsV1_1_20_2 {
common_paths: CommonClientPathsV1_1_20_2 {
keys: ClientKeysPaths {
private_identity_key_file: value.base.client.private_identity_key_file,
public_identity_key_file: value.base.client.public_identity_key_file,
private_encryption_key_file: value.base.client.private_encryption_key_file,
public_encryption_key_file: value.base.client.public_encryption_key_file,
gateway_shared_key_file: value.base.client.gateway_shared_key_file,
ack_key_file: value.base.client.ack_key_file,
},
credentials_database: value.base.client.database_path,
reply_surb_database: value.base.client.reply_surb_database_path,
},
},
logging: LoggingSettings::default(),
}
}
}
impl MigrationNymConfig for ConfigV1_1_20 {
fn default_root_directory() -> PathBuf {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
let base_dir = must_get_home();
#[cfg(any(target_os = "android", target_os = "ios"))]
let base_dir = PathBuf::from("/tmp");
base_dir.join(".nym").join("socks5-clients")
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Socks5V1_1_20 {
pub listening_port: u16,
pub provider_mix_address: String,
#[serde(default = "ProviderInterfaceVersion::new_legacy")]
pub provider_interface_version: ProviderInterfaceVersion,
#[serde(default = "Socks5ProtocolVersion::new_legacy")]
pub socks5_protocol_version: Socks5ProtocolVersion,
#[serde(default)]
pub send_anonymously: bool,
#[serde(default)]
pub socks5_debug: Socks5DebugV1_1_20,
}
impl From<Socks5V1_1_20> for Socks5V1_1_20_2 {
fn from(value: Socks5V1_1_20) -> Self {
Socks5V1_1_20_2 {
listening_port: value.listening_port,
provider_mix_address: value.provider_mix_address,
provider_interface_version: value.provider_interface_version,
socks5_protocol_version: value.socks5_protocol_version,
send_anonymously: value.send_anonymously,
socks5_debug: value.socks5_debug.into(),
}
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Socks5DebugV1_1_20 {
connection_start_surbs: u32,
per_request_surbs: u32,
}
impl From<Socks5DebugV1_1_20> for Socks5DebugV1_1_20_2 {
fn from(value: Socks5DebugV1_1_20) -> Self {
Socks5DebugV1_1_20_2 {
connection_start_surbs: value.connection_start_surbs,
per_request_surbs: value.per_request_surbs,
}
}
}
impl Default for Socks5DebugV1_1_20 {
fn default() -> Self {
Socks5DebugV1_1_20 {
connection_start_surbs: DEFAULT_CONNECTION_START_SURBS,
per_request_surbs: DEFAULT_PER_REQUEST_SURBS,
}
}
}
@@ -1,54 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::persistence::SocksClientPaths;
use crate::config::{default_config_filepath, Config};
use nym_bin_common::logging::LoggingSettings;
use nym_client_core::config::disk_persistence::old_v1_1_20_2::CommonClientPathsV1_1_20_2;
use nym_client_core::config::GatewayEndpointConfig;
use nym_config::read_config_from_toml_file;
pub use nym_socks5_client_core::config::old_config_v1_1_20_2::ConfigV1_1_20_2 as CoreConfigV1_1_20_2;
use serde::{Deserialize, Serialize};
use std::io;
use std::path::Path;
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone)]
pub struct SocksClientPathsV1_1_20_2 {
#[serde(flatten)]
pub common_paths: CommonClientPathsV1_1_20_2,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_20_2 {
pub core: CoreConfigV1_1_20_2,
pub storage_paths: SocksClientPathsV1_1_20_2,
pub logging: LoggingSettings,
}
impl ConfigV1_1_20_2 {
pub fn read_from_toml_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
read_config_from_toml_file(path)
}
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
Self::read_from_toml_file(default_config_filepath(id))
}
// in this upgrade, gateway endpoint configuration was moved out of the config file,
// so its returned to be stored elsewhere.
pub fn upgrade(self) -> (Config, GatewayEndpointConfig) {
let gateway_details = self.core.base.client.gateway_endpoint.clone().into();
let config = Config {
core: self.core.into(),
storage_paths: SocksClientPaths {
common_paths: self.storage_paths.common_paths.upgrade_default(),
},
logging: self.logging,
};
(config, gateway_details)
}
}
-20
View File
@@ -1,20 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_client_core::config::disk_persistence::CommonClientPaths;
use serde::{Deserialize, Serialize};
use std::path::Path;
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone)]
pub struct SocksClientPaths {
#[serde(flatten)]
pub common_paths: CommonClientPaths,
}
impl SocksClientPaths {
pub fn new_default<P: AsRef<Path>>(base_data_directory: P) -> Self {
SocksClientPaths {
common_paths: CommonClientPaths::new_default(base_data_directory),
}
}
}
-114
View File
@@ -1,114 +0,0 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
// While using normal toml marshalling would have been way simpler with less overhead,
// I think it's useful to have comments attached to the saved config file to explain behaviour of
// particular fields.
// Note: any changes to the template must be reflected in the appropriate structs.
pub(crate) const CONFIG_TEMPLATE: &str = r#"
# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml
##### main base client config options #####
[core.client]
# Version of the client for which this configuration was created.
version = '{{ core.client.version }}'
# Human readable ID of this particular client.
id = '{{ core.client.id }}'
# Indicates whether this client is running in a disabled credentials mode, thus attempting
# to claim bandwidth without presenting bandwidth credentials.
disabled_credentials_mode = {{ core.client.disabled_credentials_mode }}
# Addresses to nyxd validators via which the client can communicate with the chain.
nyxd_urls = [
{{#each core.client.nyxd_urls }}
'{{this}}',
{{/each}}
]
# Addresses to APIs running on validator from which the client gets the view of the network.
nym_api_urls = [
{{#each core.client.nym_api_urls }}
'{{this}}',
{{/each}}
]
[storage_paths]
# Path to file containing private identity key.
keys.private_identity_key_file = '{{ storage_paths.keys.private_identity_key_file }}'
# Path to file containing public identity key.
keys.public_identity_key_file = '{{ storage_paths.keys.public_identity_key_file }}'
# Path to file containing private encryption key.
keys.private_encryption_key_file = '{{ storage_paths.keys.private_encryption_key_file }}'
# Path to file containing public encryption key.
keys.public_encryption_key_file = '{{ storage_paths.keys.public_encryption_key_file }}'
# A gateway specific, optional, base58 stringified shared key used for
# communication with particular gateway.
keys.gateway_shared_key_file = '{{ storage_paths.keys.gateway_shared_key_file }}'
# Path to file containing key used for encrypting and decrypting the content of an
# acknowledgement so that nobody besides the client knows which packet it refers to.
keys.ack_key_file = '{{ storage_paths.keys.ack_key_file }}'
# Path to the database containing bandwidth credentials
credentials_database = '{{ storage_paths.credentials_database }}'
# Path to the persistent store for received reply surbs, unused encryption keys and used sender tags.
reply_surb_database = '{{ storage_paths.reply_surb_database }}'
# 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.
gateway_details = '{{ storage_paths.gateway_details }}'
##### socket config options #####
[core.socks5]
# The mix address of the provider to which all requests are going to be sent.
provider_mix_address = '{{ core.socks5.provider_mix_address }}'
# The port on which the client will be listening for incoming requests
listening_port = {{ core.socks5.listening_port }}
# Specifies whether this client is going to use an anonymous sender tag for communication with the service provider.
# While this is going to hide its actual address information, it will make the actual communication
# slower and consume nearly double the bandwidth as it will require sending reply SURBs.
#
# Note that some service providers might not support this.
send_anonymously = {{ core.socks5.send_anonymously }}
##### logging configuration options #####
[logging]
# TODO
##### debug configuration options #####
# The following options should not be modified unless you know EXACTLY what you are doing
# as if set incorrectly, they may impact your anonymity.
# [core.socks5.socks5_debug]
[core.debug]
[core.debug.traffic]
average_packet_delay = '{{ core.debug.traffic.average_packet_delay }}'
message_sending_average_delay = '{{ core.debug.traffic.message_sending_average_delay }}'
[core.debug.acknowledgements]
average_ack_delay = '{{ core.debug.acknowledgements.average_ack_delay }}'
[core.debug.cover_traffic]
loop_cover_traffic_average_delay = '{{ core.debug.cover_traffic.loop_cover_traffic_average_delay }}'
"#;
-1
View File
@@ -8,7 +8,6 @@ use nym_bin_common::logging::{maybe_print_banner, setup_logging};
use nym_network_defaults::setup_env; use nym_network_defaults::setup_env;
mod commands; mod commands;
mod config;
pub mod error; pub mod error;
#[tokio::main] #[tokio::main]
+25 -121
View File
@@ -62,7 +62,7 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [ dependencies = [
"getrandom 0.2.10", "getrandom 0.2.9",
"once_cell", "once_cell",
"version_check", "version_check",
] ]
@@ -225,9 +225,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.21.2" version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
[[package]] [[package]]
name = "base64ct" name = "base64ct"
@@ -622,7 +622,7 @@ dependencies = [
"cosmos-sdk-proto", "cosmos-sdk-proto",
"ecdsa", "ecdsa",
"eyre", "eyre",
"getrandom 0.2.10", "getrandom 0.2.9",
"k256", "k256",
"prost", "prost",
"prost-types", "prost-types",
@@ -995,16 +995,7 @@ version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
dependencies = [ dependencies = [
"dirs-sys 0.3.7", "dirs-sys",
]
[[package]]
name = "dirs"
version = "5.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
dependencies = [
"dirs-sys 0.4.1",
] ]
[[package]] [[package]]
@@ -1018,18 +1009,6 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "dirs-sys"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
dependencies = [
"libc",
"option-ext",
"redox_users",
"windows-sys 0.48.0",
]
[[package]] [[package]]
name = "dotenv" name = "dotenv"
version = "0.15.0" version = "0.15.0"
@@ -1442,9 +1421,9 @@ dependencies = [
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.10" version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
"js-sys", "js-sys",
@@ -2240,7 +2219,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-bin-common" name = "nym-bin-common"
version = "0.6.0" version = "0.5.0"
dependencies = [ dependencies = [
"atty", "atty",
"clap", "clap",
@@ -2249,18 +2228,16 @@ dependencies = [
"log", "log",
"pretty_env_logger", "pretty_env_logger",
"semver 0.11.0", "semver 0.11.0",
"serde",
"vergen", "vergen",
] ]
[[package]] [[package]]
name = "nym-client-core" name = "nym-client-core"
version = "1.1.15" version = "1.1.14"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"base64 0.21.2",
"dashmap", "dashmap",
"dirs 4.0.0", "dirs",
"futures", "futures",
"gloo-timers", "gloo-timers",
"humantime-serde", "humantime-serde",
@@ -2281,7 +2258,6 @@ dependencies = [
"rand 0.7.3", "rand 0.7.3",
"serde", "serde",
"serde_json", "serde_json",
"sha2 0.10.6",
"sqlx 0.6.3", "sqlx 0.6.3",
"tap", "tap",
"thiserror", "thiserror",
@@ -2300,7 +2276,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-client-wasm" name = "nym-client-wasm"
version = "1.1.1" version = "1.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@@ -2345,7 +2321,7 @@ dependencies = [
"bs58", "bs58",
"digest 0.9.0", "digest 0.9.0",
"ff 0.11.1", "ff 0.11.1",
"getrandom 0.2.10", "getrandom 0.2.9",
"group 0.11.0", "group 0.11.0",
"itertools", "itertools",
"nym-dkg", "nym-dkg",
@@ -2394,12 +2370,12 @@ dependencies = [
name = "nym-config" name = "nym-config"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"dirs 5.0.1", "cfg-if 1.0.0",
"handlebars", "handlebars",
"log", "log",
"nym-network-defaults", "nym-network-defaults",
"serde", "serde",
"toml 0.7.4", "toml",
"url", "url",
] ]
@@ -2489,7 +2465,7 @@ name = "nym-gateway-client"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"futures", "futures",
"getrandom 0.2.10", "getrandom 0.2.9",
"log", "log",
"nym-bandwidth-controller", "nym-bandwidth-controller",
"nym-coconut-interface", "nym-coconut-interface",
@@ -2636,7 +2612,7 @@ dependencies = [
"chacha20", "chacha20",
"chacha20poly1305", "chacha20poly1305",
"curve25519-dalek", "curve25519-dalek",
"getrandom 0.2.10", "getrandom 0.2.9",
"log", "log",
"rand 0.7.3", "rand 0.7.3",
"rayon", "rayon",
@@ -2697,7 +2673,6 @@ dependencies = [
"nym-pemstore", "nym-pemstore",
"nym-sphinx-addressing", "nym-sphinx-addressing",
"nym-sphinx-params", "nym-sphinx-params",
"nym-sphinx-routing",
"nym-sphinx-types", "nym-sphinx-types",
"nym-topology", "nym-topology",
"rand 0.7.3", "rand 0.7.3",
@@ -2724,7 +2699,6 @@ dependencies = [
"nym-crypto", "nym-crypto",
"nym-sphinx-addressing", "nym-sphinx-addressing",
"nym-sphinx-params", "nym-sphinx-params",
"nym-sphinx-routing",
"nym-sphinx-types", "nym-sphinx-types",
"nym-topology", "nym-topology",
"rand 0.7.3", "rand 0.7.3",
@@ -2755,7 +2729,6 @@ dependencies = [
"nym-sphinx-chunking", "nym-sphinx-chunking",
"nym-sphinx-forwarding", "nym-sphinx-forwarding",
"nym-sphinx-params", "nym-sphinx-params",
"nym-sphinx-routing",
"nym-sphinx-types", "nym-sphinx-types",
"nym-topology", "nym-topology",
"rand 0.7.3", "rand 0.7.3",
@@ -2819,7 +2792,7 @@ dependencies = [
"aes-gcm", "aes-gcm",
"argon2", "argon2",
"generic-array 0.14.7", "generic-array 0.14.7",
"getrandom 0.2.10", "getrandom 0.2.9",
"rand 0.8.5", "rand 0.8.5",
"serde", "serde",
"serde_json", "serde_json",
@@ -2887,7 +2860,6 @@ dependencies = [
"nym-service-provider-directory-common", "nym-service-provider-directory-common",
"nym-vesting-contract", "nym-vesting-contract",
"nym-vesting-contract-common", "nym-vesting-contract-common",
"openssl",
"prost", "prost",
"reqwest", "reqwest",
"serde", "serde",
@@ -2978,15 +2950,6 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-src"
version = "111.26.0+1.1.1u"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efc62c9f12b22b8f5208c23a7200a442b2e5999f8bdf80233852122b5a4f6f37"
dependencies = [
"cc",
]
[[package]] [[package]]
name = "openssl-sys" name = "openssl-sys"
version = "0.9.87" version = "0.9.87"
@@ -2995,17 +2958,10 @@ checksum = "8e17f59264b2809d77ae94f0e1ebabc434773f370d6ca667bd223ea10e06cc7e"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",
"openssl-src",
"pkg-config", "pkg-config",
"vcpkg", "vcpkg",
] ]
[[package]]
name = "option-ext"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]] [[package]]
name = "pairing" name = "pairing"
version = "0.20.0" version = "0.20.0"
@@ -3416,7 +3372,7 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [ dependencies = [
"getrandom 0.2.10", "getrandom 0.2.9",
] ]
[[package]] [[package]]
@@ -3484,7 +3440,7 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
dependencies = [ dependencies = [
"getrandom 0.2.10", "getrandom 0.2.9",
"redox_syscall 0.2.16", "redox_syscall 0.2.16",
"thiserror", "thiserror",
] ]
@@ -3512,7 +3468,7 @@ version = "0.11.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55"
dependencies = [ dependencies = [
"base64 0.21.2", "base64 0.21.0",
"bytes", "bytes",
"encoding_rs", "encoding_rs",
"futures-core", "futures-core",
@@ -3646,7 +3602,7 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b"
dependencies = [ dependencies = [
"base64 0.21.2", "base64 0.21.0",
] ]
[[package]] [[package]]
@@ -3878,15 +3834,6 @@ dependencies = [
"syn 2.0.16", "syn 2.0.16",
] ]
[[package]]
name = "serde_spanned"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93107647184f6027e3b7dcb2e11034cf95ffa1e3a682c67951963ac69c1c007d"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "serde_urlencoded" name = "serde_urlencoded"
version = "0.7.1" version = "0.7.1"
@@ -4370,7 +4317,7 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"tendermint", "tendermint",
"toml 0.5.11", "toml",
"url", "url",
] ]
@@ -4402,7 +4349,7 @@ dependencies = [
"bytes", "bytes",
"flex-error", "flex-error",
"futures", "futures",
"getrandom 0.2.10", "getrandom 0.2.9",
"http", "http",
"hyper", "hyper",
"hyper-proxy", "hyper-proxy",
@@ -4607,40 +4554,6 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "toml"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6135d499e69981f9ff0ef2167955a5333c35e36f6937d382974566b3d5b94ec"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]]
name = "toml_datetime"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.19.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"winnow",
]
[[package]] [[package]]
name = "tower-service" name = "tower-service"
version = "0.3.2" version = "0.3.2"
@@ -4802,7 +4715,7 @@ version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2" checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2"
dependencies = [ dependencies = [
"getrandom 0.2.10", "getrandom 0.2.9",
"wasm-bindgen", "wasm-bindgen",
] ]
@@ -4978,7 +4891,7 @@ name = "wasm-utils"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"futures", "futures",
"getrandom 0.2.10", "getrandom 0.2.9",
"indexed_db_futures", "indexed_db_futures",
"js-sys", "js-sys",
"nym-store-cipher", "nym-store-cipher",
@@ -5229,15 +5142,6 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
[[package]]
name = "winnow"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "winreg" name = "winreg"
version = "0.10.1" version = "0.10.1"
+1 -1
View File
@@ -1,7 +1,7 @@
[package] [package]
name = "nym-client-wasm" name = "nym-client-wasm"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jedrzej Stuczynski <andrew@nymtech.net>"] authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jedrzej Stuczynski <andrew@nymtech.net>"]
version = "1.1.1" version = "1.1.0"
edition = "2021" edition = "2021"
keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"] keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"]
license = "Apache-2.0" license = "Apache-2.0"
+10 -47
View File
@@ -107,20 +107,20 @@ function printAndDisplayTestResult(result) {
} }
async function testWithTester() { async function testWithTester() {
const preferredGateway = "336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9"; const dummyGateway = "336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9";
// A) construct with hardcoded topology // A) construct with hardcoded topology
const topology = dummyTopology() const topology = dummyTopology()
const nodeTester = await new NymNodeTester(topology, preferredGateway); const nodeTester = await new NymNodeTester(topology, dummyGateway);
// B) first get topology directly from nym-api // B) first get topology directly from nym-api
// const validator = 'https://qwerty-validator-api.qa.nymte.ch/api'; // const validator = 'https://qwerty-validator-api.qa.nymte.ch/api';
// const topology = await current_network_topology(validator) // const topology = await current_network_topology(validator)
// const nodeTester = await new NymNodeTester(topology, undefined, preferredGateway); // const nodeTester = await new NymNodeTester(topology, dummyGateway);
// //
// C) use nym-api in the constructor (note: it does no filtering for 'good' nodes on other layers) // C) use nym-api in the constructor (note: it does no filtering for 'good' nodes on other layers)
// const validator = 'https://qwerty-validator-api.qa.nymte.ch/api'; // const validator = 'https://qwerty-validator-api.qa.nymte.ch/api';
// const nodeTester = await NymNodeTester.new_with_api(validator, undefined, preferredGateway) // const nodeTester = await NymNodeTester.new_with_api(validator, dummyGateway)
// D, E, F) you also don't have to specify the gateway. if you don't, a random one (from your topology) will be used // D, E, F) you also don't have to specify the gateway. if you don't, a random one (from your topology) will be used
// const topology = dummyTopology() // const topology = dummyTopology()
@@ -139,44 +139,10 @@ async function testWithTester() {
} }
} }
}; };
}
async function testerReconnection() {
const validator = 'https://qwerty-validator-api.qa.nymte.ch/api';
const nodeTester = await NymNodeTester.new_with_api(validator);
self.onmessage = async event => {
if (event.data && event.data.kind) {
switch (event.data.kind) {
case 'TestPacket': {
const {mixnodeIdentity} = event.data.args;
console.log("starting node test...");
let result1 = await nodeTester.test_node(mixnodeIdentity);
console.log("sleeping for 5s");
await new Promise(r => setTimeout(r, 5000));
await nodeTester.disconnect_from_gateway();
console.log("sleeping for 5s");
await new Promise(r => setTimeout(r, 5000));
await nodeTester.reconnect_to_gateway();
let result2 = await nodeTester.test_node(mixnodeIdentity);
printAndDisplayTestResult(result1)
printAndDisplayTestResult(result2)
}
}
}
};
} }
async function testWithNymClient() { async function testWithNymClient() {
const preferredGateway = "336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9"; const dummyGateway = "336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9";
const topology = dummyTopology() const topology = dummyTopology()
let received = 0 let received = 0
@@ -198,7 +164,7 @@ async function testWithNymClient() {
console.log('Instantiating WASM client...'); console.log('Instantiating WASM client...');
let clientBuilder = NymClientBuilder.new_tester(topology, onMessageHandler, preferredGateway) let clientBuilder = NymClientBuilder.new_tester(topology, onMessageHandler, dummyGateway)
console.log('Web worker creating WASM client...'); console.log('Web worker creating WASM client...');
let local_client = await clientBuilder.start_client(); let local_client = await clientBuilder.start_client();
console.log('WASM client running!'); console.log('WASM client running!');
@@ -256,10 +222,10 @@ async function normalNymClientUsage() {
debug.topology_refresh_rate_ms = BigInt(60000) debug.topology_refresh_rate_ms = BigInt(60000)
const preferredGateway = "336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9"; const dummyGateway = "336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9";
const validator = 'https://qwerty-validator-api.qa.nymte.ch/api'; const validator = 'https://qwerty-validator-api.qa.nymte.ch/api';
const config = new Config('my-awesome-wasm-client', validator, debug); const config = new Config('my-awesome-wasm-client', validator, dummyGateway, debug);
const onMessageHandler = (message) => { const onMessageHandler = (message) => {
console.log(message); console.log(message);
@@ -364,17 +330,14 @@ async function main() {
// sets up better stack traces in case of in-rust panics // sets up better stack traces in case of in-rust panics
set_panic_hook(); set_panic_hook();
// show reconnection capabilities
// await testerReconnection()
// run test on simplified and dedicated tester: // run test on simplified and dedicated tester:
await testWithTester() // await testWithTester()
// hook-up the whole client for testing // hook-up the whole client for testing
// await testWithNymClient() // await testWithNymClient()
// 'Normal' client setup (to send 'normal' messages) // 'Normal' client setup (to send 'normal' messages)
// await normalNymClientUsage() await normalNymClientUsage()
} }
// Let's get started! // Let's get started!
+86 -72
View File
@@ -7,49 +7,73 @@
#![allow(clippy::drop_copy)] #![allow(clippy::drop_copy)]
use nym_client_core::config::{ use nym_client_core::config::{
Acknowledgements as ConfigAcknowledgements, Config as BaseClientConfig, Acknowledgements as ConfigAcknowledgements, CoverTraffic as ConfigCoverTraffic,
CoverTraffic as ConfigCoverTraffic, DebugConfig as ConfigDebug, DebugConfig as ConfigDebug, GatewayConnection as ConfigGatewayConnection,
GatewayConnection as ConfigGatewayConnection, ReplySurbs as ConfigReplySurbs, ReplySurbs as ConfigReplySurbs, Topology as ConfigTopology, Traffic as ConfigTraffic,
Topology as ConfigTopology, Traffic as ConfigTraffic,
}; };
use nym_sphinx::params::{PacketSize, PacketType}; use nym_sphinx::params::{PacketSize, PacketType};
use nym_validator_client::client::IdentityKey;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::time::Duration; use std::time::Duration;
use url::Url;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
#[wasm_bindgen] #[wasm_bindgen]
#[derive(Debug, Deserialize, PartialEq, Serialize)] #[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct Config { pub struct Config {
pub(crate) base: BaseClientConfig, /// ID specifies the human readable ID of this particular client.
pub(crate) id: String,
pub(crate) nym_api_url: Option<Url>,
pub(crate) disabled_credentials_mode: bool,
/// Information regarding how the client should choose gateway.
/// If unspecified, the client will attempt to load the config from the storage.
pub(crate) gateway: Option<IdentityKey>,
pub(crate) debug: ConfigDebug,
pub(crate) packet_type: PacketType,
} }
#[wasm_bindgen] #[wasm_bindgen]
impl Config { impl Config {
#[wasm_bindgen(constructor)] #[wasm_bindgen(constructor)]
pub fn new(id: String, validator_server: String, debug: Option<DebugWasm>) -> Self { pub fn new(
id: String,
validator_server: String,
packet_type: Option<String>,
gateway: Option<IdentityKey>,
debug: Option<Debug>,
) -> Self {
let packet_type = if let Some(packet_type) = packet_type {
match packet_type.as_str() {
"outfox" => PacketType::Outfox,
_ => PacketType::Mix,
}
} else {
PacketType::Mix
};
Config { Config {
base: BaseClientConfig::new(id, env!("CARGO_PKG_VERSION").to_string()) id,
.with_custom_nyxd(vec![validator_server nym_api_url: Some(
validator_server
.parse() .parse()
.expect("provided url was malformed")]) .expect("provided url was malformed"),
.with_debug_config(debug.map(Into::into).unwrap_or_default()), ),
} disabled_credentials_mode: true,
} gateway,
debug: debug.map(Into::into).unwrap_or_default(),
pub(crate) fn new_tester_config<S: Into<String>>(id: S) -> Self { packet_type,
Config {
base: BaseClientConfig::new(id.into(), env!("CARGO_PKG_VERSION").to_string())
.with_disabled_credentials(true)
.with_disabled_cover_traffic(true)
.with_disabled_topology_refresh(true),
} }
} }
} }
#[wasm_bindgen] #[wasm_bindgen]
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct TrafficWasm { pub struct Traffic {
/// The parameter of Poisson distribution determining how long, on average, /// The parameter of Poisson distribution determining how long, on average,
/// sent packet is going to be delayed at any given mix node. /// 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 /// So for a packet going through three mix nodes, on average, it will take three times this value
@@ -68,23 +92,14 @@ pub struct TrafficWasm {
/// Controls whether the sent sphinx packet use the NON-DEFAULT bigger size. /// Controls whether the sent sphinx packet use the NON-DEFAULT bigger size.
pub use_extended_packet_size: bool, pub use_extended_packet_size: bool,
/// Controls whether the sent packets should use outfox as opposed to the default sphinx.
pub use_outfox: bool,
} }
impl From<TrafficWasm> for ConfigTraffic { impl From<Traffic> for ConfigTraffic {
fn from(traffic: TrafficWasm) -> Self { fn from(traffic: Traffic) -> Self {
let use_extended_packet_size = traffic let use_extended_packet_size = traffic
.use_extended_packet_size .use_extended_packet_size
.then(|| PacketSize::ExtendedPacket32); .then(|| PacketSize::ExtendedPacket32);
let packet_type = if traffic.use_outfox {
PacketType::Outfox
} else {
PacketType::Mix
};
ConfigTraffic { ConfigTraffic {
average_packet_delay: Duration::from_millis(traffic.average_packet_delay_ms), average_packet_delay: Duration::from_millis(traffic.average_packet_delay_ms),
message_sending_average_delay: Duration::from_millis( message_sending_average_delay: Duration::from_millis(
@@ -94,28 +109,27 @@ impl From<TrafficWasm> for ConfigTraffic {
.disable_main_poisson_packet_distribution, .disable_main_poisson_packet_distribution,
primary_packet_size: PacketSize::RegularPacket, primary_packet_size: PacketSize::RegularPacket,
secondary_packet_size: use_extended_packet_size, secondary_packet_size: use_extended_packet_size,
packet_type, packet_type: None,
} }
} }
} }
impl From<ConfigTraffic> for TrafficWasm { impl From<ConfigTraffic> for Traffic {
fn from(traffic: ConfigTraffic) -> Self { fn from(traffic: ConfigTraffic) -> Self {
TrafficWasm { Traffic {
average_packet_delay_ms: traffic.average_packet_delay.as_millis() as u64, average_packet_delay_ms: traffic.average_packet_delay.as_millis() as u64,
message_sending_average_delay_ms: traffic.message_sending_average_delay.as_millis() message_sending_average_delay_ms: traffic.message_sending_average_delay.as_millis()
as u64, as u64,
disable_main_poisson_packet_distribution: traffic disable_main_poisson_packet_distribution: traffic
.disable_main_poisson_packet_distribution, .disable_main_poisson_packet_distribution,
use_extended_packet_size: traffic.secondary_packet_size.is_some(), use_extended_packet_size: traffic.secondary_packet_size.is_some(),
use_outfox: traffic.packet_type == PacketType::Outfox,
} }
} }
} }
#[wasm_bindgen] #[wasm_bindgen]
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct CoverTrafficWasm { pub struct CoverTraffic {
/// The parameter of Poisson distribution determining how long, on average, /// The parameter of Poisson distribution determining how long, on average,
/// it is going to take for another loop cover traffic message to be sent. /// it is going to take for another loop cover traffic message to be sent.
pub loop_cover_traffic_average_delay_ms: u64, pub loop_cover_traffic_average_delay_ms: u64,
@@ -129,8 +143,8 @@ pub struct CoverTrafficWasm {
pub disable_loop_cover_traffic_stream: bool, pub disable_loop_cover_traffic_stream: bool,
} }
impl From<CoverTrafficWasm> for ConfigCoverTraffic { impl From<CoverTraffic> for ConfigCoverTraffic {
fn from(cover_traffic: CoverTrafficWasm) -> Self { fn from(cover_traffic: CoverTraffic) -> Self {
ConfigCoverTraffic { ConfigCoverTraffic {
loop_cover_traffic_average_delay: Duration::from_millis( loop_cover_traffic_average_delay: Duration::from_millis(
cover_traffic.loop_cover_traffic_average_delay_ms, cover_traffic.loop_cover_traffic_average_delay_ms,
@@ -141,9 +155,9 @@ impl From<CoverTrafficWasm> for ConfigCoverTraffic {
} }
} }
impl From<ConfigCoverTraffic> for CoverTrafficWasm { impl From<ConfigCoverTraffic> for CoverTraffic {
fn from(cover_traffic: ConfigCoverTraffic) -> Self { fn from(cover_traffic: ConfigCoverTraffic) -> Self {
CoverTrafficWasm { CoverTraffic {
loop_cover_traffic_average_delay_ms: cover_traffic loop_cover_traffic_average_delay_ms: cover_traffic
.loop_cover_traffic_average_delay .loop_cover_traffic_average_delay
.as_millis() as u64, .as_millis() as u64,
@@ -155,14 +169,14 @@ impl From<ConfigCoverTraffic> for CoverTrafficWasm {
#[wasm_bindgen] #[wasm_bindgen]
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct GatewayConnectionWasm { pub struct GatewayConnection {
/// How long we're willing to wait for a response to a message sent to the gateway, /// How long we're willing to wait for a response to a message sent to the gateway,
/// before giving up on it. /// before giving up on it.
pub gateway_response_timeout_ms: u64, pub gateway_response_timeout_ms: u64,
} }
impl From<GatewayConnectionWasm> for ConfigGatewayConnection { impl From<GatewayConnection> for ConfigGatewayConnection {
fn from(gateway_connection: GatewayConnectionWasm) -> Self { fn from(gateway_connection: GatewayConnection) -> Self {
ConfigGatewayConnection { ConfigGatewayConnection {
gateway_response_timeout: Duration::from_millis( gateway_response_timeout: Duration::from_millis(
gateway_connection.gateway_response_timeout_ms, gateway_connection.gateway_response_timeout_ms,
@@ -171,9 +185,9 @@ impl From<GatewayConnectionWasm> for ConfigGatewayConnection {
} }
} }
impl From<ConfigGatewayConnection> for GatewayConnectionWasm { impl From<ConfigGatewayConnection> for GatewayConnection {
fn from(gateway_connection: ConfigGatewayConnection) -> Self { fn from(gateway_connection: ConfigGatewayConnection) -> Self {
GatewayConnectionWasm { GatewayConnection {
gateway_response_timeout_ms: gateway_connection.gateway_response_timeout.as_millis() gateway_response_timeout_ms: gateway_connection.gateway_response_timeout.as_millis()
as u64, as u64,
} }
@@ -182,7 +196,7 @@ impl From<ConfigGatewayConnection> for GatewayConnectionWasm {
#[wasm_bindgen] #[wasm_bindgen]
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct AcknowledgementsWasm { pub struct Acknowledgements {
/// The parameter of Poisson distribution determining how long, on average, /// The parameter of Poisson distribution determining how long, on average,
/// sent acknowledgement is going to be delayed at any given mix node. /// 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 /// So for an ack going through three mix nodes, on average, it will take three times this value
@@ -200,8 +214,8 @@ pub struct AcknowledgementsWasm {
pub ack_wait_addition_ms: u64, pub ack_wait_addition_ms: u64,
} }
impl From<AcknowledgementsWasm> for ConfigAcknowledgements { impl From<Acknowledgements> for ConfigAcknowledgements {
fn from(acknowledgements: AcknowledgementsWasm) -> Self { fn from(acknowledgements: Acknowledgements) -> Self {
ConfigAcknowledgements { ConfigAcknowledgements {
average_ack_delay: Duration::from_millis(acknowledgements.average_ack_delay_ms), average_ack_delay: Duration::from_millis(acknowledgements.average_ack_delay_ms),
ack_wait_multiplier: acknowledgements.ack_wait_multiplier, ack_wait_multiplier: acknowledgements.ack_wait_multiplier,
@@ -210,9 +224,9 @@ impl From<AcknowledgementsWasm> for ConfigAcknowledgements {
} }
} }
impl From<ConfigAcknowledgements> for AcknowledgementsWasm { impl From<ConfigAcknowledgements> for Acknowledgements {
fn from(acknowledgements: ConfigAcknowledgements) -> Self { fn from(acknowledgements: ConfigAcknowledgements) -> Self {
AcknowledgementsWasm { Acknowledgements {
average_ack_delay_ms: acknowledgements.average_ack_delay.as_millis() as u64, average_ack_delay_ms: acknowledgements.average_ack_delay.as_millis() as u64,
ack_wait_multiplier: acknowledgements.ack_wait_multiplier, ack_wait_multiplier: acknowledgements.ack_wait_multiplier,
ack_wait_addition_ms: acknowledgements.ack_wait_addition.as_millis() as u64, ack_wait_addition_ms: acknowledgements.ack_wait_addition.as_millis() as u64,
@@ -222,7 +236,7 @@ impl From<ConfigAcknowledgements> for AcknowledgementsWasm {
#[wasm_bindgen] #[wasm_bindgen]
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct TopologyWasm { pub struct Topology {
/// The uniform delay every which clients are querying the directory server /// The uniform delay every which clients are querying the directory server
/// to try to obtain a compatible network topology to send sphinx packets through. /// to try to obtain a compatible network topology to send sphinx packets through.
pub topology_refresh_rate_ms: u64, pub topology_refresh_rate_ms: u64,
@@ -238,8 +252,8 @@ pub struct TopologyWasm {
pub disable_refreshing: bool, pub disable_refreshing: bool,
} }
impl From<TopologyWasm> for ConfigTopology { impl From<Topology> for ConfigTopology {
fn from(topology: TopologyWasm) -> Self { fn from(topology: Topology) -> Self {
ConfigTopology { ConfigTopology {
topology_refresh_rate: Duration::from_millis(topology.topology_refresh_rate_ms), topology_refresh_rate: Duration::from_millis(topology.topology_refresh_rate_ms),
topology_resolution_timeout: Duration::from_millis( topology_resolution_timeout: Duration::from_millis(
@@ -250,9 +264,9 @@ impl From<TopologyWasm> for ConfigTopology {
} }
} }
impl From<ConfigTopology> for TopologyWasm { impl From<ConfigTopology> for Topology {
fn from(topology: ConfigTopology) -> Self { fn from(topology: ConfigTopology) -> Self {
TopologyWasm { Topology {
topology_refresh_rate_ms: topology.topology_refresh_rate.as_millis() as u64, topology_refresh_rate_ms: topology.topology_refresh_rate.as_millis() as u64,
topology_resolution_timeout_ms: topology.topology_resolution_timeout.as_millis() as u64, topology_resolution_timeout_ms: topology.topology_resolution_timeout.as_millis() as u64,
disable_refreshing: topology.disable_refreshing, disable_refreshing: topology.disable_refreshing,
@@ -262,7 +276,7 @@ impl From<ConfigTopology> for TopologyWasm {
#[wasm_bindgen] #[wasm_bindgen]
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct ReplySurbsWasm { pub struct ReplySurbs {
/// Defines the minimum number of reply surbs the client wants to keep in its storage at all times. /// 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. /// It can only allow to go below that value if its to request additional reply surbs.
pub minimum_reply_surb_storage_threshold: usize, pub minimum_reply_surb_storage_threshold: usize,
@@ -296,8 +310,8 @@ pub struct ReplySurbsWasm {
pub maximum_reply_key_age_ms: u64, pub maximum_reply_key_age_ms: u64,
} }
impl From<ReplySurbsWasm> for ConfigReplySurbs { impl From<ReplySurbs> for ConfigReplySurbs {
fn from(reply_surbs: ReplySurbsWasm) -> Self { fn from(reply_surbs: ReplySurbs) -> Self {
ConfigReplySurbs { ConfigReplySurbs {
minimum_reply_surb_storage_threshold: reply_surbs.minimum_reply_surb_storage_threshold, minimum_reply_surb_storage_threshold: reply_surbs.minimum_reply_surb_storage_threshold,
maximum_reply_surb_storage_threshold: reply_surbs.maximum_reply_surb_storage_threshold, maximum_reply_surb_storage_threshold: reply_surbs.maximum_reply_surb_storage_threshold,
@@ -317,9 +331,9 @@ impl From<ReplySurbsWasm> for ConfigReplySurbs {
} }
} }
impl From<ConfigReplySurbs> for ReplySurbsWasm { impl From<ConfigReplySurbs> for ReplySurbs {
fn from(reply_surbs: ConfigReplySurbs) -> Self { fn from(reply_surbs: ConfigReplySurbs) -> Self {
ReplySurbsWasm { ReplySurbs {
minimum_reply_surb_storage_threshold: reply_surbs.minimum_reply_surb_storage_threshold, minimum_reply_surb_storage_threshold: reply_surbs.minimum_reply_surb_storage_threshold,
maximum_reply_surb_storage_threshold: reply_surbs.maximum_reply_surb_storage_threshold, maximum_reply_surb_storage_threshold: reply_surbs.maximum_reply_surb_storage_threshold,
minimum_reply_surb_request_size: reply_surbs.minimum_reply_surb_request_size, minimum_reply_surb_request_size: reply_surbs.minimum_reply_surb_request_size,
@@ -341,28 +355,28 @@ impl From<ConfigReplySurbs> for ReplySurbsWasm {
// just a helper structure to more easily pass through the JS boundary // just a helper structure to more easily pass through the JS boundary
#[wasm_bindgen] #[wasm_bindgen]
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct DebugWasm { pub struct Debug {
/// Defines all configuration options related to traffic streams. /// Defines all configuration options related to traffic streams.
pub traffic: TrafficWasm, pub traffic: Traffic,
/// Defines all configuration options related to cover traffic stream(s). /// Defines all configuration options related to cover traffic stream(s).
pub cover_traffic: CoverTrafficWasm, pub cover_traffic: CoverTraffic,
/// Defines all configuration options related to the gateway connection. /// Defines all configuration options related to the gateway connection.
pub gateway_connection: GatewayConnectionWasm, pub gateway_connection: GatewayConnection,
/// Defines all configuration options related to acknowledgements, such as delays or wait timeouts. /// Defines all configuration options related to acknowledgements, such as delays or wait timeouts.
pub acknowledgements: AcknowledgementsWasm, pub acknowledgements: Acknowledgements,
/// Defines all configuration options related topology, such as refresh rates or timeouts. /// Defines all configuration options related topology, such as refresh rates or timeouts.
pub topology: TopologyWasm, pub topology: Topology,
/// Defines all configuration options related to reply SURBs. /// Defines all configuration options related to reply SURBs.
pub reply_surbs: ReplySurbsWasm, pub reply_surbs: ReplySurbs,
} }
impl From<DebugWasm> for ConfigDebug { impl From<Debug> for ConfigDebug {
fn from(debug: DebugWasm) -> Self { fn from(debug: Debug) -> Self {
ConfigDebug { ConfigDebug {
traffic: debug.traffic.into(), traffic: debug.traffic.into(),
cover_traffic: debug.cover_traffic.into(), cover_traffic: debug.cover_traffic.into(),
@@ -374,9 +388,9 @@ impl From<DebugWasm> for ConfigDebug {
} }
} }
impl From<ConfigDebug> for DebugWasm { impl From<ConfigDebug> for Debug {
fn from(debug: ConfigDebug) -> Self { fn from(debug: ConfigDebug) -> Self {
DebugWasm { Debug {
traffic: debug.traffic.into(), traffic: debug.traffic.into(),
cover_traffic: debug.cover_traffic.into(), cover_traffic: debug.cover_traffic.into(),
gateway_connection: debug.gateway_connection.into(), gateway_connection: debug.gateway_connection.into(),
@@ -388,6 +402,6 @@ impl From<ConfigDebug> for DebugWasm {
} }
#[wasm_bindgen] #[wasm_bindgen]
pub fn default_debug() -> DebugWasm { pub fn default_debug() -> Debug {
ConfigDebug::default().into() ConfigDebug::default().into()
} }
+91 -50
View File
@@ -7,7 +7,7 @@ use crate::client::response_pusher::ResponsePusher;
use crate::constants::NODE_TESTER_CLIENT_ID; use crate::constants::NODE_TESTER_CLIENT_ID;
use crate::error::WasmClientError; use crate::error::WasmClientError;
use crate::helpers::{ use crate::helpers::{
parse_recipient, parse_sender_tag, setup_from_topology, setup_gateway_from_api, choose_gateway, gateway_from_topology, parse_recipient, parse_sender_tag,
setup_reply_surb_storage_backend, setup_reply_surb_storage_backend,
}; };
use crate::storage::traits::FullWasmClientStorage; use crate::storage::traits::FullWasmClientStorage;
@@ -15,11 +15,14 @@ use crate::storage::ClientStorage;
use crate::topology::WasmNymTopology; use crate::topology::WasmNymTopology;
use js_sys::Promise; use js_sys::Promise;
use nym_bandwidth_controller::wasm_mockups::{Client as FakeClient, DirectSigningNyxdClient}; use nym_bandwidth_controller::wasm_mockups::{Client as FakeClient, DirectSigningNyxdClient};
use nym_bandwidth_controller::BandwidthController;
use nym_client_core::client::base_client::{ use nym_client_core::client::base_client::{
BaseClientBuilder, ClientInput, ClientOutput, ClientState, BaseClientBuilder, ClientInput, ClientOutput, ClientState, CredentialsToggle,
}; };
use nym_client_core::client::inbound_messages::InputMessage; use nym_client_core::client::inbound_messages::InputMessage;
use nym_credential_storage::ephemeral_storage::EphemeralStorage as EphemeralCredentialStorage; use nym_client_core::client::replies::reply_storage::browser_backend;
use nym_client_core::config::{CoverTraffic, DebugConfig, Topology, Traffic};
use nym_credential_storage::ephemeral_storage::EphemeralStorage;
use nym_sphinx::params::PacketType; use nym_sphinx::params::PacketType;
use nym_task::connections::TransmissionLane; use nym_task::connections::TransmissionLane;
use nym_task::TaskManager; use nym_task::TaskManager;
@@ -27,7 +30,7 @@ use nym_topology::provider_trait::{HardcodedTopologyProvider, TopologyProvider};
use nym_topology::NymTopology; use nym_topology::NymTopology;
use nym_validator_client::client::IdentityKey; use nym_validator_client::client::IdentityKey;
use rand::rngs::OsRng; use rand::rngs::OsRng;
use rand::RngCore; use rand::{thread_rng, RngCore};
use std::sync::Arc; use std::sync::Arc;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::future_to_promise; use wasm_bindgen_futures::future_to_promise;
@@ -50,18 +53,24 @@ pub struct NymClient {
// even though we don't use graceful shutdowns, other components rely on existence of this struct // even though we don't use graceful shutdowns, other components rely on existence of this struct
// and if it's dropped, everything will start going offline // and if it's dropped, everything will start going offline
_task_manager: TaskManager, _task_manager: TaskManager,
packet_type: Option<PacketType>,
packet_type: PacketType,
} }
#[wasm_bindgen] #[wasm_bindgen]
pub struct NymClientBuilder { pub struct NymClientBuilder {
config: Config, config: Config,
custom_topology: Option<NymTopology>, custom_topology: Option<NymTopology>,
preferred_gateway: Option<IdentityKey>,
storage_passphrase: Option<String>, storage_passphrase: Option<String>,
reply_surb_storage_backend: browser_backend::Backend,
on_message: js_sys::Function, on_message: js_sys::Function,
// unimplemented:
bandwidth_controller:
Option<BandwidthController<FakeClient<DirectSigningNyxdClient>, EphemeralStorage>>,
disabled_credentials: bool,
packet_type: Option<PacketType>,
} }
#[wasm_bindgen] #[wasm_bindgen]
@@ -70,15 +79,17 @@ impl NymClientBuilder {
pub fn new( pub fn new(
config: Config, config: Config,
on_message: js_sys::Function, on_message: js_sys::Function,
preferred_gateway: Option<IdentityKey>,
storage_passphrase: Option<String>, storage_passphrase: Option<String>,
) -> Self { ) -> Self {
NymClientBuilder { NymClientBuilder {
reply_surb_storage_backend: setup_reply_surb_storage_backend(config.debug.reply_surbs),
config, config,
custom_topology: None, custom_topology: None,
storage_passphrase, storage_passphrase,
on_message, on_message,
preferred_gateway, bandwidth_controller: None,
disabled_credentials: true,
packet_type: None,
} }
} }
@@ -97,14 +108,40 @@ impl NymClientBuilder {
} }
} }
let full_config = Config::new_tester_config(NODE_TESTER_CLIENT_ID); let full_config = Config {
id: NODE_TESTER_CLIENT_ID.to_string(),
nym_api_url: None,
disabled_credentials_mode: true,
gateway,
debug: DebugConfig {
traffic: Traffic {
disable_main_poisson_packet_distribution: true,
..Default::default()
},
cover_traffic: CoverTraffic {
disable_loop_cover_traffic_stream: true,
..Default::default()
},
topology: Topology {
disable_refreshing: true,
..Default::default()
},
..Default::default()
},
packet_type: PacketType::Mix,
};
NymClientBuilder { NymClientBuilder {
reply_surb_storage_backend: setup_reply_surb_storage_backend(
full_config.debug.reply_surbs,
),
config: full_config, config: full_config,
custom_topology: Some(topology.into()), custom_topology: Some(topology.into()),
on_message, on_message,
bandwidth_controller: None,
disabled_credentials: true,
storage_passphrase: None, storage_passphrase: None,
preferred_gateway: gateway, packet_type: None,
} }
} }
@@ -120,48 +157,59 @@ impl NymClientBuilder {
} }
} }
fn initialise_storage(config: &Config, base_storage: ClientStorage) -> FullWasmClientStorage {
FullWasmClientStorage {
keys_and_gateway_store: base_storage,
reply_storage: setup_reply_surb_storage_backend(config.base.debug.reply_surbs),
credential_storage: EphemeralCredentialStorage::default(),
}
}
async fn start_client_async(mut self) -> Result<NymClient, WasmClientError> { async fn start_client_async(mut self) -> Result<NymClient, WasmClientError> {
console_log!("Starting the wasm client"); console_log!("Starting the wasm client");
let nym_api_endpoints = self.config.base.client.nym_api_urls.clone(); let disabled_credentials = if self.disabled_credentials {
CredentialsToggle::Disabled
} else {
CredentialsToggle::Enabled
};
let nym_api_endpoints = match &self.config.nym_api_url {
Some(endpoint) => vec![endpoint.clone()],
None => Vec::new(),
};
// TODO: this will have to be re-used for surbs. but this is a problem for another PR. // TODO: this will have to be re-used for surbs. but this is a problem for another PR.
let client_store = let client_store =
ClientStorage::new_async(&self.config.base.client.id, self.storage_passphrase.take()) ClientStorage::new_async(&self.config.id, self.storage_passphrase.take()).await?;
.await?;
let user_chosen = self.preferred_gateway.clone();
// if we provided hardcoded topology, get gateway from it, otherwise get it the 'standard' way // if we provided hardcoded topology, get gateway from it, otherwise get it the 'standard' way
if let Some(topology) = &self.custom_topology { let gateway_endpoint = if let Some(topology) = &self.custom_topology {
setup_from_topology(user_chosen, topology, &client_store).await? gateway_from_topology(
&mut thread_rng(),
self.config.gateway.as_deref(),
topology,
&client_store,
)
.await?
} else { } else {
setup_gateway_from_api(&client_store, user_chosen, &nym_api_endpoints).await? choose_gateway(
&client_store,
self.config.gateway.clone(),
&nym_api_endpoints,
)
.await?
}; };
let packet_type = self.config.base.debug.traffic.packet_type;
let storage = Self::initialise_storage(&self.config, client_store);
let maybe_topology_provider = self.topology_provider(); let maybe_topology_provider = self.topology_provider();
let mut base_builder: BaseClientBuilder<_, FullWasmClientStorage> = let mut base_builder: BaseClientBuilder<_, FullWasmClientStorage> = BaseClientBuilder::new(
BaseClientBuilder::<FakeClient<DirectSigningNyxdClient>, _>::new( &gateway_endpoint,
&self.config.base, &self.config.debug,
storage, client_store,
None, self.bandwidth_controller,
); self.reply_surb_storage_backend,
disabled_credentials,
nym_api_endpoints,
);
if let Some(topology_provider) = maybe_topology_provider { if let Some(topology_provider) = maybe_topology_provider {
base_builder = base_builder.with_topology_provider(topology_provider); base_builder = base_builder.with_topology_provider(topology_provider);
} }
let mut started_client = base_builder.start_base().await?; let packet_type = self.config.packet_type;
let mut started_client = base_builder.start_base(packet_type).await?;
let self_address = started_client.address.to_string(); let self_address = started_client.address.to_string();
let client_input = started_client.client_input.register_producer(); let client_input = started_client.client_input.register_producer();
@@ -175,7 +223,7 @@ impl NymClientBuilder {
client_state: Arc::new(started_client.client_state), client_state: Arc::new(started_client.client_state),
_full_topology: None, _full_topology: None,
_task_manager: started_client.task_manager, _task_manager: started_client.task_manager,
packet_type, packet_type: self.packet_type,
}) })
} }
@@ -189,10 +237,9 @@ impl NymClient {
async fn _new( async fn _new(
config: Config, config: Config,
on_message: js_sys::Function, on_message: js_sys::Function,
preferred_gateway: Option<IdentityKey>,
storage_passphrase: Option<String>, storage_passphrase: Option<String>,
) -> Result<NymClient, WasmClientError> { ) -> Result<NymClient, WasmClientError> {
NymClientBuilder::new(config, on_message, preferred_gateway, storage_passphrase) NymClientBuilder::new(config, on_message, storage_passphrase)
.start_client_async() .start_client_async()
.await .await
} }
@@ -202,11 +249,10 @@ impl NymClient {
pub fn new( pub fn new(
config: Config, config: Config,
on_message: js_sys::Function, on_message: js_sys::Function,
preferred_gateway: Option<IdentityKey>,
storage_passphrase: Option<String>, storage_passphrase: Option<String>,
) -> Promise { ) -> Promise {
future_to_promise(async move { future_to_promise(async move {
Self::_new(config, on_message, preferred_gateway, storage_passphrase) Self::_new(config, on_message, storage_passphrase)
.await .await
.into_promise_result() .into_promise_result()
}) })
@@ -273,7 +319,7 @@ impl NymClient {
let lane = TransmissionLane::General; let lane = TransmissionLane::General;
let input_msg = InputMessage::new_regular(recipient, message, lane, Some(self.packet_type)); let input_msg = InputMessage::new_regular(recipient, message, lane, self.packet_type);
self.client_input.send_message(input_msg) self.client_input.send_message(input_msg)
} }
@@ -300,13 +346,8 @@ impl NymClient {
let lane = TransmissionLane::General; let lane = TransmissionLane::General;
let input_msg = InputMessage::new_anonymous( let input_msg =
recipient, InputMessage::new_anonymous(recipient, message, reply_surbs, lane, self.packet_type);
message,
reply_surbs,
lane,
Some(self.packet_type),
);
self.client_input.send_message(input_msg) self.client_input.send_message(input_msg)
} }
@@ -324,7 +365,7 @@ impl NymClient {
let lane = TransmissionLane::General; let lane = TransmissionLane::General;
let input_msg = InputMessage::new_reply(sender_tag, message, lane, Some(self.packet_type)); let input_msg = InputMessage::new_reply(sender_tag, message, lane, self.packet_type);
self.client_input.send_message(input_msg) self.client_input.send_message(input_msg)
} }
} }
+84 -33
View File
@@ -7,21 +7,22 @@ use crate::topology::WasmNymTopology;
use js_sys::Promise; use js_sys::Promise;
use nym_client_core::client::replies::reply_storage::browser_backend; use nym_client_core::client::replies::reply_storage::browser_backend;
use nym_client_core::config; use nym_client_core::config;
use nym_client_core::init::helpers::current_gateways; use nym_client_core::config::GatewayEndpointConfig;
use nym_client_core::init::{setup_gateway_from, GatewaySetup, InitialisationDetails}; use nym_client_core::init::GatewaySetup;
use nym_crypto::asymmetric::identity;
use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag; use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
use nym_topology::{gateway, NymTopology}; use nym_topology::NymTopology;
use nym_validator_client::client::IdentityKey; use nym_validator_client::client::{IdentityKey, IdentityKeyRef};
use nym_validator_client::NymApiClient; use nym_validator_client::NymApiClient;
use rand::thread_rng; use rand::{CryptoRng, Rng};
use url::Url; use url::Url;
use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen_futures::future_to_promise; use wasm_bindgen_futures::future_to_promise;
use wasm_utils::PromisableResult; use wasm_utils::{console_log, PromisableResult};
// don't get too excited about the name, under the hood it's just a big fat placeholder // don't get too excited about the name, under the hood it's just a big fat placeholder
// with no disk_persistence // with no persistence
pub(crate) fn setup_reply_surb_storage_backend( pub(crate) fn setup_reply_surb_storage_backend(
config: config::ReplySurbs, config: config::ReplySurbs,
) -> browser_backend::Backend { ) -> browser_backend::Backend {
@@ -78,37 +79,87 @@ pub fn current_network_topology(nym_api_url: String) -> Promise {
}) })
} }
async fn setup_gateway( pub(crate) async fn choose_gateway(
client_store: &ClientStorage,
chosen_gateway: Option<IdentityKey>,
gateways: &[gateway::Node],
) -> Result<InitialisationDetails, WasmClientError> {
let setup = if client_store.has_full_gateway_info().await? {
GatewaySetup::MustLoad
} else {
GatewaySetup::new_fresh(chosen_gateway.clone(), None)
};
setup_gateway_from(&setup, client_store, client_store, false, Some(gateways))
.await
.map_err(Into::into)
}
pub(crate) async fn setup_gateway_from_api(
client_store: &ClientStorage, client_store: &ClientStorage,
chosen_gateway: Option<IdentityKey>, chosen_gateway: Option<IdentityKey>,
nym_apis: &[Url], nym_apis: &[Url],
) -> Result<InitialisationDetails, WasmClientError> { ) -> Result<GatewayEndpointConfig, WasmClientError> {
let mut rng = thread_rng(); let existing_gateway_config = client_store.read_gateway_config().await?;
let gateways = current_gateways(&mut rng, nym_apis).await?;
setup_gateway(client_store, chosen_gateway, &gateways).await console_log!("loaded: {:?}", existing_gateway_config);
if let Some(existing) = existing_gateway_config {
if let Some(provided) = &chosen_gateway {
if provided != &existing.gateway_id {
return Err(WasmClientError::AlreadyRegistered {
gateway_config: existing,
});
}
}
return Ok(existing);
};
// if NOTHING is specified nor available, choose gateway randomly.
let setup = GatewaySetup::new(None, chosen_gateway, None);
let config = setup.try_get_gateway_details(nym_apis).await?;
// perform registration + persist the new gateway info
// TODO: this is actually quite bad. we shouldn't be persisting gateway info here since we did not have persisted
// the shared key yet. this will only happen when we start the base client itself.
// but unfortunately, we can't do much more until we do a bit more refactoring.
client_store.store_gateway_config(&config).await?;
console_log!("stored: {:?}", config);
Ok(config)
} }
pub(crate) async fn setup_from_topology( pub(crate) async fn gateway_from_topology<R: Rng + CryptoRng>(
explicit_gateway: Option<IdentityKey>, rng: &mut R,
explicit_gateway: Option<IdentityKeyRef<'_>>,
topology: &NymTopology, topology: &NymTopology,
client_store: &ClientStorage, client_store: &ClientStorage,
) -> Result<InitialisationDetails, WasmClientError> { ) -> Result<GatewayEndpointConfig, WasmClientError> {
let gateways = topology.gateways(); let existing_gateway_config = client_store.read_gateway_config().await?;
setup_gateway(client_store, explicit_gateway, gateways).await console_log!("loaded: {:?}", existing_gateway_config);
let new_gateway: GatewayEndpointConfig = if let Some(provided) = explicit_gateway {
if let Some(existing) = existing_gateway_config {
// we have stored gateway info and explicitly provided identity key
//
// check if they match, otherwise return an error
return if provided != existing.gateway_id {
Err(WasmClientError::AlreadyRegistered {
gateway_config: existing,
})
} else {
Ok(existing)
};
} else {
// we have explicitly provided identity key and didn't have any prior stored data
//
// try to grab details from the topology
let gateway_identity = identity::PublicKey::from_base58_string(provided)
.map_err(|source| WasmClientError::InvalidGatewayIdentity { source })?;
if let Some(gateway) = topology.get_gateway(&gateway_identity) {
gateway.clone().into()
} else {
return Err(WasmClientError::NonExistentGateway {
gateway_identity: gateway_identity.to_base58_string(),
});
}
}
} else if let Some(existing) = existing_gateway_config {
// we have stored data and didn't provide anything separately - use what's stored!
return Ok(existing);
} else {
// we don't have anything stored nor we have provided anything
//
// just grab random gateway from our topology
topology.random_gateway(rng)?.clone().into()
};
console_log!("storing: {:?}", new_gateway);
client_store.store_gateway_config(&new_gateway).await?;
Ok(new_gateway)
} }
@@ -16,9 +16,6 @@ pub enum ClientStorageError {
#[error("{typ} cryptographic key is not available in storage")] #[error("{typ} cryptographic key is not available in storage")]
CryptoKeyNotInStorage { typ: String }, CryptoKeyNotInStorage { typ: String },
#[error("the prior gateway details are not available in the storage")]
GatewayDetailsNotInStorage,
} }
impl From<ClientStorageError> for JsValue { impl From<ClientStorageError> for JsValue {
+10 -44
View File
@@ -1,10 +1,9 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::client::config::Config;
use crate::storage::errors::ClientStorageError; use crate::storage::errors::ClientStorageError;
use js_sys::Promise; use js_sys::Promise;
use nym_client_core::client::base_client::storage::gateway_details::PersistedGatewayDetails; use nym_client_core::config::GatewayEndpointConfig;
use nym_crypto::asymmetric::{encryption, identity}; use nym_crypto::asymmetric::{encryption, identity};
use nym_gateway_client::SharedKeys; use nym_gateway_client::SharedKeys;
use nym_sphinx::acknowledgements::AckKey; use nym_sphinx::acknowledgements::AckKey;
@@ -28,8 +27,8 @@ mod v1 {
pub const CORE_STORE: &str = "core"; pub const CORE_STORE: &str = "core";
// keys // keys
pub const CONFIG: &str = "config"; // TODO: to replace with FULL config
pub const GATEWAY_DETAILS: &str = "gateway_details"; pub const GATEWAY_CONFIG: &str = "gateway_config";
pub const ED25519_IDENTITY_KEYPAIR: &str = "ed25519_identity_keypair"; pub const ED25519_IDENTITY_KEYPAIR: &str = "ed25519_identity_keypair";
pub const X25519_ENCRYPTION_KEYPAIR: &str = "x25519_encryption_keypair"; pub const X25519_ENCRYPTION_KEYPAIR: &str = "x25519_encryption_keypair";
@@ -111,32 +110,15 @@ impl ClientStorage {
}) })
} }
// TODO: persist client's config pub(crate) async fn read_gateway_config(
#[allow(dead_code)] &self,
pub(crate) async fn read_config(&self) -> Result<Option<Config>, ClientStorageError> { ) -> Result<Option<GatewayEndpointConfig>, ClientStorageError> {
self.inner self.inner
.read_value(v1::CORE_STORE, JsValue::from_str(v1::CONFIG)) .read_value(v1::CORE_STORE, JsValue::from_str(v1::GATEWAY_CONFIG))
.await .await
.map_err(Into::into) .map_err(Into::into)
} }
pub(crate) async fn may_read_gateway_details(
&self,
) -> Result<Option<PersistedGatewayDetails>, ClientStorageError> {
self.inner
.read_value(v1::CORE_STORE, JsValue::from_str(v1::GATEWAY_DETAILS))
.await
.map_err(Into::into)
}
pub(crate) async fn must_read_gateway_details(
&self,
) -> Result<PersistedGatewayDetails, ClientStorageError> {
self.may_read_gateway_details()
.await?
.ok_or(ClientStorageError::GatewayDetailsNotInStorage)
}
async fn may_read_identity_keypair( async fn may_read_identity_keypair(
&self, &self,
) -> Result<Option<identity::KeyPair>, ClientStorageError> { ) -> Result<Option<identity::KeyPair>, ClientStorageError> {
@@ -262,33 +244,17 @@ impl ClientStorage {
.map_err(Into::into) .map_err(Into::into)
} }
pub(crate) async fn store_gateway_details( pub(crate) async fn store_gateway_config(
&self, &self,
gateway_endpoint: &PersistedGatewayDetails, gateway_endpoint: &GatewayEndpointConfig,
) -> Result<(), ClientStorageError> { ) -> Result<(), ClientStorageError> {
self.inner self.inner
.store_value( .store_value(
v1::CORE_STORE, v1::CORE_STORE,
JsValue::from_str(v1::GATEWAY_DETAILS), JsValue::from_str(v1::GATEWAY_CONFIG),
gateway_endpoint, gateway_endpoint,
) )
.await .await
.map_err(Into::into) .map_err(Into::into)
} }
// TODO: persist client's config
#[allow(dead_code)]
pub(crate) async fn store_config(&self, config: &Config) -> Result<(), ClientStorageError> {
self.inner
.store_value(v1::CORE_STORE, JsValue::from_str(v1::CONFIG), config)
.await
.map_err(Into::into)
}
pub(crate) async fn has_full_gateway_info(&self) -> Result<bool, ClientStorageError> {
let has_keys = self.may_read_gateway_shared_key().await?.is_some();
let has_details = self.may_read_gateway_details().await?.is_some();
Ok(has_keys && has_details)
}
} }
+6 -31
View File
@@ -4,9 +4,6 @@
use crate::storage::errors::ClientStorageError; use crate::storage::errors::ClientStorageError;
use crate::storage::ClientStorage; use crate::storage::ClientStorage;
use async_trait::async_trait; use async_trait::async_trait;
use nym_client_core::client::base_client::storage::gateway_details::{
GatewayDetailsStore, PersistedGatewayDetails,
};
use nym_client_core::client::base_client::storage::MixnetClientStorage; use nym_client_core::client::base_client::storage::MixnetClientStorage;
use nym_client_core::client::key_manager::persistence::KeyStore; use nym_client_core::client::key_manager::persistence::KeyStore;
use nym_client_core::client::key_manager::KeyManager; use nym_client_core::client::key_manager::KeyManager;
@@ -17,9 +14,9 @@ use wasm_utils::console_log;
// temporary until other variants are properly implemented (probably it should get changed into `ClientStorage` // temporary until other variants are properly implemented (probably it should get changed into `ClientStorage`
// implementing all traits and everything getting combined // implementing all traits and everything getting combined
pub struct FullWasmClientStorage { pub struct FullWasmClientStorage {
pub(crate) keys_and_gateway_store: ClientStorage, key_store: ClientStorage,
pub(crate) reply_storage: browser_backend::Backend, reply_storage: browser_backend::Backend,
pub(crate) credential_storage: EphemeralCredentialStorage, credential_storage: EphemeralCredentialStorage,
} }
impl MixnetClientStorage for FullWasmClientStorage { impl MixnetClientStorage for FullWasmClientStorage {
@@ -27,14 +24,12 @@ impl MixnetClientStorage for FullWasmClientStorage {
type ReplyStore = browser_backend::Backend; type ReplyStore = browser_backend::Backend;
type CredentialStore = EphemeralCredentialStorage; type CredentialStore = EphemeralCredentialStorage;
type GatewayDetailsStore = ClientStorage; fn into_split(self) -> (Self::KeyStore, Self::ReplyStore, Self::CredentialStore) {
(self.key_store, self.reply_storage, self.credential_storage)
fn into_runtime_stores(self) -> (Self::ReplyStore, Self::CredentialStore) {
(self.reply_storage, self.credential_storage)
} }
fn key_store(&self) -> &Self::KeyStore { fn key_store(&self) -> &Self::KeyStore {
&self.keys_and_gateway_store &self.key_store
} }
fn reply_store(&self) -> &Self::ReplyStore { fn reply_store(&self) -> &Self::ReplyStore {
@@ -44,10 +39,6 @@ impl MixnetClientStorage for FullWasmClientStorage {
fn credential_store(&self) -> &Self::CredentialStore { fn credential_store(&self) -> &Self::CredentialStore {
&self.credential_storage &self.credential_storage
} }
fn gateway_details_store(&self) -> &Self::GatewayDetailsStore {
&self.keys_and_gateway_store
}
} }
#[async_trait(?Send)] #[async_trait(?Send)]
@@ -83,19 +74,3 @@ impl KeyStore for ClientStorage {
.await .await
} }
} }
#[async_trait(?Send)]
impl GatewayDetailsStore for ClientStorage {
type StorageError = ClientStorageError;
async fn load_gateway_details(&self) -> Result<PersistedGatewayDetails, Self::StorageError> {
self.must_read_gateway_details().await
}
async fn store_gateway_details(
&self,
details: &PersistedGatewayDetails,
) -> Result<(), Self::StorageError> {
self.store_gateway_details(details).await
}
}
-38
View File
@@ -4,9 +4,6 @@
// due to expansion of #[wasm_bindgen] macro on NodeTestResult // due to expansion of #[wasm_bindgen] macro on NodeTestResult
#![allow(clippy::drop_non_drop)] #![allow(clippy::drop_non_drop)]
use crate::error::WasmClientError;
use crate::tester::LockedGatewayClient;
use js_sys::Promise;
use nym_node_tester_utils::processor::Received; use nym_node_tester_utils::processor::Received;
use nym_node_tester_utils::receiver::ReceivedReceiver; use nym_node_tester_utils::receiver::ReceivedReceiver;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -15,7 +12,6 @@ use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::{Mutex as AsyncMutex, MutexGuard as AsyncMutexGuard}; use tokio::sync::{Mutex as AsyncMutex, MutexGuard as AsyncMutexGuard};
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::future_to_promise;
use wasm_utils::{console_log, console_warn}; use wasm_utils::{console_log, console_warn};
#[derive(Clone)] #[derive(Clone)]
@@ -111,37 +107,3 @@ impl Drop for TestMarker {
self.value.store(false, Ordering::SeqCst) self.value.store(false, Ordering::SeqCst)
} }
} }
pub(crate) trait GatewayReconnection {
fn disconnect_from_gateway(&self) -> Promise;
fn reconnect_to_gateway(&self) -> Promise;
}
impl GatewayReconnection for LockedGatewayClient {
fn disconnect_from_gateway(&self) -> Promise {
let this = self.clone();
future_to_promise(async move {
let mut guard = this.lock().await;
guard
.disconnect()
.await
.map_err(|err| JsValue::from(WasmClientError::from(err)))?;
Ok(JsValue::undefined())
})
}
fn reconnect_to_gateway(&self) -> Promise {
let this = self.clone();
future_to_promise(async move {
let mut guard = this.lock().await;
guard
.try_reconnect()
.await
.map_err(|err| JsValue::from(WasmClientError::from(err)))?;
Ok(JsValue::undefined())
})
}
}
+32 -56
View File
@@ -3,11 +3,11 @@
use crate::constants::NODE_TESTER_ID; use crate::constants::NODE_TESTER_ID;
use crate::error::WasmClientError; use crate::error::WasmClientError;
use crate::helpers::{current_network_topology_async, setup_from_topology}; use crate::helpers::{current_network_topology_async, gateway_from_topology};
use crate::storage::ClientStorage; use crate::storage::ClientStorage;
use crate::tester::ephemeral_receiver::EphemeralTestReceiver; use crate::tester::ephemeral_receiver::EphemeralTestReceiver;
use crate::tester::helpers::{ use crate::tester::helpers::{
GatewayReconnection, NodeTestResult, ReceivedReceiverWrapper, TestMarker, WasmTestMessageExt, NodeTestResult, ReceivedReceiverWrapper, TestMarker, WasmTestMessageExt,
}; };
use crate::topology::WasmNymTopology; use crate::topology::WasmNymTopology;
use futures::channel::mpsc; use futures::channel::mpsc;
@@ -15,7 +15,7 @@ use js_sys::Promise;
use nym_bandwidth_controller::wasm_mockups::{Client as FakeClient, DirectSigningNyxdClient}; use nym_bandwidth_controller::wasm_mockups::{Client as FakeClient, DirectSigningNyxdClient};
use nym_bandwidth_controller::BandwidthController; use nym_bandwidth_controller::BandwidthController;
use nym_client_core::client::key_manager::ManagedKeys; use nym_client_core::client::key_manager::ManagedKeys;
use nym_client_core::init::InitialisationDetails; use nym_client_core::config::GatewayEndpointConfig;
use nym_credential_storage::ephemeral_storage::EphemeralStorage; use nym_credential_storage::ephemeral_storage::EphemeralStorage;
use nym_gateway_client::GatewayClient; use nym_gateway_client::GatewayClient;
use nym_node_tester_utils::receiver::SimpleMessageReceiver; use nym_node_tester_utils::receiver::SimpleMessageReceiver;
@@ -28,6 +28,7 @@ use nym_task::TaskManager;
use nym_topology::NymTopology; use nym_topology::NymTopology;
use nym_validator_client::client::IdentityKey; use nym_validator_client::client::IdentityKey;
use rand::rngs::OsRng; use rand::rngs::OsRng;
use rand::{CryptoRng, Rng};
use std::collections::HashSet; use std::collections::HashSet;
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use std::sync::{Arc, Mutex as SyncMutex}; use std::sync::{Arc, Mutex as SyncMutex};
@@ -73,7 +74,6 @@ pub struct NymNodeTester {
#[wasm_bindgen] #[wasm_bindgen]
pub struct NymNodeTesterBuilder { pub struct NymNodeTesterBuilder {
gateway: Option<IdentityKey>, gateway: Option<IdentityKey>,
id: Option<String>,
base_topology: NymTopology, base_topology: NymTopology,
@@ -95,11 +95,9 @@ impl NymNodeTesterBuilder {
#[wasm_bindgen(constructor)] #[wasm_bindgen(constructor)]
pub fn new( pub fn new(
base_topology: WasmNymTopology, base_topology: WasmNymTopology,
id: Option<String>,
gateway: Option<IdentityKey>, gateway: Option<IdentityKey>,
) -> NymNodeTesterBuilder { ) -> NymNodeTesterBuilder {
NymNodeTesterBuilder { NymNodeTesterBuilder {
id,
gateway, gateway,
base_topology: base_topology.into(), base_topology: base_topology.into(),
bandwidth_controller: None, bandwidth_controller: None,
@@ -108,51 +106,43 @@ impl NymNodeTesterBuilder {
async fn _new_with_api( async fn _new_with_api(
api_url: String, api_url: String,
id: Option<String>,
gateway: Option<IdentityKey>, gateway: Option<IdentityKey>,
) -> Result<Self, WasmClientError> { ) -> Result<Self, WasmClientError> {
let topology = current_network_topology_async(api_url).await?; let topology = current_network_topology_async(api_url).await?;
Ok(NymNodeTesterBuilder::new(topology, id, gateway)) Ok(NymNodeTesterBuilder::new(topology, gateway))
} }
pub fn new_with_api( pub fn new_with_api(gateway: Option<IdentityKey>, api_url: String) -> Promise {
api_url: String,
id: Option<String>,
gateway: Option<IdentityKey>,
) -> Promise {
future_to_promise(async move { future_to_promise(async move {
Self::_new_with_api(api_url, id, gateway) Self::_new_with_api(api_url, gateway)
.await .await
.into_promise_result() .into_promise_result()
}) })
} }
async fn gateway_info( async fn gateway_info<R: Rng + CryptoRng>(
&self, &self,
rng: &mut R,
client_store: &ClientStorage, client_store: &ClientStorage,
) -> Result<InitialisationDetails, WasmClientError> { ) -> Result<GatewayEndpointConfig, WasmClientError> {
if let Ok(loaded) = InitialisationDetails::try_load(client_store, client_store).await { gateway_from_topology(
Ok(loaded) rng,
} else { self.gateway.as_deref(),
setup_from_topology(self.gateway.clone(), &self.base_topology, client_store).await &self.base_topology,
} client_store,
)
.await
} }
async fn _setup_client(mut self) -> Result<NymNodeTester, WasmClientError> { async fn _setup_client(mut self) -> Result<NymNodeTester, WasmClientError> {
let mut rng = OsRng;
let task_manager = TaskManager::default(); let task_manager = TaskManager::default();
let storage_id = if let Some(client_id) = &self.id { let client_store = ClientStorage::new_async(NODE_TESTER_ID, None).await?;
format!("{NODE_TESTER_ID}-{client_id}")
} else {
NODE_TESTER_ID.to_owned()
};
let client_store = ClientStorage::new_async(&storage_id, None).await?; let gateway_endpoint = self.gateway_info(&mut rng, &client_store).await?;
let init_details = self.gateway_info(&client_store).await?;
let gateway_endpoint = init_details.gateway_details;
let gateway_identity = gateway_endpoint.try_get_gateway_identity_key()?; let gateway_identity = gateway_endpoint.try_get_gateway_identity_key()?;
let managed_keys = init_details.managed_keys; let mut managed_keys = ManagedKeys::load_or_generate(&mut rng, &client_store).await;
let (mixnet_message_sender, mixnet_message_receiver) = mpsc::unbounded(); let (mixnet_message_sender, mixnet_message_receiver) = mpsc::unbounded();
let (ack_sender, ack_receiver) = mpsc::unbounded(); let (ack_sender, ack_receiver) = mpsc::unbounded();
@@ -161,7 +151,7 @@ impl NymNodeTesterBuilder {
gateway_endpoint.gateway_listener, gateway_endpoint.gateway_listener,
managed_keys.identity_keypair(), managed_keys.identity_keypair(),
gateway_identity, gateway_identity,
Some(managed_keys.must_get_gateway_shared_key()), managed_keys.gateway_shared_key(),
mixnet_message_sender, mixnet_message_sender,
ack_sender, ack_sender,
Duration::from_secs(10), Duration::from_secs(10),
@@ -170,11 +160,14 @@ impl NymNodeTesterBuilder {
); );
gateway_client.set_disabled_credentials_mode(true); gateway_client.set_disabled_credentials_mode(true);
gateway_client.authenticate_and_start().await?; let shared_keys = gateway_client.authenticate_and_start().await?;
managed_keys
.deal_with_gateway_key(shared_keys, &client_store)
.await?;
// TODO: make those values configurable later // TODO: make those values configurable later
let tester = NodeTester::new( let tester = NodeTester::new(
OsRng, rng,
self.base_topology, self.base_topology,
Some(address(&managed_keys, gateway_identity)), Some(address(&managed_keys, gateway_identity)),
PacketSize::default(), PacketSize::default(),
@@ -248,46 +241,29 @@ async fn test_mixnode(
impl NymNodeTester { impl NymNodeTester {
#[wasm_bindgen(constructor)] #[wasm_bindgen(constructor)]
#[allow(clippy::new_ret_no_self)] #[allow(clippy::new_ret_no_self)]
pub fn new( pub fn new(topology: WasmNymTopology, gateway: Option<IdentityKey>) -> Promise {
topology: WasmNymTopology,
id: Option<String>,
gateway: Option<IdentityKey>,
) -> Promise {
console_log!("constructing node tester!"); console_log!("constructing node tester!");
NymNodeTesterBuilder::new(topology, id, gateway).setup_client() NymNodeTesterBuilder::new(topology, gateway).setup_client()
} }
async fn _new_with_api( async fn _new_with_api(
api_url: String, api_url: String,
id: Option<String>,
gateway: Option<IdentityKey>, gateway: Option<IdentityKey>,
) -> Result<Self, WasmClientError> { ) -> Result<Self, WasmClientError> {
NymNodeTesterBuilder::_new_with_api(api_url, id, gateway) NymNodeTesterBuilder::_new_with_api(api_url, gateway)
.await? .await?
._setup_client() ._setup_client()
.await .await
} }
pub fn new_with_api( pub fn new_with_api(api_url: String, gateway: Option<IdentityKey>) -> Promise {
api_url: String,
id: Option<String>,
gateway: Option<IdentityKey>,
) -> Promise {
future_to_promise(async move { future_to_promise(async move {
Self::_new_with_api(api_url, id, gateway) Self::_new_with_api(api_url, gateway)
.await .await
.into_promise_result() .into_promise_result()
}) })
} }
pub fn disconnect_from_gateway(&self) -> Promise {
self.gateway_client.disconnect_from_gateway()
}
pub fn reconnect_to_gateway(&self) -> Promise {
self.gateway_client.reconnect_to_gateway()
}
fn prepare_test_packets( fn prepare_test_packets(
&self, &self,
mixnode_identity: String, mixnode_identity: String,
+5 -17
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-bin-common" name = "nym-bin-common"
version = "0.6.0" version = "0.5.0"
description = "Common code for nym binaries" description = "Common code for nym binaries"
edition = { workspace = true } edition = { workspace = true }
authors = { workspace = true } authors = { workspace = true }
@@ -15,21 +15,15 @@ clap_complete_fig = "4.0"
log = { workspace = true } log = { workspace = true }
pretty_env_logger = "0.4.0" pretty_env_logger = "0.4.0"
semver = "0.11" semver = "0.11"
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"], optional = true }
serde_json = { workspace = true, optional = true } serde_json = { workspace = true, optional = true }
## tracing ## tracing
tracing-appender = { version = "0.2.2", optional = true }
tracing-subscriber = { version = "0.3.16", features = [ tracing-subscriber = { version = "0.3.16", features = [
"env-filter", "env-filter",
], optional = true } ], optional = true }
tracing-tree = { version = "0.2.2", optional = true } tracing-tree = { version = "0.2.2", optional = true }
opentelemetry-jaeger = { version = "0.18.0", optional = true, features = [
"rt-tokio",
"collector_client",
"isahc_collector_client",
] }
tracing-opentelemetry = { version = "0.19.0", optional = true }
opentelemetry = { version = "0.19.0", optional = true, features = ["rt-tokio"] }
[build-dependencies] [build-dependencies]
@@ -42,11 +36,5 @@ vergen = { version = "=7.4.3", default-features = false, features = [
[features] [features]
default = [] default = []
output_format = ["serde_json"] output_format = ["serde", "serde_json"]
tracing = [ tracing = ["tracing-appender", "tracing-subscriber", "tracing-tree"]
"tracing-subscriber",
"tracing-tree",
"opentelemetry-jaeger",
"tracing-opentelemetry",
"opentelemetry",
]
@@ -4,8 +4,6 @@
// TODO: at a later date this crate should probably also expose `ContractBuildInformation` // TODO: at a later date this crate should probably also expose `ContractBuildInformation`
// and be used by our smart contracts // and be used by our smart contracts
use serde::{Deserialize, Serialize};
#[derive(Debug)] #[derive(Debug)]
pub struct BinaryBuildInformation { pub struct BinaryBuildInformation {
// VERGEN_BUILD_TIMESTAMP // VERGEN_BUILD_TIMESTAMP
@@ -101,7 +99,8 @@ impl BinaryBuildInformation {
} }
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BinaryBuildInformationOwned { pub struct BinaryBuildInformationOwned {
// VERGEN_BUILD_TIMESTAMP // VERGEN_BUILD_TIMESTAMP
/// Provides the build timestamp, for example `2021-02-23T20:14:46.558472672+00:00`. /// Provides the build timestamp, for example `2021-02-23T20:14:46.558472672+00:00`.
+1 -3
View File
@@ -4,7 +4,5 @@
pub mod build_information; pub mod build_information;
pub mod completions; pub mod completions;
pub mod logging; pub mod logging;
pub mod version_checker;
#[cfg(feature = "output_format")]
pub mod output_format; pub mod output_format;
pub mod version_checker;
+18 -34
View File
@@ -1,25 +1,17 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// use tracing_subscriber::{
use serde::{Deserialize, Serialize}; // fmt::Layer, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Registry,
// };
// use tracing_tree::HierarchicalLayer;
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
pub use opentelemetry; pub use tracing_appender;
#[cfg(feature = "tracing")]
pub use opentelemetry_jaeger;
#[cfg(feature = "tracing")]
pub use tracing_opentelemetry;
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
pub use tracing_subscriber; pub use tracing_subscriber;
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
pub use tracing_tree; pub use tracing_tree;
#[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...
}
// I'd argue we should start transitioning from `log` to `tracing` // I'd argue we should start transitioning from `log` to `tracing`
pub fn setup_logging() { pub fn setup_logging() {
let mut log_builder = pretty_env_logger::formatted_timed_builder(); let mut log_builder = pretty_env_logger::formatted_timed_builder();
@@ -47,35 +39,27 @@ pub fn setup_logging() {
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
#[macro_export] #[macro_export]
macro_rules! setup_tracing { macro_rules! setup_tracing {
($service_name: expr) => { ($file_name: expr) => {
use nym_bin_common::logging::tracing_subscriber::layer::SubscriberExt; use nym_bin_common::logging::tracing_subscriber::layer::SubscriberExt;
use nym_bin_common::logging::tracing_subscriber::util::SubscriberInitExt; use nym_bin_common::logging::tracing_subscriber::util::SubscriberInitExt;
let registry = nym_bin_common::logging::tracing_subscriber::Registry::default() let file_appender =
nym_bin_common::logging::tracing_appender::rolling::hourly($file_name, "log");
let (non_blocking, _guard) =
nym_bin_common::logging::tracing_appender::non_blocking(file_appender);
let appender_layer = nym_bin_common::logging::tracing_subscriber::fmt::Layer::new()
.with_ansi(false)
.with_writer(non_blocking);
nym_bin_common::logging::tracing_subscriber::Registry::default()
.with(nym_bin_common::logging::tracing_subscriber::EnvFilter::from_default_env()) .with(nym_bin_common::logging::tracing_subscriber::EnvFilter::from_default_env())
.with(appender_layer)
.with( .with(
nym_bin_common::logging::tracing_tree::HierarchicalLayer::new(4) nym_bin_common::logging::tracing_tree::HierarchicalLayer::new(4)
.with_targets(true) .with_targets(true)
.with_bracketed_fields(true), .with_bracketed_fields(true),
);
let tracer = nym_bin_common::logging::opentelemetry_jaeger::new_collector_pipeline()
.with_endpoint("http://44.199.230.10:14268/api/traces")
.with_service_name($service_name)
.with_isahc()
.with_trace_config(
nym_bin_common::logging::opentelemetry::sdk::trace::config().with_sampler(
nym_bin_common::logging::opentelemetry::sdk::trace::Sampler::TraceIdRatioBased(
0.1,
),
),
) )
.install_batch(nym_bin_common::logging::opentelemetry::runtime::Tokio) .init();
.expect("Could not init tracer");
let telemetry = nym_bin_common::logging::tracing_opentelemetry::layer().with_tracer(tracer);
registry.with(telemetry).init();
}; };
} }
+1 -3
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-client-core" name = "nym-client-core"
version = "1.1.15" version = "1.1.14"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"] authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2021" edition = "2021"
rust-version = "1.66" rust-version = "1.66"
@@ -9,7 +9,6 @@ rust-version = "1.66"
[dependencies] [dependencies]
async-trait = { workspace = true } async-trait = { workspace = true }
base64 = "0.21.2"
dirs = "4.0" dirs = "4.0"
dashmap = "5.4.0" dashmap = "5.4.0"
futures = "0.3" futures = "0.3"
@@ -18,7 +17,6 @@ log = { workspace = true }
rand = { version = "0.7.3", features = ["wasm-bindgen"] } rand = { version = "0.7.3", features = ["wasm-bindgen"] }
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true } serde_json = { workspace = true }
sha2 = "0.10.6"
tap = "1.0.1" tap = "1.0.1"
thiserror = "1.0.34" thiserror = "1.0.34"
url = { version ="2.2", features = ["serde"] } url = { version ="2.2", features = ["serde"] }
@@ -42,6 +42,7 @@ use nym_sphinx::receiver::{ReconstructedMessage, SphinxMessageReceiver};
use nym_task::connections::{ConnectionCommandReceiver, ConnectionCommandSender, LaneQueueLengths}; use nym_task::connections::{ConnectionCommandReceiver, ConnectionCommandSender, LaneQueueLengths};
use nym_task::{TaskClient, TaskManager}; use nym_task::{TaskClient, TaskManager};
use nym_topology::provider_trait::TopologyProvider; use nym_topology::provider_trait::TopologyProvider;
use rand::rngs::OsRng;
use std::sync::Arc; use std::sync::Arc;
use tap::TapFallible; use tap::TapFallible;
use url::Url; use url::Url;
@@ -49,8 +50,6 @@ use url::Url;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
use nym_bandwidth_controller::wasm_mockups::DkgQueryClient; use nym_bandwidth_controller::wasm_mockups::DkgQueryClient;
use crate::client::base_client::storage::gateway_details::GatewayDetailsStore;
use crate::init::{setup_gateway, GatewaySetup, InitialisationDetails};
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use nym_validator_client::nyxd::traits::DkgQueryClient; use nym_validator_client::nyxd::traits::DkgQueryClient;
@@ -158,11 +157,17 @@ impl From<bool> for CredentialsToggle {
} }
pub struct BaseClientBuilder<'a, C, S: MixnetClientStorage> { pub struct BaseClientBuilder<'a, C, S: MixnetClientStorage> {
config: &'a Config, // due to wasm limitations I had to split it like this : (
client_store: S, gateway_config: &'a GatewayEndpointConfig,
dkg_query_client: Option<C>, debug_config: &'a DebugConfig,
disabled_credentials: bool,
nym_api_endpoints: Vec<Url>,
reply_storage_backend: S::ReplyStore,
key_store: S::KeyStore,
custom_topology_provider: Option<Box<dyn TopologyProvider + Send + Sync>>, custom_topology_provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
setup_method: GatewaySetup, bandwidth_controller: Option<BandwidthController<C, S::CredentialStore>>,
managed_keys: ManagedKeys,
} }
impl<'a, C, S> BaseClientBuilder<'a, C, S> impl<'a, C, S> BaseClientBuilder<'a, C, S>
@@ -170,23 +175,47 @@ where
S: MixnetClientStorage + 'static, S: MixnetClientStorage + 'static,
C: DkgQueryClient + Send + Sync + 'static, C: DkgQueryClient + Send + Sync + 'static,
{ {
pub fn new( // TODO: combine all storages
base_config: &'a Config, pub fn new_from_base_config<T>(
client_store: S, base_config: &'a Config<T>,
dkg_query_client: Option<C>, key_store: S::KeyStore,
bandwidth_controller: Option<BandwidthController<C, S::CredentialStore>>,
reply_storage_backend: S::ReplyStore,
) -> BaseClientBuilder<'a, C, S> { ) -> BaseClientBuilder<'a, C, S> {
BaseClientBuilder { BaseClientBuilder {
config: base_config, gateway_config: base_config.get_gateway_endpoint_config(),
client_store, debug_config: base_config.get_debug_config(),
dkg_query_client, disabled_credentials: base_config.get_disabled_credentials_mode(),
nym_api_endpoints: base_config.get_nym_api_endpoints(),
bandwidth_controller,
reply_storage_backend,
key_store,
managed_keys: ManagedKeys::Invalidated,
custom_topology_provider: None, custom_topology_provider: None,
setup_method: GatewaySetup::MustLoad,
} }
} }
pub fn with_gateway_setup(mut self, setup: GatewaySetup) -> Self { // TODO: combine all storages
self.setup_method = setup; pub fn new(
self gateway_config: &'a GatewayEndpointConfig,
debug_config: &'a DebugConfig,
key_store: S::KeyStore,
bandwidth_controller: Option<BandwidthController<C, S::CredentialStore>>,
reply_storage_backend: S::ReplyStore,
credentials_toggle: CredentialsToggle,
nym_api_endpoints: Vec<Url>,
) -> BaseClientBuilder<'a, C, S> {
BaseClientBuilder {
gateway_config,
debug_config,
disabled_credentials: credentials_toggle.is_disabled(),
nym_api_endpoints,
reply_storage_backend,
custom_topology_provider: None,
bandwidth_controller,
key_store,
managed_keys: ManagedKeys::Invalidated,
}
} }
pub fn with_topology_provider( pub fn with_topology_provider(
@@ -199,16 +228,13 @@ where
// note: do **NOT** make this method public as its only valid usage is from within `start_base` // note: do **NOT** make this method public as its only valid usage is from within `start_base`
// because it relies on the crypto keys being already loaded // because it relies on the crypto keys being already loaded
fn mix_address( fn as_mix_recipient(&self) -> Recipient {
managed_keys: &ManagedKeys,
gateway_config: &GatewayEndpointConfig,
) -> Recipient {
Recipient::new( Recipient::new(
*managed_keys.identity_public_key(), *self.managed_keys.identity_public_key(),
*managed_keys.encryption_public_key(), *self.managed_keys.encryption_public_key(),
// TODO: below only works under assumption that gateway address == gateway id // TODO: below only works under assumption that gateway address == gateway id
// (which currently is true) // (which currently is true)
NodeIdentity::from_base58_string(&gateway_config.gateway_id).unwrap(), NodeIdentity::from_base58_string(&self.gateway_config.gateway_id).unwrap(),
) )
} }
@@ -292,10 +318,7 @@ where
} }
async fn start_gateway_client( async fn start_gateway_client(
config: &Config, &mut self,
gateway_config: GatewayEndpointConfig,
managed_keys: &ManagedKeys,
bandwidth_controller: Option<BandwidthController<C, S::CredentialStore>>,
mixnet_message_sender: MixnetMessageSender, mixnet_message_sender: MixnetMessageSender,
ack_sender: AcknowledgementSender, ack_sender: AcknowledgementSender,
shutdown: TaskClient, shutdown: TaskClient,
@@ -304,26 +327,33 @@ where
<S::KeyStore as KeyStore>::StorageError: Send + Sync + 'static, <S::KeyStore as KeyStore>::StorageError: Send + Sync + 'static,
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static, <S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static,
{ {
let gateway_address = gateway_config.gateway_listener.clone(); let gateway_id = self.gateway_config.gateway_id.clone();
let gateway_id = gateway_config.gateway_id; if gateway_id.is_empty() {
return Err(ClientCoreError::GatewayIdUnknown);
}
let gateway_address = self.gateway_config.gateway_listener.clone();
if gateway_address.is_empty() {
return Err(ClientCoreError::GatewayAddressUnknown);
}
// TODO: in theory, at this point, this should be infallible
let gateway_identity = identity::PublicKey::from_base58_string(gateway_id) let gateway_identity = identity::PublicKey::from_base58_string(gateway_id)
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?; .map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
let mut gateway_client = GatewayClient::new( let mut gateway_client = GatewayClient::new(
gateway_address, gateway_address,
managed_keys.identity_keypair(), self.managed_keys.identity_keypair(),
gateway_identity, gateway_identity,
Some(managed_keys.must_get_gateway_shared_key()), self.managed_keys.gateway_shared_key(),
mixnet_message_sender, mixnet_message_sender,
ack_sender, ack_sender,
config.debug.gateway_connection.gateway_response_timeout, self.debug_config
bandwidth_controller, .gateway_connection
.gateway_response_timeout,
self.bandwidth_controller.take(),
shutdown, shutdown,
); );
gateway_client.set_disabled_credentials_mode(config.client.disabled_credentials_mode); gateway_client.set_disabled_credentials_mode(self.disabled_credentials);
let shared_key = gateway_client let shared_key = gateway_client
.authenticate_and_start() .authenticate_and_start()
@@ -332,7 +362,12 @@ where
log::error!("Could not authenticate and start up the gateway connection - {err}") log::error!("Could not authenticate and start up the gateway connection - {err}")
})?; })?;
managed_keys.ensure_gateway_key(shared_key); self.managed_keys
.deal_with_gateway_key(shared_key, &self.key_store)
.await
.map_err(|source| ClientCoreError::KeyStoreError {
source: Box::new(source),
})?;
Ok(gateway_client) Ok(gateway_client)
} }
@@ -438,41 +473,24 @@ where
Ok(mem_store) Ok(mem_store)
} }
async fn initialise_keys_and_gateway(&self) -> Result<InitialisationDetails, ClientCoreError> async fn initial_key_setup(&mut self) {
where assert!(!self.managed_keys.is_valid());
<S::KeyStore as KeyStore>::StorageError: Sync + Send, let mut rng = OsRng;
<S::GatewayDetailsStore as GatewayDetailsStore>::StorageError: Sync + Send, self.managed_keys = ManagedKeys::load_or_generate(&mut rng, &self.key_store).await;
{
setup_gateway(
&self.setup_method,
self.client_store.key_store(),
self.client_store.gateway_details_store(),
false,
Some(&self.config.client.nym_api_urls),
)
.await
} }
pub async fn start_base(mut self) -> Result<BaseClient, ClientCoreError> pub async fn start_base(
mut self,
packet_type: PacketType,
) -> Result<BaseClient, ClientCoreError>
where where
<S::ReplyStore as ReplyStorageBackend>::StorageError: Sync + Send,
S::ReplyStore: Send + Sync, S::ReplyStore: Send + Sync,
<S::KeyStore as KeyStore>::StorageError: Send + Sync, <S::KeyStore as KeyStore>::StorageError: Send + Sync,
<S::ReplyStore as ReplyStorageBackend>::StorageError: Sync + Send,
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static, <S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static,
<S::GatewayDetailsStore as GatewayDetailsStore>::StorageError: Sync + Send,
{ {
info!("Starting nym client"); info!("Starting nym client");
self.initial_key_setup().await;
// derive (or load) client keys and gateway configuration
let details = self.initialise_keys_and_gateway().await?;
let gateway_config = details.gateway_details;
let managed_keys = details.managed_keys;
let (reply_storage_backend, credential_store) = self.client_store.into_runtime_stores();
let bandwidth_controller = self
.dkg_query_client
.map(|client| BandwidthController::new(credential_store, client));
// channels for inter-component communication // channels for inter-component communication
// TODO: make the channels be internally created by the relevant components // TODO: make the channels be internally created by the relevant components
@@ -500,39 +518,34 @@ where
let (reply_controller_sender, reply_controller_receiver) = let (reply_controller_sender, reply_controller_receiver) =
reply_controller::requests::new_control_channels(); reply_controller::requests::new_control_channels();
let self_address = Self::mix_address(&managed_keys, &gateway_config); let self_address = self.as_mix_recipient();
// the components are started in very specific order. Unless you know what you are doing, // the components are started in very specific order. Unless you know what you are doing,
// do not change that. // do not change that.
let gateway_client = Self::start_gateway_client( let gateway_client = self
self.config, .start_gateway_client(mixnet_messages_sender, ack_sender, task_manager.subscribe())
gateway_config, .await?;
&managed_keys,
bandwidth_controller, let reply_storage = Self::setup_persistent_reply_storage(
mixnet_messages_sender, self.reply_storage_backend,
ack_sender,
task_manager.subscribe(), task_manager.subscribe(),
) )
.await?; .await?;
let reply_storage =
Self::setup_persistent_reply_storage(reply_storage_backend, task_manager.subscribe())
.await?;
let topology_provider = Self::setup_topology_provider( let topology_provider = Self::setup_topology_provider(
self.custom_topology_provider.take(), self.custom_topology_provider.take(),
self.config.get_nym_api_endpoints(), self.nym_api_endpoints,
); );
Self::start_topology_refresher( Self::start_topology_refresher(
topology_provider, topology_provider,
self.config.debug.topology, self.debug_config.topology,
shared_topology_accessor.clone(), shared_topology_accessor.clone(),
task_manager.subscribe(), task_manager.subscribe(),
) )
.await?; .await?;
Self::start_received_messages_buffer_controller( Self::start_received_messages_buffer_controller(
managed_keys.encryption_keypair(), self.managed_keys.encryption_keypair(),
received_buffer_request_receiver, received_buffer_request_receiver,
mixnet_messages_receiver, mixnet_messages_receiver,
reply_storage.key_storage(), reply_storage.key_storage(),
@@ -556,8 +569,8 @@ where
let shared_lane_queue_lengths = LaneQueueLengths::new(); let shared_lane_queue_lengths = LaneQueueLengths::new();
let controller_config = real_messages_control::Config::new( let controller_config = real_messages_control::Config::new(
&self.config.debug, self.debug_config,
managed_keys.ack_key(), self.managed_keys.ack_key(),
self_address, self_address,
); );
@@ -573,18 +586,17 @@ where
shared_lane_queue_lengths.clone(), shared_lane_queue_lengths.clone(),
client_connection_rx, client_connection_rx,
task_manager.subscribe(), task_manager.subscribe(),
self.config.debug.traffic.packet_type, packet_type,
); );
if !self if !self
.config .debug_config
.debug
.cover_traffic .cover_traffic
.disable_loop_cover_traffic_stream .disable_loop_cover_traffic_stream
{ {
Self::start_cover_traffic_stream( Self::start_cover_traffic_stream(
&self.config.debug, self.debug_config,
managed_keys.ack_key(), self.managed_keys.ack_key(),
self_address, self_address,
shared_topology_accessor.clone(), shared_topology_accessor.clone(),
message_sender, message_sender,
@@ -101,8 +101,8 @@ pub async fn setup_fs_reply_surb_backend<P: AsRef<Path>>(
} }
} }
pub fn create_bandwidth_controller<St: CredentialStorage>( pub fn create_bandwidth_controller<T, St: CredentialStorage>(
config: &Config, config: &Config<T>,
storage: St, storage: St,
) -> BandwidthController<Client<QueryNyxdClient>, St> { ) -> BandwidthController<Client<QueryNyxdClient>, St> {
let nyxd_url = config let nyxd_url = config
@@ -122,30 +122,13 @@ pub fn create_bandwidth_controller_with_urls<St: CredentialStorage>(
nym_api_url: Url, nym_api_url: Url,
storage: St, storage: St,
) -> BandwidthController<Client<QueryNyxdClient>, St> { ) -> BandwidthController<Client<QueryNyxdClient>, St> {
let client = default_query_dkg_client(nyxd_url, nym_api_url);
BandwidthController::new(storage, client)
}
pub fn default_query_dkg_client_from_config(config: &Config) -> Client<QueryNyxdClient> {
let nyxd_url = config
.get_validator_endpoints()
.pop()
.expect("No nyxd validator endpoint provided");
let api_url = config
.get_nym_api_endpoints()
.pop()
.expect("No validator api endpoint provided");
default_query_dkg_client(nyxd_url, api_url)
}
pub fn default_query_dkg_client(nyxd_url: Url, nym_api_url: Url) -> Client<QueryNyxdClient> {
let details = nym_network_defaults::NymNetworkDetails::new_from_env(); let details = nym_network_defaults::NymNetworkDetails::new_from_env();
let mut client_config = nym_validator_client::Config::try_from_nym_network_details(&details) let mut client_config = nym_validator_client::Config::try_from_nym_network_details(&details)
.expect("failed to construct validator client config"); .expect("failed to construct validator client config");
// overwrite env configuration with config URLs // overwrite env configuration with config URLs
client_config = client_config.with_urls(nyxd_url, nym_api_url); client_config = client_config.with_urls(nyxd_url, nym_api_url);
nym_validator_client::Client::new_query(client_config) let client = nym_validator_client::Client::new_query(client_config)
.expect("Could not construct query client") .expect("Could not construct query client");
BandwidthController::new(storage, client)
} }
@@ -1,201 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::GatewayEndpointConfig;
use async_trait::async_trait;
use nym_gateway_requests::registration::handshake::SharedKeys;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::error::Error;
use std::ops::Deref;
use tokio::sync::Mutex;
use zeroize::Zeroizing;
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait GatewayDetailsStore {
type StorageError: Error;
async fn load_gateway_details(&self) -> Result<PersistedGatewayDetails, Self::StorageError>;
async fn store_gateway_details(
&self,
details: &PersistedGatewayDetails,
) -> Result<(), Self::StorageError>;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PersistedGatewayDetails {
// TODO: should we also verify correctness of the details themselves?
// i.e. we could include a checksum or tag (via the shared keys)
// counterargument: if we wanted to modify, say, the host information in the stored file on disk,
// in order to actually use it, we'd have to recompute the whole checksum which would be a huge pain.
/// The hash of the shared keys to ensure the correct ones are used with those gateway details.
#[serde(with = "base64")]
key_hash: Vec<u8>,
/// Actual gateway details being persisted.
pub(crate) details: GatewayEndpointConfig,
}
impl From<PersistedGatewayDetails> for GatewayEndpointConfig {
fn from(value: PersistedGatewayDetails) -> Self {
value.details
}
}
impl PersistedGatewayDetails {
pub fn new(details: GatewayEndpointConfig, shared_key: &SharedKeys) -> Self {
let key_bytes = Zeroizing::new(shared_key.to_bytes());
let mut key_hasher = Sha256::new();
key_hasher.update(&key_bytes);
let key_hash = key_hasher.finalize().to_vec();
PersistedGatewayDetails { key_hash, details }
}
pub fn verify(&self, shared_key: &SharedKeys) -> bool {
let key_bytes = Zeroizing::new(shared_key.to_bytes());
let mut key_hasher = Sha256::new();
key_hasher.update(&key_bytes);
let key_hash = key_hasher.finalize();
self.key_hash == key_hash.deref()
}
}
// helper to make Vec<u8> serialization use base64 representation to make it human readable
// so that it would be easier for users to copy contents from the disk if they wanted to use it elsewhere
mod base64 {
use base64::{engine::general_purpose::STANDARD, Engine as _};
use serde::{Deserialize, Deserializer, Serializer};
pub fn serialize<S: Serializer>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&STANDARD.encode(bytes))
}
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Vec<u8>, D::Error> {
let s = <String>::deserialize(deserializer)?;
STANDARD.decode(s).map_err(serde::de::Error::custom)
}
}
#[cfg(not(target_arch = "wasm32"))]
#[derive(Debug, thiserror::Error)]
pub enum OnDiskGatewayDetailsError {
#[error("JSON failure: {0}")]
SerializationFailure(#[from] serde_json::Error),
#[error("failed to store gateway details to {path}: {err}")]
StoreFailure {
path: String,
#[source]
err: std::io::Error,
},
#[error("failed to load gateway details from {path}: {err}")]
LoadFailure {
path: String,
#[source]
err: std::io::Error,
},
}
#[cfg(not(target_arch = "wasm32"))]
pub struct OnDiskGatewayDetails {
file_location: std::path::PathBuf,
}
#[cfg(not(target_arch = "wasm32"))]
impl OnDiskGatewayDetails {
pub fn new<P: AsRef<std::path::Path>>(path: P) -> Self {
OnDiskGatewayDetails {
file_location: path.as_ref().to_owned(),
}
}
pub fn load_from_disk(&self) -> Result<PersistedGatewayDetails, OnDiskGatewayDetailsError> {
let file = std::fs::File::open(&self.file_location).map_err(|err| {
OnDiskGatewayDetailsError::LoadFailure {
path: self.file_location.display().to_string(),
err,
}
})?;
Ok(serde_json::from_reader(file)?)
}
pub fn store_to_disk(
&self,
details: &PersistedGatewayDetails,
) -> Result<(), OnDiskGatewayDetailsError> {
// ensure the whole directory structure exists
if let Some(parent_dir) = &self.file_location.parent() {
std::fs::create_dir_all(parent_dir).map_err(|err| {
OnDiskGatewayDetailsError::StoreFailure {
path: self.file_location.display().to_string(),
err,
}
})?
}
let file = std::fs::File::create(&self.file_location).map_err(|err| {
OnDiskGatewayDetailsError::StoreFailure {
path: self.file_location.display().to_string(),
err,
}
})?;
Ok(serde_json::to_writer_pretty(file, details)?)
}
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl GatewayDetailsStore for OnDiskGatewayDetails {
type StorageError = OnDiskGatewayDetailsError;
async fn load_gateway_details(&self) -> Result<PersistedGatewayDetails, Self::StorageError> {
self.load_from_disk()
}
async fn store_gateway_details(
&self,
gateway_details: &PersistedGatewayDetails,
) -> Result<(), Self::StorageError> {
self.store_to_disk(gateway_details)
}
}
#[derive(Default)]
pub struct InMemGatewayDetails {
details: Mutex<Option<PersistedGatewayDetails>>,
}
#[derive(Debug, thiserror::Error)]
#[error("old ephemeral gateway details can't be loaded from storage")]
pub struct EphemeralGatewayDetailsError;
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl GatewayDetailsStore for InMemGatewayDetails {
type StorageError = EphemeralGatewayDetailsError;
async fn load_gateway_details(&self) -> Result<PersistedGatewayDetails, Self::StorageError> {
self.details
.lock()
.await
.clone()
.ok_or(EphemeralGatewayDetailsError)
}
async fn store_gateway_details(
&self,
gateway_details: &PersistedGatewayDetails,
) -> Result<(), Self::StorageError> {
*self.details.lock().await = Some(gateway_details.clone());
Ok(())
}
}
@@ -4,57 +4,46 @@
// TODO: combine those more closely. Perhaps into a single underlying store. // TODO: combine those more closely. Perhaps into a single underlying store.
// Like for persistent, on-disk, storage, what's the point of having 3 different databases? // Like for persistent, on-disk, storage, what's the point of having 3 different databases?
use crate::client::base_client::storage::gateway_details::{
GatewayDetailsStore, InMemGatewayDetails,
};
use crate::client::key_manager::persistence::{InMemEphemeralKeys, KeyStore}; use crate::client::key_manager::persistence::{InMemEphemeralKeys, KeyStore};
use crate::client::replies::reply_storage; use crate::client::replies::reply_storage;
use crate::client::replies::reply_storage::ReplyStorageBackend; use crate::client::replies::reply_storage::ReplyStorageBackend;
use nym_credential_storage::ephemeral_storage::EphemeralStorage as EphemeralCredentialStorage; use nym_credential_storage::ephemeral_storage::{
EphemeralStorage as EphemeralCredentialStorage, EphemeralStorage,
};
use nym_credential_storage::storage::Storage as CredentialStorage; use nym_credential_storage::storage::Storage as CredentialStorage;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))] #[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use crate::client::base_client::non_wasm_helpers; use crate::client::base_client::non_wasm_helpers;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))] #[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use crate::client::base_client::storage::gateway_details::OnDiskGatewayDetails;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use crate::client::key_manager::persistence::OnDiskKeys; use crate::client::key_manager::persistence::OnDiskKeys;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))] #[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use crate::client::replies::reply_storage::fs_backend; use crate::config::{persistence::key_pathfinder::ClientKeyPathfinder, Config};
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use crate::config::{self, disk_persistence::CommonClientPaths};
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))] #[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use crate::error::ClientCoreError; use crate::error::ClientCoreError;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))] #[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use nym_credential_storage::persistent_storage::PersistentStorage as PersistentCredentialStorage; use nym_credential_storage::persistent_storage::PersistentStorage as PersistentCredentialStorage;
pub mod gateway_details; #[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use crate::client::replies::reply_storage::fs_backend;
// TODO: ideally this should be changed into
// `MixnetClientStorage: KeyStore + ReplyStorageBackend + CredentialStorage + GatewayDetailsStore`
pub trait MixnetClientStorage { pub trait MixnetClientStorage {
type KeyStore: KeyStore; type KeyStore: KeyStore;
type ReplyStore: ReplyStorageBackend; type ReplyStore: ReplyStorageBackend;
type CredentialStore: CredentialStorage; type CredentialStore: CredentialStorage;
type GatewayDetailsStore: GatewayDetailsStore;
// this is a TERRIBLE name... // this is a TERRIBLE name...
// fn into_split(self) -> (Self::KeyStore, Self::ReplyStore, Self::CredentialStore, Self::GatewayDetailsStore); fn into_split(self) -> (Self::KeyStore, Self::ReplyStore, Self::CredentialStore);
fn into_runtime_stores(self) -> (Self::ReplyStore, Self::CredentialStore);
fn key_store(&self) -> &Self::KeyStore; fn key_store(&self) -> &Self::KeyStore;
fn reply_store(&self) -> &Self::ReplyStore; fn reply_store(&self) -> &Self::ReplyStore;
fn credential_store(&self) -> &Self::CredentialStore; fn credential_store(&self) -> &Self::CredentialStore;
fn gateway_details_store(&self) -> &Self::GatewayDetailsStore;
} }
#[derive(Default)] #[derive(Default)]
pub struct Ephemeral { pub struct Ephemeral {
key_store: InMemEphemeralKeys, key_store: InMemEphemeralKeys,
reply_store: reply_storage::Empty, reply_store: reply_storage::Empty,
credential_store: EphemeralCredentialStorage, credential_store: EphemeralStorage,
gateway_details_store: InMemGatewayDetails,
} }
impl Ephemeral { impl Ephemeral {
@@ -67,10 +56,9 @@ impl MixnetClientStorage for Ephemeral {
type KeyStore = InMemEphemeralKeys; type KeyStore = InMemEphemeralKeys;
type ReplyStore = reply_storage::Empty; type ReplyStore = reply_storage::Empty;
type CredentialStore = EphemeralCredentialStorage; type CredentialStore = EphemeralCredentialStorage;
type GatewayDetailsStore = InMemGatewayDetails;
fn into_runtime_stores(self) -> (Self::ReplyStore, Self::CredentialStore) { fn into_split(self) -> (Self::KeyStore, Self::ReplyStore, Self::CredentialStore) {
(self.reply_store, self.credential_store) (self.key_store, self.reply_store, self.credential_store)
} }
fn key_store(&self) -> &Self::KeyStore { fn key_store(&self) -> &Self::KeyStore {
@@ -84,10 +72,6 @@ impl MixnetClientStorage for Ephemeral {
fn credential_store(&self) -> &Self::CredentialStore { fn credential_store(&self) -> &Self::CredentialStore {
&self.credential_store &self.credential_store
} }
fn gateway_details_store(&self) -> &Self::GatewayDetailsStore {
&self.gateway_details_store
}
} }
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))] #[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
@@ -95,7 +79,6 @@ pub struct OnDiskPersistent {
pub(crate) key_store: OnDiskKeys, pub(crate) key_store: OnDiskKeys,
pub(crate) reply_store: fs_backend::Backend, pub(crate) reply_store: fs_backend::Backend,
pub(crate) credential_store: PersistentCredentialStorage, pub(crate) credential_store: PersistentCredentialStorage,
pub(crate) gateway_details_store: OnDiskGatewayDetails,
} }
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))] #[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
@@ -104,38 +87,31 @@ impl OnDiskPersistent {
key_store: OnDiskKeys, key_store: OnDiskKeys,
reply_store: fs_backend::Backend, reply_store: fs_backend::Backend,
credential_store: PersistentCredentialStorage, credential_store: PersistentCredentialStorage,
gateway_details_store: OnDiskGatewayDetails,
) -> Self { ) -> Self {
Self { Self {
key_store, key_store,
reply_store, reply_store,
credential_store, credential_store,
gateway_details_store,
} }
} }
pub async fn from_paths( pub async fn from_config<T>(config: &Config<T>) -> Result<Self, ClientCoreError> {
paths: CommonClientPaths, let pathfinder = ClientKeyPathfinder::new_from_config(config);
debug_config: &config::DebugConfig, let key_store = OnDiskKeys::new(pathfinder);
) -> Result<Self, ClientCoreError> {
let key_store = OnDiskKeys::new(paths.keys);
let reply_store = non_wasm_helpers::setup_fs_reply_surb_backend( let reply_store = non_wasm_helpers::setup_fs_reply_surb_backend(
paths.reply_surb_database, config.get_reply_surb_database_path(),
&debug_config.reply_surbs, &config.get_debug_config().reply_surbs,
) )
.await?; .await?;
let credential_store = let credential_store =
nym_credential_storage::initialise_persistent_storage(paths.credentials_database).await; nym_credential_storage::initialise_persistent_storage(config.get_database_path()).await;
let gateway_details_store = OnDiskGatewayDetails::new(paths.gateway_details);
Ok(OnDiskPersistent { Ok(OnDiskPersistent {
key_store, key_store,
reply_store, reply_store,
credential_store, credential_store,
gateway_details_store,
}) })
} }
} }
@@ -145,10 +121,9 @@ impl MixnetClientStorage for OnDiskPersistent {
type KeyStore = OnDiskKeys; type KeyStore = OnDiskKeys;
type ReplyStore = fs_backend::Backend; type ReplyStore = fs_backend::Backend;
type CredentialStore = PersistentCredentialStorage; type CredentialStore = PersistentCredentialStorage;
type GatewayDetailsStore = OnDiskGatewayDetails;
fn into_runtime_stores(self) -> (Self::ReplyStore, Self::CredentialStore) { fn into_split(self) -> (Self::KeyStore, Self::ReplyStore, Self::CredentialStore) {
(self.reply_store, self.credential_store) (self.key_store, self.reply_store, self.credential_store)
} }
fn key_store(&self) -> &Self::KeyStore { fn key_store(&self) -> &Self::KeyStore {
@@ -162,8 +137,4 @@ impl MixnetClientStorage for OnDiskPersistent {
fn credential_store(&self) -> &Self::CredentialStore { fn credential_store(&self) -> &Self::CredentialStore {
&self.credential_store &self.credential_store
} }
fn gateway_details_store(&self) -> &Self::GatewayDetailsStore {
&self.gateway_details_store
}
} }
@@ -10,7 +10,7 @@ use log::*;
use nym_sphinx::acknowledgements::AckKey; use nym_sphinx::acknowledgements::AckKey;
use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::cover::generate_loop_cover_packet; use nym_sphinx::cover::generate_loop_cover_packet;
use nym_sphinx::params::{PacketSize, PacketType}; use nym_sphinx::params::PacketSize;
use nym_sphinx::utils::sample_poisson_duration; use nym_sphinx::utils::sample_poisson_duration;
use rand::{rngs::OsRng, CryptoRng, Rng}; use rand::{rngs::OsRng, CryptoRng, Rng};
use std::pin::Pin; use std::pin::Pin;
@@ -63,8 +63,6 @@ where
/// Optional secondary predefined packet size used for the loop cover messages. /// Optional secondary predefined packet size used for the loop cover messages.
secondary_packet_size: Option<PacketSize>, secondary_packet_size: Option<PacketSize>,
packet_type: PacketType,
} }
impl<R> Stream for LoopCoverTrafficStream<R> impl<R> Stream for LoopCoverTrafficStream<R>
@@ -137,7 +135,6 @@ impl LoopCoverTrafficStream<OsRng> {
topology_access, topology_access,
primary_packet_size: traffic_config.primary_packet_size, primary_packet_size: traffic_config.primary_packet_size,
secondary_packet_size: traffic_config.secondary_packet_size, secondary_packet_size: traffic_config.secondary_packet_size,
packet_type: traffic_config.packet_type,
} }
} }
@@ -197,7 +194,7 @@ impl LoopCoverTrafficStream<OsRng> {
self.average_ack_delay, self.average_ack_delay,
self.cover_traffic.loop_cover_traffic_average_delay, self.cover_traffic.loop_cover_traffic_average_delay,
cover_traffic_packet_size, cover_traffic_packet_size,
self.packet_type, nym_sphinx::params::PacketType::Mix,
) )
.expect("Somehow failed to generate a loop cover message with a valid topology"); .expect("Somehow failed to generate a loop cover message with a valid topology");
@@ -6,7 +6,6 @@ use nym_crypto::asymmetric::{encryption, identity};
use nym_gateway_requests::registration::handshake::SharedKeys; use nym_gateway_requests::registration::handshake::SharedKeys;
use nym_sphinx::acknowledgements::AckKey; use nym_sphinx::acknowledgements::AckKey;
use rand::{CryptoRng, RngCore}; use rand::{CryptoRng, RngCore};
use std::fmt::{Debug, Formatter};
use std::sync::Arc; use std::sync::Arc;
use zeroize::ZeroizeOnDrop; use zeroize::ZeroizeOnDrop;
@@ -21,16 +20,6 @@ pub enum ManagedKeys {
Invalidated, Invalidated,
} }
impl Debug for ManagedKeys {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
ManagedKeys::Initial(_) => write!(f, "initial"),
ManagedKeys::FullyDerived(_) => write!(f, "fully derived"),
ManagedKeys::Invalidated => write!(f, "invalidated"),
}
}
}
impl From<KeyManagerBuilder> for ManagedKeys { impl From<KeyManagerBuilder> for ManagedKeys {
fn from(value: KeyManagerBuilder) -> Self { fn from(value: KeyManagerBuilder) -> Self {
ManagedKeys::Initial(value) ManagedKeys::Initial(value)
@@ -95,11 +84,6 @@ impl ManagedKeys {
} }
} }
pub fn must_get_gateway_shared_key(&self) -> Arc<SharedKeys> {
self.gateway_shared_key()
.expect("failed to extract gateway shared key")
}
pub fn gateway_shared_key(&self) -> Option<Arc<SharedKeys>> { pub fn gateway_shared_key(&self) -> Option<Arc<SharedKeys>> {
match self { match self {
ManagedKeys::Initial(_) => None, ManagedKeys::Initial(_) => None,
@@ -124,17 +108,6 @@ impl ManagedKeys {
} }
} }
pub fn ensure_gateway_key(&self, gateway_shared_key: Arc<SharedKeys>) {
if let ManagedKeys::FullyDerived(key_manager) = &self {
if !Arc::ptr_eq(&key_manager.gateway_shared_key, &gateway_shared_key)
|| key_manager.gateway_shared_key != gateway_shared_key
{
// this should NEVER happen thus panic here
panic!("derived fresh gateway shared key whilst already holding one!")
}
}
}
pub async fn deal_with_gateway_key<S: KeyStore>( pub async fn deal_with_gateway_key<S: KeyStore>(
&mut self, &mut self,
gateway_shared_key: Arc<SharedKeys>, gateway_shared_key: Arc<SharedKeys>,
@@ -147,7 +120,12 @@ impl ManagedKeys {
key_manager key_manager
} }
ManagedKeys::FullyDerived(key_manager) => { ManagedKeys::FullyDerived(key_manager) => {
self.ensure_gateway_key(gateway_shared_key); if !Arc::ptr_eq(&key_manager.gateway_shared_key, &gateway_shared_key)
|| key_manager.gateway_shared_key != gateway_shared_key
{
// this should NEVER happen thus panic here
panic!("derived fresh gateway shared key whilst already holding one!")
}
key_manager key_manager
} }
ManagedKeys::Invalidated => unreachable!("the managed keys got invalidated"), ManagedKeys::Invalidated => unreachable!("the managed keys got invalidated"),
@@ -4,10 +4,11 @@
use crate::client::key_manager::KeyManager; use crate::client::key_manager::KeyManager;
use async_trait::async_trait; use async_trait::async_trait;
use std::error::Error; use std::error::Error;
use tokio::sync::Mutex;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use crate::config::disk_persistence::keys_paths::ClientKeysPaths; use crate::config::persistence::key_pathfinder::ClientKeyPathfinder;
#[cfg(not(target_arch = "wasm32"))]
use crate::config::Config;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use nym_crypto::asymmetric::{encryption, identity}; use nym_crypto::asymmetric::{encryption, identity};
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
@@ -37,7 +38,6 @@ pub enum OnDiskKeysError {
KeyPairLoadFailure { KeyPairLoadFailure {
keys: String, keys: String,
paths: nym_pemstore::KeyPairPath, paths: nym_pemstore::KeyPairPath,
#[source]
err: std::io::Error, err: std::io::Error,
}, },
@@ -45,7 +45,6 @@ pub enum OnDiskKeysError {
KeyPairStoreFailure { KeyPairStoreFailure {
keys: String, keys: String,
paths: nym_pemstore::KeyPairPath, paths: nym_pemstore::KeyPairPath,
#[source]
err: std::io::Error, err: std::io::Error,
}, },
@@ -53,7 +52,6 @@ pub enum OnDiskKeysError {
KeyLoadFailure { KeyLoadFailure {
key: String, key: String,
path: String, path: String,
#[source]
err: std::io::Error, err: std::io::Error,
}, },
@@ -61,47 +59,30 @@ pub enum OnDiskKeysError {
KeyStoreFailure { KeyStoreFailure {
key: String, key: String,
path: String, path: String,
#[source]
err: std::io::Error, err: std::io::Error,
}, },
} }
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
pub struct OnDiskKeys { pub struct OnDiskKeys {
paths: ClientKeysPaths, pathfinder: ClientKeyPathfinder,
} }
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
impl From<ClientKeysPaths> for OnDiskKeys { impl From<ClientKeyPathfinder> for OnDiskKeys {
fn from(paths: ClientKeysPaths) -> Self { fn from(pathfinder: ClientKeyPathfinder) -> Self {
OnDiskKeys { paths } OnDiskKeys { pathfinder }
} }
} }
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
impl OnDiskKeys { impl OnDiskKeys {
pub fn new(paths: ClientKeysPaths) -> Self { pub fn new(pathfinder: ClientKeyPathfinder) -> Self {
OnDiskKeys { paths } OnDiskKeys { pathfinder }
} }
#[doc(hidden)] pub fn from_config<T>(config: &Config<T>) -> Self {
pub fn ephemeral_load_gateway_keys( OnDiskKeys::new(ClientKeyPathfinder::new_from_config(config))
&self,
) -> Result<zeroize::Zeroizing<SharedKeys>, OnDiskKeysError> {
self.load_key(self.paths.gateway_shared_key(), "gateway shared keys")
.map(zeroize::Zeroizing::new)
}
#[doc(hidden)]
pub fn load_encryption_keypair(&self) -> Result<encryption::KeyPair, OnDiskKeysError> {
let encryption_paths = self.paths.encryption_key_pair_path();
self.load_keypair(encryption_paths, "encryption keys")
}
#[doc(hidden)]
pub fn load_identity_keypair(&self) -> Result<identity::KeyPair, OnDiskKeysError> {
let identity_paths = self.paths.identity_key_pair_path();
self.load_keypair(identity_paths, "identity keys")
} }
fn load_key<T: PemStorableKey>( fn load_key<T: PemStorableKey>(
@@ -157,12 +138,17 @@ impl OnDiskKeys {
} }
fn load_keys(&self) -> Result<KeyManager, OnDiskKeysError> { fn load_keys(&self) -> Result<KeyManager, OnDiskKeysError> {
let identity_keypair = self.load_identity_keypair()?; let identity_paths = self.pathfinder.identity_key_pair_path();
let encryption_keypair = self.load_encryption_keypair()?; let encryption_paths = self.pathfinder.encryption_key_pair_path();
let ack_key: AckKey = self.load_key(self.paths.ack_key(), "ack key")?; let identity_keypair: identity::KeyPair =
self.load_keypair(identity_paths, "identity keys")?;
let encryption_keypair: encryption::KeyPair =
self.load_keypair(encryption_paths, "encryption keys")?;
let ack_key: AckKey = self.load_key(self.pathfinder.ack_key(), "ack key")?;
let gateway_shared_key: SharedKeys = let gateway_shared_key: SharedKeys =
self.load_key(self.paths.gateway_shared_key(), "gateway shared keys")?; self.load_key(self.pathfinder.gateway_shared_key(), "gateway shared keys")?;
Ok(KeyManager::from_keys( Ok(KeyManager::from_keys(
identity_keypair, identity_keypair,
@@ -173,8 +159,8 @@ impl OnDiskKeys {
} }
fn store_keys(&self, keys: &KeyManager) -> Result<(), OnDiskKeysError> { fn store_keys(&self, keys: &KeyManager) -> Result<(), OnDiskKeysError> {
let identity_paths = self.paths.identity_key_pair_path(); let identity_paths = self.pathfinder.identity_key_pair_path();
let encryption_paths = self.paths.encryption_key_pair_path(); let encryption_paths = self.pathfinder.encryption_key_pair_path();
self.store_keypair( self.store_keypair(
keys.identity_keypair.as_ref(), keys.identity_keypair.as_ref(),
@@ -187,10 +173,10 @@ impl OnDiskKeys {
"encryption keys", "encryption keys",
)?; )?;
self.store_key(keys.ack_key.as_ref(), self.paths.ack_key(), "ack key")?; self.store_key(keys.ack_key.as_ref(), self.pathfinder.ack_key(), "ack key")?;
self.store_key( self.store_key(
keys.gateway_shared_key.as_ref(), keys.gateway_shared_key.as_ref(),
self.paths.gateway_shared_key(), self.pathfinder.gateway_shared_key(),
"gateway shared keys", "gateway shared keys",
)?; )?;
@@ -213,12 +199,10 @@ impl KeyStore for OnDiskKeys {
} }
#[derive(Default)] #[derive(Default)]
pub struct InMemEphemeralKeys { pub struct InMemEphemeralKeys;
keys: Mutex<Option<KeyManager>>,
}
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
#[error("old ephemeral keys can't be loaded from storage")] #[error("ephemeral keys can't be loaded from storage")]
pub struct EphemeralKeysError; pub struct EphemeralKeysError;
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
@@ -227,11 +211,10 @@ impl KeyStore for InMemEphemeralKeys {
type StorageError = EphemeralKeysError; type StorageError = EphemeralKeysError;
async fn load_keys(&self) -> Result<KeyManager, Self::StorageError> { async fn load_keys(&self) -> Result<KeyManager, Self::StorageError> {
self.keys.lock().await.clone().ok_or(EphemeralKeysError) Err(EphemeralKeysError)
} }
async fn store_keys(&self, keys: &KeyManager) -> Result<(), Self::StorageError> { async fn store_keys(&self, _keys: &KeyManager) -> Result<(), Self::StorageError> {
*self.keys.lock().await = Some(keys.clone());
Ok(()) Ok(())
} }
} }
@@ -1,4 +1,4 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use self::sending_delay_controller::SendingDelayController; use self::sending_delay_controller::SendingDelayController;
@@ -247,7 +247,7 @@ where
self.config.average_ack_delay, self.config.average_ack_delay,
self.config.traffic.average_packet_delay, self.config.traffic.average_packet_delay,
cover_traffic_packet_size, cover_traffic_packet_size,
self.config.traffic.packet_type, self.config.traffic.packet_type.unwrap_or_default(),
) )
.expect( .expect(
"Somehow failed to generate a loop cover message with a valid topology", "Somehow failed to generate a loop cover message with a valid topology",
@@ -1,117 +0,0 @@
// 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 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_GATEWAY_SHARED_KEY_FILENAME: &str = "gateway_shared.pem";
pub const DEFAULT_ACK_KEY_FILENAME: &str = "ack_key.pem";
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
pub struct ClientKeysPaths {
/// Path to file containing private identity key.
pub private_identity_key_file: PathBuf,
/// Path to file containing public identity key.
pub public_identity_key_file: PathBuf,
/// Path to file containing private encryption key.
pub private_encryption_key_file: PathBuf,
/// Path to file containing public encryption key.
pub public_encryption_key_file: PathBuf,
/// Path to file containing shared key derived with the specified gateway that is used
/// for all communication with it.
pub gateway_shared_key_file: PathBuf,
/// Path to file containing key used for encrypting and decrypting the content of an
/// acknowledgement so that nobody besides the client knows which packet it refers to.
pub ack_key_file: PathBuf,
}
impl ClientKeysPaths {
pub fn new_default<P: AsRef<Path>>(base_data_directory: P) -> Self {
let base_dir = base_data_directory.as_ref();
ClientKeysPaths {
private_identity_key_file: base_dir.join(DEFAULT_PRIVATE_IDENTITY_KEY_FILENAME),
public_identity_key_file: base_dir.join(DEFAULT_PUBLIC_IDENTITY_KEY_FILENAME),
private_encryption_key_file: base_dir.join(DEFAULT_PRIVATE_ENCRYPTION_KEY_FILENAME),
public_encryption_key_file: base_dir.join(DEFAULT_PUBLIC_ENCRYPTION_KEY_FILENAME),
gateway_shared_key_file: base_dir.join(DEFAULT_GATEWAY_SHARED_KEY_FILENAME),
ack_key_file: base_dir.join(DEFAULT_ACK_KEY_FILENAME),
}
}
pub fn identity_key_pair_path(&self) -> nym_pemstore::KeyPairPath {
nym_pemstore::KeyPairPath::new(
self.private_identity_key().to_path_buf(),
self.public_identity_key().to_path_buf(),
)
}
pub fn encryption_key_pair_path(&self) -> nym_pemstore::KeyPairPath {
nym_pemstore::KeyPairPath::new(
self.private_encryption_key().to_path_buf(),
self.public_encryption_key().to_path_buf(),
)
}
pub fn any_file_exists(&self) -> bool {
matches!(self.public_identity_key_file.try_exists(), Ok(true))
|| matches!(self.private_identity_key_file.try_exists(), Ok(true))
|| matches!(self.public_encryption_key_file.try_exists(), Ok(true))
|| matches!(self.private_encryption_key_file.try_exists(), Ok(true))
|| matches!(self.gateway_shared_key_file.try_exists(), Ok(true))
|| matches!(self.ack_key_file.try_exists(), Ok(true))
}
pub fn any_file_exists_and_return(&self) -> Option<PathBuf> {
file_exists(&self.public_identity_key_file)
.or_else(|| file_exists(&self.private_identity_key_file))
.or_else(|| file_exists(&self.public_encryption_key_file))
.or_else(|| file_exists(&self.private_encryption_key_file))
.or_else(|| file_exists(&self.gateway_shared_key_file))
.or_else(|| file_exists(&self.ack_key_file))
}
pub fn gateway_key_file_exists(&self) -> bool {
matches!(self.gateway_shared_key_file.try_exists(), Ok(true))
}
pub fn private_identity_key(&self) -> &Path {
&self.private_identity_key_file
}
pub fn public_identity_key(&self) -> &Path {
&self.public_identity_key_file
}
pub fn private_encryption_key(&self) -> &Path {
&self.private_encryption_key_file
}
pub fn public_encryption_key(&self) -> &Path {
&self.public_encryption_key_file
}
pub fn gateway_shared_key(&self) -> &Path {
&self.gateway_shared_key_file
}
pub fn ack_key(&self) -> &Path {
&self.ack_key_file
}
}
fn file_exists(path: &Path) -> Option<PathBuf> {
if matches!(path.try_exists(), Ok(true)) {
return Some(path.to_path_buf());
}
None
}
@@ -1,42 +0,0 @@
// 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 const DEFAULT_GATEWAY_DETAILS_FILENAME: &str = "gateway_details.json";
pub const DEFAULT_REPLY_SURB_DB_FILENAME: &str = "persistent_reply_store.sqlite";
pub const DEFAULT_CREDENTIALS_DB_FILENAME: &str = "credentials_database.db";
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct CommonClientPaths {
pub keys: ClientKeysPaths,
/// 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.
pub gateway_details: 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_default<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_details: base_dir.join(DEFAULT_GATEWAY_DETAILS_FILENAME),
keys: ClientKeysPaths::new_default(base_data_directory),
}
}
}
@@ -1,30 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::disk_persistence::keys_paths::ClientKeysPaths;
use crate::config::disk_persistence::{CommonClientPaths, DEFAULT_GATEWAY_DETAILS_FILENAME};
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: ClientKeysPaths,
pub credentials_database: PathBuf,
pub reply_surb_database: PathBuf,
}
impl CommonClientPathsV1_1_20_2 {
pub fn upgrade_default(self) -> CommonClientPaths {
let data_dir = self
.reply_surb_database
.parent()
.expect("client paths upgrade failure");
CommonClientPaths {
keys: self.keys,
gateway_details: data_dir.join(DEFAULT_GATEWAY_DETAILS_FILENAME),
credentials_database: self.credentials_database,
reply_surb_database: self.reply_surb_database,
}
}
}
+432 -53
View File
@@ -1,10 +1,13 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use nym_config::defaults::NymNetworkDetails; use nym_config::defaults::NymNetworkDetails;
use nym_config::{NymConfig, OptionalSet, CRED_DB_FILE_NAME};
use nym_crypto::asymmetric::identity; use nym_crypto::asymmetric::identity;
use nym_sphinx::params::{PacketSize, PacketType}; use nym_sphinx::params::{PacketSize, PacketType};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::marker::PhantomData;
use std::path::{Path, PathBuf};
use std::time::Duration; use std::time::Duration;
use url::Url; use url::Url;
@@ -12,10 +15,19 @@ use crate::error::ClientCoreError;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
pub mod disk_persistence;
pub mod old_config_v1_1_13; pub mod old_config_v1_1_13;
pub mod old_config_v1_1_20; pub mod persistence;
pub mod old_config_v1_1_20_2;
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_GATEWAY_KEYS_FILENAME: &str = "gateway_shared.pem";
pub const DEFAULT_ACK_KEY_FILENAME: &str = "ack_key.pem";
pub const DEFAULT_REPLY_STORE_FILENAME: &str = "persistent_reply_store.sqlite";
pub const DEFAULT_CREDENTIAL_STORE_FILENAME: &str = CRED_DB_FILE_NAME;
pub const MISSING_VALUE: &str = "MISSING VALUE";
// 'DEBUG' // 'DEBUG'
const DEFAULT_ACK_WAIT_MULTIPLIER: f64 = 1.5; const DEFAULT_ACK_WAIT_MULTIPLIER: f64 = 1.5;
@@ -56,41 +68,172 @@ const DEFAULT_MAXIMUM_REPLY_SURB_AGE: Duration = Duration::from_secs(12 * 60 * 6
// 24 hours // 24 hours
const DEFAULT_MAXIMUM_REPLY_KEY_AGE: Duration = Duration::from_secs(24 * 60 * 60); const DEFAULT_MAXIMUM_REPLY_KEY_AGE: Duration = Duration::from_secs(24 * 60 * 60);
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)] pub fn missing_string_value() -> String {
#[serde(deny_unknown_fields)] MISSING_VALUE.to_string()
pub struct Config {
pub client: Client,
#[serde(default)]
pub debug: DebugConfig,
} }
impl Config { pub trait ClientCoreConfigTrait {
pub fn new<S: Into<String>>(id: S, version: S) -> Self { fn get_gateway_endpoint(&self) -> &GatewayEndpointConfig;
Config { }
client: Client::new_default(id, version),
debug: Default::default(),
}
}
pub fn from_client_config(client: Client, debug: DebugConfig) -> Self { #[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
Config { client, debug } #[serde(deny_unknown_fields)]
pub struct Config<T> {
client: Client<T>,
#[serde(default)]
logging: Logging,
#[serde(default)]
debug: DebugConfig,
}
impl<T> ClientCoreConfigTrait for Config<T> {
fn get_gateway_endpoint(&self) -> &GatewayEndpointConfig {
&self.client.gateway_endpoint
}
}
impl<T> OptionalSet for Config<T> where T: NymConfig {}
impl<T> Config<T> {
pub fn new<S: Into<String>>(id: S) -> Self
where
T: NymConfig,
{
Config::default().with_id(id)
} }
pub fn validate(&self) -> bool { pub fn validate(&self) -> bool {
// no other sections have explicit requirements (yet)
self.debug.validate() self.debug.validate()
} }
pub fn with_debug_config(mut self, debug: DebugConfig) -> Self { #[must_use]
self.debug = debug; pub fn with_id<S: Into<String>>(mut self, id: S) -> Self
where
T: NymConfig,
{
self.client.id = id.into();
self.set_empty_fields_to_defaults();
self self
} }
#[must_use]
#[doc(hidden)]
// TODO: this totally contradicts our trait... we REALLY have to refactor it...
pub fn reset_data_directory<P: AsRef<Path>>(mut self, dir: P) -> Self {
self.client.private_identity_key_file =
dir.as_ref().join(DEFAULT_PRIVATE_IDENTITY_KEY_FILENAME);
self.client.public_identity_key_file =
dir.as_ref().join(DEFAULT_PUBLIC_IDENTITY_KEY_FILENAME);
self.client.private_encryption_key_file =
dir.as_ref().join(DEFAULT_PRIVATE_ENCRYPTION_KEY_FILENAME);
self.client.public_encryption_key_file =
dir.as_ref().join(DEFAULT_PUBLIC_ENCRYPTION_KEY_FILENAME);
self.client.gateway_shared_key_file = dir.as_ref().join(DEFAULT_GATEWAY_KEYS_FILENAME);
self.client.ack_key_file = dir.as_ref().join(DEFAULT_ACK_KEY_FILENAME);
self.client.reply_surb_database_path = dir.as_ref().join(DEFAULT_REPLY_STORE_FILENAME);
self.client.database_path = dir.as_ref().join(DEFAULT_CREDENTIAL_STORE_FILENAME);
self
}
#[must_use]
#[doc(hidden)]
// TODO: this totally contradicts our trait... we REALLY have to refactor it...
pub fn reset_nym_root_directory<P: AsRef<Path>>(mut self, dir: P) -> Self
where
T: NymConfig,
{
self.client.nym_root_directory = dir.as_ref().to_owned();
self
}
pub fn set_empty_fields_to_defaults(&mut self) -> bool
where
T: NymConfig,
{
let id = &self.client.id;
let mut changes_made = false;
// identity key setting
if self.client.private_identity_key_file.as_os_str().is_empty() {
changes_made = true;
self.client.private_identity_key_file =
self::Client::<T>::default_private_identity_key_file(id);
}
if self.client.public_identity_key_file.as_os_str().is_empty() {
changes_made = true;
self.client.public_identity_key_file =
self::Client::<T>::default_public_identity_key_file(id);
}
// encryption key setting
if self
.client
.private_encryption_key_file
.as_os_str()
.is_empty()
{
changes_made = true;
self.client.private_encryption_key_file =
self::Client::<T>::default_private_encryption_key_file(id);
}
if self
.client
.public_encryption_key_file
.as_os_str()
.is_empty()
{
changes_made = true;
self.client.public_encryption_key_file =
self::Client::<T>::default_public_encryption_key_file(id);
}
// shared gateway key setting
if self.client.gateway_shared_key_file.as_os_str().is_empty() {
changes_made = true;
self.client.gateway_shared_key_file =
self::Client::<T>::default_gateway_shared_key_file(id);
}
// ack key setting
if self.client.ack_key_file.as_os_str().is_empty() {
changes_made = true;
self.client.ack_key_file = self::Client::<T>::default_ack_key_file(id);
}
if self.client.reply_surb_database_path.as_os_str().is_empty() {
changes_made = true;
self.client.reply_surb_database_path =
self::Client::<T>::default_reply_surb_database_path(id);
}
if self.client.database_path.as_os_str().is_empty() {
changes_made = true;
self.client.database_path = self::Client::<T>::default_database_path(id);
}
changes_made
}
pub fn with_disabled_credentials(mut self, disabled_credentials_mode: bool) -> Self { pub fn with_disabled_credentials(mut self, disabled_credentials_mode: bool) -> Self {
self.client.disabled_credentials_mode = disabled_credentials_mode; self.client.disabled_credentials_mode = disabled_credentials_mode;
self self
} }
pub fn set_gateway_endpoint(&mut self, gateway_endpoint: GatewayEndpointConfig) {
self.client.gateway_endpoint = gateway_endpoint;
}
pub fn with_gateway_endpoint(mut self, gateway_endpoint: GatewayEndpointConfig) -> Self {
self.client.gateway_endpoint = gateway_endpoint;
self
}
pub fn with_gateway_id<S: Into<String>>(&mut self, id: S) {
self.client.gateway_endpoint.gateway_id = id.into();
}
pub fn with_custom_nyxd(mut self, urls: Vec<Url>) -> Self { pub fn with_custom_nyxd(mut self, urls: Vec<Url>) -> Self {
self.client.nyxd_urls = urls; self.client.nyxd_urls = urls;
self self
@@ -117,7 +260,7 @@ impl Config {
} }
pub fn with_packet_type(mut self, packet_type: PacketType) -> Self { pub fn with_packet_type(mut self, packet_type: PacketType) -> Self {
self.debug.traffic.packet_type = packet_type; self.client.packet_type = Some(packet_type);
self self
} }
@@ -137,11 +280,6 @@ impl Config {
self self
} }
pub fn with_disabled_topology_refresh(mut self, disable_topology_refresh: bool) -> Self {
self.debug.topology.disable_refreshing = disable_topology_refresh;
self
}
pub fn set_no_cover_traffic(&mut self) { pub fn set_no_cover_traffic(&mut self) {
self.debug.cover_traffic.disable_loop_cover_traffic_stream = true; self.debug.cover_traffic.disable_loop_cover_traffic_stream = true;
self.debug.traffic.disable_main_poisson_packet_distribution = true; self.debug.traffic.disable_main_poisson_packet_distribution = true;
@@ -159,6 +297,34 @@ impl Config {
self.client.disabled_credentials_mode self.client.disabled_credentials_mode
} }
pub fn get_nym_root_directory(&self) -> PathBuf {
self.client.nym_root_directory.clone()
}
pub fn get_private_identity_key_file(&self) -> PathBuf {
self.client.private_identity_key_file.clone()
}
pub fn get_public_identity_key_file(&self) -> PathBuf {
self.client.public_identity_key_file.clone()
}
pub fn get_private_encryption_key_file(&self) -> PathBuf {
self.client.private_encryption_key_file.clone()
}
pub fn get_public_encryption_key_file(&self) -> PathBuf {
self.client.public_encryption_key_file.clone()
}
pub fn get_gateway_shared_key_file(&self) -> PathBuf {
self.client.gateway_shared_key_file.clone()
}
pub fn get_ack_key_file(&self) -> PathBuf {
self.client.ack_key_file.clone()
}
pub fn get_validator_endpoints(&self) -> Vec<Url> { pub fn get_validator_endpoints(&self) -> Vec<Url> {
self.client.nyxd_urls.clone() self.client.nyxd_urls.clone()
} }
@@ -166,6 +332,139 @@ impl Config {
pub fn get_nym_api_endpoints(&self) -> Vec<Url> { pub fn get_nym_api_endpoints(&self) -> Vec<Url> {
self.client.nym_api_urls.clone() self.client.nym_api_urls.clone()
} }
pub fn get_gateway_id(&self) -> String {
self.client.gateway_endpoint.gateway_id.clone()
}
pub fn get_gateway_owner(&self) -> String {
self.client.gateway_endpoint.gateway_owner.clone()
}
pub fn get_gateway_listener(&self) -> String {
self.client.gateway_endpoint.gateway_listener.clone()
}
pub fn get_gateway_endpoint_config(&self) -> &GatewayEndpointConfig {
&self.client.gateway_endpoint
}
pub fn get_database_path(&self) -> PathBuf {
self.client.database_path.clone()
}
pub fn get_reply_surb_database_path(&self) -> PathBuf {
self.client.reply_surb_database_path.clone()
}
pub fn get_version(&self) -> &str {
&self.client.version
}
// Debug getters
pub fn get_debug_config(&self) -> &DebugConfig {
&self.debug
}
pub fn get_average_packet_delay(&self) -> Duration {
self.debug.traffic.average_packet_delay
}
pub fn get_average_ack_delay(&self) -> Duration {
self.debug.acknowledgements.average_ack_delay
}
pub fn get_ack_wait_multiplier(&self) -> f64 {
self.debug.acknowledgements.ack_wait_multiplier
}
pub fn get_ack_wait_addition(&self) -> Duration {
self.debug.acknowledgements.ack_wait_addition
}
pub fn get_loop_cover_traffic_average_delay(&self) -> Duration {
self.debug.cover_traffic.loop_cover_traffic_average_delay
}
pub fn get_message_sending_average_delay(&self) -> Duration {
self.debug.traffic.message_sending_average_delay
}
pub fn get_gateway_response_timeout(&self) -> Duration {
self.debug.gateway_connection.gateway_response_timeout
}
pub fn get_topology_refresh_rate(&self) -> Duration {
self.debug.topology.topology_refresh_rate
}
pub fn get_topology_resolution_timeout(&self) -> Duration {
self.debug.topology.topology_resolution_timeout
}
pub fn get_disabled_loop_cover_traffic_stream(&self) -> bool {
self.debug.cover_traffic.disable_loop_cover_traffic_stream
}
pub fn get_disabled_main_poisson_packet_distribution(&self) -> bool {
self.debug.traffic.disable_main_poisson_packet_distribution
}
pub fn get_minimum_reply_surb_storage_threshold(&self) -> usize {
self.debug.reply_surbs.minimum_reply_surb_storage_threshold
}
pub fn get_maximum_reply_surb_storage_threshold(&self) -> usize {
self.debug.reply_surbs.maximum_reply_surb_storage_threshold
}
pub fn get_minimum_reply_surb_request_size(&self) -> u32 {
self.debug.reply_surbs.minimum_reply_surb_request_size
}
pub fn get_maximum_reply_surb_request_size(&self) -> u32 {
self.debug.reply_surbs.maximum_reply_surb_request_size
}
pub fn get_maximum_allowed_reply_surb_request_size(&self) -> u32 {
self.debug
.reply_surbs
.maximum_allowed_reply_surb_request_size
}
pub fn get_maximum_reply_surb_rerequest_waiting_period(&self) -> Duration {
self.debug
.reply_surbs
.maximum_reply_surb_rerequest_waiting_period
}
pub fn get_maximum_reply_surb_drop_waiting_period(&self) -> Duration {
self.debug
.reply_surbs
.maximum_reply_surb_drop_waiting_period
}
pub fn get_maximum_reply_surb_age(&self) -> Duration {
self.debug.reply_surbs.maximum_reply_surb_age
}
pub fn get_maximum_reply_key_age(&self) -> Duration {
self.debug.reply_surbs.maximum_reply_key_age
}
pub fn get_packet_type(&self) -> PacketType {
self.client.packet_type.unwrap_or(PacketType::Mix)
}
}
impl<T: NymConfig> Default for Config<T> {
fn default() -> Self {
Config {
client: Client::<T>::default(),
logging: Default::default(),
debug: Default::default(),
}
}
} }
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)] #[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
@@ -218,10 +517,9 @@ impl From<nym_topology::gateway::Node> for GatewayEndpointConfig {
} }
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)] #[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
// note: the deny_unknown_fields is VITAL here to allow upgrades from v1.1.20_2 pub struct Client<T> {
#[serde(deny_unknown_fields)]
pub struct Client {
/// Version of the client for which this configuration was created. /// Version of the client for which this configuration was created.
#[serde(default = "missing_string_value")]
pub version: String, pub version: String,
/// ID specifies the human readable ID of this particular client. /// ID specifies the human readable ID of this particular client.
@@ -229,7 +527,6 @@ pub struct Client {
/// Indicates whether this client is running in a disabled credentials mode, thus attempting /// Indicates whether this client is running in a disabled credentials mode, thus attempting
/// to claim bandwidth without presenting bandwidth credentials. /// to claim bandwidth without presenting bandwidth credentials.
// TODO: this should be moved to `debug.gateway_connection`
#[serde(default)] #[serde(default)]
pub disabled_credentials_mode: bool, pub disabled_credentials_mode: bool,
@@ -240,10 +537,52 @@ pub struct Client {
/// Addresses to APIs running on validator from which the client gets the view of the network. /// Addresses to APIs running on validator from which the client gets the view of the network.
#[serde(alias = "validator_api_urls")] #[serde(alias = "validator_api_urls")]
pub nym_api_urls: Vec<Url>, pub nym_api_urls: Vec<Url>,
/// Path to file containing private identity key.
pub private_identity_key_file: PathBuf,
/// Path to file containing public identity key.
pub public_identity_key_file: PathBuf,
/// Path to file containing private encryption key.
pub private_encryption_key_file: PathBuf,
/// Path to file containing public encryption key.
pub public_encryption_key_file: PathBuf,
/// Path to file containing shared key derived with the specified gateway that is used
/// for all communication with it.
pub gateway_shared_key_file: PathBuf,
/// Path to file containing key used for encrypting and decrypting the content of an
/// acknowledgement so that nobody besides the client knows which packet it refers to.
pub ack_key_file: PathBuf,
/// Information regarding how the client should send data to gateway.
pub gateway_endpoint: GatewayEndpointConfig,
/// Path to the database containing bandwidth credentials of this client.
pub database_path: PathBuf,
/// Path to the persistent store for received reply surbs, unused encryption keys and used sender tags.
// this was set to use #[serde(default)] for the purposes of compatibility for multi-surbs introduced in 1.1.4.
// if you're reading this message and we have already introduced some breaking changes, feel free
// to remove that attribute since at this point the client configs should have gotten regenerated
#[serde(default)]
pub reply_surb_database_path: PathBuf,
/// nym_home_directory specifies absolute path to the home nym Clients directory.
/// It is expected to use default value and hence .toml file should not redefine this field.
pub nym_root_directory: PathBuf,
#[serde(skip)]
pub super_struct: PhantomData<T>,
pub packet_type: Option<PacketType>,
} }
impl Client { impl<T: NymConfig> Default for Client<T> {
pub fn new_default<S: Into<String>>(id: S, version: S) -> Self { fn default() -> Self {
let network = NymNetworkDetails::new_mainnet(); let network = NymNetworkDetails::new_mainnet();
let nyxd_urls = network let nyxd_urls = network
.endpoints .endpoints
@@ -256,31 +595,71 @@ impl Client {
.filter_map(|validator| validator.api_url()) .filter_map(|validator| validator.api_url())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if nym_api_urls.is_empty() {
panic!("we do not have any default nym-api urls available!")
}
// there must be explicit checks for whether id is not empty later
Client { Client {
version: version.into(), version: env!("CARGO_PKG_VERSION").to_string(),
id: id.into(), id: "".to_string(),
disabled_credentials_mode: true, disabled_credentials_mode: true,
nyxd_urls, nyxd_urls,
nym_api_urls, nym_api_urls,
} private_identity_key_file: Default::default(),
} public_identity_key_file: Default::default(),
private_encryption_key_file: Default::default(),
pub fn new<S: Into<String>>( public_encryption_key_file: Default::default(),
id: S, gateway_shared_key_file: Default::default(),
disabled_credentials_mode: bool, ack_key_file: Default::default(),
nyxd_urls: Vec<Url>, gateway_endpoint: Default::default(),
nym_api_urls: Vec<Url>, database_path: Default::default(),
) -> Self { reply_surb_database_path: Default::default(),
Client { nym_root_directory: T::default_root_directory(),
version: env!("CARGO_PKG_VERSION").to_string(), super_struct: Default::default(),
id: id.into(), packet_type: Default::default(),
disabled_credentials_mode,
nyxd_urls,
nym_api_urls,
} }
} }
} }
impl<T: NymConfig> Client<T> {
fn default_private_identity_key_file(id: &str) -> PathBuf {
T::default_data_directory(id).join("private_identity.pem")
}
fn default_public_identity_key_file(id: &str) -> PathBuf {
T::default_data_directory(id).join("public_identity.pem")
}
fn default_private_encryption_key_file(id: &str) -> PathBuf {
T::default_data_directory(id).join("private_encryption.pem")
}
fn default_public_encryption_key_file(id: &str) -> PathBuf {
T::default_data_directory(id).join("public_encryption.pem")
}
fn default_gateway_shared_key_file(id: &str) -> PathBuf {
T::default_data_directory(id).join("gateway_shared.pem")
}
fn default_ack_key_file(id: &str) -> PathBuf {
T::default_data_directory(id).join("ack_key.pem")
}
fn default_reply_surb_database_path(id: &str) -> PathBuf {
T::default_data_directory(id).join("persistent_reply_store.sqlite")
}
fn default_database_path(id: &str) -> PathBuf {
T::default_data_directory(id).join(CRED_DB_FILE_NAME)
}
}
#[derive(Debug, Clone, Default, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Logging {}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)] #[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default)] #[serde(default)]
pub struct Traffic { pub struct Traffic {
@@ -311,7 +690,7 @@ pub struct Traffic {
/// Do not set it it unless you understand the consequences of that change. /// Do not set it it unless you understand the consequences of that change.
pub secondary_packet_size: Option<PacketSize>, pub secondary_packet_size: Option<PacketSize>,
pub packet_type: PacketType, pub packet_type: Option<PacketType>,
} }
impl Traffic { impl Traffic {
@@ -335,7 +714,7 @@ impl Default for Traffic {
disable_main_poisson_packet_distribution: false, disable_main_poisson_packet_distribution: false,
primary_packet_size: PacketSize::RegularPacket, primary_packet_size: PacketSize::RegularPacket,
secondary_packet_size: None, secondary_packet_size: None,
packet_type: PacketType::Mix, packet_type: None,
} }
} }
} }
@@ -1,18 +1,19 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::config::old_config_v1_1_20::{ use crate::config::{
AcknowledgementsV1_1_20, ClientV1_1_20, ConfigV1_1_20, CoverTrafficV1_1_20, DebugConfigV1_1_20, Acknowledgements, Client, Config, CoverTraffic, DebugConfig, GatewayConnection, Logging,
GatewayConnectionV1_1_20, LoggingV1_1_20, ReplySurbsV1_1_20, TopologyV1_1_20, TrafficV1_1_20, ReplySurbs, Topology, Traffic, DEFAULT_ACK_WAIT_ADDITION, DEFAULT_ACK_WAIT_MULTIPLIER,
DEFAULT_ACK_WAIT_ADDITION, DEFAULT_ACK_WAIT_MULTIPLIER, DEFAULT_AVERAGE_PACKET_DELAY, DEFAULT_AVERAGE_PACKET_DELAY, DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
DEFAULT_GATEWAY_RESPONSE_TIMEOUT, DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY, DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY, DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE,
DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE, DEFAULT_MAXIMUM_REPLY_KEY_AGE, DEFAULT_MAXIMUM_REPLY_KEY_AGE, DEFAULT_MAXIMUM_REPLY_SURB_AGE,
DEFAULT_MAXIMUM_REPLY_SURB_AGE, DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD, DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD, DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE,
DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE, DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD, DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD,
DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD, DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY, DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD, DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE, DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD, DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE, DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
DEFAULT_TOPOLOGY_REFRESH_RATE, DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT, DEFAULT_TOPOLOGY_REFRESH_RATE, DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
}; };
use nym_config::NymConfig;
use nym_sphinx::params::PacketSize; use nym_sphinx::params::PacketSize;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::marker::PhantomData; use std::marker::PhantomData;
@@ -39,21 +40,21 @@ impl From<ExtendedPacketSize> for PacketSize {
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)] #[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct OldConfigV1_1_13<T> { pub struct OldConfigV1_1_13<T> {
pub client: ClientV1_1_20<T>, pub client: Client<T>,
#[serde(default)] #[serde(default)]
pub logging: OldLoggingV1_1_13, logging: Logging,
#[serde(default)] #[serde(default)]
pub debug: OldDebugConfigV1_1_13, debug: OldDebugConfigV1_1_13,
} }
#[derive(Debug, Clone, Default, Deserialize, PartialEq, Eq, Serialize)] impl<T: NymConfig> Default for OldConfigV1_1_13<T> {
#[serde(deny_unknown_fields)] fn default() -> Self {
pub struct OldLoggingV1_1_13 {} OldConfigV1_1_13 {
client: Client::<T>::default(),
impl From<OldLoggingV1_1_13> for LoggingV1_1_20 { logging: Default::default(),
fn from(_value: OldLoggingV1_1_13) -> Self { debug: Default::default(),
LoggingV1_1_20 {} }
} }
} }
@@ -114,36 +115,37 @@ pub struct OldDebugConfigV1_1_13 {
pub maximum_reply_key_age: Duration, pub maximum_reply_key_age: Duration,
} }
impl From<OldDebugConfigV1_1_13> for DebugConfigV1_1_20 { impl From<OldDebugConfigV1_1_13> for DebugConfig {
fn from(value: OldDebugConfigV1_1_13) -> Self { fn from(value: OldDebugConfigV1_1_13) -> Self {
DebugConfigV1_1_20 { DebugConfig {
traffic: TrafficV1_1_20 { traffic: Traffic {
average_packet_delay: value.average_packet_delay, average_packet_delay: value.average_packet_delay,
message_sending_average_delay: value.message_sending_average_delay, message_sending_average_delay: value.message_sending_average_delay,
disable_main_poisson_packet_distribution: value disable_main_poisson_packet_distribution: value
.disable_main_poisson_packet_distribution, .disable_main_poisson_packet_distribution,
primary_packet_size: PacketSize::RegularPacket, primary_packet_size: PacketSize::RegularPacket,
secondary_packet_size: value.use_extended_packet_size.map(Into::into), secondary_packet_size: value.use_extended_packet_size.map(Into::into),
packet_type: None,
}, },
cover_traffic: CoverTrafficV1_1_20 { cover_traffic: CoverTraffic {
loop_cover_traffic_average_delay: value.loop_cover_traffic_average_delay, loop_cover_traffic_average_delay: value.loop_cover_traffic_average_delay,
disable_loop_cover_traffic_stream: value.disable_loop_cover_traffic_stream, disable_loop_cover_traffic_stream: value.disable_loop_cover_traffic_stream,
..CoverTrafficV1_1_20::default() ..CoverTraffic::default()
}, },
gateway_connection: GatewayConnectionV1_1_20 { gateway_connection: GatewayConnection {
gateway_response_timeout: value.gateway_response_timeout, gateway_response_timeout: value.gateway_response_timeout,
}, },
acknowledgements: AcknowledgementsV1_1_20 { acknowledgements: Acknowledgements {
average_ack_delay: value.average_ack_delay, average_ack_delay: value.average_ack_delay,
ack_wait_multiplier: value.ack_wait_multiplier, ack_wait_multiplier: value.ack_wait_multiplier,
ack_wait_addition: value.ack_wait_addition, ack_wait_addition: value.ack_wait_addition,
}, },
topology: TopologyV1_1_20 { topology: Topology {
topology_refresh_rate: value.topology_refresh_rate, topology_refresh_rate: value.topology_refresh_rate,
topology_resolution_timeout: value.topology_resolution_timeout, topology_resolution_timeout: value.topology_resolution_timeout,
disable_refreshing: false, disable_refreshing: false,
}, },
reply_surbs: ReplySurbsV1_1_20 { reply_surbs: ReplySurbs {
minimum_reply_surb_storage_threshold: value.minimum_reply_surb_storage_threshold, minimum_reply_surb_storage_threshold: value.minimum_reply_surb_storage_threshold,
maximum_reply_surb_storage_threshold: value.maximum_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, minimum_reply_surb_request_size: value.minimum_reply_surb_request_size,
@@ -190,10 +192,10 @@ impl Default for OldDebugConfigV1_1_13 {
} }
} }
impl<T, U> From<OldConfigV1_1_13<T>> for ConfigV1_1_20<U> { impl<T, U> From<OldConfigV1_1_13<T>> for Config<U> {
fn from(value: OldConfigV1_1_13<T>) -> Self { fn from(value: OldConfigV1_1_13<T>) -> Self {
ConfigV1_1_20 { Config {
client: ClientV1_1_20 { client: Client {
version: value.client.version, version: value.client.version,
id: value.client.id, id: value.client.id,
disabled_credentials_mode: value.client.disabled_credentials_mode, disabled_credentials_mode: value.client.disabled_credentials_mode,
@@ -209,10 +211,10 @@ impl<T, U> From<OldConfigV1_1_13<T>> for ConfigV1_1_20<U> {
database_path: value.client.database_path, database_path: value.client.database_path,
reply_surb_database_path: value.client.reply_surb_database_path, reply_surb_database_path: value.client.reply_surb_database_path,
nym_root_directory: value.client.nym_root_directory, nym_root_directory: value.client.nym_root_directory,
super_struct: PhantomData, super_struct: PhantomData,
packet_type: Some(nym_sphinx::params::PacketType::Mix),
}, },
logging: value.logging.into(), logging: value.logging,
debug: value.debug.into(), debug: value.debug.into(),
} }
} }
@@ -1,339 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
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 serde::{Deserialize, Serialize};
use std::marker::PhantomData;
use std::path::PathBuf;
use std::time::Duration;
use url::Url;
// 'DEBUG'
pub(crate) const DEFAULT_ACK_WAIT_MULTIPLIER: f64 = 1.5;
pub(crate) const DEFAULT_ACK_WAIT_ADDITION: Duration = Duration::from_millis(1_500);
pub(crate) const DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(200);
pub(crate) const DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(20);
pub(crate) const DEFAULT_AVERAGE_PACKET_DELAY: Duration = Duration::from_millis(50);
pub(crate) const DEFAULT_TOPOLOGY_REFRESH_RATE: Duration = Duration::from_secs(5 * 60); // every 5min
pub(crate) const DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT: Duration = Duration::from_millis(5_000);
// 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
pub(crate) const DEFAULT_GATEWAY_RESPONSE_TIMEOUT: Duration = Duration::from_secs(5 * 60);
pub(crate) 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
pub(crate) const DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD: usize = 10;
pub(crate) 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
pub(crate) const DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE: u32 = 10;
pub(crate) const DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE: u32 = 100;
pub(crate) const DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE: u32 = 500;
pub(crate) const DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD: Duration =
Duration::from_secs(10);
pub(crate) const DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD: Duration =
Duration::from_secs(5 * 60);
// 12 hours
pub(crate) const DEFAULT_MAXIMUM_REPLY_SURB_AGE: Duration = Duration::from_secs(12 * 60 * 60);
// 24 hours
pub(crate) 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 ConfigV1_1_20<T> {
pub client: ClientV1_1_20<T>,
#[serde(default)]
pub logging: LoggingV1_1_20,
#[serde(default)]
pub debug: DebugConfigV1_1_20,
}
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
pub struct GatewayEndpointConfigV1_1_20 {
pub gateway_id: String,
pub gateway_owner: String,
pub gateway_listener: String,
}
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,
}
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
pub struct ClientV1_1_20<T> {
pub version: String,
pub id: String,
#[serde(default)]
pub disabled_credentials_mode: bool,
#[serde(alias = "validator_urls")]
pub nyxd_urls: Vec<Url>,
#[serde(alias = "validator_api_urls")]
pub nym_api_urls: Vec<Url>,
pub private_identity_key_file: PathBuf,
pub public_identity_key_file: PathBuf,
pub private_encryption_key_file: PathBuf,
pub public_encryption_key_file: PathBuf,
pub gateway_shared_key_file: PathBuf,
pub ack_key_file: PathBuf,
pub gateway_endpoint: GatewayEndpointConfigV1_1_20,
pub database_path: PathBuf,
#[serde(default)]
pub reply_surb_database_path: PathBuf,
pub nym_root_directory: PathBuf,
#[serde(skip)]
pub super_struct: PhantomData<T>,
}
#[derive(Debug, Clone, Default, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct LoggingV1_1_20 {}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default)]
pub struct TrafficV1_1_20 {
#[serde(with = "humantime_serde")]
pub average_packet_delay: Duration,
#[serde(with = "humantime_serde")]
pub message_sending_average_delay: Duration,
pub disable_main_poisson_packet_distribution: bool,
pub primary_packet_size: PacketSize,
pub secondary_packet_size: Option<PacketSize>,
}
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
.disable_main_poisson_packet_distribution,
primary_packet_size: value.primary_packet_size,
secondary_packet_size: value.secondary_packet_size,
packet_type: PacketType::Mix,
}
}
}
impl Default for TrafficV1_1_20 {
fn default() -> Self {
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,
primary_packet_size: PacketSize::RegularPacket,
secondary_packet_size: None,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
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<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,
}
}
}
impl Default for CoverTrafficV1_1_20 {
fn default() -> Self {
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,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct GatewayConnectionV1_1_20 {
#[serde(with = "humantime_serde")]
pub gateway_response_timeout: Duration,
}
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 GatewayConnectionV1_1_20 {
fn default() -> Self {
GatewayConnectionV1_1_20 {
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct AcknowledgementsV1_1_20 {
#[serde(with = "humantime_serde")]
pub average_ack_delay: Duration,
pub ack_wait_multiplier: f64,
#[serde(with = "humantime_serde")]
pub ack_wait_addition: Duration,
}
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,
}
}
}
impl Default for AcknowledgementsV1_1_20 {
fn default() -> Self {
AcknowledgementsV1_1_20 {
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 TopologyV1_1_20 {
#[serde(with = "humantime_serde")]
pub topology_refresh_rate: Duration,
#[serde(with = "humantime_serde")]
pub topology_resolution_timeout: Duration,
pub disable_refreshing: bool,
}
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,
}
}
}
impl Default for TopologyV1_1_20 {
fn default() -> Self {
TopologyV1_1_20 {
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
disable_refreshing: false,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
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,
pub maximum_reply_surb_request_size: u32,
pub maximum_allowed_reply_surb_request_size: u32,
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_rerequest_waiting_period: Duration,
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_drop_waiting_period: Duration,
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_age: Duration,
#[serde(with = "humantime_serde")]
pub maximum_reply_key_age: Duration,
}
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,
maximum_reply_surb_request_size: value.maximum_reply_surb_request_size,
maximum_allowed_reply_surb_request_size: value.maximum_allowed_reply_surb_request_size,
maximum_reply_surb_rerequest_waiting_period: value
.maximum_reply_surb_rerequest_waiting_period,
maximum_reply_surb_drop_waiting_period: value.maximum_reply_surb_drop_waiting_period,
maximum_reply_surb_age: value.maximum_reply_surb_age,
maximum_reply_key_age: value.maximum_reply_key_age,
}
}
}
impl Default for ReplySurbsV1_1_20 {
fn default() -> Self {
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,
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,
}
}
}
#[derive(Debug, Default, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
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<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(),
acknowledgements: value.acknowledgements.into(),
topology: value.topology.into(),
reply_surbs: value.reply_surbs.into(),
}
}
}
@@ -1,348 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::{
Acknowledgements, Client, Config, CoverTraffic, DebugConfig, GatewayConnection,
GatewayEndpointConfig, ReplySurbs, Topology, Traffic,
};
use nym_sphinx::params::{PacketSize, PacketType};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use url::Url;
// '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);
// 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 ConfigV1_1_20_2 {
pub client: ClientV1_1_20_2,
#[serde(default)]
pub debug: DebugConfigV1_1_20_2,
}
impl From<ConfigV1_1_20_2> for Config {
fn from(value: ConfigV1_1_20_2) -> Self {
Config {
client: value.client.into(),
debug: value.debug.into(),
}
}
}
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
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,
/// Address of the gateway owner to which the client should send messages.
pub gateway_owner: String,
/// Address of the gateway listener to which all client requests should be sent.
pub gateway_listener: String,
}
impl From<GatewayEndpointConfigV1_1_20_2> for GatewayEndpointConfig {
fn from(value: GatewayEndpointConfigV1_1_20_2) -> Self {
GatewayEndpointConfig {
gateway_id: value.gateway_id,
gateway_owner: value.gateway_owner,
gateway_listener: value.gateway_listener,
}
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
pub struct ClientV1_1_20_2 {
pub version: String,
pub id: String,
#[serde(default)]
pub disabled_credentials_mode: bool,
#[serde(alias = "validator_urls")]
pub nyxd_urls: Vec<Url>,
#[serde(alias = "validator_api_urls")]
pub nym_api_urls: Vec<Url>,
pub gateway_endpoint: GatewayEndpointConfigV1_1_20_2,
}
impl From<ClientV1_1_20_2> for Client {
fn from(value: ClientV1_1_20_2) -> Self {
Client {
version: value.version,
id: value.id,
disabled_credentials_mode: value.disabled_credentials_mode,
nyxd_urls: value.nyxd_urls,
nym_api_urls: value.nym_api_urls,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default)]
pub struct TrafficV1_1_20_2 {
#[serde(with = "humantime_serde")]
pub average_packet_delay: Duration,
#[serde(with = "humantime_serde")]
pub message_sending_average_delay: Duration,
pub disable_main_poisson_packet_distribution: bool,
pub primary_packet_size: PacketSize,
pub secondary_packet_size: Option<PacketSize>,
pub packet_type: PacketType,
}
impl From<TrafficV1_1_20_2> for Traffic {
fn from(value: TrafficV1_1_20_2) -> Self {
Traffic {
average_packet_delay: value.average_packet_delay,
message_sending_average_delay: value.message_sending_average_delay,
disable_main_poisson_packet_distribution: value
.disable_main_poisson_packet_distribution,
primary_packet_size: value.primary_packet_size,
secondary_packet_size: value.secondary_packet_size,
packet_type: PacketType::Mix,
}
}
}
impl Default for TrafficV1_1_20_2 {
fn default() -> Self {
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,
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 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<CoverTrafficV1_1_20_2> for CoverTraffic {
fn from(value: CoverTrafficV1_1_20_2) -> Self {
CoverTraffic {
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,
}
}
}
impl Default for CoverTrafficV1_1_20_2 {
fn default() -> Self {
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,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct GatewayConnectionV1_1_20_2 {
#[serde(with = "humantime_serde")]
pub gateway_response_timeout: Duration,
}
impl From<GatewayConnectionV1_1_20_2> for GatewayConnection {
fn from(value: GatewayConnectionV1_1_20_2) -> Self {
GatewayConnection {
gateway_response_timeout: value.gateway_response_timeout,
}
}
}
impl Default for GatewayConnectionV1_1_20_2 {
fn default() -> Self {
GatewayConnectionV1_1_20_2 {
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct AcknowledgementsV1_1_20_2 {
#[serde(with = "humantime_serde")]
pub average_ack_delay: Duration,
pub ack_wait_multiplier: f64,
#[serde(with = "humantime_serde")]
pub ack_wait_addition: Duration,
}
impl From<AcknowledgementsV1_1_20_2> for Acknowledgements {
fn from(value: AcknowledgementsV1_1_20_2) -> Self {
Acknowledgements {
average_ack_delay: value.average_ack_delay,
ack_wait_multiplier: value.ack_wait_multiplier,
ack_wait_addition: value.ack_wait_addition,
}
}
}
impl Default for AcknowledgementsV1_1_20_2 {
fn default() -> Self {
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,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct TopologyV1_1_20_2 {
#[serde(with = "humantime_serde")]
pub topology_refresh_rate: Duration,
#[serde(with = "humantime_serde")]
pub topology_resolution_timeout: Duration,
pub disable_refreshing: bool,
}
impl Default for TopologyV1_1_20_2 {
fn default() -> Self {
TopologyV1_1_20_2 {
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
disable_refreshing: false,
}
}
}
impl From<TopologyV1_1_20_2> for Topology {
fn from(value: TopologyV1_1_20_2) -> Self {
Topology {
topology_refresh_rate: value.topology_refresh_rate,
topology_resolution_timeout: value.topology_resolution_timeout,
disable_refreshing: value.disable_refreshing,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
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,
pub maximum_reply_surb_request_size: u32,
pub maximum_allowed_reply_surb_request_size: u32,
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_rerequest_waiting_period: Duration,
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_drop_waiting_period: Duration,
#[serde(with = "humantime_serde")]
pub maximum_reply_surb_age: Duration,
#[serde(with = "humantime_serde")]
pub maximum_reply_key_age: Duration,
}
impl Default for ReplySurbsV1_1_20_2 {
fn default() -> Self {
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,
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,
}
}
}
impl From<ReplySurbsV1_1_20_2> for ReplySurbs {
fn from(value: ReplySurbsV1_1_20_2) -> Self {
ReplySurbs {
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,
maximum_reply_surb_request_size: value.maximum_reply_surb_request_size,
maximum_allowed_reply_surb_request_size: value.maximum_allowed_reply_surb_request_size,
maximum_reply_surb_rerequest_waiting_period: value
.maximum_reply_surb_rerequest_waiting_period,
maximum_reply_surb_drop_waiting_period: value.maximum_reply_surb_drop_waiting_period,
maximum_reply_surb_age: value.maximum_reply_surb_age,
maximum_reply_key_age: value.maximum_reply_key_age,
}
}
}
#[derive(Debug, Default, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
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<DebugConfigV1_1_20_2> for DebugConfig {
fn from(value: DebugConfigV1_1_20_2) -> Self {
DebugConfig {
traffic: value.traffic.into(),
cover_traffic: value.cover_traffic.into(),
gateway_connection: value.gateway_connection.into(),
acknowledgements: value.acknowledgements.into(),
topology: value.topology.into(),
reply_surbs: value.reply_surbs.into(),
}
}
}
@@ -0,0 +1,108 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::Config;
use std::path::{Path, PathBuf};
#[derive(Debug)]
pub struct ClientKeyPathfinder {
pub identity_private_key: PathBuf,
pub identity_public_key: PathBuf,
pub encryption_private_key: PathBuf,
pub encryption_public_key: PathBuf,
pub gateway_shared_key: PathBuf,
pub ack_key: PathBuf,
}
impl ClientKeyPathfinder {
pub fn new(id: String) -> Self {
let os_config_dir = dirs::config_dir().expect("no config directory known for this OS"); // grabs the OS default config dir
let config_dir = os_config_dir.join("nym").join("clients").join(id);
ClientKeyPathfinder {
identity_private_key: config_dir.join("private_identity.pem"),
identity_public_key: config_dir.join("public_identity.pem"),
encryption_private_key: config_dir.join("private_encryption.pem"),
encryption_public_key: config_dir.join("public_encryption.pem"),
gateway_shared_key: config_dir.join("gateway_shared.pem"),
ack_key: config_dir.join("ack_key.pem"),
}
}
pub fn new_from_config<T>(config: &Config<T>) -> Self {
ClientKeyPathfinder {
identity_private_key: config.get_private_identity_key_file(),
identity_public_key: config.get_public_identity_key_file(),
encryption_private_key: config.get_private_encryption_key_file(),
encryption_public_key: config.get_public_encryption_key_file(),
gateway_shared_key: config.get_gateway_shared_key_file(),
ack_key: config.get_ack_key_file(),
}
}
pub fn identity_key_pair_path(&self) -> nym_pemstore::KeyPairPath {
nym_pemstore::KeyPairPath::new(
self.private_identity_key().to_path_buf(),
self.public_identity_key().to_path_buf(),
)
}
pub fn encryption_key_pair_path(&self) -> nym_pemstore::KeyPairPath {
nym_pemstore::KeyPairPath::new(
self.private_encryption_key().to_path_buf(),
self.public_encryption_key().to_path_buf(),
)
}
pub fn any_file_exists(&self) -> bool {
matches!(self.identity_public_key.try_exists(), Ok(true))
|| matches!(self.identity_private_key.try_exists(), Ok(true))
|| matches!(self.encryption_public_key.try_exists(), Ok(true))
|| matches!(self.encryption_private_key.try_exists(), Ok(true))
|| matches!(self.gateway_shared_key.try_exists(), Ok(true))
|| matches!(self.ack_key.try_exists(), Ok(true))
}
pub fn any_file_exists_and_return(&self) -> Option<PathBuf> {
file_exists(&self.identity_public_key)
.or_else(|| file_exists(&self.identity_private_key))
.or_else(|| file_exists(&self.encryption_public_key))
.or_else(|| file_exists(&self.encryption_private_key))
.or_else(|| file_exists(&self.gateway_shared_key))
.or_else(|| file_exists(&self.ack_key))
}
pub fn gateway_key_file_exists(&self) -> bool {
matches!(self.gateway_shared_key.try_exists(), Ok(true))
}
pub fn private_identity_key(&self) -> &Path {
&self.identity_private_key
}
pub fn public_identity_key(&self) -> &Path {
&self.identity_public_key
}
pub fn private_encryption_key(&self) -> &Path {
&self.encryption_private_key
}
pub fn public_encryption_key(&self) -> &Path {
&self.encryption_public_key
}
pub fn gateway_shared_key(&self) -> &Path {
&self.gateway_shared_key
}
pub fn ack_key(&self) -> &Path {
&self.ack_key
}
}
fn file_exists(path: &Path) -> Option<PathBuf> {
if matches!(path.try_exists(), Ok(true)) {
return Some(path.to_path_buf());
}
None
}
@@ -0,0 +1,4 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod key_pathfinder;
-19
View File
@@ -50,11 +50,6 @@ pub enum ClientCoreError {
source: Box<dyn Error + Send + Sync>, source: Box<dyn Error + Send + Sync>,
}, },
#[error("experienced a failure with our gateway details storage: {source}")]
GatewayDetailsStoreError {
source: Box<dyn Error + Send + Sync>,
},
#[error("The gateway id is invalid - {0}")] #[error("The gateway id is invalid - {0}")]
UnableToCreatePublicKeyFromGatewayId(Ed25519RecoveryError), UnableToCreatePublicKeyFromGatewayId(Ed25519RecoveryError),
@@ -102,20 +97,6 @@ pub enum ClientCoreError {
"This operation would have resulted in clients keys being overwritten without permission" "This operation would have resulted in clients keys being overwritten without permission"
)] )]
ForbiddenKeyOverwrite, ForbiddenKeyOverwrite,
#[error("gateway details are unavailable")]
UnavailableGatewayDetails {
source: Box<dyn Error + Send + Sync>,
},
#[error("gateway shared key is unavailable whilst we have full node information")]
UnavailableSharedKey,
#[error("attempted to obtain fresh gateway details whilst already knowing about one")]
UnexpectedGatewayDetails,
#[error("the provided gateway details (for gateway {gateway_id}) do not correspond to the shared keys")]
MismatchedGatewayDetails { gateway_id: String },
} }
/// Set of messages that the client can send to listeners via the task manager /// Set of messages that the client can send to listeners via the task manager
+9 -9
View File
@@ -44,18 +44,18 @@ const MEASUREMENTS: usize = 3;
const CONN_TIMEOUT: Duration = Duration::from_millis(1500); const CONN_TIMEOUT: Duration = Duration::from_millis(1500);
const PING_TIMEOUT: Duration = Duration::from_millis(1000); const PING_TIMEOUT: Duration = Duration::from_millis(1000);
struct GatewayWithLatency<'a> { struct GatewayWithLatency {
gateway: &'a gateway::Node, gateway: gateway::Node,
latency: Duration, latency: Duration,
} }
impl<'a> GatewayWithLatency<'a> { impl GatewayWithLatency {
fn new(gateway: &'a gateway::Node, latency: Duration) -> Self { fn new(gateway: gateway::Node, latency: Duration) -> Self {
GatewayWithLatency { gateway, latency } GatewayWithLatency { gateway, latency }
} }
} }
pub async fn current_gateways<R: Rng>( pub(super) async fn current_gateways<R: Rng>(
rng: &mut R, rng: &mut R,
nym_apis: &[Url], nym_apis: &[Url],
) -> Result<Vec<gateway::Node>, ClientCoreError> { ) -> Result<Vec<gateway::Node>, ClientCoreError> {
@@ -64,7 +64,7 @@ pub async fn current_gateways<R: Rng>(
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?; .ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
let client = nym_validator_client::client::NymApiClient::new(nym_api.clone()); let client = nym_validator_client::client::NymApiClient::new(nym_api.clone());
log::trace!("Fetching list of gateways from: {nym_api}"); log::trace!("Fetching list of gateways from: {}", nym_api);
let gateways = client.get_cached_gateways().await?; let gateways = client.get_cached_gateways().await?;
let valid_gateways = gateways let valid_gateways = gateways
@@ -91,7 +91,7 @@ async fn connect(endpoint: &str) -> Result<WsConn, ClientCoreError> {
JSWebsocket::new(endpoint).map_err(|_| ClientCoreError::GatewayJsConnectionFailure) JSWebsocket::new(endpoint).map_err(|_| ClientCoreError::GatewayJsConnectionFailure)
} }
async fn measure_latency(gateway: &gateway::Node) -> Result<GatewayWithLatency, ClientCoreError> { async fn measure_latency(gateway: gateway::Node) -> Result<GatewayWithLatency, ClientCoreError> {
let addr = gateway.clients_address(); let addr = gateway.clients_address();
trace!( trace!(
"establishing connection to {} ({addr})...", "establishing connection to {} ({addr})...",
@@ -156,7 +156,7 @@ async fn measure_latency(gateway: &gateway::Node) -> Result<GatewayWithLatency,
pub(super) async fn choose_gateway_by_latency<R: Rng>( pub(super) async fn choose_gateway_by_latency<R: Rng>(
rng: &mut R, rng: &mut R,
gateways: &[gateway::Node], gateways: Vec<gateway::Node>,
) -> Result<gateway::Node, ClientCoreError> { ) -> Result<gateway::Node, ClientCoreError> {
info!("choosing gateway by latency..."); info!("choosing gateway by latency...");
@@ -189,7 +189,7 @@ pub(super) async fn choose_gateway_by_latency<R: Rng>(
pub(super) fn uniformly_random_gateway<R: Rng>( pub(super) fn uniformly_random_gateway<R: Rng>(
rng: &mut R, rng: &mut R,
gateways: &[gateway::Node], gateways: Vec<gateway::Node>,
) -> Result<gateway::Node, ClientCoreError> { ) -> Result<gateway::Node, ClientCoreError> {
gateways gateways
.choose(rng) .choose(rng)
+206 -275
View File
@@ -3,82 +3,31 @@
//! Collection of initialization steps used by client implementations //! Collection of initialization steps used by client implementations
use crate::client::base_client::storage::gateway_details::{ use crate::client::base_client::storage::MixnetClientStorage;
GatewayDetailsStore, PersistedGatewayDetails,
};
use crate::client::key_manager::persistence::KeyStore; use crate::client::key_manager::persistence::KeyStore;
use crate::client::key_manager::ManagedKeys; use crate::client::key_manager::{KeyManager, ManagedKeys};
use crate::init::helpers::{choose_gateway_by_latency, current_gateways, uniformly_random_gateway}; use crate::init::helpers::{choose_gateway_by_latency, current_gateways, uniformly_random_gateway};
use crate::{ use crate::{
config::{Config, GatewayEndpointConfig}, config::{
persistence::key_pathfinder::ClientKeyPathfinder, ClientCoreConfigTrait, Config,
GatewayEndpointConfig,
},
error::ClientCoreError, error::ClientCoreError,
}; };
use nym_crypto::asymmetric::identity; use nym_config::NymConfig;
use nym_crypto::asymmetric::{encryption, identity};
use nym_sphinx::addressing::{clients::Recipient, nodes::NodeIdentity}; use nym_sphinx::addressing::{clients::Recipient, nodes::NodeIdentity};
use nym_topology::gateway;
use nym_validator_client::client::IdentityKey; use nym_validator_client::client::IdentityKey;
use rand::rngs::OsRng; use rand::rngs::OsRng;
use serde::Serialize; use serde::Serialize;
use std::fmt::{Debug, Display}; use std::fmt::{Debug, Display};
use tap::TapFallible;
use url::Url; use url::Url;
pub mod helpers; mod helpers;
// TODO: rename to something better... #[derive(Clone)]
#[derive(Debug)]
pub struct InitialisationDetails {
pub gateway_details: GatewayEndpointConfig,
pub managed_keys: ManagedKeys,
}
impl InitialisationDetails {
pub fn new(gateway_details: GatewayEndpointConfig, managed_keys: ManagedKeys) -> Self {
InitialisationDetails {
gateway_details,
managed_keys,
}
}
pub async fn try_load<K, D>(key_store: &K, details_store: &D) -> Result<Self, ClientCoreError>
where
K: KeyStore,
D: GatewayDetailsStore,
K::StorageError: Send + Sync + 'static,
D::StorageError: Send + Sync + 'static,
{
let loaded_details = _load_gateway_details(details_store).await?;
let loaded_keys = _load_managed_keys(key_store).await?;
if !loaded_details.verify(&loaded_keys.must_get_gateway_shared_key()) {
return Err(ClientCoreError::MismatchedGatewayDetails {
gateway_id: loaded_details.details.gateway_id,
});
}
Ok(InitialisationDetails {
gateway_details: loaded_details.into(),
managed_keys: loaded_keys,
})
}
pub fn client_address(&self) -> Result<Recipient, ClientCoreError> {
let client_recipient = Recipient::new(
*self.managed_keys.identity_public_key(),
*self.managed_keys.encryption_public_key(),
// TODO: below only works under assumption that gateway address == gateway id
// (which currently is true)
NodeIdentity::from_base58_string(&self.gateway_details.gateway_id)?,
);
Ok(client_recipient)
}
}
#[derive(Debug, Clone)]
pub enum GatewaySetup { pub enum GatewaySetup {
/// The gateway specification MUST BE loaded from the underlying storage.
MustLoad,
/// Specifies usage of a new, random, gateway. /// Specifies usage of a new, random, gateway.
New { New {
/// Should the new gateway be selected based on latency. /// Should the new gateway be selected based on latency.
@@ -90,13 +39,13 @@ pub enum GatewaySetup {
}, },
Predefined { Predefined {
/// Full gateway configuration /// Full gateway configuration
details: PersistedGatewayDetails, config: GatewayEndpointConfig,
}, },
} }
impl From<PersistedGatewayDetails> for GatewaySetup { impl From<GatewayEndpointConfig> for GatewaySetup {
fn from(details: PersistedGatewayDetails) -> Self { fn from(config: GatewayEndpointConfig) -> Self {
GatewaySetup::Predefined { details } GatewaySetup::Predefined { config }
} }
} }
@@ -113,11 +62,14 @@ impl Default for GatewaySetup {
} }
impl GatewaySetup { impl GatewaySetup {
pub fn new_fresh( pub fn new(
gateway_identity: Option<String>, full_config: Option<GatewayEndpointConfig>,
gateway_identity: Option<IdentityKey>,
latency_based_selection: Option<bool>, latency_based_selection: Option<bool>,
) -> Self { ) -> Self {
if let Some(gateway_identity) = gateway_identity { if let Some(config) = full_config {
GatewaySetup::Predefined { config }
} else if let Some(gateway_identity) = gateway_identity {
GatewaySetup::Specified { gateway_identity } GatewaySetup::Specified { gateway_identity }
} else { } else {
GatewaySetup::New { GatewaySetup::New {
@@ -126,22 +78,15 @@ impl GatewaySetup {
} }
} }
pub fn is_must_load(&self) -> bool { pub async fn try_get_gateway_details(
matches!(self, GatewaySetup::MustLoad) self,
} validator_servers: &[Url],
pub fn has_full_details(&self) -> bool {
matches!(self, GatewaySetup::Predefined { .. }) || self.is_must_load()
}
pub async fn choose_gateway(
&self,
gateways: &[gateway::Node],
) -> Result<GatewayEndpointConfig, ClientCoreError> { ) -> Result<GatewayEndpointConfig, ClientCoreError> {
match self { match self {
GatewaySetup::New { by_latency } => { GatewaySetup::New { by_latency } => {
let mut rng = OsRng; let mut rng = OsRng;
if *by_latency { let gateways = current_gateways(&mut rng, validator_servers).await?;
if by_latency {
choose_gateway_by_latency(&mut rng, gateways).await choose_gateway_by_latency(&mut rng, gateways).await
} else { } else {
uniformly_random_gateway(&mut rng, gateways) uniformly_random_gateway(&mut rng, gateways)
@@ -149,28 +94,20 @@ impl GatewaySetup {
} }
.map(Into::into), .map(Into::into),
GatewaySetup::Specified { gateway_identity } => { GatewaySetup::Specified { gateway_identity } => {
let user_gateway = identity::PublicKey::from_base58_string(gateway_identity) let user_gateway = identity::PublicKey::from_base58_string(&gateway_identity)
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?; .map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
let mut rng = OsRng;
let gateways = current_gateways(&mut rng, validator_servers).await?;
gateways gateways
.iter() .into_iter()
.find(|gateway| gateway.identity_key == user_gateway) .find(|gateway| gateway.identity_key == user_gateway)
.ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_identity.to_string())) .ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_identity.to_string()))
.cloned()
} }
.map(Into::into), .map(Into::into),
_ => Err(ClientCoreError::UnexpectedGatewayDetails), GatewaySetup::Predefined { config } => Ok(config),
} }
} }
pub async fn try_get_new_gateway_details(
&self,
validator_servers: &[Url],
) -> Result<GatewayEndpointConfig, ClientCoreError> {
let mut rng = OsRng;
let gateways = current_gateways(&mut rng, validator_servers).await?;
self.choose_gateway(&gateways).await
}
} }
/// Struct describing the results of the client initialization procedure. /// Struct describing the results of the client initialization procedure.
@@ -185,14 +122,17 @@ pub struct InitResults {
} }
impl InitResults { impl InitResults {
pub fn new(config: &Config, address: &Recipient, gateway: &GatewayEndpointConfig) -> Self { pub fn new<T>(config: &Config<T>, address: &Recipient) -> Self
where
T: NymConfig,
{
Self { Self {
version: config.client.version.clone(), version: config.get_version().to_string(),
id: config.client.id.clone(), id: config.get_id(),
identity_key: address.identity().to_base58_string(), identity_key: address.identity().to_base58_string(),
encryption_key: address.encryption_key().to_base58_string(), encryption_key: address.encryption_key().to_base58_string(),
gateway_id: gateway.gateway_id.clone(), gateway_id: config.get_gateway_id(),
gateway_listener: gateway.gateway_listener.clone(), gateway_listener: config.get_gateway_listener(),
} }
} }
} }
@@ -208,164 +148,38 @@ impl Display for InitResults {
} }
} }
// helpers for error wrapping /// Recovers the already present gateway information or attempts to register with new gateway
async fn _store_gateway_details<D>( /// and stores the newly obtained key
details_store: &D, pub async fn get_registered_gateway<S>(
details: &PersistedGatewayDetails, validator_servers: Vec<Url>,
) -> Result<(), ClientCoreError> key_store: &S::KeyStore,
setup: GatewaySetup,
overwrite_keys: bool,
) -> Result<(GatewayEndpointConfig, ManagedKeys), ClientCoreError>
where where
D: GatewayDetailsStore, S: MixnetClientStorage,
D::StorageError: Send + Sync + 'static, <S::KeyStore as KeyStore>::StorageError: Send + Sync + 'static,
{
details_store
.store_gateway_details(details)
.await
.map_err(|source| ClientCoreError::GatewayDetailsStoreError {
source: Box::new(source),
})
}
async fn _load_gateway_details<D>(
details_store: &D,
) -> Result<PersistedGatewayDetails, ClientCoreError>
where
D: GatewayDetailsStore,
D::StorageError: Send + Sync + 'static,
{
details_store
.load_gateway_details()
.await
.map_err(|source| ClientCoreError::UnavailableGatewayDetails {
source: Box::new(source),
})
}
async fn _load_managed_keys<K>(key_store: &K) -> Result<ManagedKeys, ClientCoreError>
where
K: KeyStore,
K::StorageError: Send + Sync + 'static,
{
ManagedKeys::try_load(key_store)
.await
.map_err(|source| ClientCoreError::KeyStoreError {
source: Box::new(source),
})
}
fn ensure_valid_details(
details: &PersistedGatewayDetails,
loaded_keys: &ManagedKeys,
) -> Result<(), ClientCoreError> {
if !details.verify(&loaded_keys.must_get_gateway_shared_key()) {
Err(ClientCoreError::MismatchedGatewayDetails {
gateway_id: details.details.gateway_id.clone(),
})
} else {
Ok(())
}
}
pub async fn setup_gateway_from<K, D>(
setup: &GatewaySetup,
key_store: &K,
details_store: &D,
overwrite_data: bool,
gateways: Option<&[gateway::Node]>,
) -> Result<InitialisationDetails, ClientCoreError>
where
K: KeyStore,
D: GatewayDetailsStore,
K::StorageError: Send + Sync + 'static,
D::StorageError: Send + Sync + 'static,
{ {
let mut rng = OsRng; let mut rng = OsRng;
// try load gateway details // try load keys
let loaded_details = _load_gateway_details(details_store).await;
// try load keys and decide what to do based on the GatewaySetup
let mut managed_keys = match ManagedKeys::try_load(key_store).await { let mut managed_keys = match ManagedKeys::try_load(key_store).await {
Ok(loaded_keys) => { Ok(loaded_keys) => {
match setup { // if we loaded something and we don't have full gateway details, check if we can overwrite the data
GatewaySetup::MustLoad => { if let GatewaySetup::Predefined { config } = setup {
// get EVERYTHING from the storage // we already have defined gateway details AND a shared key, so nothing more for us to do
let details = loaded_details?; return Ok((config, loaded_keys));
ensure_valid_details(&details, &loaded_keys)?; } else if overwrite_keys {
ManagedKeys::generate_new(&mut rng)
// no need to persist anything as we got everything from the storage } else {
return Ok(InitialisationDetails::new(details.into(), loaded_keys)); return Err(ClientCoreError::ForbiddenKeyOverwrite);
}
GatewaySetup::Predefined { details } => {
// we already have defined gateway details AND a shared key
ensure_valid_details(details, &loaded_keys)?;
// if nothing was stored or we're allowed to overwrite what's there, just persist the passed data
if overwrite_data || loaded_details.is_err() {
_store_gateway_details(details_store, details).await?;
}
return Ok(InitialisationDetails::new(
details.clone().into(),
loaded_keys,
));
}
GatewaySetup::Specified { gateway_identity } => {
// if that data was already stored...
if let Ok(existing_gateway) = loaded_details {
ensure_valid_details(&existing_gateway, &loaded_keys)?;
if &existing_gateway.details.gateway_id != gateway_identity
&& !overwrite_data
{
// if our loaded details don't match requested value and we CANT overwrite it...
return Err(ClientCoreError::UnexpectedGatewayDetails);
} else if &existing_gateway.details.gateway_id == gateway_identity {
// if they do match up, just return it
return Ok(InitialisationDetails::new(
existing_gateway.into(),
loaded_keys,
));
}
}
// we didn't get full details from the store and we have loaded some keys
// so we can only continue if we're allowed to overwrite keys
if overwrite_data {
ManagedKeys::generate_new(&mut rng)
} else {
return Err(ClientCoreError::ForbiddenKeyOverwrite);
}
}
GatewaySetup::New { .. } => {
if let Ok(existing_gateway) = loaded_details {
ensure_valid_details(&existing_gateway, &loaded_keys)?;
return Ok(InitialisationDetails::new(
existing_gateway.into(),
loaded_keys,
));
}
// we didn't get full details from the store and we have loaded some keys
// so we can only continue if we're allowed to overwrite keys
if overwrite_data {
ManagedKeys::generate_new(&mut rng)
} else {
return Err(ClientCoreError::ForbiddenKeyOverwrite);
}
}
} }
} }
Err(_) => { Err(_) => ManagedKeys::generate_new(&mut rng),
// if we failed to load the keys, ensure we didn't provide gateway details in some form
// (in that case we CAN'T generate new keys
if setup.has_full_details() {
return Err(ClientCoreError::UnavailableSharedKey);
}
ManagedKeys::generate_new(&mut rng)
}
}; };
// choose gateway // choose gateway
let gateway_details = setup.choose_gateway(gateways.unwrap_or_default()).await?; let gateway_details = setup.try_get_gateway_details(&validator_servers).await?;
// get our identity key // get our identity key
let our_identity = managed_keys.identity_keypair(); let our_identity = managed_keys.identity_keypair();
@@ -373,9 +187,6 @@ where
// Establish connection, authenticate and generate keys for talking with the gateway // Establish connection, authenticate and generate keys for talking with the gateway
let shared_keys = helpers::register_with_gateway(&gateway_details, our_identity).await?; let shared_keys = helpers::register_with_gateway(&gateway_details, our_identity).await?;
let persisted_details = PersistedGatewayDetails::new(gateway_details, &shared_keys);
// persist gateway keys
managed_keys managed_keys
.deal_with_gateway_key(shared_keys, key_store) .deal_with_gateway_key(shared_keys, key_store)
.await .await
@@ -383,39 +194,159 @@ where
source: Box::new(source), source: Box::new(source),
})?; })?;
// persist gateway config // TODO: here we should be probably persisting gateway details as opposed to returning them
_store_gateway_details(details_store, &persisted_details).await?;
Ok(InitialisationDetails::new( Ok((gateway_details, managed_keys))
persisted_details.into(),
managed_keys,
))
} }
pub async fn setup_gateway<K, D>( /// Convenience function for setting up the gateway for a client given a `Config`. Depending on the
setup: &GatewaySetup, /// arguments given it will do the sensible thing. Either it will
key_store: &K, ///
details_store: &D, /// a. Reuse existing gateway configuration from storage.
overwrite_data: bool, /// b. Create a new gateway configuration but keep existing keys. This assumes that the caller
validator_servers: Option<&[Url]>, /// knows what they are doing and that the keys match the requested gateway.
) -> Result<InitialisationDetails, ClientCoreError> /// c. Create a new gateway configuration with a newly registered gateway and keys.
pub async fn setup_gateway_from_config<C, T, KSt>(
key_store: &KSt,
register_gateway: bool,
user_chosen_gateway_id: Option<identity::PublicKey>,
config: &Config<T>,
by_latency: bool,
) -> Result<GatewayEndpointConfig, ClientCoreError>
where where
K: KeyStore, C: NymConfig + ClientCoreConfigTrait,
D: GatewayDetailsStore, T: NymConfig,
K::StorageError: Send + Sync + 'static, KSt: KeyStore,
D::StorageError: Send + Sync + 'static, <KSt as KeyStore>::StorageError: Send + Sync + 'static,
{ {
let mut rng = OsRng; let id = config.get_id();
let gateways = current_gateways(&mut rng, validator_servers.unwrap_or_default()).await?;
setup_gateway_from( // If we are not going to register gateway, and an explicitly chosen gateway is not passed in,
setup, // load the existing configuration file
key_store, if !register_gateway && user_chosen_gateway_id.is_none() {
details_store, eprintln!("Not registering gateway, will reuse existing config and keys");
overwrite_data, return load_existing_gateway_config::<C>(&id);
Some(&gateways), }
let gateway_setup = GatewaySetup::new(
None,
user_chosen_gateway_id.map(|id| id.to_base58_string()),
Some(by_latency),
);
// Else, we proceed by querying the nym-api
let gateway = gateway_setup
.try_get_gateway_details(&config.get_nym_api_endpoints())
.await?;
log::debug!("Querying gateway gives: {:?}", gateway);
// If we are not registering, just return this and assume the caller has the keys already and
// wants to keep the,
if !register_gateway && user_chosen_gateway_id.is_some() {
eprintln!("Using gateway provided by user, keeping existing keys");
return Ok(gateway);
}
let mut rng = OsRng;
let mut managed_keys =
crate::client::key_manager::ManagedKeys::load_or_generate(&mut rng, key_store).await;
// Create new keys and derive our identity
let our_identity = managed_keys.identity_keypair();
// Establish connection, authenticate and generate keys for talking with the gateway
eprintln!("Registering with new gateway");
let shared_keys = helpers::register_with_gateway(&gateway, our_identity).await?;
managed_keys
.deal_with_gateway_key(shared_keys, key_store)
.await
.map_err(|source| ClientCoreError::KeyStoreError {
source: Box::new(source),
})?;
Ok(gateway)
}
/// Read and reuse the existing gateway configuration from a file that was generate earlier.
pub fn load_existing_gateway_config<T>(id: &str) -> Result<GatewayEndpointConfig, ClientCoreError>
where
T: NymConfig + ClientCoreConfigTrait,
{
T::load_from_file(id)
.map(|existing_config| existing_config.get_gateway_endpoint().clone())
.map_err(|err| {
log::error!(
"Unable to configure gateway: {err}. \n
Seems like the client was already initialized but it was not possible to read \
the existing configuration file. \n
CAUTION: Consider backing up your gateway keys and try force gateway registration, or \
removing the existing configuration and starting over."
);
ClientCoreError::CouldNotLoadExistingGatewayConfiguration(err)
})
}
/// Get the full client address from the client keys and the gateway identity
pub fn get_client_address(
key_manager: &KeyManager,
gateway_config: &GatewayEndpointConfig,
) -> Recipient {
Recipient::new(
*key_manager.identity_keypair().public_key(),
*key_manager.encryption_keypair().public_key(),
// TODO: below only works under assumption that gateway address == gateway id
// (which currently is true)
NodeIdentity::from_base58_string(&gateway_config.gateway_id).unwrap(),
) )
.await }
pub fn load_identity_keys(
pathfinder: &ClientKeyPathfinder,
) -> Result<identity::KeyPair, ClientCoreError> {
let identity_keypair: identity::KeyPair =
nym_pemstore::load_keypair(&pathfinder.identity_key_pair_path())
.tap_err(|_| log::error!("Failed to read stored identity key files"))?;
Ok(identity_keypair)
}
/// Get the client address by loading the keys from stored files.
// TODO: rethink that sucker
pub fn get_client_address_from_stored_ondisk_keys<T>(
config: &Config<T>,
) -> Result<Recipient, ClientCoreError>
where
T: nym_config::NymConfig,
{
fn load_identity_keys(
pathfinder: &ClientKeyPathfinder,
) -> Result<identity::KeyPair, ClientCoreError> {
let identity_keypair: identity::KeyPair =
nym_pemstore::load_keypair(&pathfinder.identity_key_pair_path())
.tap_err(|_| log::error!("Failed to read stored identity key files"))?;
Ok(identity_keypair)
}
fn load_sphinx_keys(
pathfinder: &ClientKeyPathfinder,
) -> Result<encryption::KeyPair, ClientCoreError> {
let sphinx_keypair: encryption::KeyPair =
nym_pemstore::load_keypair(&pathfinder.encryption_key_pair_path())
.tap_err(|_| log::error!("Failed to read stored sphinx key files"))?;
Ok(sphinx_keypair)
}
let pathfinder = ClientKeyPathfinder::new_from_config(config);
let identity_keypair = load_identity_keys(&pathfinder)?;
let sphinx_keypair = load_sphinx_keys(&pathfinder)?;
let client_recipient = Recipient::new(
*identity_keypair.public_key(),
*sphinx_keypair.public_key(),
// TODO: below only works under assumption that gateway address == gateway id
// (which currently is true)
NodeIdentity::from_base58_string(config.get_gateway_id())?,
);
Ok(client_recipient)
} }
pub fn output_to_json<T: Serialize>(init_results: &T, output_file: &str) { pub fn output_to_json<T: Serialize>(init_results: &T, output_file: &str) {
@@ -724,7 +724,7 @@ impl<C, St> GatewayClient<C, St> {
Ok(()) Ok(())
} }
pub async fn try_reconnect(&mut self) -> Result<(), GatewayClientError> { async fn try_reconnect(&mut self) -> Result<(), GatewayClientError> {
if !self.connection.is_established() { if !self.connection.is_established() {
self.establish_connection().await?; self.establish_connection().await?;
} }
@@ -738,12 +738,6 @@ impl<C, St> GatewayClient<C, St> {
Ok(()) Ok(())
} }
pub async fn disconnect(&mut self) -> Result<(), GatewayClientError> {
self.recover_socket_connection().await?;
self.connection = SocketState::NotConnected;
Ok(())
}
pub async fn authenticate_and_start(&mut self) -> Result<Arc<SharedKeys>, GatewayClientError> pub async fn authenticate_and_start(&mut self) -> Result<Arc<SharedKeys>, GatewayClientError>
where where
C: DkgQueryClient + Send + Sync, C: DkgQueryClient + Send + Sync,
@@ -48,21 +48,17 @@ impl PacketRouter {
// data he takes the SURB-ACK and first hop address. // data he takes the SURB-ACK and first hop address.
// currently SURB-ACKs are attached in EVERY packet, even cover, so this is always true // currently SURB-ACKs are attached in EVERY packet, even cover, so this is always true
let ack_overhead = PacketSize::AckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN; let ack_overhead = PacketSize::AckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN;
let outfox_ack_overhead =
PacketSize::OutfoxAckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN;
for received_packet in unwrapped_packets { for received_packet in unwrapped_packets {
if received_packet.len() == PacketSize::AckPacket.plaintext_size() if received_packet.len() == PacketSize::AckPacket.plaintext_size()
// we don't know the real size of the payload, it could be anything <= 48 bytes || received_packet.len() == PacketSize::OutfoxAckPacket.plaintext_size()
|| received_packet.len() <= PacketSize::OutfoxAckPacket.plaintext_size()
{ {
received_acks.push(received_packet); received_acks.push(received_packet);
} else if received_packet.len() } else if received_packet.len()
== PacketSize::RegularPacket.plaintext_size() - ack_overhead == PacketSize::RegularPacket.plaintext_size() - ack_overhead
|| received_packet.len() || received_packet.len()
== PacketSize::OutfoxRegularPacket.plaintext_size() - outfox_ack_overhead == PacketSize::OutfoxRegularPacket.plaintext_size() - ack_overhead
|| received_packet.len() || received_packet.len() == PacketSize::OutfoxRegularPacket.size() - 6
== PacketSize::OutfoxRegularPacket.size() - outfox_ack_overhead
{ {
trace!("routing regular packet"); trace!("routing regular packet");
received_messages.push(received_packet); received_messages.push(received_packet);
@@ -29,7 +29,6 @@ log = { workspace = true }
url = { version = "2.2", features = ["serde"] } url = { version = "2.2", features = ["serde"] }
tokio = { version = "1.24.1", features = ["sync", "time"] } tokio = { version = "1.24.1", features = ["sync", "time"] }
futures = "0.3" futures = "0.3"
openssl = { version = "0.10", features = ["vendored"], optional = true }
nym-coconut-interface = { path = "../../coconut-interface" } nym-coconut-interface = { path = "../../coconut-interface" }
nym-network-defaults = { path = "../../network-defaults" } nym-network-defaults = { path = "../../network-defaults" }
@@ -78,14 +77,13 @@ required-features = ["nyxd-client"]
nyxd-client = [ nyxd-client = [
"async-trait", "async-trait",
"cosmrs", "cosmrs",
"cosmwasm-std",
"cw3", "cw3",
"cw4", "cw4",
"flate2",
"itertools",
"openssl",
"prost", "prost",
"flate2",
"sha2", "sha2",
"itertools",
"cosmwasm-std",
"signing" "signing"
] ]
signing = [ signing = [
-3
View File
@@ -41,6 +41,3 @@ nym-multisig-contract-common = { path = "../cosmwasm-smart-contracts/multisig-co
nym-service-provider-directory-common = { path = "../cosmwasm-smart-contracts/service-provider-directory" } nym-service-provider-directory-common = { path = "../cosmwasm-smart-contracts/service-provider-directory" }
nym-name-service-common = { path = "../cosmwasm-smart-contracts/name-service" } nym-name-service-common = { path = "../cosmwasm-smart-contracts/name-service" }
nym-sphinx = { path = "../../common/nymsphinx" } nym-sphinx = { path = "../../common/nymsphinx" }
nym-pemstore = { path = "../../common/pemstore", version = "0.3.0" }
nym-types = { path = "../../common/types" }
@@ -1,68 +0,0 @@
use clap::{Args, Parser, Subcommand};
use nym_bin_common::output_format::OutputFormat;
use nym_crypto::asymmetric::identity;
use nym_types::helpers::ConsoleSigningOutput;
use nym_validator_client::nyxd::error::NyxdError;
use std::path::PathBuf;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct MixnetOperatorsIdentityKey {
#[clap(subcommand)]
pub command: MixnetOperatorsIdentityKeyCommands,
}
#[derive(Debug, Subcommand)]
pub enum MixnetOperatorsIdentityKeyCommands {
/// Register a name alias for a nym address
Sign(SignArgs),
}
#[derive(Debug, Parser)]
pub struct SignArgs {
/// Path to private identity key (example: private_identity_key.pem)
#[clap(long)]
private_key: PathBuf,
/// Base58 encoded message to sign
#[clap(long)]
base58_msg: String,
#[clap(short, long, default_value_t = OutputFormat::default())]
output: OutputFormat,
}
pub async fn sign(args: SignArgs) -> Result<(), NyxdError> {
eprintln!(">>> loading: {}", args.private_key.display());
let private_identity_key: identity::PrivateKey =
nym_pemstore::load_key(args.private_key).expect("failed to load key");
print_signed_msg(&private_identity_key, &args.base58_msg, args.output);
Ok(())
}
fn print_signed_msg(private_key: &identity::PrivateKey, raw_msg: &str, output: OutputFormat) {
let trimmed = raw_msg.trim();
eprintln!(">>> attempting to sign: {trimmed}");
let Ok(decoded) = bs58::decode(trimmed).into_vec() else {
println!("failed to base58 decode the message, did you copy it correctly?");
return;
};
eprintln!(">>> decoding the message...");
// we don't really care about what particular information is embedded inside of it,
// we just want to know if user correctly copied the string, i.e. whether it's a valid bs58 encoded json
if serde_json::from_slice::<serde_json::Value>(&decoded).is_err() {
println!("failed to parse the message after decoding, did you copy it correctly?");
return;
};
// if this is a valid json, it MUST be a valid string
let decoded_string = String::from_utf8(decoded.clone()).unwrap();
let signature = private_key.sign(&decoded).to_base58_string();
let sign_output = ConsoleSigningOutput::new(decoded_string, signature);
println!("{}", output.format(&sign_output));
}
@@ -4,7 +4,6 @@
use clap::{Args, Subcommand}; use clap::{Args, Subcommand};
pub mod gateway; pub mod gateway;
pub mod identity_key;
pub mod mixnode; pub mod mixnode;
pub mod name; pub mod name;
pub mod service; pub mod service;
@@ -27,6 +26,4 @@ pub enum MixnetOperatorsCommands {
ServiceProvider(service::MixnetOperatorsService), ServiceProvider(service::MixnetOperatorsService),
/// Manage your registered name /// Manage your registered name
Name(name::MixnetOperatorsName), Name(name::MixnetOperatorsName),
/// Sign messages using your private identity key
IdentityKey(identity_key::MixnetOperatorsIdentityKey),
} }
+3 -6
View File
@@ -7,14 +7,11 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
dirs = { version = "5.0.1", optional = true } cfg-if = "1.0.0"
handlebars = "3.5.5" handlebars = "3.0.1"
log = { workspace = true } log = { workspace = true }
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
toml = "0.7.4" toml = "0.5.6"
url = "2.2" url = "2.2"
nym-network-defaults = { path = "../network-defaults" } nym-network-defaults = { path = "../network-defaults" }
[features]
default = ["dirs"]
-208
View File
@@ -1,208 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_network_defaults::mainnet::read_var_if_not_default;
use nym_network_defaults::var_names::CONFIGURED;
use std::any::type_name;
use std::fmt::Debug;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::str::FromStr;
pub const MISSING_VALUE: &str = "MISSING VALUE";
/// Helper for providing default value for templated config fields.
pub fn missing_string_value<T: From<String>>() -> T {
MISSING_VALUE.to_string().into()
}
/// Helper for providing default INADDR_ANY IpAddr, i.e. `0.0.0.0`
pub fn inaddr_any() -> IpAddr {
IpAddr::V4(Ipv4Addr::UNSPECIFIED)
}
/// Helper for providing default IN6ADDR_ANY_INIT IpAddr, i.e. `::`
pub fn in6addr_any_init() -> IpAddr {
IpAddr::V6(Ipv6Addr::UNSPECIFIED)
}
/// Helper for providing binding warnings if node tries to bind to any of those
pub const SPECIAL_ADDRESSES: &[IpAddr] = &[
IpAddr::V4(Ipv4Addr::LOCALHOST),
IpAddr::V4(Ipv4Addr::UNSPECIFIED),
IpAddr::V4(Ipv4Addr::BROADCAST),
IpAddr::V6(Ipv6Addr::LOCALHOST),
IpAddr::V6(Ipv6Addr::UNSPECIFIED),
];
// TODO: is it really part of 'Config'?
pub trait OptionalSet {
/// If the value is available (i.e. `Some`), the provided closure is applied.
/// Otherwise `self` is returned with no modifications.
fn with_optional<F, T>(self, f: F, val: Option<T>) -> Self
where
F: Fn(Self, T) -> Self,
Self: Sized,
{
if let Some(val) = val {
f(self, val)
} else {
self
}
}
/// If the value is available (i.e. `Some`) it is validated and then the provided closure is applied.
/// Otherwise `self` is returned with no modifications.
fn with_validated_optional<F, T, V, E>(
self,
f: F,
value: Option<T>,
validate: V,
) -> Result<Self, E>
where
F: Fn(Self, T) -> Self,
V: Fn(&T) -> Result<(), E>,
Self: Sized,
{
if let Some(val) = value {
validate(&val)?;
Ok(f(self, val))
} else {
Ok(self)
}
}
/// If the value is available (i.e. `Some`), the provided closure is applied.
/// Otherwise, if the environment was configured and the corresponding variable was set,
/// the value is parsed using the `FromStr` implementation and the closure is applied on that instead.
/// Finally, if none of those were available, `self` is returned with no modifications.
fn with_optional_env<F, T>(self, f: F, val: Option<T>, env_var: &str) -> Self
where
F: Fn(Self, T) -> Self,
T: FromStr,
<T as FromStr>::Err: Debug,
Self: Sized,
{
if let Some(val) = val {
return f(self, val);
} else if std::env::var(CONFIGURED).is_ok() {
if let Some(raw) = read_var_if_not_default(env_var) {
return f(
self,
raw.parse().unwrap_or_else(|err| {
panic!(
"failed to parse value of {raw} into type {}. the error was {:?}",
type_name::<T>(),
err
)
}),
);
}
}
self
}
/// If the value is available (i.e. `Some`), the provided closure is applied.
/// Otherwise, if the environment was configured and the corresponding variable was set,
/// the value is parsed using the provided parser and the closure is applied on that instead.
/// Finally, if none of those were available, `self` is returned with no modifications.
fn with_optional_custom_env<F, T, G>(
self,
f: F,
val: Option<T>,
env_var: &str,
parser: G,
) -> Self
where
F: Fn(Self, T) -> Self,
G: Fn(&str) -> T,
Self: Sized,
{
if let Some(val) = val {
return f(self, val);
} else if std::env::var(CONFIGURED).is_ok() {
if let Some(raw) = read_var_if_not_default(env_var) {
return f(self, parser(&raw));
}
}
self
}
}
// helper for when we want to use `OptionalSet` on an inner field
// (used by clients wanting to set the `BaseConfig` values)
#[macro_export]
macro_rules! define_optional_set_inner {
( $x: ident, $inner_field_name: ident, $inner_field_typ: ty ) => {
impl $x {
pub fn with_optional_inner<F, T>(mut self, f: F, val: Option<T>) -> Self
where
F: Fn($inner_field_typ, T) -> $inner_field_typ,
{
self.$inner_field_name = self.$inner_field_name.with_optional(f, val);
self
}
pub fn with_validated_optional_inner<F, T, V, E>(
mut self,
f: F,
value: Option<T>,
validate: V,
) -> Result<Self, E>
where
F: Fn($inner_field_typ, T) -> $inner_field_typ,
V: Fn(&T) -> Result<(), E>,
{
self.$inner_field_name = self
.$inner_field_name
.with_validated_optional(f, value, validate)?;
Ok(self)
}
pub fn with_optional_env_inner<F, T>(
mut self,
f: F,
val: Option<T>,
env_var: &str,
) -> Self
where
F: Fn($inner_field_typ, T) -> $inner_field_typ,
T: FromStr,
<T as FromStr>::Err: Debug,
{
self.$inner_field_name = self.$inner_field_name.with_optional_env(f, val, env_var);
self
}
pub fn with_optional_custom_env_inner<F, T, G>(
mut self,
f: F,
val: Option<T>,
env_var: &str,
parser: G,
) -> Self
where
F: Fn($inner_field_typ, T) -> $inner_field_typ,
G: Fn(&str) -> T,
{
self.$inner_field_name = self
.$inner_field_name
.with_optional_custom_env(f, val, env_var, parser);
self
}
}
};
}
// this function is only used for parsing values from the network defaults and thus the "expect" there are fine
pub fn parse_urls(raw: &str) -> Vec<url::Url> {
raw.split(',')
.map(|raw_url| {
raw_url
.trim()
.parse()
.expect("one of the provided urls was invalid")
})
.collect()
}
impl<T> OptionalSet for T {}
-55
View File
@@ -1,55 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
// removed in 1.1.19/1.1.20
pub mod nym_config {
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::path::{Path, PathBuf};
use std::{fs, io};
pub const CONFIG_DIR: &str = "config";
pub const DATA_DIR: &str = "data";
// no need for anything to do with saving.
pub trait MigrationNymConfig: Serialize + DeserializeOwned {
fn config_file_name() -> String {
"config.toml".to_string()
}
fn default_root_directory() -> PathBuf;
fn default_data_directory(id: &str) -> PathBuf {
Self::default_data_directory_with_root(Self::default_root_directory(), id)
}
fn default_data_directory_with_root<P: AsRef<Path>>(root: P, id: &str) -> PathBuf {
root.as_ref().join(id).join(DATA_DIR)
}
fn default_config_directory(id: &str) -> PathBuf {
Self::default_config_directory_with_root(Self::default_root_directory(), id)
}
fn default_config_directory_with_root<P: AsRef<Path>>(root: P, id: &str) -> PathBuf {
root.as_ref().join(id).join(CONFIG_DIR)
}
fn default_config_file_path(id: &str) -> PathBuf {
Self::default_config_directory(id).join(Self::config_file_name())
}
fn load_from_file(id: &str) -> io::Result<Self> {
let file = Self::default_config_file_path(id);
Self::load_from_filepath(file)
}
fn load_from_filepath<P: AsRef<Path>>(filepath: P) -> io::Result<Self> {
log::trace!("Loading from file: {:#?}", filepath.as_ref().to_owned());
let config_contents = fs::read_to_string(filepath)?;
toml::from_str(&config_contents)
.map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err))
}
}
}
+188 -168
View File
@@ -1,199 +1,219 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net> // Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use handlebars::{Handlebars, TemplateRenderError}; use handlebars::Handlebars;
use nym_network_defaults::mainnet::read_var_if_not_default;
use nym_network_defaults::var_names::CONFIGURED;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use serde::Serialize; use serde::Serialize;
use std::fs::File; use std::any::type_name;
use std::io::Write; use std::fmt::Debug;
#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::{fs, io}; use std::{fs, io};
pub use helpers::{parse_urls, OptionalSet};
pub use toml::de::Error as TomlDeError;
pub mod defaults; pub mod defaults;
pub mod helpers;
pub mod legacy_helpers;
pub const NYM_DIR: &str = ".nym"; pub const CONFIG_DIR: &str = "config";
pub const DEFAULT_CONFIG_DIR: &str = "config"; pub const DATA_DIR: &str = "data";
pub const DEFAULT_DATA_DIR: &str = "data"; pub const CRED_DB_FILE_NAME: &str = "credentials_database.db";
pub const DEFAULT_CONFIG_FILENAME: &str = "config.toml";
#[cfg(feature = "dirs")] pub trait NymConfig: Default + Serialize + DeserializeOwned {
pub fn must_get_home() -> PathBuf {
dirs::home_dir().expect("Failed to evaluate $HOME value")
}
#[cfg(feature = "dirs")]
pub fn may_get_home() -> Option<PathBuf> {
dirs::home_dir()
}
pub trait NymConfigTemplate: Serialize {
fn template() -> &'static str; fn template() -> &'static str;
fn format_to_string(&self) -> String { fn config_file_name() -> String {
// it is responsibility of whoever is implementing the trait to ensure the template is valid "config.toml".to_string()
Handlebars::new()
.render_template(Self::template(), &self)
.unwrap()
} }
fn format_to_writer<W: Write>(&self, writer: W) -> io::Result<()> { fn default_root_directory() -> PathBuf;
if let Err(err) =
Handlebars::new().render_template_to_write(Self::template(), &self, writer) // default, most probable, implementations; can be easily overridden where required
{ fn default_config_directory(id: &str) -> PathBuf {
match err { Self::default_config_directory_with_root(Self::default_root_directory(), id)
TemplateRenderError::IOError(err, _) => return Err(err), }
other_err => {
// it is responsibility of whoever is implementing the trait to ensure the template is valid fn default_config_directory_with_root<P: AsRef<Path>>(root: P, id: &str) -> PathBuf {
panic!("invalid template: {other_err}") root.as_ref().join(id).join(CONFIG_DIR)
}
fn default_data_directory(id: &str) -> PathBuf {
Self::default_data_directory_with_root(Self::default_root_directory(), id)
}
fn default_data_directory_with_root<P: AsRef<Path>>(root: P, id: &str) -> PathBuf {
root.as_ref().join(id).join(DATA_DIR)
}
fn default_config_file_path(id: &str) -> PathBuf {
Self::default_config_directory(id).join(Self::config_file_name())
}
fn default_config_file_path_with_root<P: AsRef<Path>>(root: P, id: &str) -> PathBuf {
Self::default_config_directory_with_root(root, id).join(Self::config_file_name())
}
// We provide a second set of functions that tries to not panic.
fn try_default_root_directory() -> Option<PathBuf>;
fn try_default_config_directory(id: &str) -> Option<PathBuf> {
Self::try_default_root_directory().map(|d| d.join(id).join(CONFIG_DIR))
}
fn try_default_data_directory(id: &str) -> Option<PathBuf> {
Self::try_default_root_directory().map(|d| d.join(id).join(DATA_DIR))
}
fn try_default_config_file_path(id: &str) -> Option<PathBuf> {
Self::try_default_config_directory(id).map(|d| d.join(Self::config_file_name()))
}
fn root_directory(&self) -> PathBuf;
fn config_directory(&self) -> PathBuf;
fn data_directory(&self) -> PathBuf;
fn save_to_file(&self, custom_location: Option<PathBuf>) -> io::Result<()> {
let reg = Handlebars::new();
// it's whoever is implementing the trait responsibility to make sure you can execute your own template on your data
let templated_config = reg.render_template(Self::template(), self).unwrap();
// make sure the whole directory structure actually exists
match custom_location.clone() {
Some(loc) => {
if let Some(parent_dir) = loc.parent() {
fs::create_dir_all(parent_dir)
} else {
Ok(())
} }
} }
None => fs::create_dir_all(self.config_directory()),
}?;
let location = custom_location
.unwrap_or_else(|| self.config_directory().join(Self::config_file_name()));
log::info!("Configuration file will be saved to {:?}", location);
cfg_if::cfg_if! {
if #[cfg(unix)] {
fs::write(location.clone(), templated_config)?;
let mut perms = fs::metadata(location.clone())?.permissions();
perms.set_mode(0o600);
fs::set_permissions(location, perms)?;
} else {
fs::write(location, templated_config)?;
}
} }
Ok(()) Ok(())
} }
}
pub fn save_formatted_config_to_file<C, P>(config: &C, path: P) -> io::Result<()> fn load_from_file(id: &str) -> io::Result<Self> {
where let file = Self::default_config_file_path(id);
C: NymConfigTemplate, Self::load_from_filepath(file)
P: AsRef<Path>,
{
log::trace!("trying to save config file to {}", path.as_ref().display());
let file = File::create(path.as_ref())?;
// TODO: check for whether any of our configs stores anything sensitive
// and change that to 0o644 instead
#[cfg(target_family = "unix")]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = fs::metadata(path.as_ref())?.permissions();
perms.set_mode(0o600);
fs::set_permissions(path, perms)?;
} }
config.format_to_writer(file) fn load_from_filepath<P: AsRef<Path>>(filepath: P) -> io::Result<Self> {
log::trace!("Loading from file: {:#?}", filepath.as_ref().to_owned());
let config_contents = fs::read_to_string(filepath)?;
toml::from_str(&config_contents)
.map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err))
}
} }
pub fn deserialize_config_from_toml_str<C>(raw: &str) -> Result<C, TomlDeError> // this function is only used for parsing values from the network defaults and thus the "expect" there are fine
where pub fn parse_urls(raw: &str) -> Vec<url::Url> {
C: DeserializeOwned, raw.split(',')
{ .map(|raw_url| {
toml::from_str(raw) raw_url
.trim()
.parse()
.expect("one of the provided nym api urls is invalid")
})
.collect()
} }
pub fn read_config_from_toml_file<C, P>(path: P) -> io::Result<C> pub trait OptionalSet {
where fn with_optional<F, T>(self, f: F, val: Option<T>) -> Self
C: DeserializeOwned, where
P: AsRef<Path>, F: Fn(Self, T) -> Self,
{ Self: Sized,
log::trace!( {
"trying to read config file from {}", if let Some(val) = val {
path.as_ref().display() f(self, val)
); } else {
let content = fs::read_to_string(path)?; self
}
}
// TODO: should we be preserving original error type instead? fn with_validated_optional<F, T, V, E>(
deserialize_config_from_toml_str(&content) self,
.map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err)) f: F,
value: Option<T>,
validate: V,
) -> Result<Self, E>
where
F: Fn(Self, T) -> Self,
V: Fn(&T) -> Result<(), E>,
Self: Sized,
{
if let Some(val) = value {
validate(&val)?;
Ok(f(self, val))
} else {
Ok(self)
}
}
fn with_optional_env<F, T>(self, f: F, val: Option<T>, env_var: &str) -> Self
where
F: Fn(Self, T) -> Self,
T: FromStr,
<T as FromStr>::Err: Debug,
Self: Sized,
{
if let Some(val) = val {
return f(self, val);
} else if std::env::var(CONFIGURED).is_ok() {
if let Some(raw) = read_var_if_not_default(env_var) {
return f(
self,
raw.parse().unwrap_or_else(|err| {
panic!(
"failed to parse value of {raw} into type {}. the error was {:?}",
type_name::<T>(),
err
)
}),
);
}
}
self
}
fn with_optional_custom_env<F, T, G>(
self,
f: F,
val: Option<T>,
env_var: &str,
parser: G,
) -> Self
where
F: Fn(Self, T) -> Self,
G: Fn(&str) -> T,
Self: Sized,
{
if let Some(val) = val {
return f(self, val);
} else if std::env::var(CONFIGURED).is_ok() {
if let Some(raw) = read_var_if_not_default(env_var) {
return f(self, parser(&raw));
}
}
self
}
} }
// impl<T> OptionalSet for T where T: NymConfig {}
//
//
// pub trait NymConfig: Default + Serialize + DeserializeOwned {
// fn template() -> &'static str;
//
// fn config_file_name() -> String {
// "config.toml".to_string()
// }
//
// fn default_root_directory() -> PathBuf;
//
// // default, most probable, implementations; can be easily overridden where required
// fn default_config_directory(id: &str) -> PathBuf {
// Self::default_root_directory()
// .join(id)
// .join(DEFAULT_CONFIG_DIR)
// }
//
// fn default_data_directory(id: &str) -> PathBuf {
// Self::default_root_directory()
// .join(id)
// .join(DEFAULT_DATA_DIR)
// }
//
// fn default_config_file_path(id: &str) -> PathBuf {
// Self::default_config_directory(id).join(Self::config_file_name())
// }
//
// // We provide a second set of functions that tries to not panic.
//
// fn try_default_root_directory() -> Option<PathBuf>;
//
// fn try_default_config_directory(id: &str) -> Option<PathBuf> {
// Self::try_default_root_directory().map(|d| d.join(id).join(DEFAULT_CONFIG_DIR))
// }
//
// fn try_default_data_directory(id: &str) -> Option<PathBuf> {
// Self::try_default_root_directory().map(|d| d.join(id).join(DEFAULT_DATA_DIR))
// }
//
// fn try_default_config_file_path(id: &str) -> Option<PathBuf> {
// Self::try_default_config_directory(id).map(|d| d.join(Self::config_file_name()))
// }
//
// fn root_directory(&self) -> PathBuf;
// fn config_directory(&self) -> PathBuf;
// fn data_directory(&self) -> PathBuf;
//
// fn save_to_file(&self, custom_location: Option<PathBuf>) -> io::Result<()> {
// Ok(())
// // let reg = Handlebars::new();
// // // it's whoever is implementing the trait responsibility to make sure you can execute your own template on your data
// // let templated_config = reg.render_template(Self::template(), self).unwrap();
// //
// // // make sure the whole directory structure actually exists
// // match custom_location.clone() {
// // Some(loc) => {
// // if let Some(parent_dir) = loc.parent() {
// // fs::create_dir_all(parent_dir)
// // } else {
// // Ok(())
// // }
// // }
// // None => fs::create_dir_all(self.config_directory()),
// // }?;
// //
// // let location = custom_location
// // .unwrap_or_else(|| self.config_directory().join(Self::config_file_name()));
// // log::info!("Configuration file will be saved to {:?}", location);
// //
// // cfg_if::cfg_if! {
// // if #[cfg(unix)] {
// // fs::write(location.clone(), templated_config)?;
// // let mut perms = fs::metadata(location.clone())?.permissions();
// // perms.set_mode(0o600);
// // fs::set_permissions(location, perms)?;
// // } else {
// // fs::write(location, templated_config)?;
// // }
// // }
// //
// // Ok(())
// }
//
// fn load_from_file(id: &str) -> io::Result<Self> {
// let file = Self::default_config_file_path(id);
// log::trace!("Loading from file: {:#?}", file);
// let config_contents = fs::read_to_string(file)?;
//
// toml::from_str(&config_contents)
// .map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err))
// }
// }
+1 -4
View File
@@ -4,11 +4,8 @@
*/ */
use crate::ephemeral_storage::EphemeralStorage; use crate::ephemeral_storage::EphemeralStorage;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use crate::persistent_storage::PersistentStorage; use crate::persistent_storage::PersistentStorage;
#[cfg(not(target_arch = "wasm32"))]
use std::path::Path;
mod backends; mod backends;
pub mod ephemeral_storage; pub mod ephemeral_storage;
@@ -19,7 +16,7 @@ pub mod persistent_storage;
pub mod storage; pub mod storage;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
pub async fn initialise_persistent_storage<P: AsRef<Path>>(path: P) -> PersistentStorage { pub async fn initialise_persistent_storage(path: std::path::PathBuf) -> PersistentStorage {
match persistent_storage::PersistentStorage::init(path).await { match persistent_storage::PersistentStorage::init(path).await {
Err(err) => panic!("failed to initialise credential storage - {err}"), Err(err) => panic!("failed to initialise credential storage - {err}"),
Ok(storage) => storage, Ok(storage) => storage,
@@ -23,7 +23,7 @@ impl PersistentStorage {
/// # Arguments /// # Arguments
/// ///
/// * `database_path`: path to the database. /// * `database_path`: path to the database.
pub async fn init<P: AsRef<Path>>(database_path: P) -> Result<Self, StorageError> { pub async fn init<P: AsRef<Path> + Send>(database_path: P) -> Result<Self, StorageError> {
debug!( debug!(
"Attempting to connect to database {:?}", "Attempting to connect to database {:?}",
database_path.as_ref().as_os_str() database_path.as_ref().as_os_str()
+14 -14
View File
@@ -19,20 +19,20 @@ macro_rules! measure {
cfg_if::cfg_if! { cfg_if::cfg_if! {
if #[cfg(feature = "cpucycles")] { if #[cfg(feature = "cpucycles")] {
let start_cycles = $crate::cpu_cycles(); let start_cycles = $crate::cpu_cycles();
// if the block needs to return something, we can return it // if the block needs to return something, we can return it
let r = $x; let r = $x;
let end_cycles = $crate::cpu_cycles(); let end_cycles = $crate::cpu_cycles();
let name = if let Some(meta) = tracing::Span::current().metadata() { let name = if let Some(meta) = tracing::Span::current().metadata() {
meta.name() meta.name()
} else { } else {
"measure" "measure"
}; };
match (start_cycles, end_cycles) { match (start_cycles, end_cycles) {
(Ok(start), Ok(end)) => log::trace!("{} cpucycles: {}", name, end - start), (Ok(start), Ok(end)) => info!("{} cpucycles: {}", name, end - start),
(Err(e), _) => error!("{e}"), (Err(e), _) => error!("{e}"),
(_, Err(e)) => error!("{e}"), (_, Err(e)) => error!("{e}"),
} }
r r
} else { } else {
$x $x
} }
@@ -3,7 +3,7 @@
use nym_sphinx_acknowledgements::surb_ack::SurbAckRecoveryError; use nym_sphinx_acknowledgements::surb_ack::SurbAckRecoveryError;
use nym_sphinx_addressing::nodes::NymNodeRoutingAddressError; use nym_sphinx_addressing::nodes::NymNodeRoutingAddressError;
use nym_sphinx_types::{NymPacketError, OutfoxError, SphinxError}; use nym_sphinx_types::{NymPacketError, SphinxError};
use thiserror::Error; use thiserror::Error;
#[derive(Error, Debug)] #[derive(Error, Debug)]
@@ -25,7 +25,4 @@ pub enum MixProcessingError {
#[error("the received packet was set to use the very old and very much deprecated 'VPN' mode")] #[error("the received packet was set to use the very old and very much deprecated 'VPN' mode")]
ReceivedOldTypeVpnPacket, ReceivedOldTypeVpnPacket,
#[error("failed to process received outfox packet: {0}")]
OutfoxProcessingError(#[from] OutfoxError),
} }
@@ -20,14 +20,12 @@ use tracing::instrument;
type ForwardAck = MixPacket; type ForwardAck = MixPacket;
#[derive(Debug)]
pub struct ProcessedFinalHop { pub struct ProcessedFinalHop {
pub destination: DestinationAddressBytes, pub destination: DestinationAddressBytes,
pub forward_ack: Option<ForwardAck>, pub forward_ack: Option<ForwardAck>,
pub message: Vec<u8>, pub message: Vec<u8>,
} }
#[derive(Debug)]
pub enum MixProcessingResult { pub enum MixProcessingResult {
/// Contains unwrapped data that should first get delayed before being sent to next hop. /// Contains unwrapped data that should first get delayed before being sent to next hop.
ForwardHop(MixPacket, Option<SphinxDelay>), ForwardHop(MixPacket, Option<SphinxDelay>),
@@ -143,7 +141,7 @@ impl SphinxPacketProcessor {
match SurbAck::try_recover_first_hop_packet(&ack_data, packet_type) { match SurbAck::try_recover_first_hop_packet(&ack_data, packet_type) {
Ok((first_hop, packet)) => (first_hop, packet), Ok((first_hop, packet)) => (first_hop, packet),
Err(err) => { Err(err) => {
info!("Failed to recover first hop from ack data: {err}"); debug!("Failed to recover first hop from ack data: {err}");
return Err(err.into()); return Err(err.into());
} }
}; };
@@ -207,7 +205,7 @@ impl SphinxPacketProcessor {
if packet.is_final_hop() { if packet.is_final_hop() {
self.process_final_hop( self.process_final_hop(
DestinationAddressBytes::from_bytes(next_address), DestinationAddressBytes::from_bytes(next_address),
packet.recover_plaintext()?.to_vec(), packet.recover_plaintext().to_vec(),
packet_size, packet_size,
packet_type, packet_type,
) )
@@ -241,14 +239,7 @@ impl SphinxPacketProcessor {
// for forward packets, extract next hop and set delay (but do NOT delay here) // for forward packets, extract next hop and set delay (but do NOT delay here)
// for final packets, extract SURBAck // for final packets, extract SURBAck
let final_processing_result = self.perform_final_processing(processed_packet, packet_size, packet_type)
self.perform_final_processing(processed_packet, packet_size, packet_type);
if final_processing_result.is_err() {
error!("{:?}", final_processing_result)
}
final_processing_result
}) })
} }
} }
+549
View File
@@ -0,0 +1,549 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
// This should be modified whenever an updated Ethereum contract is uploaded
pub const ETH_JSON_ABI: &str = r#"
[
{
"inputs": [
{
"internalType": "contract CosmosERC20",
"name": "_erc20",
"type": "address"
},
{
"internalType": "contract Gravity",
"name": "_gravityBridge",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "Bandwidth",
"type": "uint256"
},
{
"indexed": true,
"internalType": "uint256",
"name": "VerificationKey",
"type": "uint256"
},
{
"indexed": false,
"internalType": "bytes",
"name": "SignedVerificationKey",
"type": "bytes"
},
{
"indexed": false,
"internalType": "string",
"name": "CosmosRecipient",
"type": "string"
}
],
"name": "BBCredentialPurchased",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "bool",
"name": "Enabled",
"type": "bool"
}
],
"name": "CredentialGenerationSwitch",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferred",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "NewBytesPerToken",
"type": "uint256"
}
],
"name": "RatioChanged",
"type": "event"
},
{
"inputs": [],
"name": "BytesPerToken",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_amount",
"type": "uint256"
}
],
"name": "bandwidthFromToken",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_newBytesPerTokenAmount",
"type": "uint256"
}
],
"name": "changeRatio",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "credentialGenerationEnabled",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bool",
"name": "_generation",
"type": "bool"
}
],
"name": "credentialGenerationSwitch",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "erc20",
"outputs": [
{
"internalType": "contract CosmosERC20",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_amount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_verificationKey",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "_signedVerificationKey",
"type": "bytes"
},
{
"internalType": "string",
"name": "_cosmosRecipient",
"type": "string"
}
],
"name": "generateBasicBandwidthCredential",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "gravityBridge",
"outputs": [
{
"internalType": "contract Gravity",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]
"#;
pub const ETH_ERC20_JSON_ABI: &str = r#"
[
{
"inputs": [
{
"internalType": "string",
"name": "name_",
"type": "string"
},
{
"internalType": "string",
"name": "symbol_",
"type": "string"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Approval",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "address",
"name": "spender",
"type": "address"
}
],
"name": "allowance",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "approve",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "decimals",
"outputs": [
{
"internalType": "uint8",
"name": "",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "subtractedValue",
"type": "uint256"
}
],
"name": "decreaseAllowance",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "addedValue",
"type": "uint256"
}
],
"name": "increaseAllowance",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "name",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "symbol",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "totalSupply",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "transferFrom",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
}
]
"#;
+2 -3
View File
@@ -15,12 +15,11 @@ thiserror = { workspace = true }
zeroize = { workspace = true } zeroize = { workspace = true }
nym-crypto = { path = "../../crypto", features = ["symmetric", "rand"] } nym-crypto = { path = "../../crypto", features = ["symmetric", "rand"] }
nym-pemstore = { path = "../../pemstore" }
nym-sphinx-addressing = { path = "../addressing" } nym-sphinx-addressing = { path = "../addressing" }
nym-sphinx-params = { path = "../params" } nym-sphinx-params = { path = "../params" }
nym-sphinx-routing = { path = "../routing" }
nym-sphinx-types = { path = "../types" } nym-sphinx-types = { path = "../types" }
nym-pemstore = { path = "../../pemstore" }
nym-topology = { path = "../../topology" } nym-topology = { path = "../../topology" }
[features] [features]
serde = ["serde_crate", "generic-array"] serde = ["serde_crate", "generic-array"]
@@ -9,7 +9,7 @@ use nym_sphinx_addressing::nodes::{
}; };
use nym_sphinx_params::packet_sizes::PacketSize; use nym_sphinx_params::packet_sizes::PacketSize;
use nym_sphinx_params::{PacketType, DEFAULT_NUM_MIX_HOPS}; use nym_sphinx_params::{PacketType, DEFAULT_NUM_MIX_HOPS};
use nym_sphinx_types::delays::Delay; use nym_sphinx_types::delays::{self, Delay};
use nym_sphinx_types::{NymPacket, NymPacketError, MIN_PACKET_SIZE}; use nym_sphinx_types::{NymPacket, NymPacketError, MIN_PACKET_SIZE};
use nym_topology::{NymTopology, NymTopologyError}; use nym_topology::{NymTopology, NymTopologyError};
use rand::{CryptoRng, RngCore}; use rand::{CryptoRng, RngCore};
@@ -51,14 +51,13 @@ impl SurbAck {
{ {
let route = let route =
topology.random_route_to_gateway(rng, DEFAULT_NUM_MIX_HOPS, recipient.gateway())?; topology.random_route_to_gateway(rng, DEFAULT_NUM_MIX_HOPS, recipient.gateway())?;
let delays = nym_sphinx_routing::generate_hop_delays(average_delay, route.len()); let delays = delays::generate_from_average_duration(route.len(), average_delay);
let destination = recipient.as_sphinx_destination(); let destination = recipient.as_sphinx_destination();
let surb_ack_payload = prepare_identifier(rng, ack_key, marshaled_fragment_id); let surb_ack_payload = prepare_identifier(rng, ack_key, marshaled_fragment_id);
let packet_size = match packet_type { let packet_size = match packet_type {
PacketType::Outfox => surb_ack_payload.len().max(MIN_PACKET_SIZE), PacketType::Outfox => surb_ack_payload.len().max(MIN_PACKET_SIZE),
PacketType::Mix => PacketSize::AckPacket.payload_size(), PacketType::Mix => PacketSize::AckPacket.payload_size(),
#[allow(deprecated)]
PacketType::Vpn => PacketSize::AckPacket.payload_size(), PacketType::Vpn => PacketSize::AckPacket.payload_size(),
}; };
@@ -76,7 +75,6 @@ impl SurbAck {
&destination, &destination,
&delays, &delays,
)?, )?,
#[allow(deprecated)]
PacketType::Vpn => NymPacket::sphinx_build( PacketType::Vpn => NymPacket::sphinx_build(
packet_size, packet_size,
surb_ack_payload, surb_ack_payload,
@@ -107,7 +105,6 @@ impl SurbAck {
PacketSize::OutfoxAckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN PacketSize::OutfoxAckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN
} }
PacketType::Mix => PacketSize::AckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN, PacketType::Mix => PacketSize::AckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN,
#[allow(deprecated)]
PacketType::Vpn => PacketSize::AckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN, PacketType::Vpn => PacketSize::AckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN,
} }
} }
@@ -140,7 +137,6 @@ impl SurbAck {
let packet = match packet_type { let packet = match packet_type {
PacketType::Outfox => NymPacket::outfox_from_bytes(&b[address_offset..])?, PacketType::Outfox => NymPacket::outfox_from_bytes(&b[address_offset..])?,
PacketType::Mix => NymPacket::sphinx_from_bytes(&b[address_offset..])?, PacketType::Mix => NymPacket::sphinx_from_bytes(&b[address_offset..])?,
#[allow(deprecated)]
PacketType::Vpn => NymPacket::sphinx_from_bytes(&b[address_offset..])?, PacketType::Vpn => NymPacket::sphinx_from_bytes(&b[address_offset..])?,
}; };
@@ -16,7 +16,6 @@ thiserror = "1"
nym-crypto = { path = "../../crypto", features = ["symmetric", "rand"] } nym-crypto = { path = "../../crypto", features = ["symmetric", "rand"] }
nym-sphinx-addressing = { path = "../addressing" } nym-sphinx-addressing = { path = "../addressing" }
nym-sphinx-params = { path = "../params" } nym-sphinx-params = { path = "../params" }
nym-sphinx-routing = { path = "../routing" }
nym-sphinx-types = { path = "../types" } nym-sphinx-types = { path = "../types" }
nym-topology = { path = "../../topology" } nym-topology = { path = "../../topology" }
@@ -7,7 +7,7 @@ use nym_sphinx_addressing::clients::Recipient;
use nym_sphinx_addressing::nodes::{NymNodeRoutingAddress, MAX_NODE_ADDRESS_UNPADDED_LEN}; use nym_sphinx_addressing::nodes::{NymNodeRoutingAddress, MAX_NODE_ADDRESS_UNPADDED_LEN};
use nym_sphinx_params::packet_sizes::PacketSize; use nym_sphinx_params::packet_sizes::PacketSize;
use nym_sphinx_params::{PacketType, ReplySurbKeyDigestAlgorithm, DEFAULT_NUM_MIX_HOPS}; use nym_sphinx_params::{PacketType, ReplySurbKeyDigestAlgorithm, DEFAULT_NUM_MIX_HOPS};
use nym_sphinx_types::{NymPacket, SURBMaterial, SphinxError, SURB}; use nym_sphinx_types::{delays, NymPacket, SURBMaterial, SphinxError, SURB};
use nym_topology::{NymTopology, NymTopologyError}; use nym_topology::{NymTopology, NymTopologyError};
use rand::{CryptoRng, RngCore}; use rand::{CryptoRng, RngCore};
use serde::de::{Error as SerdeError, Visitor}; use serde::de::{Error as SerdeError, Visitor};
@@ -96,7 +96,7 @@ impl ReplySurb {
{ {
let route = let route =
topology.random_route_to_gateway(rng, DEFAULT_NUM_MIX_HOPS, recipient.gateway())?; topology.random_route_to_gateway(rng, DEFAULT_NUM_MIX_HOPS, recipient.gateway())?;
let delays = nym_sphinx_routing::generate_hop_delays(average_delay, route.len()); let delays = delays::generate_from_average_duration(route.len(), average_delay);
let destination = recipient.as_sphinx_destination(); let destination = recipient.as_sphinx_destination();
let surb_material = SURBMaterial::new(route, delays, destination); let surb_material = SURBMaterial::new(route, delays, destination);
+1 -2
View File
@@ -15,8 +15,7 @@ nym-crypto = { path = "../../crypto" }
nym-sphinx-acknowledgements = { path = "../acknowledgements" } nym-sphinx-acknowledgements = { path = "../acknowledgements" }
nym-sphinx-addressing = { path = "../addressing" } nym-sphinx-addressing = { path = "../addressing" }
nym-sphinx-chunking = { path = "../chunking" } nym-sphinx-chunking = { path = "../chunking" }
nym-sphinx-forwarding = { path = "../forwarding" }
nym-sphinx-params = { path = "../params" } nym-sphinx-params = { path = "../params" }
nym-sphinx-routing = { path = "../routing" } nym-sphinx-forwarding = { path = "../forwarding" }
nym-sphinx-types = { path = "../types" } nym-sphinx-types = { path = "../types" }
nym-topology = { path = "../../topology" } nym-topology = { path = "../../topology" }
+12 -29
View File
@@ -13,7 +13,7 @@ use nym_sphinx_params::packet_sizes::PacketSize;
use nym_sphinx_params::{ use nym_sphinx_params::{
PacketEncryptionAlgorithm, PacketHkdfAlgorithm, PacketType, DEFAULT_NUM_MIX_HOPS, PacketEncryptionAlgorithm, PacketHkdfAlgorithm, PacketType, DEFAULT_NUM_MIX_HOPS,
}; };
use nym_sphinx_types::NymPacket; use nym_sphinx_types::{delays, NymPacket};
use nym_topology::{NymTopology, NymTopologyError}; use nym_topology::{NymTopology, NymTopologyError};
use rand::{CryptoRng, RngCore}; use rand::{CryptoRng, RngCore};
use std::convert::TryFrom; use std::convert::TryFrom;
@@ -91,7 +91,6 @@ where
>(rng, full_address.encryption_key()); >(rng, full_address.encryption_key());
let public_key_bytes = ephemeral_keypair.public_key().to_bytes(); let public_key_bytes = ephemeral_keypair.public_key().to_bytes();
let cover_size = packet_size.plaintext_size() - public_key_bytes.len() - ack_bytes.len(); let cover_size = packet_size.plaintext_size() - public_key_bytes.len() - ack_bytes.len();
let mut cover_content: Vec<_> = LOOP_COVER_MESSAGE_PAYLOAD let mut cover_content: Vec<_> = LOOP_COVER_MESSAGE_PAYLOAD
@@ -120,38 +119,22 @@ where
let route = let route =
topology.random_route_to_gateway(rng, DEFAULT_NUM_MIX_HOPS, full_address.gateway())?; topology.random_route_to_gateway(rng, DEFAULT_NUM_MIX_HOPS, full_address.gateway())?;
let delays = nym_sphinx_routing::generate_hop_delays(average_packet_delay, route.len()); let delays = delays::generate_from_average_duration(route.len(), average_packet_delay);
let destination = full_address.as_sphinx_destination(); let destination = full_address.as_sphinx_destination();
// once merged, that's an easy rng injection point for sphinx packets : )
let packet = NymPacket::sphinx_build(
packet_size.payload_size(),
packet_payload,
&route,
&destination,
&delays,
)?;
let first_hop_address = let first_hop_address =
NymNodeRoutingAddress::try_from(route.first().unwrap().address).unwrap(); NymNodeRoutingAddress::try_from(route.first().unwrap().address).unwrap();
// once merged, that's an easy rng injection point for sphinx packets : ) Ok(MixPacket::new(first_hop_address, packet, PacketType::Mix))
let packet = match packet_type {
PacketType::Mix => NymPacket::sphinx_build(
packet_size.payload_size(),
packet_payload,
&route,
&destination,
&delays,
)?,
#[allow(deprecated)]
PacketType::Vpn => NymPacket::sphinx_build(
packet_size.payload_size(),
packet_payload,
&route,
&destination,
&delays,
)?,
PacketType::Outfox => NymPacket::outfox_build(
packet_payload,
&route,
&destination,
Some(packet_size.plaintext_size()),
)?,
};
Ok(MixPacket::new(first_hop_address, packet, packet_type))
} }
/// Helper function used to determine if given message represents a loop cover message. /// Helper function used to determine if given message represents a loop cover message.
-1
View File
@@ -85,7 +85,6 @@ impl Decoder for NymCodec {
match header.packet_type { match header.packet_type {
PacketType::Outfox => NymPacket::outfox_from_bytes(slice)?, PacketType::Outfox => NymPacket::outfox_from_bytes(slice)?,
PacketType::Mix => NymPacket::sphinx_from_bytes(slice)?, PacketType::Mix => NymPacket::sphinx_from_bytes(slice)?,
#[allow(deprecated)]
PacketType::Vpn => NymPacket::sphinx_from_bytes(slice)?, PacketType::Vpn => NymPacket::sphinx_from_bytes(slice)?,
} }
} else { } else {
@@ -244,7 +244,6 @@ impl PacketSize {
) -> Result<Self, InvalidPacketSize> { ) -> Result<Self, InvalidPacketSize> {
let overhead = match packet_type { let overhead = match packet_type {
PacketType::Mix => SPHINX_PACKET_OVERHEAD, PacketType::Mix => SPHINX_PACKET_OVERHEAD,
#[allow(deprecated)]
PacketType::Vpn => SPHINX_PACKET_OVERHEAD, PacketType::Vpn => SPHINX_PACKET_OVERHEAD,
PacketType::Outfox => OUTFOX_PACKET_OVERHEAD, PacketType::Outfox => OUTFOX_PACKET_OVERHEAD,
}; };
+2 -11
View File
@@ -1,15 +1,13 @@
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#![allow(deprecated)]
// allow the u8 repr of `Vpn` PacketType whilst deprecating all of its other uses
use crate::PacketSize;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::convert::TryFrom; use std::convert::TryFrom;
use std::fmt; use std::fmt;
use thiserror::Error; use thiserror::Error;
use crate::PacketSize;
#[derive(Error, Debug)] #[derive(Error, Debug)]
#[error("{received} is not a valid packet mode tag")] #[error("{received} is not a valid packet mode tag")]
pub struct InvalidPacketType { pub struct InvalidPacketType {
@@ -17,23 +15,17 @@ pub struct InvalidPacketType {
} }
#[repr(u8)] #[repr(u8)]
#[allow(deprecated)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum PacketType { pub enum PacketType {
/// Represents 'normal' packet sent through the network that should be delayed by an appropriate /// Represents 'normal' packet sent through the network that should be delayed by an appropriate
/// value at each hop. /// value at each hop.
#[default] #[default]
#[serde(rename = "mix")]
#[serde(alias = "sphinx")]
Mix = 0, Mix = 0,
/// Represents a packet that should be sent through the network as fast as possible. /// Represents a packet that should be sent through the network as fast as possible.
#[deprecated]
#[serde(rename = "unsupported-mix-vpn")]
Vpn = 1, Vpn = 1,
/// Abusing this to add Outfox support /// Abusing this to add Outfox support
#[serde(rename = "outfox")]
Outfox = 2, Outfox = 2,
} }
@@ -41,7 +33,6 @@ impl fmt::Display for PacketType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match self {
PacketType::Mix => write!(f, "Mix"), PacketType::Mix => write!(f, "Mix"),
#[allow(deprecated)]
PacketType::Vpn => write!(f, "Vpn"), PacketType::Vpn => write!(f, "Vpn"),
PacketType::Outfox => write!(f, "Outfox"), PacketType::Outfox => write!(f, "Outfox"),
} }
+1 -11
View File
@@ -1,10 +1,8 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use std::time::Duration;
use nym_sphinx_addressing::clients::Recipient; use nym_sphinx_addressing::clients::Recipient;
use nym_sphinx_types::{delays, Delay, Node}; use nym_sphinx_types::Node;
use thiserror::Error; use thiserror::Error;
pub trait SphinxRouteMaker { pub trait SphinxRouteMaker {
@@ -43,11 +41,3 @@ impl SphinxRouteMaker for Vec<Node> {
} }
} }
} }
pub fn generate_hop_delays(average_packet_delay: Duration, num_hops: usize) -> Vec<Delay> {
if average_packet_delay.is_zero() {
vec![nym_sphinx_types::Delay::new_from_millis(0); num_hops]
} else {
delays::generate_from_average_duration(num_hops, average_packet_delay)
}
}
+2 -3
View File
@@ -14,7 +14,7 @@ use nym_sphinx_chunking::fragment::{Fragment, FragmentIdentifier};
use nym_sphinx_forwarding::packet::MixPacket; use nym_sphinx_forwarding::packet::MixPacket;
use nym_sphinx_params::packet_sizes::PacketSize; use nym_sphinx_params::packet_sizes::PacketSize;
use nym_sphinx_params::{PacketType, ReplySurbKeyDigestAlgorithm, DEFAULT_NUM_MIX_HOPS}; use nym_sphinx_params::{PacketType, ReplySurbKeyDigestAlgorithm, DEFAULT_NUM_MIX_HOPS};
use nym_sphinx_types::{Delay, NymPacket}; use nym_sphinx_types::{delays, Delay, NymPacket};
use nym_topology::{NymTopology, NymTopologyError}; use nym_topology::{NymTopology, NymTopologyError};
use rand::{CryptoRng, Rng}; use rand::{CryptoRng, Rng};
use std::convert::TryFrom; use std::convert::TryFrom;
@@ -233,7 +233,7 @@ pub trait FragmentPreparer {
// including set of delays // including set of delays
let delays = let delays =
nym_sphinx_routing::generate_hop_delays(self.average_packet_delay(), route.len()); delays::generate_from_average_duration(route.len(), self.average_packet_delay());
// create the actual sphinx packet here. With valid route and correct payload size, // create the actual sphinx packet here. With valid route and correct payload size,
// there's absolutely no reason for this call to fail. // there's absolutely no reason for this call to fail.
@@ -251,7 +251,6 @@ pub trait FragmentPreparer {
&destination, &destination,
&delays, &delays,
)?, )?,
#[allow(deprecated)]
PacketType::Vpn => NymPacket::sphinx_build( PacketType::Vpn => NymPacket::sphinx_build(
packet_size.payload_size(), packet_size.payload_size(),
packet_payload, packet_payload,
+168 -35
View File
@@ -1,41 +1,88 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
pub use nym_client_core::config::Config as BaseClientConfig; use crate::config::template::config_template;
pub use nym_client_core::config::Config as BaseConfig;
pub use nym_client_core::config::MISSING_VALUE;
use nym_client_core::config::{ClientCoreConfigTrait, DebugConfig};
use nym_config::defaults::DEFAULT_SOCKS5_LISTENING_PORT; use nym_config::defaults::DEFAULT_SOCKS5_LISTENING_PORT;
use nym_config::OptionalSet; use nym_config::{NymConfig, OptionalSet};
use nym_service_providers_common::interface::ProviderInterfaceVersion;
use nym_socks5_requests::Socks5ProtocolVersion;
use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::addressing::clients::Recipient;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt::Debug; use std::fmt::Debug;
use std::path::{Path, PathBuf};
use std::str::FromStr; use std::str::FromStr;
pub mod old_config_v1_1_20_2; pub mod old_config_v1_1_13;
mod template;
pub use nym_service_providers_common::interface::ProviderInterfaceVersion;
pub use nym_socks5_requests::Socks5ProtocolVersion;
const DEFAULT_CONNECTION_START_SURBS: u32 = 20; const DEFAULT_CONNECTION_START_SURBS: u32 = 20;
const DEFAULT_PER_REQUEST_SURBS: u32 = 3; const DEFAULT_PER_REQUEST_SURBS: u32 = 3;
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct Config { pub struct Config {
#[serde(flatten)] #[serde(flatten)]
pub base: BaseClientConfig, base: BaseConfig<Config>,
pub socks5: Socks5, socks5: Socks5,
}
impl NymConfig for Config {
fn template() -> &'static str {
config_template()
}
fn default_root_directory() -> PathBuf {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
let base_dir = dirs::home_dir().expect("Failed to evaluate $HOME value");
#[cfg(any(target_os = "android", target_os = "ios"))]
let base_dir = PathBuf::from("/tmp");
base_dir.join(".nym").join("socks5-clients")
}
fn try_default_root_directory() -> Option<PathBuf> {
dirs::home_dir().map(|path| path.join(".nym").join("socks5-clients"))
}
fn root_directory(&self) -> PathBuf {
self.base.get_nym_root_directory()
}
fn config_directory(&self) -> PathBuf {
self.root_directory()
.join(self.base.get_id())
.join("config")
}
fn data_directory(&self) -> PathBuf {
self.root_directory().join(self.base.get_id()).join("data")
}
}
impl ClientCoreConfigTrait for Config {
fn get_gateway_endpoint(&self) -> &nym_client_core::config::GatewayEndpointConfig {
self.base.get_gateway_endpoint()
}
} }
impl Config { impl Config {
pub fn new<S: Into<String>>(id: S, version: S, provider_mix_address: S) -> Self { pub fn new<S: Into<String>>(id: S, provider_mix_address: S) -> Self {
Config { Config {
base: BaseClientConfig::new(id, version), base: BaseConfig::new(id),
socks5: Socks5::new(provider_mix_address), socks5: Socks5::new(provider_mix_address),
} }
} }
pub fn from_base(base: BaseClientConfig, socks5: Socks5) -> Self { #[must_use]
Config { base, socks5 } pub fn with_root_directory<P: AsRef<Path>>(mut self, root_dir: P) -> Self {
self.base = self.base.reset_nym_root_directory(root_dir);
let data_dir = self.data_directory();
self.base = self.base.reset_data_directory(data_dir);
self
} }
pub fn validate(&self) -> bool { pub fn validate(&self) -> bool {
@@ -43,38 +90,63 @@ impl Config {
self.base.validate() self.base.validate()
} }
pub fn with_port(mut self, port: u16) -> Self { // getters
self.socks5.listening_port = port; pub fn get_base(&self) -> &BaseConfig<Self> {
self &self.base
} }
pub fn with_anonymous_replies(mut self, anonymous_replies: bool) -> Self { pub fn get_base_mut(&mut self) -> &mut BaseConfig<Self> {
self.socks5.send_anonymously = anonymous_replies; &mut self.base
self }
pub fn get_socks5(&self) -> &Socks5 {
&self.socks5
}
pub fn get_socks5_mut(&mut self) -> &mut Socks5 {
&mut self.socks5
}
pub fn get_debug_settings(&self) -> &DebugConfig {
self.get_base().get_debug_config()
}
pub fn get_config_file_save_location(&self) -> PathBuf {
self.config_directory().join(Self::config_file_name())
} }
// poor man's 'builder' method // poor man's 'builder' method
pub fn with_base<F, T>(mut self, f: F, val: T) -> Self pub fn with_base<F, T>(mut self, f: F, val: T) -> Self
where where
F: Fn(BaseClientConfig, T) -> BaseClientConfig, F: Fn(BaseConfig<Self>, T) -> BaseConfig<Self>,
{ {
self.base = f(self.base, val); self.base = f(self.base, val);
self self
} }
pub fn with_port(mut self, port: u16) -> Self {
self.socks5.with_port(port);
self
}
pub fn with_anonymous_replies(mut self, anonymous_replies: bool) -> Self {
self.socks5.with_anonymous_replies(anonymous_replies);
self
}
// helper methods to use `OptionalSet` trait. Those are defined due to very... ehm. 'specific' structure of this config // helper methods to use `OptionalSet` trait. Those are defined due to very... ehm. 'specific' structure of this config
// (plz, lets refactor it) // (plz, lets refactor it)
pub fn with_optional_base<F, T>(mut self, f: F, val: Option<T>) -> Self pub fn with_optional_ext<F, T>(mut self, f: F, val: Option<T>) -> Self
where where
F: Fn(BaseClientConfig, T) -> BaseClientConfig, F: Fn(BaseConfig<Self>, T) -> BaseConfig<Self>,
{ {
self.base = self.base.with_optional(f, val); self.base = self.base.with_optional(f, val);
self self
} }
pub fn with_optional_base_env<F, T>(mut self, f: F, val: Option<T>, env_var: &str) -> Self pub fn with_optional_env_ext<F, T>(mut self, f: F, val: Option<T>, env_var: &str) -> Self
where where
F: Fn(BaseClientConfig, T) -> BaseClientConfig, F: Fn(BaseConfig<Self>, T) -> BaseConfig<Self>,
T: FromStr, T: FromStr,
<T as FromStr>::Err: Debug, <T as FromStr>::Err: Debug,
{ {
@@ -82,7 +154,7 @@ impl Config {
self self
} }
pub fn with_optional_base_custom_env<F, T, G>( pub fn with_optional_custom_env_ext<F, T, G>(
mut self, mut self,
f: F, f: F,
val: Option<T>, val: Option<T>,
@@ -90,7 +162,7 @@ impl Config {
parser: G, parser: G,
) -> Self ) -> Self
where where
F: Fn(BaseClientConfig, T) -> BaseClientConfig, F: Fn(BaseConfig<Self>, T) -> BaseConfig<Self>,
G: Fn(&str) -> T, G: Fn(&str) -> T,
{ {
self.base = self.base.with_optional_custom_env(f, val, env_var, parser); self.base = self.base.with_optional_custom_env(f, val, env_var, parser);
@@ -102,19 +174,19 @@ impl Config {
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct Socks5 { pub struct Socks5 {
/// The port on which the client will be listening for incoming requests /// The port on which the client will be listening for incoming requests
pub listening_port: u16, listening_port: u16,
/// The mix address of the provider to which all requests are going to be sent. /// The mix address of the provider to which all requests are going to be sent.
pub provider_mix_address: String, provider_mix_address: String,
/// The version of the 'service provider' this client is going to use in its communication with the /// The version of the 'service provider' this client is going to use in its communication with the
/// specified socks5 provider. /// specified socks5 provider.
// if in doubt, use the legacy version as initially nobody will be using the updated binaries // if in doubt, use the legacy version as initially nobody will be using the updated binaries
#[serde(default = "ProviderInterfaceVersion::new_legacy")] #[serde(default = "ProviderInterfaceVersion::new_legacy")]
pub provider_interface_version: ProviderInterfaceVersion, provider_interface_version: ProviderInterfaceVersion,
#[serde(default = "Socks5ProtocolVersion::new_legacy")] #[serde(default = "Socks5ProtocolVersion::new_legacy")]
pub socks5_protocol_version: Socks5ProtocolVersion, socks5_protocol_version: Socks5ProtocolVersion,
/// Specifies whether this client is going to use an anonymous sender tag for communication with the service provider. /// Specifies whether this client is going to use an anonymous sender tag for communication with the service provider.
/// While this is going to hide its actual address information, it will make the actual communication /// While this is going to hide its actual address information, it will make the actual communication
@@ -122,10 +194,10 @@ pub struct Socks5 {
/// ///
/// Note that some service providers might not support this. /// Note that some service providers might not support this.
#[serde(default)] #[serde(default)]
pub send_anonymously: bool, send_anonymously: bool,
#[serde(default)] #[serde(default)]
pub socks5_debug: Socks5Debug, socks5_debug: Socks5Debug,
} }
impl Socks5 { impl Socks5 {
@@ -140,20 +212,81 @@ impl Socks5 {
} }
} }
pub fn with_port(&mut self, port: u16) {
self.listening_port = port;
}
pub fn with_provider_mix_address(&mut self, address: String) {
self.provider_mix_address = address;
}
pub fn with_provider_interface_version(&mut self, version: ProviderInterfaceVersion) {
self.provider_interface_version = version;
}
pub fn with_socks5_protocol_version(&mut self, version: Socks5ProtocolVersion) {
self.socks5_protocol_version = version;
}
pub fn with_anonymous_replies(&mut self, anonymous_replies: bool) {
self.send_anonymously = anonymous_replies;
}
pub fn get_raw_provider_mix_address(&self) -> String {
self.provider_mix_address.clone()
}
pub fn get_provider_mix_address(&self) -> Recipient { pub fn get_provider_mix_address(&self) -> Recipient {
Recipient::try_from_base58_string(&self.provider_mix_address) Recipient::try_from_base58_string(&self.provider_mix_address)
.expect("malformed provider address") .expect("malformed provider address")
} }
pub fn get_provider_interface_version(&self) -> ProviderInterfaceVersion {
self.provider_interface_version
}
pub fn get_socks5_protocol_version(&self) -> Socks5ProtocolVersion {
self.socks5_protocol_version
}
pub fn get_send_anonymously(&self) -> bool {
self.send_anonymously
}
pub fn get_listening_port(&self) -> u16 {
self.listening_port
}
pub fn get_connection_start_surbs(&self) -> u32 {
self.socks5_debug.connection_start_surbs
}
pub fn get_per_request_surbs(&self) -> u32 {
self.socks5_debug.per_request_surbs
}
} }
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Serialize)] impl Default for Socks5 {
fn default() -> Self {
Socks5 {
listening_port: DEFAULT_SOCKS5_LISTENING_PORT,
provider_mix_address: "".into(),
provider_interface_version: ProviderInterfaceVersion::Legacy,
socks5_protocol_version: Socks5ProtocolVersion::Legacy,
send_anonymously: false,
socks5_debug: Default::default(),
}
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct Socks5Debug { pub struct Socks5Debug {
/// Number of reply SURBs attached to each `Request::Connect` message. /// Number of reply SURBs attached to each `Request::Connect` message.
pub connection_start_surbs: u32, connection_start_surbs: u32,
/// Number of reply SURBs attached to each `Request::Send` message. /// Number of reply SURBs attached to each `Request::Send` message.
pub per_request_surbs: u32, per_request_surbs: u32,
} }
impl Default for Socks5Debug { impl Default for Socks5Debug {
@@ -3,60 +3,60 @@
use crate::config::{Config, Socks5}; use crate::config::{Config, Socks5};
use nym_client_core::config::old_config_v1_1_13::OldConfigV1_1_13 as OldBaseConfigV1_1_13; use nym_client_core::config::old_config_v1_1_13::OldConfigV1_1_13 as OldBaseConfigV1_1_13;
use nym_config::NymConfig;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::path::PathBuf; use std::path::PathBuf;
// #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
// #[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
// pub struct OldConfigV1_1_13 { pub struct OldConfigV1_1_13 {
// #[serde(flatten)] #[serde(flatten)]
// base: OldBaseConfigV1_1_13<OldConfigV1_1_13>, base: OldBaseConfigV1_1_13<OldConfigV1_1_13>,
//
// socks5: Socks5, socks5: Socks5,
// } }
//
// // impl NymConfig for OldConfigV1_1_13 {
// // impl NymConfig for OldConfigV1_1_13 { fn template() -> &'static str {
// // fn template() -> &'static str { // not intended to be used
// // // not intended to be used unimplemented!()
// // unimplemented!() }
// // }
// // fn default_root_directory() -> PathBuf {
// // fn default_root_directory() -> PathBuf { #[cfg(not(any(target_os = "android", target_os = "ios")))]
// // #[cfg(not(target_os = "android"))] let base_dir = dirs::home_dir().expect("Failed to evaluate $HOME value");
// // let base_dir = dirs::home_dir().expect("Failed to evaluate $HOME value"); #[cfg(any(target_os = "android", target_os = "ios"))]
// // #[cfg(target_os = "android")] let base_dir = PathBuf::from("/tmp");
// // let base_dir = PathBuf::from("/tmp");
// // base_dir.join(".nym").join("socks5-clients")
// // base_dir.join(".nym").join("socks5-clients") }
// // }
// // fn try_default_root_directory() -> Option<PathBuf> {
// // fn try_default_root_directory() -> Option<PathBuf> { dirs::home_dir().map(|path| path.join(".nym").join("socks5-clients"))
// // dirs::home_dir().map(|path| path.join(".nym").join("socks5-clients")) }
// // }
// // fn root_directory(&self) -> PathBuf {
// // fn root_directory(&self) -> PathBuf { self.base.client.nym_root_directory.clone()
// // self.base.client.nym_root_directory.clone() }
// // }
// // fn config_directory(&self) -> PathBuf {
// // fn config_directory(&self) -> PathBuf { self.root_directory()
// // self.root_directory() .join(&self.base.client.id)
// // .join(&self.base.client.id) .join("config")
// // .join("config") }
// // }
// // fn data_directory(&self) -> PathBuf {
// // fn data_directory(&self) -> PathBuf { self.root_directory()
// // self.root_directory() .join(&self.base.client.id)
// // .join(&self.base.client.id) .join("data")
// // .join("data") }
// // } }
// // }
// impl From<OldConfigV1_1_13> for Config {
// impl From<OldConfigV1_1_13> for Config { fn from(value: OldConfigV1_1_13) -> Self {
// fn from(value: OldConfigV1_1_13) -> Self { Config {
// Config { base: value.base.into(),
// base: value.base.into(), socks5: value.socks5,
// socks5: value.socks5, }
// } }
// } }
// }
@@ -1,87 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub use nym_client_core::config::old_config_v1_1_20_2::ConfigV1_1_20_2 as BaseClientConfigV1_1_20_2;
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use crate::config::{Config, Socks5, Socks5Debug};
pub use nym_service_providers_common::interface::ProviderInterfaceVersion;
pub use nym_socks5_requests::Socks5ProtocolVersion;
const DEFAULT_CONNECTION_START_SURBS: u32 = 20;
const DEFAULT_PER_REQUEST_SURBS: u32 = 3;
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_20_2 {
#[serde(flatten)]
pub base: BaseClientConfigV1_1_20_2,
pub socks5: Socks5V1_1_20_2,
}
impl From<ConfigV1_1_20_2> for Config {
fn from(value: ConfigV1_1_20_2) -> Self {
Config {
base: value.base.into(),
socks5: value.socks5.into(),
}
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Socks5V1_1_20_2 {
pub listening_port: u16,
pub provider_mix_address: String,
#[serde(default = "ProviderInterfaceVersion::new_legacy")]
pub provider_interface_version: ProviderInterfaceVersion,
#[serde(default = "Socks5ProtocolVersion::new_legacy")]
pub socks5_protocol_version: Socks5ProtocolVersion,
#[serde(default)]
pub send_anonymously: bool,
#[serde(default)]
pub socks5_debug: Socks5DebugV1_1_20_2,
}
impl From<Socks5V1_1_20_2> for Socks5 {
fn from(value: Socks5V1_1_20_2) -> Self {
Socks5 {
listening_port: value.listening_port,
provider_mix_address: value.provider_mix_address,
provider_interface_version: value.provider_interface_version,
socks5_protocol_version: value.socks5_protocol_version,
send_anonymously: value.send_anonymously,
socks5_debug: value.socks5_debug.into(),
}
}
}
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Socks5DebugV1_1_20_2 {
/// Number of reply SURBs attached to each `Request::Connect` message.
pub connection_start_surbs: u32,
/// Number of reply SURBs attached to each `Request::Send` message.
pub per_request_surbs: u32,
}
impl From<Socks5DebugV1_1_20_2> for Socks5Debug {
fn from(value: Socks5DebugV1_1_20_2) -> Self {
Socks5Debug {
connection_start_surbs: value.connection_start_surbs,
per_request_surbs: value.per_request_surbs,
}
}
}
impl Default for Socks5DebugV1_1_20_2 {
fn default() -> Self {
Socks5DebugV1_1_20_2 {
connection_start_surbs: DEFAULT_CONNECTION_START_SURBS,
per_request_surbs: DEFAULT_PER_REQUEST_SURBS,
}
}
}
@@ -1,97 +1,102 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// While using normal toml marshalling would have been way simpler with less overhead, pub(crate) fn config_template() -> &'static str {
// I think it's useful to have comments attached to the saved config file to explain behaviour of // While using normal toml marshalling would have been way simpler with less overhead,
// particular fields. // I think it's useful to have comments attached to the saved config file to explain behaviour of
// Note: any changes to the template must be reflected in the appropriate structs. // particular fields.
pub(crate) const CONFIG_TEMPLATE: &str = r#" // Note: any changes to the template must be reflected in the appropriate structs.
r#"
# This is a TOML config file. # This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml # For more information, see https://github.com/toml-lang/toml
##### main base client config options ##### ##### main base client config options #####
[core.client] [client]
# Version of the client for which this configuration was created. # Version of the client for which this configuration was created.
version = '{{ core.client.version }}' version = '{{ client.version }}'
# Human readable ID of this particular client. # Human readable ID of this particular client.
id = '{{ core.client.id }}' id = '{{ client.id }}'
# Indicates whether this client is running in a disabled credentials mode, thus attempting # Indicates whether this client is running in a disabled credentials mode, thus attempting
# to claim bandwidth without presenting bandwidth credentials. # to claim bandwidth without presenting bandwidth credentials.
disabled_credentials_mode = {{ core.client.disabled_credentials_mode }} disabled_credentials_mode = {{ client.disabled_credentials_mode }}
# Addresses to nyxd validators via which the client can communicate with the chain. # Addresses to nyxd validators via which the client can communicate with the chain.
nyxd_urls = [ nyxd_urls = [
{{#each core.client.nyxd_urls }} {{#each client.nyxd_urls }}
'{{this}}', '{{this}}',
{{/each}} {{/each}}
] ]
# Addresses to APIs running on validator from which the client gets the view of the network. # Addresses to APIs running on validator from which the client gets the view of the network.
nym_api_urls = [ nym_api_urls = [
{{#each core.client.nym_api_urls }} {{#each client.nym_api_urls }}
'{{this}}', '{{this}}',
{{/each}} {{/each}}
] ]
[storage_paths]
# Path to file containing private identity key. # Path to file containing private identity key.
keys.private_identity_key_file = '{{ storage_paths.keys.private_identity_key_file }}' private_identity_key_file = '{{ client.private_identity_key_file }}'
# Path to file containing public identity key. # Path to file containing public identity key.
keys.public_identity_key_file = '{{ storage_paths.keys.public_identity_key_file }}' public_identity_key_file = '{{ client.public_identity_key_file }}'
# Path to file containing private encryption key. # Path to file containing private encryption key.
keys.private_encryption_key_file = '{{ storage_paths.keys.private_encryption_key_file }}' private_encryption_key_file = '{{ client.private_encryption_key_file }}'
# Path to file containing public encryption key. # Path to file containing public encryption key.
keys.public_encryption_key_file = '{{ storage_paths.keys.public_encryption_key_file }}' public_encryption_key_file = '{{ client.public_encryption_key_file }}'
# Path to the database containing bandwidth credentials
database_path = '{{ client.database_path }}'
# Path to the persistent store for received reply surbs, unused encryption keys and used sender tags.
reply_surb_database_path = '{{ client.reply_surb_database_path }}'
##### additional client config options #####
# A gateway specific, optional, base58 stringified shared key used for # A gateway specific, optional, base58 stringified shared key used for
# communication with particular gateway. # communication with particular gateway.
keys.gateway_shared_key_file = '{{ storage_paths.keys.gateway_shared_key_file }}' gateway_shared_key_file = '{{ client.gateway_shared_key_file }}'
# Path to file containing key used for encrypting and decrypting the content of an # Path to file containing key used for encrypting and decrypting the content of an
# acknowledgement so that nobody besides the client knows which packet it refers to. # acknowledgement so that nobody besides the client knows which packet it refers to.
keys.ack_key_file = '{{ storage_paths.keys.ack_key_file }}' ack_key_file = '{{ client.ack_key_file }}'
# Path to the database containing bandwidth credentials ##### advanced configuration options #####
credentials_database = '{{ storage_paths.credentials_database }}'
# Path to the persistent store for received reply surbs, unused encryption keys and used sender tags. # Absolute path to the home Nym Clients directory.
reply_surb_database = '{{ storage_paths.reply_surb_database }}' nym_root_directory = '{{ client.nym_root_directory }}'
# DEPRECATED [client.gateway_endpoint]
[core.client.gateway_endpoint]
# ID of the gateway from which the client should be fetching messages. # ID of the gateway from which the client should be fetching messages.
gateway_id = '{{ core.client.gateway_endpoint.gateway_id }}' gateway_id = '{{ client.gateway_endpoint.gateway_id }}'
# Address of the gateway owner to which the client should send messages. # Address of the gateway owner to which the client should send messages.
gateway_owner = '{{ core.client.gateway_endpoint.gateway_owner }}' gateway_owner = '{{ client.gateway_endpoint.gateway_owner }}'
# Address of the gateway listener to which all client requests should be sent. # Address of the gateway listener to which all client requests should be sent.
gateway_listener = '{{ core.client.gateway_endpoint.gateway_listener }}' gateway_listener = '{{ client.gateway_endpoint.gateway_listener }}'
##### socket config options ##### ##### socket config options #####
[core.socks5] [socks5]
# The mix address of the provider to which all requests are going to be sent. # The mix address of the provider to which all requests are going to be sent.
provider_mix_address = '{{ core.socks5.provider_mix_address }}' provider_mix_address = '{{ socks5.provider_mix_address }}'
# The port on which the client will be listening for incoming requests # The port on which the client will be listening for incoming requests
listening_port = {{ core.socks5.listening_port }} listening_port = {{ socks5.listening_port }}
# Specifies whether this client is going to use an anonymous sender tag for communication with the service provider. # Specifies whether this client is going to use an anonymous sender tag for communication with the service provider.
# While this is going to hide its actual address information, it will make the actual communication # While this is going to hide its actual address information, it will make the actual communication
# slower and consume nearly double the bandwidth as it will require sending reply SURBs. # slower and consume nearly double the bandwidth as it will require sending reply SURBs.
# #
# Note that some service providers might not support this. # Note that some service providers might not support this.
send_anonymously = {{ core.socks5.send_anonymously }} send_anonymously = {{ socks5.send_anonymously }}
##### logging configuration options ##### ##### logging configuration options #####
@@ -104,19 +109,20 @@ send_anonymously = {{ core.socks5.send_anonymously }}
# The following options should not be modified unless you know EXACTLY what you are doing # The following options should not be modified unless you know EXACTLY what you are doing
# as if set incorrectly, they may impact your anonymity. # as if set incorrectly, they may impact your anonymity.
# [core.socks5.socks5_debug] # [socks5.socks5_debug]
[core.debug] [debug]
[core.debug.traffic] [debug.traffic]
average_packet_delay = '{{ core.debug.traffic.average_packet_delay }}' average_packet_delay = '{{ debug.traffic.average_packet_delay }}'
message_sending_average_delay = '{{ core.debug.traffic.message_sending_average_delay }}' message_sending_average_delay = '{{ debug.traffic.message_sending_average_delay }}'
[core.debug.acknowledgements] [debug.acknowledgements]
average_ack_delay = '{{ core.debug.acknowledgements.average_ack_delay }}' average_ack_delay = '{{ debug.acknowledgements.average_ack_delay }}'
[core.debug.cover_traffic] [debug.cover_traffic]
loop_cover_traffic_average_delay = '{{ core.debug.cover_traffic.loop_cover_traffic_average_delay }}' loop_cover_traffic_average_delay = '{{ debug.cover_traffic.loop_cover_traffic_average_delay }}'
"#; "#
}
+36 -39
View File
@@ -1,7 +1,7 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::config::Config; use crate::config::{Config, Socks5};
use crate::error::Socks5ClientCoreError; use crate::error::Socks5ClientCoreError;
use crate::socks::{ use crate::socks::{
authentication::{AuthenticationMethods, Authenticator, User}, authentication::{AuthenticationMethods, Authenticator, User},
@@ -10,16 +10,13 @@ use crate::socks::{
use futures::channel::mpsc; use futures::channel::mpsc;
use futures::StreamExt; use futures::StreamExt;
use log::*; use log::*;
use nym_client_core::client::base_client::non_wasm_helpers::default_query_dkg_client_from_config;
use nym_client_core::client::base_client::storage::gateway_details::GatewayDetailsStore;
use nym_client_core::client::base_client::storage::MixnetClientStorage; use nym_client_core::client::base_client::storage::MixnetClientStorage;
use nym_client_core::client::base_client::{ use nym_client_core::client::base_client::{
BaseClientBuilder, ClientInput, ClientOutput, ClientState, non_wasm_helpers, BaseClientBuilder, ClientInput, ClientOutput, ClientState,
}; };
use nym_client_core::client::key_manager::persistence::KeyStore; use nym_client_core::client::key_manager::persistence::KeyStore;
use nym_client_core::client::replies::reply_storage::ReplyStorageBackend; use nym_client_core::client::replies::reply_storage::ReplyStorageBackend;
use nym_client_core::config::DebugConfig; use nym_client_core::config::DebugConfig;
use nym_client_core::init::GatewaySetup;
use nym_credential_storage::storage::Storage as CredentialStorage; use nym_credential_storage::storage::Storage as CredentialStorage;
use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::params::PacketType; use nym_sphinx::params::PacketType;
@@ -54,8 +51,6 @@ pub struct NymClient<S> {
config: Config, config: Config,
storage: S, storage: S,
setup_method: GatewaySetup,
} }
impl<S> NymClient<S> impl<S> NymClient<S>
@@ -64,26 +59,16 @@ where
S::ReplyStore: Send + Sync, S::ReplyStore: Send + Sync,
<S::ReplyStore as ReplyStorageBackend>::StorageError: Sync + Send, <S::ReplyStore as ReplyStorageBackend>::StorageError: Sync + Send,
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync, <S::CredentialStore as CredentialStorage>::StorageError: Send + Sync,
<S::GatewayDetailsStore as GatewayDetailsStore>::StorageError: Sync + Send,
<S::KeyStore as KeyStore>::StorageError: Send + Sync, <S::KeyStore as KeyStore>::StorageError: Send + Sync,
{ {
pub fn new(config: Config, storage: S) -> Self { pub fn new(config: Config, storage: S) -> Self {
NymClient { NymClient { config, storage }
config,
storage,
setup_method: GatewaySetup::MustLoad,
}
}
pub fn with_gateway_setup(mut self, setup: GatewaySetup) -> Self {
self.setup_method = setup;
self
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn start_socks5_listener( pub fn start_socks5_listener(
socks5_config: &config::Socks5, socks5_config: &Socks5,
base_debug: DebugConfig, debug_config: DebugConfig,
client_input: ClientInput, client_input: ClientInput,
client_output: ClientOutput, client_output: ClientOutput,
client_status: ClientState, client_status: ClientState,
@@ -109,24 +94,25 @@ where
.. ..
} = client_status; } = client_status;
let packet_size = base_debug let packet_size = debug_config
.traffic .traffic
.secondary_packet_size .secondary_packet_size
.unwrap_or(base_debug.traffic.primary_packet_size); .unwrap_or(debug_config.traffic.primary_packet_size);
let authenticator = Authenticator::new(auth_methods, allowed_users); let authenticator = Authenticator::new(auth_methods, allowed_users);
let mut sphinx_socks = NymSocksServer::new( let mut sphinx_socks = NymSocksServer::new(
socks5_config.listening_port, socks5_config.get_listening_port(),
authenticator, authenticator,
socks5_config.get_provider_mix_address(), socks5_config.get_provider_mix_address(),
self_address, self_address,
shared_lane_queue_lengths, shared_lane_queue_lengths,
socks::client::Config::new( socks::client::Config::new(
packet_size, packet_size,
socks5_config.provider_interface_version, socks5_config.get_provider_interface_version(),
socks5_config.socks5_protocol_version, socks5_config.get_socks5_protocol_version(),
socks5_config.send_anonymously, socks5_config.get_send_anonymously(),
socks5_config.socks5_debug, socks5_config.get_connection_start_surbs(),
socks5_config.get_per_request_surbs(),
), ),
shutdown.clone(), shutdown.clone(),
packet_type, packet_type,
@@ -202,35 +188,46 @@ where
} }
pub async fn start(self) -> Result<StartedSocks5Client, Socks5ClientCoreError> { pub async fn start(self) -> Result<StartedSocks5Client, Socks5ClientCoreError> {
// don't create dkg client for the bandwidth controller if credentials are disabled let (key_store, reply_storage_backend, credential_store) = self.storage.into_split();
let dkg_query_client = if self.config.base.client.disabled_credentials_mode {
// don't create bandwidth controller if credentials are disabled
let bandwidth_controller = if self.config.get_base().get_disabled_credentials_mode() {
None None
} else { } else {
Some(default_query_dkg_client_from_config(&self.config.base)) Some(non_wasm_helpers::create_bandwidth_controller(
self.config.get_base(),
credential_store,
))
}; };
let base_builder = let base_builder = BaseClientBuilder::<_, S>::new_from_base_config(
BaseClientBuilder::new(&self.config.base, self.storage, dkg_query_client) self.config.get_base(),
.with_gateway_setup(self.setup_method); key_store,
bandwidth_controller,
reply_storage_backend,
);
let packet_type = self.config.base.debug.traffic.packet_type; let packet_type = self.config.get_base().get_packet_type();
let mut started_client = base_builder.start_base().await?; let mut started_client = base_builder.start_base(packet_type).await?;
let self_address = started_client.address; let self_address = started_client.address;
let client_input = started_client.client_input.register_producer(); let client_input = started_client.client_input.register_producer();
let client_output = started_client.client_output.register_consumer(); let client_output = started_client.client_output.register_consumer();
let client_state = started_client.client_state; let client_state = started_client.client_state;
info!("Running with {packet_type} packets",); info!(
"Running with {:?} packets",
self.config.get_base().get_packet_type()
);
Self::start_socks5_listener( Self::start_socks5_listener(
&self.config.socks5, self.config.get_socks5(),
self.config.base.debug, *self.config.get_debug_settings(),
client_input, client_input,
client_output, client_output,
client_state, client_state,
self_address, self_address,
started_client.task_manager.subscribe(), started_client.task_manager.subscribe(),
packet_type, self.config.get_base().get_packet_type(),
); );
info!("Client startup finished!"); info!("Client startup finished!");

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