Compare commits

...

27 Commits

Author SHA1 Message Date
Tommy Verrall 8c5f264cbf Update build-and-upload-binaries-ci.yml
change rust version due to wasm-opt issue
2023-06-07 10:50:18 +02:00
Jędrzej Stuczyński 73b74ad4d2 removed explicit packet_type argument when starting base client
it's known implicitly from the previously passed config struct
2023-06-05 10:07:15 +01:00
Jędrzej Stuczyński 4b4f211cba fixed wasm client build 2023-06-05 09:54:14 +01:00
Jędrzej Stuczyński 7a26e2ef57 clippy 2023-06-02 17:48:56 +01:00
Jędrzej Stuczyński 9782945c92 defined socks5 lib config 2023-06-02 17:32:46 +01:00
Jędrzej Stuczyński 573a91e015 outfox fixes 2023-06-02 16:32:50 +01:00
Jędrzej Stuczyński eb06285653 nym-connect config updates 2023-06-02 16:20:30 +01:00
Jędrzej Stuczyński 728542181e nym-connect clippy 2023-06-02 16:20:30 +01:00
Jędrzej Stuczyński 8f52c2a229 removed deprecations (that will be resolved in the following PRs) + fixed clippy 2023-06-02 16:20:30 +01:00
Jędrzej Stuczyński 3916bbf632 NR config migration 2023-06-02 16:20:30 +01:00
Jędrzej Stuczyński f8ce87a205 socks5 client config migration 2023-06-02 16:20:30 +01:00
Jędrzej Stuczyński c9b5ac2abf native client config migration 2023-06-02 16:20:30 +01:00
Jędrzej Stuczyński bd5577c0a4 nym-api config migration 2023-06-02 16:20:30 +01:00
Jędrzej Stuczyński 3e9178a664 gateway config migration 2023-06-02 16:20:30 +01:00
Jędrzej Stuczyński ac822164af mixnode config migration 2023-06-02 16:20:30 +01:00
Jędrzej Stuczyński edc404d6d7 renamed paths to storage_paths and fixed mixnode template 2023-06-02 16:20:30 +01:00
Jędrzej Stuczyński e0e5317b44 creating full directory structure on init 2023-06-02 16:20:30 +01:00
Jędrzej Stuczyński 6711a3bb3c everything compiling once more
but definitely not compatible with CI and older versions (yet)
2023-06-02 16:20:28 +01:00
Jędrzej Stuczyński 20130fb2a2 nym-sdk 2023-06-02 16:19:18 +01:00
Jędrzej Stuczyński 59560ac125 nym-api 2023-06-02 16:18:42 +01:00
Jędrzej Stuczyński 893648cad0 compiling updated gateway 2023-06-02 16:17:57 +01:00
Jędrzej Stuczyński 27502b9c75 using const for mixnnode config template 2023-06-02 16:17:57 +01:00
Jędrzej Stuczyński 2ab4a445b7 building socks5 2023-06-02 16:17:55 +01:00
Jędrzej Stuczyński 7f5e2f2909 wip 2023-06-02 16:16:18 +01:00
Jędrzej Stuczyński d5bc26d2e3 native client config revamping 2023-06-02 16:13:25 +01:00
Jędrzej Stuczyński aaa965937b wip 2023-06-02 16:12:49 +01:00
Jędrzej Stuczyński b3f883e82b revamping mixnode connfig 2023-06-02 16:09:01 +01:00
147 changed files with 6289 additions and 4815 deletions
@@ -63,7 +63,7 @@ jobs:
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
toolchain: 1.69.0
- name: Build all binaries
uses: actions-rs/cargo@v1
@@ -74,7 +74,7 @@ jobs:
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
toolchain: 1.69.0
target: wasm32-unknown-unknown
override: true
components: rustfmt, clippy
Generated
+98 -16
View File
@@ -1437,7 +1437,16 @@ version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
dependencies = [
"dirs-sys",
"dirs-sys 0.3.7",
]
[[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]]
@@ -1451,6 +1460,18 @@ dependencies = [
"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]]
name = "doc-comment"
version = "0.3.3"
@@ -1741,7 +1762,7 @@ dependencies = [
"atomic",
"pear",
"serde",
"toml",
"toml 0.5.11",
"uncased",
"version_check",
]
@@ -3189,7 +3210,7 @@ dependencies = [
"cw-utils",
"cw3",
"cw4",
"dirs",
"dirs 4.0.0",
"futures",
"getset",
"humantime-serde",
@@ -3241,6 +3262,7 @@ dependencies = [
"tokio-stream",
"ts-rs",
"url",
"zeroize",
]
[[package]]
@@ -3367,7 +3389,7 @@ dependencies = [
"tap",
"thiserror",
"time 0.3.21",
"toml",
"toml 0.5.11",
"url",
]
@@ -3376,7 +3398,7 @@ name = "nym-client"
version = "1.1.19"
dependencies = [
"clap 4.2.7",
"dirs",
"dirs 4.0.0",
"futures",
"lazy_static",
"log",
@@ -3413,7 +3435,7 @@ version = "1.1.14"
dependencies = [
"async-trait",
"dashmap 5.4.0",
"dirs",
"dirs 4.0.0",
"futures",
"gloo-timers",
"humantime-serde",
@@ -3520,12 +3542,12 @@ dependencies = [
name = "nym-config"
version = "0.1.0"
dependencies = [
"cfg-if",
"dirs 5.0.1",
"handlebars",
"log",
"nym-network-defaults",
"serde",
"toml",
"toml 0.7.4",
"url",
]
@@ -3549,6 +3571,7 @@ dependencies = [
"log",
"nym-bandwidth-controller",
"nym-bin-common",
"nym-client-core",
"nym-config",
"nym-credential-storage",
"nym-credentials",
@@ -3654,7 +3677,7 @@ dependencies = [
"clap 4.2.7",
"colored",
"dashmap 4.0.2",
"dirs",
"dirs 4.0.0",
"dotenvy",
"futures",
"humantime-serde",
@@ -3804,7 +3827,7 @@ dependencies = [
"colored",
"cpu-cycles",
"cupid",
"dirs",
"dirs 4.0.0",
"futures",
"humantime-serde",
"lazy_static",
@@ -3833,7 +3856,7 @@ dependencies = [
"sysinfo",
"tokio",
"tokio-util",
"toml",
"toml 0.5.11",
"tracing",
"url",
]
@@ -3910,7 +3933,7 @@ dependencies = [
"async-file-watcher",
"async-trait",
"clap 4.2.7",
"dirs",
"dirs 4.0.0",
"futures",
"humantime-serde",
"ipnetwork 0.20.0",
@@ -3949,7 +3972,7 @@ dependencies = [
name = "nym-network-statistics"
version = "1.1.19"
dependencies = [
"dirs",
"dirs 4.0.0",
"log",
"nym-bin-common",
"nym-statistics-common",
@@ -4054,7 +4077,7 @@ dependencies = [
"tap",
"thiserror",
"tokio",
"toml",
"toml 0.5.11",
"url",
]
@@ -4117,7 +4140,7 @@ dependencies = [
name = "nym-socks5-client-core"
version = "0.1.0"
dependencies = [
"dirs",
"dirs 4.0.0",
"futures",
"log",
"nym-bandwidth-controller",
@@ -4158,6 +4181,7 @@ dependencies = [
"openssl",
"rand 0.7.3",
"safer-ffi",
"serde",
"tokio",
]
@@ -4644,6 +4668,12 @@ dependencies = [
"thiserror",
]
[[package]]
name = "option-ext"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "os_str_bytes"
version = "6.5.0"
@@ -6017,6 +6047,15 @@ dependencies = [
"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]]
name = "serde_urlencoded"
version = "0.7.1"
@@ -6645,7 +6684,7 @@ dependencies = [
"serde",
"serde_json",
"tendermint",
"toml",
"toml 0.5.11",
"url",
]
@@ -6968,6 +7007,40 @@ dependencies = [
"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]]
name = "tonic"
version = "0.9.2"
@@ -7787,6 +7860,15 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
[[package]]
name = "winnow"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699"
dependencies = [
"memchr",
]
[[package]]
name = "winreg"
version = "0.10.1"
+2
View File
@@ -12,7 +12,9 @@ serde = { workspace = true, features = ["derive"] }
thiserror = "1.0"
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal", "macros"] } # async runtime
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
nym-client-core = { path = "../../common/client-core" }
nym-config = { path = "../../common/config" }
nym-credentials = { path = "../../common/credentials" }
nym-credential-storage = { path = "../../common/credential-storage" }
+8 -6
View File
@@ -1,4 +1,4 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
mod commands;
@@ -9,13 +9,14 @@ use commands::*;
use error::Result;
use log::*;
use nym_bin_common::completions::fig_generate;
use nym_config::{CRED_DB_FILE_NAME, DATA_DIR};
use nym_config::DEFAULT_DATA_DIR;
use nym_network_defaults::{setup_env, NymNetworkDetails};
use std::process::exit;
use std::time::{Duration, SystemTime};
use clap::{CommandFactory, Parser};
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::{Coin, CosmWasmClient};
use nym_validator_client::Config;
@@ -71,10 +72,11 @@ async fn main() -> Result<()> {
match args.command {
Command::Run(r) => {
let db_path = r
.client_home_directory
.join(DATA_DIR)
.join(CRED_DB_FILE_NAME);
// we assume the structure of <home-dir>/data
let data_dir = r.client_home_directory.join(DEFAULT_DATA_DIR);
let paths = CommonClientPaths::new_default(data_dir);
let db_path = paths.credentials_database;
let shared_storage =
nym_credential_storage::initialise_persistent_storage(db_path).await;
let recovery_storage = recovery_storage::RecoveryStorage::new(r.recovery_dir)?;
+82 -92
View File
@@ -1,23 +1,57 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::config::template::config_template;
use nym_client_core::config::ClientCoreConfigTrait;
use crate::client::config::persistence::ClientPaths;
use crate::client::config::template::CONFIG_TEMPLATE;
use nym_bin_common::logging::LoggingSettings;
use nym_config::defaults::DEFAULT_WEBSOCKET_LISTENING_PORT;
use nym_config::{NymConfig, OptionalSet};
use nym_config::{
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 std::fmt::Debug;
use std::io;
use std::net::{IpAddr, Ipv4Addr};
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use std::str::FromStr;
pub use nym_client_core::config::Config as BaseConfig;
pub use nym_client_core::config::MISSING_VALUE;
pub use nym_client_core::config::Config as BaseClientConfig;
pub use nym_client_core::config::{DebugConfig, GatewayEndpointConfig};
pub mod old_config_v1_1_13;
pub mod old_config_v1_1_19;
mod persistence;
mod template;
const DEFAULT_CLIENTS_DIR: &str = "clients";
/// Derive default path to clients's config directory.
/// It should get resolved to `$HOME/.nym/mixnodes/<id>/config`
pub fn default_config_directory<P: AsRef<Path>>(id: P) -> PathBuf {
must_get_home()
.join(NYM_DIR)
.join(DEFAULT_CLIENTS_DIR)
.join(id)
.join(DEFAULT_CONFIG_DIR)
}
/// Derive default path to client's config file.
/// It should get resolved to `$HOME/.nym/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/clients/<id>/data`
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, Eq, Serialize, Clone, Copy)]
#[serde(deny_unknown_fields)]
pub enum SocketType {
@@ -26,74 +60,57 @@ pub enum SocketType {
}
impl SocketType {
pub fn from_string<S: Into<String>>(val: S) -> Self {
let mut upper = val.into();
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(Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
#[derive(Debug, Deserialize, PartialEq, Serialize)]
pub struct Config {
#[serde(flatten)]
base: BaseConfig<Config>,
pub base: BaseClientConfig,
socket: Socket,
pub socket: Socket,
// pub paths: CommonClientPathfinder,
pub storage_paths: ClientPaths,
pub logging: LoggingSettings,
}
impl NymConfig for Config {
impl NymConfigTemplate for Config {
fn template() -> &'static str {
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()
CONFIG_TEMPLATE
}
}
impl Config {
pub fn new<S: Into<String>>(id: S) -> Self {
pub fn new<S: AsRef<str>>(id: S) -> Self {
Config {
base: BaseConfig::new(id),
base: BaseClientConfig::new(id.as_ref()),
storage_paths: ClientPaths::new_default(default_data_directory(id.as_ref())),
logging: 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 {
// no other sections have explicit requirements (yet)
self.base.validate()
@@ -123,39 +140,10 @@ impl Config {
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
pub fn with_base<F, T>(mut self, f: F, val: T) -> Self
where
F: Fn(BaseConfig<Self>, T) -> BaseConfig<Self>,
F: Fn(BaseClientConfig, T) -> BaseClientConfig,
{
self.base = f(self.base, val);
self
@@ -165,7 +153,7 @@ impl Config {
// (plz, lets refactor it)
pub fn with_optional_ext<F, T>(mut self, f: F, val: Option<T>) -> Self
where
F: Fn(BaseConfig<Self>, T) -> BaseConfig<Self>,
F: Fn(BaseClientConfig, T) -> BaseClientConfig,
{
self.base = self.base.with_optional(f, val);
self
@@ -173,7 +161,7 @@ impl Config {
pub fn with_optional_env_ext<F, T>(mut self, f: F, val: Option<T>, env_var: &str) -> Self
where
F: Fn(BaseConfig<Self>, T) -> BaseConfig<Self>,
F: Fn(BaseClientConfig, T) -> BaseClientConfig,
T: FromStr,
<T as FromStr>::Err: Debug,
{
@@ -189,7 +177,7 @@ impl Config {
parser: G,
) -> Self
where
F: Fn(BaseConfig<Self>, T) -> BaseConfig<Self>,
F: Fn(BaseClientConfig, T) -> BaseClientConfig,
G: Fn(&str) -> T,
{
self.base = self.base.with_optional_custom_env(f, val, env_var, parser);
@@ -197,19 +185,21 @@ impl Config {
}
}
// define_optional_set_inner!(Config, base, BaseClientConfig);
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct Socket {
socket_type: SocketType,
host: IpAddr,
listening_port: u16,
pub socket_type: SocketType,
pub host: IpAddr,
pub listening_port: u16,
}
impl Default for Socket {
fn default() -> Self {
Socket {
socket_type: SocketType::WebSocket,
host: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
host: IpAddr::V4(Ipv4Addr::LOCALHOST),
listening_port: DEFAULT_WEBSOCKET_LISTENING_PORT,
}
}
@@ -1,58 +1,33 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::config::{Config, Socket};
use crate::client::config::old_config_v1_1_19::{ConfigV1_1_19, SocketV1_1_19};
use nym_client_core::config::old_config_v1_1_13::OldConfigV1_1_13 as OldBaseConfigV1_1_13;
use nym_config::NymConfig;
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct OldConfigV1_1_13 {
#[serde(flatten)]
base: OldBaseConfigV1_1_13<OldConfigV1_1_13>,
pub base: OldBaseConfigV1_1_13<OldConfigV1_1_13>,
socket: Socket,
pub socket: SocketV1_1_19,
}
impl NymConfig for OldConfigV1_1_13 {
fn template() -> &'static str {
// not intended to be used
unimplemented!()
}
impl MigrationNymConfig for OldConfigV1_1_13 {
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.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 Config {
impl From<OldConfigV1_1_13> for ConfigV1_1_19 {
fn from(value: OldConfigV1_1_13) -> Self {
Config {
ConfigV1_1_19 {
base: value.base.into(),
socket: value.socket,
}
@@ -0,0 +1,112 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::config::persistence::ClientPaths;
use crate::client::config::{Config, Socket, SocketType};
use nym_bin_common::logging::LoggingSettings;
use nym_client_core::config::disk_persistence::keys_paths::ClientKeysPaths;
use nym_client_core::config::disk_persistence::CommonClientPaths;
use nym_client_core::config::old_config_v1_1_19::ConfigV1_1_19 as BaseConfigV1_1_19;
use nym_client_core::config::{Client, Config as BaseConfig};
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_19 {
WebSocket,
None,
}
impl From<SocketTypeV1_1_19> for SocketType {
fn from(value: SocketTypeV1_1_19) -> Self {
match value {
SocketTypeV1_1_19::WebSocket => SocketType::WebSocket,
SocketTypeV1_1_19::None => SocketType::None,
}
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_19 {
#[serde(flatten)]
pub base: BaseConfigV1_1_19<ConfigV1_1_19>,
pub socket: SocketV1_1_19,
}
impl From<ConfigV1_1_19> for Config {
fn from(value: ConfigV1_1_19) -> Self {
Config {
base: BaseConfig {
client: Client {
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: ClientPaths {
common_paths: CommonClientPaths {
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_19 {
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_19 {
socket_type: SocketTypeV1_1_19,
host: IpAddr,
listening_port: u16,
}
impl From<SocketV1_1_19> for Socket {
fn from(value: SocketV1_1_19) -> Self {
Socket {
socket_type: value.socket_type.into(),
host: value.host,
listening_port: value.listening_port,
}
}
}
impl Default for SocketV1_1_19 {
fn default() -> Self {
SocketV1_1_19 {
socket_type: SocketTypeV1_1_19::WebSocket,
host: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
listening_port: DEFAULT_WEBSOCKET_LISTENING_PORT,
}
}
}
@@ -0,0 +1,20 @@
// 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),
}
}
}
+21 -26
View File
@@ -1,12 +1,11 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub(crate) fn config_template() -> &'static str {
// 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.
r#"
// 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
@@ -37,39 +36,35 @@ nym_api_urls = [
{{/each}}
]
[storage_paths]
# Path to file containing private identity key.
private_identity_key_file = '{{ client.private_identity_key_file }}'
keys.private_identity_key_file = '{{ storage_paths.keys.private_identity_key_file }}'
# Path to file containing public identity key.
public_identity_key_file = '{{ client.public_identity_key_file }}'
keys.public_identity_key_file = '{{ storage_paths.keys.public_identity_key_file }}'
# Path to file containing private encryption key.
private_encryption_key_file = '{{ client.private_encryption_key_file }}'
keys.private_encryption_key_file = '{{ storage_paths.keys.private_encryption_key_file }}'
# Path to file containing public encryption key.
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 #####
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.
gateway_shared_key_file = '{{ client.gateway_shared_key_file }}'
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.
ack_key_file = '{{ client.ack_key_file }}'
##### advanced configuration options #####
keys.ack_key_file = '{{ storage_paths.keys.ack_key_file }}'
# Absolute path to the home Nym Clients directory.
nym_root_directory = '{{ client.nym_root_directory }}'
# 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 }}'
# DEPRECATED
[client.gateway_endpoint]
# ID of the gateway from which the client should be fetching messages.
gateway_id = '{{ client.gateway_endpoint.gateway_id }}'
@@ -120,5 +115,5 @@ average_ack_delay = '{{ debug.acknowledgements.average_ack_delay }}'
[debug.cover_traffic]
loop_cover_traffic_average_delay = '{{ debug.cover_traffic.loop_cover_traffic_average_delay }}'
"#
}
"#;
+15 -17
View File
@@ -1,4 +1,4 @@
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::config::Config;
@@ -17,7 +17,6 @@ use nym_client_core::client::key_manager::persistence::OnDiskKeys;
use nym_client_core::client::received_buffer::{
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::params::PacketType;
@@ -50,11 +49,11 @@ impl SocketClient {
config: &Config,
) -> BandwidthController<Client<QueryNyxdClient>, PersistentStorage> {
let storage = nym_credential_storage::initialise_persistent_storage(
config.get_base().get_database_path(),
&config.storage_paths.common_paths.credentials_database,
)
.await;
create_bandwidth_controller(config.get_base(), storage)
create_bandwidth_controller(&config.base, storage)
}
fn start_websocket_listener(
@@ -93,7 +92,7 @@ impl SocketClient {
Some(packet_type),
);
websocket::Listener::new(config.get_listening_ip(), config.get_listening_port())
websocket::Listener::new(config.socket.host, config.socket.listening_port)
.start(websocket_handler, shutdown);
}
@@ -107,26 +106,25 @@ impl SocketClient {
}
fn key_store(&self) -> OnDiskKeys {
let pathfinder = ClientKeyPathfinder::new_from_config(self.config.get_base());
OnDiskKeys::new(pathfinder)
OnDiskKeys::new(self.config.storage_paths.common_paths.keys.clone())
}
// TODO: see if this could also be shared with socks5 client / nym-sdk maybe
async fn create_base_client_builder(&self) -> Result<NativeClientBuilder, ClientError> {
// don't create bandwidth controller if credentials are disabled
let bandwidth_controller = if self.config.get_base().get_disabled_credentials_mode() {
let bandwidth_controller = if self.config.base.client.disabled_credentials_mode {
None
} else {
Some(Self::create_bandwidth_controller(&self.config).await)
};
let base_client = BaseClientBuilder::new_from_base_config(
self.config.get_base(),
&self.config.base,
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,
&self.config.storage_paths.common_paths.reply_surb_database,
&self.config.base.debug.reply_surbs,
)
.await?,
);
@@ -135,13 +133,13 @@ impl SocketClient {
}
pub async fn start_socket(self) -> Result<TaskManager, ClientError> {
if !self.config.get_socket_type().is_websocket() {
if !self.config.socket.socket_type.is_websocket() {
return Err(ClientError::InvalidSocketMode);
}
let base_builder = self.create_base_client_builder().await?;
let packet_type = self.config.get_base().get_packet_type();
let mut started_client = base_builder.start_base(packet_type).await?;
let packet_type = self.config.base.debug.traffic.packet_type;
let mut started_client = base_builder.start_base().await?;
let self_address = started_client.address;
let client_input = started_client.client_input.register_producer();
let client_output = started_client.client_output.register_consumer();
@@ -164,13 +162,13 @@ impl SocketClient {
}
pub async fn start_direct(self) -> Result<DirectClient, ClientError> {
if self.config.get_socket_type().is_websocket() {
if self.config.socket.socket_type.is_websocket() {
return Err(ClientError::InvalidSocketMode);
}
let base_builder = self.create_base_client_builder().await?;
let packet_type = self.config.get_base().get_packet_type();
let mut started_client = base_builder.start_base(packet_type).await?;
let packet_type = self.config.base.debug.traffic.packet_type;
let mut started_client = base_builder.start_base().await?;
let address = started_client.address;
let client_input = started_client.client_input.register_producer();
let client_output = started_client.client_output.register_consumer();
+37 -31
View File
@@ -1,7 +1,10 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::try_upgrade_v1_1_13_config;
use crate::client::config::{
default_config_directory, default_config_filepath, default_data_directory,
};
use crate::commands::try_upgrade_config;
use crate::{
client::config::Config,
commands::{override_config, OverrideConfig},
@@ -10,12 +13,12 @@ use crate::{
use clap::Args;
use nym_bin_common::output_format::OutputFormat;
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
use nym_config::NymConfig;
use nym_crypto::asymmetric::identity;
use nym_sphinx::addressing::clients::Recipient;
use serde::Serialize;
use std::fmt::Display;
use std::net::IpAddr;
use std::{fs, io};
use tap::TapFallible;
#[derive(Args, Clone)]
@@ -97,15 +100,15 @@ impl From<Init> for OverrideConfig {
pub struct InitResults {
#[serde(flatten)]
client_core: nym_client_core::init::InitResults,
client_listening_port: String,
client_listening_port: u16,
client_address: String,
}
impl InitResults {
fn new(config: &Config, address: &Recipient) -> Self {
Self {
client_core: nym_client_core::init::InitResults::new(config.get_base(), address),
client_listening_port: config.get_listening_port().to_string(),
client_core: nym_client_core::init::InitResults::new(&config.base, address),
client_listening_port: config.socket.listening_port,
client_address: address.to_string(),
}
}
@@ -119,18 +122,26 @@ 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> {
eprintln!("Initialising client...");
let id = &args.id;
let already_init = Config::default_config_file_path(id).exists();
if already_init {
let already_init = if default_config_filepath(id).exists() {
// in case we're using old config, try to upgrade it
// (if we're using the current version, it's a no-op)
try_upgrade_v1_1_13_config(id)?;
try_upgrade_config(id)?;
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
// re-registering if wanted.
@@ -152,42 +163,37 @@ pub(crate) async fn execute(args: &Init) -> Result<(), ClientError> {
// 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.
let key_store = OnDiskKeys::from_config(config.get_base());
let gateway = nym_client_core::init::setup_gateway_from_config::<Config, _, _>(
let key_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone());
let gateway = nym_client_core::init::setup_gateway_from_config::<_>(
&key_store,
register_gateway,
user_chosen_gateway_id,
config.get_base(),
&config.base,
args.latency_based_selection,
)
.await
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?;
config.get_base_mut().set_gateway_endpoint(gateway);
config.base.set_gateway_endpoint(gateway);
config.save_to_file(None).tap_err(|_| {
let config_save_location = config.default_location();
config.save_to_default_location().tap_err(|_| {
log::error!("Failed to save the config file");
})?;
eprintln!(
"Saved configuration file to {}",
config_save_location.display()
);
print_saved_config(&config);
let address = nym_client_core::init::get_client_address_from_stored_ondisk_keys(
&config.storage_paths.common_paths.keys,
&config.base.client.gateway_endpoint,
)?;
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);
println!("{}", args.output.format(&init_results));
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");
}
+66 -12
View File
@@ -2,14 +2,16 @@
// SPDX-License-Identifier: Apache-2.0
use crate::client::config::old_config_v1_1_13::OldConfigV1_1_13;
use crate::client::config::{BaseConfig, Config};
use crate::client::config::old_config_v1_1_19::ConfigV1_1_19;
use crate::client::config::{BaseClientConfig, Config};
use crate::error::ClientError;
use clap::CommandFactory;
use clap::{Parser, Subcommand};
use lazy_static::lazy_static;
use log::info;
use log::{error, info};
use nym_bin_common::build_information::BinaryBuildInformation;
use nym_bin_common::completions::{fig_generate, ArgShell};
use nym_config::{NymConfig, OptionalSet};
use nym_config::OptionalSet;
use std::error::Error;
use std::net::IpAddr;
@@ -82,40 +84,92 @@ pub(crate) async fn execute(args: &Cli) -> Result<(), Box<dyn Error + Send + Syn
pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
config
.with_optional(Config::with_disabled_socket, args.disable_socket)
.with_base(BaseConfig::with_high_default_traffic_volume, args.fastmode)
.with_base(BaseConfig::with_disabled_cover_traffic, args.no_cover)
.with_base(
BaseClientConfig::with_high_default_traffic_volume,
args.fastmode,
)
.with_base(BaseClientConfig::with_disabled_cover_traffic, args.no_cover)
.with_optional(Config::with_port, args.port)
.with_optional(Config::with_host, args.host)
.with_optional_custom_env_ext(
BaseConfig::with_custom_nym_apis,
BaseClientConfig::with_custom_nym_apis,
args.nym_apis,
nym_network_defaults::var_names::NYM_API,
nym_config::parse_urls,
)
.with_optional_custom_env_ext(
BaseConfig::with_custom_nyxd,
BaseClientConfig::with_custom_nyxd,
args.nyxd_urls,
nym_network_defaults::var_names::NYXD,
nym_config::parse_urls,
)
.with_optional_ext(
BaseConfig::with_disabled_credentials,
BaseClientConfig::with_disabled_credentials,
args.enabled_credentials_mode.map(|b| !b),
)
}
fn try_upgrade_v1_1_13_config(id: &str) -> std::io::Result<()> {
// explicitly load it as v1.1.13 (which is incompatible with the current, i.e. 1.1.14+)
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 {
// 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(());
return Ok(false);
};
info!("It seems the client is using <= v1.1.13 config template.");
info!("It is going to get updated to the current specification.");
let updated_step1: ConfigV1_1_19 = old_config.into();
let updated: Config = updated_step1.into();
updated.save_to_default_location()?;
Ok(true)
}
fn try_upgrade_v1_1_19_config(id: &str) -> Result<bool, ClientError> {
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
// explicitly load it as v1.1.19 (which is incompatible with the current one, i.e. +1.1.20)
let Ok(old_config) = ConfigV1_1_19::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.19 config template.");
info!("It is going to get updated to the current specification.");
let updated: Config = old_config.into();
updated.save_to_file(None)
updated.save_to_default_location()?;
Ok(true)
}
fn try_upgrade_config(id: &str) -> Result<(), ClientError> {
let upgraded = try_upgrade_v1_1_13_config(id)?;
if !upgraded {
try_upgrade_v1_1_19_config(id)?;
}
Ok(())
}
fn try_load_current_config(id: &str) -> Result<Config, ClientError> {
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)]
+7 -29
View File
@@ -1,10 +1,7 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::error::Error;
use std::net::IpAddr;
use crate::commands::try_upgrade_v1_1_13_config;
use crate::commands::try_load_current_config;
use crate::{
client::{config::Config, SocketClient},
commands::{override_config, OverrideConfig},
@@ -13,8 +10,9 @@ use crate::{
use clap::Args;
use log::*;
use nym_bin_common::version_checker::is_minor_version_compatible;
use nym_config::NymConfig;
use nym_crypto::asymmetric::identity;
use std::error::Error;
use std::net::IpAddr;
#[derive(Args, Clone)]
pub(crate) struct Run {
@@ -82,7 +80,7 @@ impl From<Run> for OverrideConfig {
// network version. It might do so in the future.
fn version_check(cfg: &Config) -> bool {
let binary_version = env!("CARGO_PKG_VERSION");
let config_version = cfg.get_base().get_version();
let config_version = &cfg.base.client.version;
if binary_version == config_version {
true
} else {
@@ -98,30 +96,10 @@ fn version_check(cfg: &Config) -> bool {
}
pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn Error + Send + Sync>> {
let id = &args.id;
eprintln!("Starting client {}...", args.id);
// in case we're using old config, try to upgrade it
// (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.");
}
let mut config = try_load_current_config(&args.id)?;
config = override_config(config, OverrideConfig::from(args.clone()));
if !version_check(&config) {
error!("failed the local version check");
+16 -84
View File
@@ -1,42 +1,14 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::config::{Config, MISSING_VALUE};
use nym_bin_common::version_checker::Version;
use nym_config::NymConfig;
use crate::client::config::Config;
use crate::commands::try_load_current_config;
use clap::Args;
use std::fmt::Display;
use nym_bin_common::version_checker::Version;
use std::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 {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!");
fn unimplemented_upgrade(current_version: &Version, config_version: &Version) -> ! {
eprintln!("Cannot perform upgrade from {config_version} to {current_version} as it hasn't been implemented yet");
process::exit(1)
}
@@ -48,7 +20,7 @@ pub(crate) struct Upgrade {
}
fn parse_config_version(config: &Config) -> Version {
let version = Version::parse(config.get_base().get_version()).unwrap_or_else(|err| {
let version = Version::parse(&config.base.client.version).unwrap_or_else(|err| {
eprintln!("failed to parse client version! - {err}");
process::exit(1)
});
@@ -77,53 +49,14 @@ fn parse_package_version() -> Version {
version
}
fn minor_0_12_upgrade(
mut config: Config,
_matches: &Upgrade,
config_version: &Version,
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)
};
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),
}
fn do_upgrade(config: Config, _args: &Upgrade, package_version: &Version) {
let config_version = parse_config_version(&config);
if &config_version == package_version {
println!("You're using the most recent version!");
return;
}
unimplemented_upgrade(package_version, &config_version)
}
pub(crate) fn execute(args: &Upgrade) {
@@ -131,16 +64,15 @@ pub(crate) fn execute(args: &Upgrade) {
let id = &args.id;
let existing_config = Config::load_from_file(id).unwrap_or_else(|err| {
let existing_config = try_load_current_config(id).unwrap_or_else(|err| {
eprintln!("failed to load existing config file! - {err}");
process::exit(1)
});
if existing_config.get_base().get_version() == MISSING_VALUE {
if existing_config.base.client.version.is_empty() {
eprintln!("the existing configuration file does not seem to contain version number.");
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)
}
+34 -32
View File
@@ -1,7 +1,10 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::try_upgrade_v1_1_13_config;
use crate::commands::try_upgrade_config;
use crate::config::{
default_config_directory, default_config_filepath, default_data_directory, Config,
};
use crate::{
commands::{override_config, OverrideConfig},
error::Socks5ClientError,
@@ -9,12 +12,11 @@ use crate::{
use clap::Args;
use nym_bin_common::output_format::OutputFormat;
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
use nym_config::NymConfig;
use nym_crypto::asymmetric::identity;
use nym_socks5_client_core::config::Config;
use nym_sphinx::addressing::clients::Recipient;
use serde::Serialize;
use std::fmt::Display;
use std::{fs, io};
use tap::TapFallible;
#[derive(Args, Clone)]
@@ -100,15 +102,15 @@ impl From<Init> for OverrideConfig {
pub struct InitResults {
#[serde(flatten)]
client_core: nym_client_core::init::InitResults,
socks5_listening_port: String,
socks5_listening_port: u16,
client_address: String,
}
impl InitResults {
fn new(config: &Config, address: &Recipient) -> Self {
Self {
client_core: nym_client_core::init::InitResults::new(config.get_base(), address),
socks5_listening_port: config.get_socks5().get_listening_port().to_string(),
client_core: nym_client_core::init::InitResults::new(&config.core.base, address),
socks5_listening_port: config.core.socks5.listening_port,
client_address: address.to_string(),
}
}
@@ -122,19 +124,27 @@ 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> {
eprintln!("Initialising client...");
let id = &args.id;
let provider_address = &args.provider;
let already_init = Config::default_config_file_path(id).exists();
if already_init {
let already_init = if default_config_filepath(id).exists() {
// in case we're using old config, try to upgrade it
// (if we're using the current version, it's a no-op)
try_upgrade_v1_1_13_config(id)?;
try_upgrade_config(id)?;
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
// re-registering if wanted.
@@ -159,44 +169,36 @@ pub(crate) async fn execute(args: &Init) -> Result<(), Socks5ClientError> {
// 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.
let key_store = OnDiskKeys::from_config(config.get_base());
let gateway = nym_client_core::init::setup_gateway_from_config::<Config, _, _>(
let key_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone());
let gateway = nym_client_core::init::setup_gateway_from_config::<_>(
&key_store,
register_gateway,
user_chosen_gateway_id,
config.get_base(),
&config.core.base,
args.latency_based_selection,
)
.await
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?;
config.get_base_mut().set_gateway_endpoint(gateway);
config.core.base.set_gateway_endpoint(gateway);
// TODO: ask the service provider we specified for its interface version and set it in the config
config.save_to_file(None).tap_err(|_| {
let config_save_location = config.default_location();
config.save_to_default_location().tap_err(|_| {
log::error!("Failed to save the config file");
})?;
eprintln!(
"Saved configuration file to {}",
config_save_location.display()
);
print_saved_config(&config);
let address =
nym_client_core::init::get_client_address_from_stored_ondisk_keys(config.get_base())?;
let address = nym_client_core::init::get_client_address_from_stored_ondisk_keys(
&config.storage_paths.common_paths.keys,
&config.core.base.client.gateway_endpoint,
)?;
let init_results = InitResults::new(&config, &address);
println!("{}", args.output.format(&init_results));
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");
}
+71 -17
View File
@@ -1,15 +1,17 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::old_config_v1_1_13::OldConfigV1_1_13;
use crate::config::old_config_v1_1_19::ConfigV1_1_19;
use crate::config::{BaseClientConfig, Config};
use crate::error::Socks5ClientError;
use clap::CommandFactory;
use clap::{Parser, Subcommand};
use lazy_static::lazy_static;
use log::info;
use log::{error, info};
use nym_bin_common::build_information::BinaryBuildInformation;
use nym_bin_common::completions::{fig_generate, ArgShell};
use nym_config::{NymConfig, OptionalSet};
use nym_socks5_client_core::config::old_config_v1_1_13::OldConfigV1_1_13;
use nym_socks5_client_core::config::{BaseConfig, Config};
use nym_config::OptionalSet;
use nym_sphinx::params::PacketType;
use std::error::Error;
@@ -88,41 +90,93 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
PacketType::Mix
};
config
.with_base(BaseConfig::with_high_default_traffic_volume, args.fastmode)
.with_base(BaseConfig::with_disabled_cover_traffic, args.no_cover)
.with_base(BaseConfig::with_packet_type, packet_type)
.with_base(
BaseClientConfig::with_high_default_traffic_volume,
args.fastmode,
)
.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_port, args.port)
.with_optional_custom_env_ext(
BaseConfig::with_custom_nym_apis,
.with_optional_base_custom_env(
BaseClientConfig::with_custom_nym_apis,
args.nym_apis,
nym_network_defaults::var_names::NYM_API,
nym_config::parse_urls,
)
.with_optional_custom_env_ext(
BaseConfig::with_custom_nyxd,
.with_optional_base_custom_env(
BaseClientConfig::with_custom_nyxd,
args.nyxd_urls,
nym_network_defaults::var_names::NYXD,
nym_config::parse_urls,
)
.with_optional_ext(
BaseConfig::with_disabled_credentials,
.with_optional_base(
BaseClientConfig::with_disabled_credentials,
args.enabled_credentials_mode.map(|b| !b),
)
}
fn try_upgrade_v1_1_13_config(id: &str) -> std::io::Result<()> {
// explicitly load it as v1.1.13 (which is incompatible with the current, i.e. 1.1.14+)
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 {
// 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(());
return Ok(false);
};
info!("It seems the client is using <= v1.1.13 config template.");
info!("It is going to get updated to the current specification.");
let updated_step1: ConfigV1_1_19 = old_config.into();
let updated: Config = updated_step1.into();
updated.save_to_default_location()?;
Ok(true)
}
fn try_upgrade_v1_1_19_config(id: &str) -> Result<bool, Socks5ClientError> {
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
// explicitly load it as v1.1.19 (which is incompatible with the current one, i.e. +1.1.20)
let Ok(old_config) = ConfigV1_1_19::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.19 config template.");
info!("It is going to get updated to the current specification.");
let updated: Config = old_config.into();
updated.save_to_file(None)
updated.save_to_default_location()?;
Ok(true)
}
fn try_upgrade_config(id: &str) -> Result<(), Socks5ClientError> {
let upgraded = try_upgrade_v1_1_13_config(id)?;
if !upgraded {
try_upgrade_v1_1_19_config(id)?;
}
Ok(())
}
fn try_load_current_config(id: &str) -> Result<Config, Socks5ClientError> {
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)]
+13 -38
View File
@@ -1,7 +1,8 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::try_upgrade_v1_1_13_config;
use crate::commands::try_load_current_config;
use crate::config::Config;
use crate::{
commands::{override_config, OverrideConfig},
error::Socks5ClientError,
@@ -10,9 +11,8 @@ use clap::Args;
use log::*;
use nym_bin_common::version_checker::is_minor_version_compatible;
use nym_client_core::client::base_client::storage::OnDiskPersistent;
use nym_config::NymConfig;
use nym_crypto::asymmetric::identity;
use nym_socks5_client_core::{config::Config, NymClient};
use nym_socks5_client_core::NymClient;
use nym_sphinx::addressing::clients::Recipient;
#[derive(Args, Clone)]
@@ -21,10 +21,6 @@ pub(crate) struct Run {
#[clap(long)]
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.
/// 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.
@@ -92,13 +88,12 @@ impl From<Run> for OverrideConfig {
// network version. It might do so in the future.
fn version_check(cfg: &Config) -> bool {
let binary_version = env!("CARGO_PKG_VERSION");
let config_version = cfg.get_base().get_version();
let config_version = &cfg.core.base.client.version;
if binary_version == config_version {
true
} else {
warn!(
"The mixnode binary has different version than what is specified in config file! {} and {}",
binary_version, config_version
"The socks5-client binary has different version than what is specified in config file! {binary_version} and {config_version}",
);
if is_minor_version_compatible(binary_version, config_version) {
info!("but they are still semver compatible. However, consider running the `upgrade` command");
@@ -111,38 +106,18 @@ fn version_check(cfg: &Config) -> bool {
}
pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let id = &args.id;
eprintln!("Starting client {}...", args.id);
// in case we're using old config, try to upgrade it
// (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.");
}
let mut config = try_load_current_config(&args.id)?;
config = override_config(config, OverrideConfig::from(args.clone()));
if !version_check(&config) {
error!("failed the local version check");
return Err(Box::new(Socks5ClientError::FailedLocalVersionCheck));
}
let storage = OnDiskPersistent::from_config(config.get_base()).await?;
NymClient::new(config, storage).run_forever().await
let storage =
OnDiskPersistent::from_paths(config.storage_paths.common_paths, &config.core.base.debug)
.await?;
NymClient::new(config.core, storage).run_forever().await
}
+19 -99
View File
@@ -1,50 +1,14 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_bin_common::version_checker::Version;
use nym_config::NymConfig;
use nym_socks5_client_core::config::{Config, MISSING_VALUE};
use crate::commands::try_load_current_config;
use crate::config::Config;
use clap::Args;
use std::{fmt::Display, process};
use nym_bin_common::version_checker::Version;
use std::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);
fn unimplemented_upgrade(current_version: &Version, config_version: &Version) -> ! {
eprintln!("Cannot perform upgrade from {config_version} to {current_version} as it hasn't been implemented yet");
process::exit(1)
}
@@ -56,15 +20,14 @@ pub(crate) struct Upgrade {
}
fn parse_config_version(config: &Config) -> Version {
let version = Version::parse(config.get_base().get_version()).unwrap_or_else(|err| {
let version = Version::parse(&config.core.base.client.version).unwrap_or_else(|err| {
eprintln!("failed to parse client version! - {err}");
process::exit(1)
});
if version.is_prerelease() || !version.build.is_empty() {
eprintln!(
"Trying to upgrade from a non-released version {}. This is not supported!",
version
"Trying to upgrade from a non-released version {version}. This is not supported!"
);
process::exit(1)
}
@@ -79,63 +42,21 @@ fn parse_package_version() -> Version {
// 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 version.is_prerelease() || !version.build.is_empty() {
eprintln!(
"Trying to upgrade to a non-released version {}. This is not supported!",
version
);
eprintln!("Trying to upgrade to a non-released version {version}. This is not supported!");
process::exit(1)
}
version
}
fn minor_0_12_upgrade(
mut config: Config,
_args: &Upgrade,
config_version: &Version,
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)
};
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),
}
fn do_upgrade(config: Config, _args: &Upgrade, package_version: &Version) {
let config_version = parse_config_version(&config);
if &config_version == package_version {
println!("You're using the most recent version!");
return;
}
unimplemented_upgrade(package_version, &config_version)
}
pub(crate) fn execute(args: &Upgrade) {
@@ -143,16 +64,15 @@ pub(crate) fn execute(args: &Upgrade) {
let id = &args.id;
let existing_config = Config::load_from_file(id).unwrap_or_else(|err| {
let existing_config = try_load_current_config(id).unwrap_or_else(|err| {
eprintln!("failed to load existing config file! - {err}");
process::exit(1)
});
if existing_config.get_base().get_version() == MISSING_VALUE {
if existing_config.core.base.client.version.is_empty() {
eprintln!("the existing configuration file does not seem to contain version number.");
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)
}
+155
View File
@@ -0,0 +1,155 @@
// 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_19;
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(), 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
}
}
@@ -0,0 +1,38 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::old_config_v1_1_19::{ConfigV1_1_19, Socks5V1_1_19};
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_19,
}
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_19 {
fn from(value: OldConfigV1_1_13) -> Self {
ConfigV1_1_19 {
base: value.base.into(),
socks5: value.socks5,
}
}
}
@@ -0,0 +1,135 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::persistence::SocksClientPaths;
use crate::config::{BaseClientConfig, Config, CoreConfig};
use nym_bin_common::logging::LoggingSettings;
use nym_client_core::config::disk_persistence::keys_paths::ClientKeysPaths;
use nym_client_core::config::disk_persistence::CommonClientPaths;
use nym_client_core::config::old_config_v1_1_19::ConfigV1_1_19 as BaseConfigV1_1_19;
use nym_client_core::config::Client;
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
use nym_config::must_get_home;
use nym_socks5_client_core::config::{
ProviderInterfaceVersion, Socks5, Socks5Debug, 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_19 {
#[serde(flatten)]
pub base: BaseConfigV1_1_19<ConfigV1_1_19>,
pub socks5: Socks5V1_1_19,
}
impl From<ConfigV1_1_19> for Config {
fn from(value: ConfigV1_1_19) -> Self {
Config {
core: CoreConfig {
base: BaseClientConfig {
client: Client {
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: SocksClientPaths {
common_paths: CommonClientPaths {
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_19 {
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_19 {
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_19,
}
impl From<Socks5V1_1_19> for Socks5 {
fn from(value: Socks5V1_1_19) -> 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, Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Socks5DebugV1_1_19 {
connection_start_surbs: u32,
per_request_surbs: u32,
}
impl From<Socks5DebugV1_1_19> for Socks5Debug {
fn from(value: Socks5DebugV1_1_19) -> Self {
Socks5Debug {
connection_start_surbs: value.connection_start_surbs,
per_request_surbs: value.per_request_surbs,
}
}
}
impl Default for Socks5DebugV1_1_19 {
fn default() -> Self {
Socks5DebugV1_1_19 {
connection_start_surbs: DEFAULT_CONNECTION_START_SURBS,
per_request_surbs: DEFAULT_PER_REQUEST_SURBS,
}
}
}
+20
View File
@@ -0,0 +1,20 @@
// 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),
}
}
}
@@ -1,102 +1,97 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub(crate) fn config_template() -> &'static str {
// 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.
r#"
// 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 #####
[client]
[core.client]
# Version of the client for which this configuration was created.
version = '{{ client.version }}'
version = '{{ core.client.version }}'
# Human readable ID of this particular client.
id = '{{ client.id }}'
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 = {{ client.disabled_credentials_mode }}
disabled_credentials_mode = {{ core.client.disabled_credentials_mode }}
# Addresses to nyxd validators via which the client can communicate with the chain.
nyxd_urls = [
{{#each client.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 client.nym_api_urls }}
{{#each core.client.nym_api_urls }}
'{{this}}',
{{/each}}
]
[storage_paths]
# Path to file containing private identity key.
private_identity_key_file = '{{ client.private_identity_key_file }}'
keys.private_identity_key_file = '{{ storage_paths.keys.private_identity_key_file }}'
# Path to file containing public identity key.
public_identity_key_file = '{{ client.public_identity_key_file }}'
keys.public_identity_key_file = '{{ storage_paths.keys.public_identity_key_file }}'
# Path to file containing private encryption key.
private_encryption_key_file = '{{ client.private_encryption_key_file }}'
keys.private_encryption_key_file = '{{ storage_paths.keys.private_encryption_key_file }}'
# Path to file containing public encryption key.
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 #####
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.
gateway_shared_key_file = '{{ client.gateway_shared_key_file }}'
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.
ack_key_file = '{{ client.ack_key_file }}'
keys.ack_key_file = '{{ storage_paths.keys.ack_key_file }}'
##### advanced configuration options #####
# Path to the database containing bandwidth credentials
credentials_database = '{{ storage_paths.credentials_database }}'
# Absolute path to the home Nym Clients directory.
nym_root_directory = '{{ client.nym_root_directory }}'
# Path to the persistent store for received reply surbs, unused encryption keys and used sender tags.
reply_surb_database = '{{ storage_paths.reply_surb_database }}'
[client.gateway_endpoint]
# DEPRECATED
[core.client.gateway_endpoint]
# ID of the gateway from which the client should be fetching messages.
gateway_id = '{{ client.gateway_endpoint.gateway_id }}'
gateway_id = '{{ core.client.gateway_endpoint.gateway_id }}'
# Address of the gateway owner to which the client should send messages.
gateway_owner = '{{ client.gateway_endpoint.gateway_owner }}'
gateway_owner = '{{ core.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 }}'
gateway_listener = '{{ core.client.gateway_endpoint.gateway_listener }}'
##### socket config options #####
[socks5]
[core.socks5]
# The mix address of the provider to which all requests are going to be sent.
provider_mix_address = '{{ socks5.provider_mix_address }}'
provider_mix_address = '{{ core.socks5.provider_mix_address }}'
# The port on which the client will be listening for incoming requests
listening_port = {{ socks5.listening_port }}
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 = {{ socks5.send_anonymously }}
send_anonymously = {{ core.socks5.send_anonymously }}
##### logging configuration options #####
@@ -109,20 +104,19 @@ send_anonymously = {{ socks5.send_anonymously }}
# The following options should not be modified unless you know EXACTLY what you are doing
# as if set incorrectly, they may impact your anonymity.
# [socks5.socks5_debug]
# [core.socks5.socks5_debug]
[debug]
[core.debug]
[debug.traffic]
average_packet_delay = '{{ debug.traffic.average_packet_delay }}'
message_sending_average_delay = '{{ debug.traffic.message_sending_average_delay }}'
[core.debug.traffic]
average_packet_delay = '{{ core.debug.traffic.average_packet_delay }}'
message_sending_average_delay = '{{ core.debug.traffic.message_sending_average_delay }}'
[debug.acknowledgements]
average_ack_delay = '{{ debug.acknowledgements.average_ack_delay }}'
[core.debug.acknowledgements]
average_ack_delay = '{{ core.debug.acknowledgements.average_ack_delay }}'
[debug.cover_traffic]
loop_cover_traffic_average_delay = '{{ debug.cover_traffic.loop_cover_traffic_average_delay }}'
[core.debug.cover_traffic]
loop_cover_traffic_average_delay = '{{ core.debug.cover_traffic.loop_cover_traffic_average_delay }}'
"#
}
"#;
+1
View File
@@ -8,6 +8,7 @@ use nym_bin_common::logging::{maybe_print_banner, setup_logging};
use nym_network_defaults::setup_env;
mod commands;
mod config;
pub mod error;
#[tokio::main]
+85 -5
View File
@@ -981,7 +981,16 @@ version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
dependencies = [
"dirs-sys",
"dirs-sys 0.3.7",
]
[[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]]
@@ -995,6 +1004,18 @@ dependencies = [
"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]]
name = "dotenv"
version = "0.15.0"
@@ -2214,6 +2235,7 @@ dependencies = [
"log",
"pretty_env_logger",
"semver 0.11.0",
"serde",
"vergen",
]
@@ -2223,7 +2245,7 @@ version = "1.1.14"
dependencies = [
"async-trait",
"dashmap",
"dirs",
"dirs 4.0.0",
"futures",
"gloo-timers",
"humantime-serde",
@@ -2356,12 +2378,12 @@ dependencies = [
name = "nym-config"
version = "0.1.0"
dependencies = [
"cfg-if 1.0.0",
"dirs 5.0.1",
"handlebars",
"log",
"nym-network-defaults",
"serde",
"toml",
"toml 0.7.4",
"url",
]
@@ -2944,6 +2966,12 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "option-ext"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "pairing"
version = "0.20.0"
@@ -3816,6 +3844,15 @@ dependencies = [
"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]]
name = "serde_urlencoded"
version = "0.7.1"
@@ -4299,7 +4336,7 @@ dependencies = [
"serde",
"serde_json",
"tendermint",
"toml",
"toml 0.5.11",
"url",
]
@@ -4536,6 +4573,40 @@ dependencies = [
"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]]
name = "tower-service"
version = "0.3.2"
@@ -5124,6 +5195,15 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
[[package]]
name = "winnow"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699"
dependencies = [
"memchr",
]
[[package]]
name = "winreg"
version = "0.10.1"
+8 -8
View File
@@ -107,20 +107,20 @@ function printAndDisplayTestResult(result) {
}
async function testWithTester() {
const dummyGateway = "336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9";
const preferredGateway = "336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9";
// A) construct with hardcoded topology
const topology = dummyTopology()
const nodeTester = await new NymNodeTester(topology, dummyGateway);
const nodeTester = await new NymNodeTester(topology, preferredGateway);
// B) first get topology directly from nym-api
// const validator = 'https://qwerty-validator-api.qa.nymte.ch/api';
// const topology = await current_network_topology(validator)
// const nodeTester = await new NymNodeTester(topology, dummyGateway);
// const nodeTester = await new NymNodeTester(topology, preferredGateway);
//
// 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 nodeTester = await NymNodeTester.new_with_api(validator, dummyGateway)
// const nodeTester = await NymNodeTester.new_with_api(validator, preferredGateway)
// 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()
@@ -142,7 +142,7 @@ async function testWithTester() {
}
async function testWithNymClient() {
const dummyGateway = "336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9";
const preferredGateway = "336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9";
const topology = dummyTopology()
let received = 0
@@ -164,7 +164,7 @@ async function testWithNymClient() {
console.log('Instantiating WASM client...');
let clientBuilder = NymClientBuilder.new_tester(topology, onMessageHandler, dummyGateway)
let clientBuilder = NymClientBuilder.new_tester(topology, onMessageHandler, preferredGateway)
console.log('Web worker creating WASM client...');
let local_client = await clientBuilder.start_client();
console.log('WASM client running!');
@@ -222,10 +222,10 @@ async function normalNymClientUsage() {
debug.topology_refresh_rate_ms = BigInt(60000)
const dummyGateway = "336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9";
const preferredGateway = "336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9";
const validator = 'https://qwerty-validator-api.qa.nymte.ch/api';
const config = new Config('my-awesome-wasm-client', validator, dummyGateway, debug);
const config = new Config('my-awesome-wasm-client', validator, debug);
const onMessageHandler = (message) => {
console.log(message);
+72 -86
View File
@@ -7,73 +7,49 @@
#![allow(clippy::drop_copy)]
use nym_client_core::config::{
Acknowledgements as ConfigAcknowledgements, CoverTraffic as ConfigCoverTraffic,
DebugConfig as ConfigDebug, GatewayConnection as ConfigGatewayConnection,
ReplySurbs as ConfigReplySurbs, Topology as ConfigTopology, Traffic as ConfigTraffic,
Acknowledgements as ConfigAcknowledgements, Config as BaseClientConfig,
CoverTraffic as ConfigCoverTraffic, DebugConfig as ConfigDebug,
GatewayConnection as ConfigGatewayConnection, ReplySurbs as ConfigReplySurbs,
Topology as ConfigTopology, Traffic as ConfigTraffic,
};
use nym_sphinx::params::{PacketSize, PacketType};
use nym_validator_client::client::IdentityKey;
use serde::{Deserialize, Serialize};
use std::time::Duration;
use url::Url;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Config {
/// 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,
pub(crate) base: BaseClientConfig,
}
#[wasm_bindgen]
impl Config {
#[wasm_bindgen(constructor)]
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
};
pub fn new(id: String, validator_server: String, debug: Option<DebugWasm>) -> Self {
Config {
id,
nym_api_url: Some(
validator_server
base: BaseClientConfig::new(id)
.with_custom_nyxd(vec![validator_server
.parse()
.expect("provided url was malformed"),
),
disabled_credentials_mode: true,
gateway,
debug: debug.map(Into::into).unwrap_or_default(),
packet_type,
.expect("provided url was malformed")])
.with_debug_config(debug.map(Into::into).unwrap_or_default()),
}
}
pub(crate) fn new_tester_config<S: Into<String>>(id: S) -> Self {
Config {
base: BaseClientConfig::new(id)
.with_disabled_credentials(true)
.with_disabled_cover_traffic(true)
.with_disabled_topology_refresh(true),
}
}
}
#[wasm_bindgen]
#[derive(Debug, Copy, Clone)]
pub struct Traffic {
pub struct TrafficWasm {
/// The parameter of Poisson distribution determining how long, on average,
/// sent packet is going to be delayed at any given mix node.
/// So for a packet going through three mix nodes, on average, it will take three times this value
@@ -92,14 +68,23 @@ pub struct Traffic {
/// Controls whether the sent sphinx packet use the NON-DEFAULT bigger size.
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<Traffic> for ConfigTraffic {
fn from(traffic: Traffic) -> Self {
impl From<TrafficWasm> for ConfigTraffic {
fn from(traffic: TrafficWasm) -> Self {
let use_extended_packet_size = traffic
.use_extended_packet_size
.then(|| PacketSize::ExtendedPacket32);
let packet_type = if traffic.use_outfox {
PacketType::Outfox
} else {
PacketType::Mix
};
ConfigTraffic {
average_packet_delay: Duration::from_millis(traffic.average_packet_delay_ms),
message_sending_average_delay: Duration::from_millis(
@@ -109,27 +94,28 @@ impl From<Traffic> for ConfigTraffic {
.disable_main_poisson_packet_distribution,
primary_packet_size: PacketSize::RegularPacket,
secondary_packet_size: use_extended_packet_size,
packet_type: None,
packet_type,
}
}
}
impl From<ConfigTraffic> for Traffic {
impl From<ConfigTraffic> for TrafficWasm {
fn from(traffic: ConfigTraffic) -> Self {
Traffic {
TrafficWasm {
average_packet_delay_ms: traffic.average_packet_delay.as_millis() as u64,
message_sending_average_delay_ms: traffic.message_sending_average_delay.as_millis()
as u64,
disable_main_poisson_packet_distribution: traffic
.disable_main_poisson_packet_distribution,
use_extended_packet_size: traffic.secondary_packet_size.is_some(),
use_outfox: traffic.packet_type == PacketType::Outfox,
}
}
}
#[wasm_bindgen]
#[derive(Debug, Copy, Clone)]
pub struct CoverTraffic {
pub struct CoverTrafficWasm {
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take for another loop cover traffic message to be sent.
pub loop_cover_traffic_average_delay_ms: u64,
@@ -143,8 +129,8 @@ pub struct CoverTraffic {
pub disable_loop_cover_traffic_stream: bool,
}
impl From<CoverTraffic> for ConfigCoverTraffic {
fn from(cover_traffic: CoverTraffic) -> Self {
impl From<CoverTrafficWasm> for ConfigCoverTraffic {
fn from(cover_traffic: CoverTrafficWasm) -> Self {
ConfigCoverTraffic {
loop_cover_traffic_average_delay: Duration::from_millis(
cover_traffic.loop_cover_traffic_average_delay_ms,
@@ -155,9 +141,9 @@ impl From<CoverTraffic> for ConfigCoverTraffic {
}
}
impl From<ConfigCoverTraffic> for CoverTraffic {
impl From<ConfigCoverTraffic> for CoverTrafficWasm {
fn from(cover_traffic: ConfigCoverTraffic) -> Self {
CoverTraffic {
CoverTrafficWasm {
loop_cover_traffic_average_delay_ms: cover_traffic
.loop_cover_traffic_average_delay
.as_millis() as u64,
@@ -169,14 +155,14 @@ impl From<ConfigCoverTraffic> for CoverTraffic {
#[wasm_bindgen]
#[derive(Debug, Copy, Clone)]
pub struct GatewayConnection {
pub struct GatewayConnectionWasm {
/// How long we're willing to wait for a response to a message sent to the gateway,
/// before giving up on it.
pub gateway_response_timeout_ms: u64,
}
impl From<GatewayConnection> for ConfigGatewayConnection {
fn from(gateway_connection: GatewayConnection) -> Self {
impl From<GatewayConnectionWasm> for ConfigGatewayConnection {
fn from(gateway_connection: GatewayConnectionWasm) -> Self {
ConfigGatewayConnection {
gateway_response_timeout: Duration::from_millis(
gateway_connection.gateway_response_timeout_ms,
@@ -185,9 +171,9 @@ impl From<GatewayConnection> for ConfigGatewayConnection {
}
}
impl From<ConfigGatewayConnection> for GatewayConnection {
impl From<ConfigGatewayConnection> for GatewayConnectionWasm {
fn from(gateway_connection: ConfigGatewayConnection) -> Self {
GatewayConnection {
GatewayConnectionWasm {
gateway_response_timeout_ms: gateway_connection.gateway_response_timeout.as_millis()
as u64,
}
@@ -196,7 +182,7 @@ impl From<ConfigGatewayConnection> for GatewayConnection {
#[wasm_bindgen]
#[derive(Debug, Copy, Clone)]
pub struct Acknowledgements {
pub struct AcknowledgementsWasm {
/// The parameter of Poisson distribution determining how long, on average,
/// sent acknowledgement is going to be delayed at any given mix node.
/// So for an ack going through three mix nodes, on average, it will take three times this value
@@ -214,8 +200,8 @@ pub struct Acknowledgements {
pub ack_wait_addition_ms: u64,
}
impl From<Acknowledgements> for ConfigAcknowledgements {
fn from(acknowledgements: Acknowledgements) -> Self {
impl From<AcknowledgementsWasm> for ConfigAcknowledgements {
fn from(acknowledgements: AcknowledgementsWasm) -> Self {
ConfigAcknowledgements {
average_ack_delay: Duration::from_millis(acknowledgements.average_ack_delay_ms),
ack_wait_multiplier: acknowledgements.ack_wait_multiplier,
@@ -224,9 +210,9 @@ impl From<Acknowledgements> for ConfigAcknowledgements {
}
}
impl From<ConfigAcknowledgements> for Acknowledgements {
impl From<ConfigAcknowledgements> for AcknowledgementsWasm {
fn from(acknowledgements: ConfigAcknowledgements) -> Self {
Acknowledgements {
AcknowledgementsWasm {
average_ack_delay_ms: acknowledgements.average_ack_delay.as_millis() as u64,
ack_wait_multiplier: acknowledgements.ack_wait_multiplier,
ack_wait_addition_ms: acknowledgements.ack_wait_addition.as_millis() as u64,
@@ -236,7 +222,7 @@ impl From<ConfigAcknowledgements> for Acknowledgements {
#[wasm_bindgen]
#[derive(Debug, Copy, Clone)]
pub struct Topology {
pub struct TopologyWasm {
/// The uniform delay every which clients are querying the directory server
/// to try to obtain a compatible network topology to send sphinx packets through.
pub topology_refresh_rate_ms: u64,
@@ -252,8 +238,8 @@ pub struct Topology {
pub disable_refreshing: bool,
}
impl From<Topology> for ConfigTopology {
fn from(topology: Topology) -> Self {
impl From<TopologyWasm> for ConfigTopology {
fn from(topology: TopologyWasm) -> Self {
ConfigTopology {
topology_refresh_rate: Duration::from_millis(topology.topology_refresh_rate_ms),
topology_resolution_timeout: Duration::from_millis(
@@ -264,9 +250,9 @@ impl From<Topology> for ConfigTopology {
}
}
impl From<ConfigTopology> for Topology {
impl From<ConfigTopology> for TopologyWasm {
fn from(topology: ConfigTopology) -> Self {
Topology {
TopologyWasm {
topology_refresh_rate_ms: topology.topology_refresh_rate.as_millis() as u64,
topology_resolution_timeout_ms: topology.topology_resolution_timeout.as_millis() as u64,
disable_refreshing: topology.disable_refreshing,
@@ -276,7 +262,7 @@ impl From<ConfigTopology> for Topology {
#[wasm_bindgen]
#[derive(Debug, Copy, Clone)]
pub struct ReplySurbs {
pub struct ReplySurbsWasm {
/// Defines the minimum number of reply surbs the client wants to keep in its storage at all times.
/// It can only allow to go below that value if its to request additional reply surbs.
pub minimum_reply_surb_storage_threshold: usize,
@@ -310,8 +296,8 @@ pub struct ReplySurbs {
pub maximum_reply_key_age_ms: u64,
}
impl From<ReplySurbs> for ConfigReplySurbs {
fn from(reply_surbs: ReplySurbs) -> Self {
impl From<ReplySurbsWasm> for ConfigReplySurbs {
fn from(reply_surbs: ReplySurbsWasm) -> Self {
ConfigReplySurbs {
minimum_reply_surb_storage_threshold: reply_surbs.minimum_reply_surb_storage_threshold,
maximum_reply_surb_storage_threshold: reply_surbs.maximum_reply_surb_storage_threshold,
@@ -331,9 +317,9 @@ impl From<ReplySurbs> for ConfigReplySurbs {
}
}
impl From<ConfigReplySurbs> for ReplySurbs {
impl From<ConfigReplySurbs> for ReplySurbsWasm {
fn from(reply_surbs: ConfigReplySurbs) -> Self {
ReplySurbs {
ReplySurbsWasm {
minimum_reply_surb_storage_threshold: reply_surbs.minimum_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,
@@ -355,28 +341,28 @@ impl From<ConfigReplySurbs> for ReplySurbs {
// just a helper structure to more easily pass through the JS boundary
#[wasm_bindgen]
#[derive(Debug, Copy, Clone)]
pub struct Debug {
pub struct DebugWasm {
/// Defines all configuration options related to traffic streams.
pub traffic: Traffic,
pub traffic: TrafficWasm,
/// Defines all configuration options related to cover traffic stream(s).
pub cover_traffic: CoverTraffic,
pub cover_traffic: CoverTrafficWasm,
/// Defines all configuration options related to the gateway connection.
pub gateway_connection: GatewayConnection,
pub gateway_connection: GatewayConnectionWasm,
/// Defines all configuration options related to acknowledgements, such as delays or wait timeouts.
pub acknowledgements: Acknowledgements,
pub acknowledgements: AcknowledgementsWasm,
/// Defines all configuration options related topology, such as refresh rates or timeouts.
pub topology: Topology,
pub topology: TopologyWasm,
/// Defines all configuration options related to reply SURBs.
pub reply_surbs: ReplySurbs,
pub reply_surbs: ReplySurbsWasm,
}
impl From<Debug> for ConfigDebug {
fn from(debug: Debug) -> Self {
impl From<DebugWasm> for ConfigDebug {
fn from(debug: DebugWasm) -> Self {
ConfigDebug {
traffic: debug.traffic.into(),
cover_traffic: debug.cover_traffic.into(),
@@ -388,9 +374,9 @@ impl From<Debug> for ConfigDebug {
}
}
impl From<ConfigDebug> for Debug {
impl From<ConfigDebug> for DebugWasm {
fn from(debug: ConfigDebug) -> Self {
Debug {
DebugWasm {
traffic: debug.traffic.into(),
cover_traffic: debug.cover_traffic.into(),
gateway_connection: debug.gateway_connection.into(),
@@ -402,6 +388,6 @@ impl From<ConfigDebug> for Debug {
}
#[wasm_bindgen]
pub fn default_debug() -> Debug {
pub fn default_debug() -> DebugWasm {
ConfigDebug::default().into()
}
+32 -46
View File
@@ -21,7 +21,6 @@ use nym_client_core::client::base_client::{
};
use nym_client_core::client::inbound_messages::InputMessage;
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_task::connections::TransmissionLane;
@@ -53,13 +52,15 @@ pub struct NymClient {
// 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
_task_manager: TaskManager,
packet_type: Option<PacketType>,
packet_type: PacketType,
}
#[wasm_bindgen]
pub struct NymClientBuilder {
config: Config,
custom_topology: Option<NymTopology>,
preferred_gateway: Option<IdentityKey>,
storage_passphrase: Option<String>,
reply_surb_storage_backend: browser_backend::Backend,
@@ -70,7 +71,6 @@ pub struct NymClientBuilder {
bandwidth_controller:
Option<BandwidthController<FakeClient<DirectSigningNyxdClient>, EphemeralStorage>>,
disabled_credentials: bool,
packet_type: Option<PacketType>,
}
#[wasm_bindgen]
@@ -79,17 +79,20 @@ impl NymClientBuilder {
pub fn new(
config: Config,
on_message: js_sys::Function,
preferred_gateway: Option<IdentityKey>,
storage_passphrase: Option<String>,
) -> Self {
NymClientBuilder {
reply_surb_storage_backend: setup_reply_surb_storage_backend(config.debug.reply_surbs),
reply_surb_storage_backend: setup_reply_surb_storage_backend(
config.base.debug.reply_surbs,
),
config,
custom_topology: None,
storage_passphrase,
on_message,
bandwidth_controller: None,
disabled_credentials: true,
packet_type: None,
preferred_gateway,
}
}
@@ -108,32 +111,11 @@ impl NymClientBuilder {
}
}
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,
};
let full_config = Config::new_tester_config(NODE_TESTER_CLIENT_ID);
NymClientBuilder {
reply_surb_storage_backend: setup_reply_surb_storage_backend(
full_config.debug.reply_surbs,
full_config.base.debug.reply_surbs,
),
config: full_config,
custom_topology: Some(topology.into()),
@@ -141,7 +123,7 @@ impl NymClientBuilder {
bandwidth_controller: None,
disabled_credentials: true,
storage_passphrase: None,
packet_type: None,
preferred_gateway: gateway,
}
}
@@ -166,20 +148,18 @@ impl NymClientBuilder {
CredentialsToggle::Enabled
};
let nym_api_endpoints = match &self.config.nym_api_url {
Some(endpoint) => vec![endpoint.clone()],
None => Vec::new(),
};
let nym_api_endpoints = self.config.base.client.nym_api_urls.clone();
// TODO: this will have to be re-used for surbs. but this is a problem for another PR.
let client_store =
ClientStorage::new_async(&self.config.id, self.storage_passphrase.take()).await?;
ClientStorage::new_async(&self.config.base.client.id, self.storage_passphrase.take())
.await?;
// if we provided hardcoded topology, get gateway from it, otherwise get it the 'standard' way
let gateway_endpoint = if let Some(topology) = &self.custom_topology {
gateway_from_topology(
&mut thread_rng(),
self.config.gateway.as_deref(),
self.preferred_gateway.as_deref(),
topology,
&client_store,
)
@@ -187,7 +167,7 @@ impl NymClientBuilder {
} else {
choose_gateway(
&client_store,
self.config.gateway.clone(),
self.preferred_gateway.clone(),
&nym_api_endpoints,
)
.await?
@@ -197,7 +177,7 @@ impl NymClientBuilder {
let mut base_builder: BaseClientBuilder<_, FullWasmClientStorage> = BaseClientBuilder::new(
&gateway_endpoint,
&self.config.debug,
&self.config.base.debug,
client_store,
self.bandwidth_controller,
self.reply_surb_storage_backend,
@@ -208,8 +188,7 @@ impl NymClientBuilder {
base_builder = base_builder.with_topology_provider(topology_provider);
}
let packet_type = self.config.packet_type;
let mut started_client = base_builder.start_base(packet_type).await?;
let mut started_client = base_builder.start_base().await?;
let self_address = started_client.address.to_string();
let client_input = started_client.client_input.register_producer();
@@ -223,7 +202,7 @@ impl NymClientBuilder {
client_state: Arc::new(started_client.client_state),
_full_topology: None,
_task_manager: started_client.task_manager,
packet_type: self.packet_type,
packet_type: self.config.base.debug.traffic.packet_type,
})
}
@@ -237,9 +216,10 @@ impl NymClient {
async fn _new(
config: Config,
on_message: js_sys::Function,
preferred_gateway: Option<IdentityKey>,
storage_passphrase: Option<String>,
) -> Result<NymClient, WasmClientError> {
NymClientBuilder::new(config, on_message, storage_passphrase)
NymClientBuilder::new(config, on_message, preferred_gateway, storage_passphrase)
.start_client_async()
.await
}
@@ -249,10 +229,11 @@ impl NymClient {
pub fn new(
config: Config,
on_message: js_sys::Function,
preferred_gateway: Option<IdentityKey>,
storage_passphrase: Option<String>,
) -> Promise {
future_to_promise(async move {
Self::_new(config, on_message, storage_passphrase)
Self::_new(config, on_message, preferred_gateway, storage_passphrase)
.await
.into_promise_result()
})
@@ -319,7 +300,7 @@ impl NymClient {
let lane = TransmissionLane::General;
let input_msg = InputMessage::new_regular(recipient, message, lane, self.packet_type);
let input_msg = InputMessage::new_regular(recipient, message, lane, Some(self.packet_type));
self.client_input.send_message(input_msg)
}
@@ -346,8 +327,13 @@ impl NymClient {
let lane = TransmissionLane::General;
let input_msg =
InputMessage::new_anonymous(recipient, message, reply_surbs, lane, self.packet_type);
let input_msg = InputMessage::new_anonymous(
recipient,
message,
reply_surbs,
lane,
Some(self.packet_type),
);
self.client_input.send_message(input_msg)
}
@@ -365,7 +351,7 @@ impl NymClient {
let lane = TransmissionLane::General;
let input_msg = InputMessage::new_reply(sender_tag, message, lane, self.packet_type);
let input_msg = InputMessage::new_reply(sender_tag, message, lane, Some(self.packet_type));
self.client_input.send_message(input_msg)
}
}
+1 -1
View File
@@ -22,7 +22,7 @@ use wasm_bindgen_futures::future_to_promise;
use wasm_utils::{console_log, PromisableResult};
// don't get too excited about the name, under the hood it's just a big fat placeholder
// with no persistence
// with no disk_persistence
pub(crate) fn setup_reply_surb_storage_backend(
config: config::ReplySurbs,
) -> browser_backend::Backend {
+2 -2
View File
@@ -15,7 +15,7 @@ clap_complete_fig = "4.0"
log = { workspace = true }
pretty_env_logger = "0.4.0"
semver = "0.11"
serde = { workspace = true, features = ["derive"], optional = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true, optional = true }
## tracing
@@ -36,5 +36,5 @@ vergen = { version = "=7.4.3", default-features = false, features = [
[features]
default = []
output_format = ["serde", "serde_json"]
output_format = ["serde_json"]
tracing = ["tracing-appender", "tracing-subscriber", "tracing-tree"]
@@ -4,6 +4,8 @@
// TODO: at a later date this crate should probably also expose `ContractBuildInformation`
// and be used by our smart contracts
use serde::{Deserialize, Serialize};
#[derive(Debug)]
pub struct BinaryBuildInformation {
// VERGEN_BUILD_TIMESTAMP
@@ -99,8 +101,7 @@ impl BinaryBuildInformation {
}
}
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Serialize, Deserialize)]
pub struct BinaryBuildInformationOwned {
// VERGEN_BUILD_TIMESTAMP
/// Provides the build timestamp, for example `2021-02-23T20:14:46.558472672+00:00`.
+3 -1
View File
@@ -4,5 +4,7 @@
pub mod build_information;
pub mod completions;
pub mod logging;
pub mod output_format;
pub mod version_checker;
#[cfg(feature = "output_format")]
pub mod output_format;
+9 -5
View File
@@ -1,9 +1,7 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
// use tracing_subscriber::{
// fmt::Layer, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Registry,
// };
// use tracing_tree::HierarchicalLayer;
use serde::{Deserialize, Serialize};
#[cfg(feature = "tracing")]
pub use tracing_appender;
@@ -12,6 +10,12 @@ pub use tracing_subscriber;
#[cfg(feature = "tracing")]
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`
pub fn setup_logging() {
let mut log_builder = pretty_env_logger::formatted_timed_builder();
@@ -176,15 +176,15 @@ where
C: DkgQueryClient + Send + Sync + 'static,
{
// TODO: combine all storages
pub fn new_from_base_config<T>(
base_config: &'a Config<T>,
pub fn new_from_base_config(
base_config: &'a Config,
key_store: S::KeyStore,
bandwidth_controller: Option<BandwidthController<C, S::CredentialStore>>,
reply_storage_backend: S::ReplyStore,
) -> BaseClientBuilder<'a, C, S> {
BaseClientBuilder {
gateway_config: base_config.get_gateway_endpoint_config(),
debug_config: base_config.get_debug_config(),
debug_config: &base_config.debug,
disabled_credentials: base_config.get_disabled_credentials_mode(),
nym_api_endpoints: base_config.get_nym_api_endpoints(),
bandwidth_controller,
@@ -479,10 +479,7 @@ where
self.managed_keys = ManagedKeys::load_or_generate(&mut rng, &self.key_store).await;
}
pub async fn start_base(
mut self,
packet_type: PacketType,
) -> Result<BaseClient, ClientCoreError>
pub async fn start_base(mut self) -> Result<BaseClient, ClientCoreError>
where
<S::ReplyStore as ReplyStorageBackend>::StorageError: Sync + Send,
S::ReplyStore: Send + Sync,
@@ -586,7 +583,7 @@ where
shared_lane_queue_lengths.clone(),
client_connection_rx,
task_manager.subscribe(),
packet_type,
self.debug_config.traffic.packet_type,
);
if !self
@@ -101,8 +101,8 @@ pub async fn setup_fs_reply_surb_backend<P: AsRef<Path>>(
}
}
pub fn create_bandwidth_controller<T, St: CredentialStorage>(
config: &Config<T>,
pub fn create_bandwidth_controller<St: CredentialStorage>(
config: &Config,
storage: St,
) -> BandwidthController<Client<QueryNyxdClient>, St> {
let nyxd_url = config
@@ -17,15 +17,14 @@ use crate::client::base_client::non_wasm_helpers;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use crate::client::key_manager::persistence::OnDiskKeys;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use crate::config::{persistence::key_pathfinder::ClientKeyPathfinder, Config};
use crate::client::replies::reply_storage::fs_backend;
#[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"))]
use crate::error::ClientCoreError;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use nym_credential_storage::persistent_storage::PersistentStorage as PersistentCredentialStorage;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use crate::client::replies::reply_storage::fs_backend;
pub trait MixnetClientStorage {
type KeyStore: KeyStore;
type ReplyStore: ReplyStorageBackend;
@@ -95,18 +94,20 @@ impl OnDiskPersistent {
}
}
pub async fn from_config<T>(config: &Config<T>) -> Result<Self, ClientCoreError> {
let pathfinder = ClientKeyPathfinder::new_from_config(config);
let key_store = OnDiskKeys::new(pathfinder);
pub async fn from_paths(
paths: CommonClientPaths,
debug_config: &config::DebugConfig,
) -> Result<Self, ClientCoreError> {
let key_store = OnDiskKeys::new(paths.keys);
let reply_store = non_wasm_helpers::setup_fs_reply_surb_backend(
config.get_reply_surb_database_path(),
&config.get_debug_config().reply_surbs,
paths.reply_surb_database,
&debug_config.reply_surbs,
)
.await?;
let credential_store =
nym_credential_storage::initialise_persistent_storage(config.get_database_path()).await;
nym_credential_storage::initialise_persistent_storage(paths.credentials_database).await;
Ok(OnDiskPersistent {
key_store,
@@ -6,9 +6,7 @@ use async_trait::async_trait;
use std::error::Error;
#[cfg(not(target_arch = "wasm32"))]
use crate::config::persistence::key_pathfinder::ClientKeyPathfinder;
#[cfg(not(target_arch = "wasm32"))]
use crate::config::Config;
use crate::config::disk_persistence::keys_paths::ClientKeysPaths;
#[cfg(not(target_arch = "wasm32"))]
use nym_crypto::asymmetric::{encryption, identity};
#[cfg(not(target_arch = "wasm32"))]
@@ -65,24 +63,20 @@ pub enum OnDiskKeysError {
#[cfg(not(target_arch = "wasm32"))]
pub struct OnDiskKeys {
pathfinder: ClientKeyPathfinder,
paths: ClientKeysPaths,
}
#[cfg(not(target_arch = "wasm32"))]
impl From<ClientKeyPathfinder> for OnDiskKeys {
fn from(pathfinder: ClientKeyPathfinder) -> Self {
OnDiskKeys { pathfinder }
impl From<ClientKeysPaths> for OnDiskKeys {
fn from(paths: ClientKeysPaths) -> Self {
OnDiskKeys { paths }
}
}
#[cfg(not(target_arch = "wasm32"))]
impl OnDiskKeys {
pub fn new(pathfinder: ClientKeyPathfinder) -> Self {
OnDiskKeys { pathfinder }
}
pub fn from_config<T>(config: &Config<T>) -> Self {
OnDiskKeys::new(ClientKeyPathfinder::new_from_config(config))
pub fn new(paths: ClientKeysPaths) -> Self {
OnDiskKeys { paths }
}
fn load_key<T: PemStorableKey>(
@@ -138,17 +132,17 @@ impl OnDiskKeys {
}
fn load_keys(&self) -> Result<KeyManager, OnDiskKeysError> {
let identity_paths = self.pathfinder.identity_key_pair_path();
let encryption_paths = self.pathfinder.encryption_key_pair_path();
let identity_paths = self.paths.identity_key_pair_path();
let encryption_paths = self.paths.encryption_key_pair_path();
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 ack_key: AckKey = self.load_key(self.paths.ack_key(), "ack key")?;
let gateway_shared_key: SharedKeys =
self.load_key(self.pathfinder.gateway_shared_key(), "gateway shared keys")?;
self.load_key(self.paths.gateway_shared_key(), "gateway shared keys")?;
Ok(KeyManager::from_keys(
identity_keypair,
@@ -159,8 +153,8 @@ impl OnDiskKeys {
}
fn store_keys(&self, keys: &KeyManager) -> Result<(), OnDiskKeysError> {
let identity_paths = self.pathfinder.identity_key_pair_path();
let encryption_paths = self.pathfinder.encryption_key_pair_path();
let identity_paths = self.paths.identity_key_pair_path();
let encryption_paths = self.paths.encryption_key_pair_path();
self.store_keypair(
keys.identity_keypair.as_ref(),
@@ -173,10 +167,10 @@ impl OnDiskKeys {
"encryption keys",
)?;
self.store_key(keys.ack_key.as_ref(), self.pathfinder.ack_key(), "ack key")?;
self.store_key(keys.ack_key.as_ref(), self.paths.ack_key(), "ack key")?;
self.store_key(
keys.gateway_shared_key.as_ref(),
self.pathfinder.gateway_shared_key(),
self.paths.gateway_shared_key(),
"gateway shared keys",
)?;
@@ -1,4 +1,4 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use self::sending_delay_controller::SendingDelayController;
@@ -247,7 +247,7 @@ where
self.config.average_ack_delay,
self.config.traffic.average_packet_delay,
cover_traffic_packet_size,
self.config.traffic.packet_type.unwrap_or_default(),
self.config.traffic.packet_type,
)
.expect(
"Somehow failed to generate a loop cover message with a valid topology",
@@ -0,0 +1,117 @@
// 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
}
@@ -0,0 +1,36 @@
// 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 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)]
pub struct CommonClientPaths {
pub keys: ClientKeysPaths,
// TODO:
// pub gateway_config_pathfinder: (),
/// 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),
keys: ClientKeysPaths::new_default(base_data_directory),
}
}
}
+33 -394
View File
@@ -1,13 +1,10 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_config::defaults::NymNetworkDetails;
use nym_config::{NymConfig, OptionalSet, CRED_DB_FILE_NAME};
use nym_crypto::asymmetric::identity;
use nym_sphinx::params::{PacketSize, PacketType};
use serde::{Deserialize, Serialize};
use std::marker::PhantomData;
use std::path::{Path, PathBuf};
use std::time::Duration;
use url::Url;
@@ -15,19 +12,9 @@ use crate::error::ClientCoreError;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
pub mod disk_persistence;
pub mod old_config_v1_1_13;
pub mod persistence;
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";
pub mod old_config_v1_1_19;
// 'DEBUG'
const DEFAULT_ACK_WAIT_MULTIPLIER: f64 = 1.5;
@@ -68,154 +55,32 @@ const DEFAULT_MAXIMUM_REPLY_SURB_AGE: Duration = Duration::from_secs(12 * 60 * 6
// 24 hours
const DEFAULT_MAXIMUM_REPLY_KEY_AGE: Duration = Duration::from_secs(24 * 60 * 60);
pub fn missing_string_value() -> String {
MISSING_VALUE.to_string()
}
pub trait ClientCoreConfigTrait {
fn get_gateway_endpoint(&self) -> &GatewayEndpointConfig;
}
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Config<T> {
client: Client<T>,
pub struct Config {
pub client: Client,
#[serde(default)]
logging: Logging,
#[serde(default)]
debug: DebugConfig,
pub 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)
impl Config {
pub fn new<S: Into<String>>(id: S) -> Self {
Config {
client: Client::new_default(id),
debug: Default::default(),
}
}
pub fn validate(&self) -> bool {
// no other sections have explicit requirements (yet)
self.debug.validate()
self.client.validate() && self.debug.validate()
}
#[must_use]
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();
pub fn with_debug_config(mut self, debug: DebugConfig) -> Self {
self.debug = debug;
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 {
self.client.disabled_credentials_mode = disabled_credentials_mode;
self
@@ -260,7 +125,7 @@ impl<T> Config<T> {
}
pub fn with_packet_type(mut self, packet_type: PacketType) -> Self {
self.client.packet_type = Some(packet_type);
self.debug.traffic.packet_type = packet_type;
self
}
@@ -280,6 +145,11 @@ impl<T> Config<T> {
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) {
self.debug.cover_traffic.disable_loop_cover_traffic_stream = true;
self.debug.traffic.disable_main_poisson_packet_distribution = true;
@@ -297,34 +167,6 @@ impl<T> Config<T> {
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> {
self.client.nyxd_urls.clone()
}
@@ -348,123 +190,6 @@ impl<T> Config<T> {
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)]
@@ -517,9 +242,8 @@ impl From<nym_topology::gateway::Node> for GatewayEndpointConfig {
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
pub struct Client<T> {
pub struct Client {
/// Version of the client for which this configuration was created.
#[serde(default = "missing_string_value")]
pub version: String,
/// ID specifies the human readable ID of this particular client.
@@ -538,51 +262,14 @@ pub struct Client<T> {
#[serde(alias = "validator_api_urls")]
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.
// #[deprecated(note = "this shall be moved to separate file because it doesn't belong here...")]
// TODO: this should be removed from config files and be moved to separate file instead
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<T: NymConfig> Default for Client<T> {
fn default() -> Self {
impl Client {
pub fn new_default<S: Into<String>>(id: S) -> Self {
let network = NymNetworkDetails::new_mainnet();
let nyxd_urls = network
.endpoints
@@ -595,71 +282,23 @@ impl<T: NymConfig> Default for Client<T> {
.filter_map(|validator| validator.api_url())
.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 {
version: env!("CARGO_PKG_VERSION").to_string(),
id: "".to_string(),
id: id.into(),
disabled_credentials_mode: true,
nyxd_urls,
nym_api_urls,
private_identity_key_file: Default::default(),
public_identity_key_file: Default::default(),
private_encryption_key_file: Default::default(),
public_encryption_key_file: Default::default(),
gateway_shared_key_file: Default::default(),
ack_key_file: Default::default(),
gateway_endpoint: Default::default(),
database_path: Default::default(),
reply_surb_database_path: Default::default(),
nym_root_directory: T::default_root_directory(),
super_struct: Default::default(),
packet_type: Default::default(),
}
}
}
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)
pub fn validate(&self) -> bool {
!self.gateway_endpoint.gateway_id.is_empty()
&& !self.gateway_endpoint.gateway_owner.is_empty()
&& !self.gateway_endpoint.gateway_owner.is_empty()
}
}
#[derive(Debug, Clone, Default, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Logging {}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default)]
pub struct Traffic {
@@ -690,7 +329,7 @@ pub struct Traffic {
/// Do not set it it unless you understand the consequences of that change.
pub secondary_packet_size: Option<PacketSize>,
pub packet_type: Option<PacketType>,
pub packet_type: PacketType,
}
impl Traffic {
@@ -714,7 +353,7 @@ impl Default for Traffic {
disable_main_poisson_packet_distribution: false,
primary_packet_size: PacketSize::RegularPacket,
secondary_packet_size: None,
packet_type: None,
packet_type: PacketType::Mix,
}
}
}
@@ -1,19 +1,18 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::{
Acknowledgements, Client, Config, CoverTraffic, DebugConfig, GatewayConnection, Logging,
ReplySurbs, Topology, Traffic, DEFAULT_ACK_WAIT_ADDITION, DEFAULT_ACK_WAIT_MULTIPLIER,
DEFAULT_AVERAGE_PACKET_DELAY, DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY, DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE,
DEFAULT_MAXIMUM_REPLY_KEY_AGE, DEFAULT_MAXIMUM_REPLY_SURB_AGE,
DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD, DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE,
DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD,
use crate::config::old_config_v1_1_19::{
AcknowledgementsV1_1_19, ClientV1_1_19, ConfigV1_1_19, CoverTrafficV1_1_19, DebugConfigV1_1_19,
GatewayConnectionV1_1_19, LoggingV1_1_19, ReplySurbsV1_1_19, TopologyV1_1_19, TrafficV1_1_19,
DEFAULT_ACK_WAIT_ADDITION, DEFAULT_ACK_WAIT_MULTIPLIER, DEFAULT_AVERAGE_PACKET_DELAY,
DEFAULT_GATEWAY_RESPONSE_TIMEOUT, DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE, DEFAULT_MAXIMUM_REPLY_KEY_AGE,
DEFAULT_MAXIMUM_REPLY_SURB_AGE, DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD,
DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE, DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD,
DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD, DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE, DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
DEFAULT_TOPOLOGY_REFRESH_RATE, DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
};
use nym_config::NymConfig;
use nym_sphinx::params::PacketSize;
use serde::{Deserialize, Serialize};
use std::marker::PhantomData;
@@ -40,21 +39,21 @@ impl From<ExtendedPacketSize> for PacketSize {
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct OldConfigV1_1_13<T> {
pub client: Client<T>,
pub client: ClientV1_1_19<T>,
#[serde(default)]
logging: Logging,
pub logging: OldLoggingV1_1_13,
#[serde(default)]
debug: OldDebugConfigV1_1_13,
pub debug: OldDebugConfigV1_1_13,
}
impl<T: NymConfig> Default for OldConfigV1_1_13<T> {
fn default() -> Self {
OldConfigV1_1_13 {
client: Client::<T>::default(),
logging: Default::default(),
debug: Default::default(),
}
#[derive(Debug, Clone, Default, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct OldLoggingV1_1_13 {}
impl From<OldLoggingV1_1_13> for LoggingV1_1_19 {
fn from(_value: OldLoggingV1_1_13) -> Self {
LoggingV1_1_19 {}
}
}
@@ -115,37 +114,36 @@ pub struct OldDebugConfigV1_1_13 {
pub maximum_reply_key_age: Duration,
}
impl From<OldDebugConfigV1_1_13> for DebugConfig {
impl From<OldDebugConfigV1_1_13> for DebugConfigV1_1_19 {
fn from(value: OldDebugConfigV1_1_13) -> Self {
DebugConfig {
traffic: Traffic {
DebugConfigV1_1_19 {
traffic: TrafficV1_1_19 {
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: PacketSize::RegularPacket,
secondary_packet_size: value.use_extended_packet_size.map(Into::into),
packet_type: None,
},
cover_traffic: CoverTraffic {
cover_traffic: CoverTrafficV1_1_19 {
loop_cover_traffic_average_delay: value.loop_cover_traffic_average_delay,
disable_loop_cover_traffic_stream: value.disable_loop_cover_traffic_stream,
..CoverTraffic::default()
..CoverTrafficV1_1_19::default()
},
gateway_connection: GatewayConnection {
gateway_connection: GatewayConnectionV1_1_19 {
gateway_response_timeout: value.gateway_response_timeout,
},
acknowledgements: Acknowledgements {
acknowledgements: AcknowledgementsV1_1_19 {
average_ack_delay: value.average_ack_delay,
ack_wait_multiplier: value.ack_wait_multiplier,
ack_wait_addition: value.ack_wait_addition,
},
topology: Topology {
topology: TopologyV1_1_19 {
topology_refresh_rate: value.topology_refresh_rate,
topology_resolution_timeout: value.topology_resolution_timeout,
disable_refreshing: false,
},
reply_surbs: ReplySurbs {
reply_surbs: ReplySurbsV1_1_19 {
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,
@@ -192,10 +190,10 @@ impl Default for OldDebugConfigV1_1_13 {
}
}
impl<T, U> From<OldConfigV1_1_13<T>> for Config<U> {
impl<T, U> From<OldConfigV1_1_13<T>> for ConfigV1_1_19<U> {
fn from(value: OldConfigV1_1_13<T>) -> Self {
Config {
client: Client {
ConfigV1_1_19 {
client: ClientV1_1_19 {
version: value.client.version,
id: value.client.id,
disabled_credentials_mode: value.client.disabled_credentials_mode,
@@ -211,10 +209,10 @@ impl<T, U> From<OldConfigV1_1_13<T>> for Config<U> {
database_path: value.client.database_path,
reply_surb_database_path: value.client.reply_surb_database_path,
nym_root_directory: value.client.nym_root_directory,
super_struct: PhantomData,
packet_type: Some(nym_sphinx::params::PacketType::Mix),
},
logging: value.logging,
logging: value.logging.into(),
debug: value.debug.into(),
}
}
@@ -0,0 +1,354 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::{
Acknowledgements, CoverTraffic, DebugConfig, GatewayConnection, GatewayEndpointConfig,
ReplySurbs, Topology, Traffic,
};
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_19<T> {
pub client: ClientV1_1_19<T>,
#[serde(default)]
pub logging: LoggingV1_1_19,
#[serde(default)]
pub debug: DebugConfigV1_1_19,
}
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
pub struct GatewayEndpointConfigV1_1_19 {
pub gateway_id: String,
pub gateway_owner: String,
pub gateway_listener: String,
}
impl From<GatewayEndpointConfigV1_1_19> for GatewayEndpointConfig {
fn from(value: GatewayEndpointConfigV1_1_19) -> 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_19<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_19,
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_19 {}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default)]
pub struct TrafficV1_1_19 {
#[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_19> for Traffic {
fn from(value: TrafficV1_1_19) -> 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_19 {
fn default() -> Self {
TrafficV1_1_19 {
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_19 {
#[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_19> for CoverTraffic {
fn from(value: CoverTrafficV1_1_19) -> 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_19 {
fn default() -> Self {
CoverTrafficV1_1_19 {
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_19 {
#[serde(with = "humantime_serde")]
pub gateway_response_timeout: Duration,
}
impl From<GatewayConnectionV1_1_19> for GatewayConnection {
fn from(value: GatewayConnectionV1_1_19) -> Self {
GatewayConnection {
gateway_response_timeout: value.gateway_response_timeout,
}
}
}
impl Default for GatewayConnectionV1_1_19 {
fn default() -> Self {
GatewayConnectionV1_1_19 {
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
}
}
}
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct AcknowledgementsV1_1_19 {
#[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_19> for Acknowledgements {
fn from(value: AcknowledgementsV1_1_19) -> 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_19 {
fn default() -> Self {
AcknowledgementsV1_1_19 {
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_19 {
#[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_19> for Topology {
fn from(value: TopologyV1_1_19) -> Self {
Topology {
topology_refresh_rate: value.topology_refresh_rate,
topology_resolution_timeout: value.topology_resolution_timeout,
disable_refreshing: value.disable_refreshing,
}
}
}
impl Default for TopologyV1_1_19 {
fn default() -> Self {
TopologyV1_1_19 {
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_19 {
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_19> for ReplySurbs {
fn from(value: ReplySurbsV1_1_19) -> 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,
}
}
}
impl Default for ReplySurbsV1_1_19 {
fn default() -> Self {
ReplySurbsV1_1_19 {
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, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct DebugConfigV1_1_19 {
pub traffic: TrafficV1_1_19,
pub cover_traffic: CoverTrafficV1_1_19,
pub gateway_connection: GatewayConnectionV1_1_19,
pub acknowledgements: AcknowledgementsV1_1_19,
pub topology: TopologyV1_1_19,
pub reply_surbs: ReplySurbsV1_1_19,
}
impl From<DebugConfigV1_1_19> for DebugConfig {
fn from(value: DebugConfigV1_1_19) -> 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(),
}
}
}
// it could be derived, sure, but I'd rather have an explicit implementation in case we had to change
// something manually at some point
#[allow(clippy::derivable_impls)]
impl Default for DebugConfigV1_1_19 {
fn default() -> Self {
DebugConfigV1_1_19 {
traffic: Default::default(),
cover_traffic: Default::default(),
gateway_connection: Default::default(),
acknowledgements: Default::default(),
topology: Default::default(),
reply_surbs: Default::default(),
}
}
}
@@ -1,108 +0,0 @@
// 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
}
@@ -1,4 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod key_pathfinder;
+18 -68
View File
@@ -8,20 +8,15 @@ use crate::client::key_manager::persistence::KeyStore;
use crate::client::key_manager::{KeyManager, ManagedKeys};
use crate::init::helpers::{choose_gateway_by_latency, current_gateways, uniformly_random_gateway};
use crate::{
config::{
persistence::key_pathfinder::ClientKeyPathfinder, ClientCoreConfigTrait, Config,
GatewayEndpointConfig,
},
config::{disk_persistence::keys_paths::ClientKeysPaths, Config, GatewayEndpointConfig},
error::ClientCoreError,
};
use nym_config::NymConfig;
use nym_crypto::asymmetric::{encryption, identity};
use nym_sphinx::addressing::{clients::Recipient, nodes::NodeIdentity};
use nym_validator_client::client::IdentityKey;
use rand::rngs::OsRng;
use serde::Serialize;
use std::fmt::{Debug, Display};
use tap::TapFallible;
use url::Url;
mod helpers;
@@ -122,13 +117,10 @@ pub struct InitResults {
}
impl InitResults {
pub fn new<T>(config: &Config<T>, address: &Recipient) -> Self
where
T: NymConfig,
{
pub fn new(config: &Config, address: &Recipient) -> Self {
Self {
version: config.get_version().to_string(),
id: config.get_id(),
version: config.client.version.clone(),
id: config.client.id.clone(),
identity_key: address.identity().to_base58_string(),
encryption_key: address.encryption_key().to_base58_string(),
gateway_id: config.get_gateway_id(),
@@ -206,26 +198,22 @@ where
/// b. Create a new gateway configuration but keep existing keys. This assumes that the caller
/// knows what they are doing and that the keys match the requested gateway.
/// c. Create a new gateway configuration with a newly registered gateway and keys.
pub async fn setup_gateway_from_config<C, T, KSt>(
pub async fn setup_gateway_from_config<KSt>(
key_store: &KSt,
register_gateway: bool,
user_chosen_gateway_id: Option<identity::PublicKey>,
config: &Config<T>,
config: &Config,
by_latency: bool,
) -> Result<GatewayEndpointConfig, ClientCoreError>
where
C: NymConfig + ClientCoreConfigTrait,
T: NymConfig,
KSt: KeyStore,
<KSt as KeyStore>::StorageError: Send + Sync + 'static,
{
let id = config.get_id();
// If we are not going to register gateway, and an explicitly chosen gateway is not passed in,
// load the existing configuration file
if !register_gateway && user_chosen_gateway_id.is_none() {
eprintln!("Not registering gateway, will reuse existing config and keys");
return load_existing_gateway_config::<C>(&id);
return Ok(config.client.gateway_endpoint.clone());
}
let gateway_setup = GatewaySetup::new(
@@ -266,25 +254,6 @@ where
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,
@@ -301,40 +270,21 @@ pub fn get_client_address(
/// 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)?;
pub fn get_client_address_from_stored_ondisk_keys(
keys_paths: &ClientKeysPaths,
gateway_config: &GatewayEndpointConfig,
) -> Result<Recipient, ClientCoreError> {
let public_identity: identity::PublicKey =
nym_pemstore::load_key(&keys_paths.public_identity_key_file)?;
let public_encryption: encryption::PublicKey =
nym_pemstore::load_key(&keys_paths.public_encryption_key_file)?;
let client_recipient = Recipient::new(
*identity_keypair.public_key(),
*sphinx_keypair.public_key(),
public_identity,
public_encryption,
// TODO: below only works under assumption that gateway address == gateway id
// (which currently is true)
NodeIdentity::from_base58_string(config.get_gateway_id())?,
NodeIdentity::from_base58_string(&gateway_config.gateway_id)?,
);
Ok(client_recipient)
+6 -3
View File
@@ -7,11 +7,14 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
cfg-if = "1.0.0"
handlebars = "3.0.1"
dirs = { version = "5.0.1", optional = true }
handlebars = "3.5.5"
log = { workspace = true }
serde = { workspace = true, features = ["derive"] }
toml = "0.5.6"
toml = "0.7.4"
url = "2.2"
nym-network-defaults = { path = "../network-defaults" }
[features]
default = ["dirs"]
+208
View File
@@ -0,0 +1,208 @@
// 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
@@ -0,0 +1,55 @@
// 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))
}
}
}
+172 -192
View File
@@ -1,219 +1,199 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use handlebars::Handlebars;
use nym_network_defaults::mainnet::read_var_if_not_default;
use nym_network_defaults::var_names::CONFIGURED;
use handlebars::{Handlebars, TemplateRenderError};
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::any::type_name;
use std::fmt::Debug;
#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;
use std::fs::File;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::{fs, io};
pub use helpers::{parse_urls, OptionalSet};
pub use toml::de::Error as TomlDeError;
pub mod defaults;
pub mod helpers;
pub mod legacy_helpers;
pub const CONFIG_DIR: &str = "config";
pub const DATA_DIR: &str = "data";
pub const CRED_DB_FILE_NAME: &str = "credentials_database.db";
pub const NYM_DIR: &str = ".nym";
pub const DEFAULT_CONFIG_DIR: &str = "config";
pub const DEFAULT_DATA_DIR: &str = "data";
pub const DEFAULT_CONFIG_FILENAME: &str = "config.toml";
pub trait NymConfig: Default + Serialize + DeserializeOwned {
#[cfg(feature = "dirs")]
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 config_file_name() -> String {
"config.toml".to_string()
fn format_to_string(&self) -> String {
// it is responsibility of whoever is implementing the trait to ensure the template is valid
Handlebars::new()
.render_template(Self::template(), &self)
.unwrap()
}
fn default_root_directory() -> PathBuf;
// default, most probable, implementations; can be easily overridden where required
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_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(())
fn format_to_writer<W: Write>(&self, writer: W) -> io::Result<()> {
if let Err(err) =
Handlebars::new().render_template_to_write(Self::template(), &self, writer)
{
match err {
TemplateRenderError::IOError(err, _) => return Err(err),
other_err => {
// it is responsibility of whoever is implementing the trait to ensure the template is valid
panic!("invalid template: {other_err}")
}
}
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);
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))
}
}
// 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 nym api urls is invalid")
})
.collect()
pub fn save_formatted_config_to_file<C, P>(config: &C, path: P) -> io::Result<()>
where
C: NymConfigTemplate,
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)
}
pub trait OptionalSet {
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
}
}
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)
}
}
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
}
pub fn deserialize_config_from_toml_str<C>(raw: &str) -> Result<C, TomlDeError>
where
C: DeserializeOwned,
{
toml::from_str(raw)
}
impl<T> OptionalSet for T where T: NymConfig {}
pub fn read_config_from_toml_file<C, P>(path: P) -> io::Result<C>
where
C: DeserializeOwned,
P: AsRef<Path>,
{
log::trace!(
"trying to read config file from {}",
path.as_ref().display()
);
let content = fs::read_to_string(path)?;
// TODO: should we be preserving original error type instead?
deserialize_config_from_toml_str(&content)
.map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err))
}
//
//
//
// 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))
// }
// }
+4 -1
View File
@@ -4,8 +4,11 @@
*/
use crate::ephemeral_storage::EphemeralStorage;
#[cfg(not(target_arch = "wasm32"))]
use crate::persistent_storage::PersistentStorage;
#[cfg(not(target_arch = "wasm32"))]
use std::path::Path;
mod backends;
pub mod ephemeral_storage;
@@ -16,7 +19,7 @@ pub mod persistent_storage;
pub mod storage;
#[cfg(not(target_arch = "wasm32"))]
pub async fn initialise_persistent_storage(path: std::path::PathBuf) -> PersistentStorage {
pub async fn initialise_persistent_storage<P: AsRef<Path>>(path: P) -> PersistentStorage {
match persistent_storage::PersistentStorage::init(path).await {
Err(err) => panic!("failed to initialise credential storage - {err}"),
Ok(storage) => storage,
@@ -23,7 +23,7 @@ impl PersistentStorage {
/// # Arguments
///
/// * `database_path`: path to the database.
pub async fn init<P: AsRef<Path> + Send>(database_path: P) -> Result<Self, StorageError> {
pub async fn init<P: AsRef<Path>>(database_path: P) -> Result<Self, StorageError> {
debug!(
"Attempting to connect to database {:?}",
database_path.as_ref().as_os_str()
-549
View File
@@ -1,549 +0,0 @@
// 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"
}
]
"#;
@@ -58,6 +58,7 @@ impl SurbAck {
let packet_size = match packet_type {
PacketType::Outfox => surb_ack_payload.len().max(MIN_PACKET_SIZE),
PacketType::Mix => PacketSize::AckPacket.payload_size(),
#[allow(deprecated)]
PacketType::Vpn => PacketSize::AckPacket.payload_size(),
};
@@ -75,6 +76,7 @@ impl SurbAck {
&destination,
&delays,
)?,
#[allow(deprecated)]
PacketType::Vpn => NymPacket::sphinx_build(
packet_size,
surb_ack_payload,
@@ -105,6 +107,7 @@ impl SurbAck {
PacketSize::OutfoxAckPacket.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,
}
}
@@ -137,6 +140,7 @@ impl SurbAck {
let packet = match packet_type {
PacketType::Outfox => NymPacket::outfox_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..])?,
};
+1
View File
@@ -85,6 +85,7 @@ impl Decoder for NymCodec {
match header.packet_type {
PacketType::Outfox => NymPacket::outfox_from_bytes(slice)?,
PacketType::Mix => NymPacket::sphinx_from_bytes(slice)?,
#[allow(deprecated)]
PacketType::Vpn => NymPacket::sphinx_from_bytes(slice)?,
}
} else {
@@ -244,6 +244,7 @@ impl PacketSize {
) -> Result<Self, InvalidPacketSize> {
let overhead = match packet_type {
PacketType::Mix => SPHINX_PACKET_OVERHEAD,
#[allow(deprecated)]
PacketType::Vpn => SPHINX_PACKET_OVERHEAD,
PacketType::Outfox => OUTFOX_PACKET_OVERHEAD,
};
+11 -2
View File
@@ -1,13 +1,15 @@
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
// 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 std::convert::TryFrom;
use std::fmt;
use thiserror::Error;
use crate::PacketSize;
#[derive(Error, Debug)]
#[error("{received} is not a valid packet mode tag")]
pub struct InvalidPacketType {
@@ -15,17 +17,23 @@ pub struct InvalidPacketType {
}
#[repr(u8)]
#[allow(deprecated)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum PacketType {
/// Represents 'normal' packet sent through the network that should be delayed by an appropriate
/// value at each hop.
#[default]
#[serde(rename = "mix")]
#[serde(alias = "sphinx")]
Mix = 0,
/// Represents a packet that should be sent through the network as fast as possible.
#[deprecated]
#[serde(rename = "unsupported-mix-vpn")]
Vpn = 1,
/// Abusing this to add Outfox support
#[serde(rename = "outfox")]
Outfox = 2,
}
@@ -33,6 +41,7 @@ impl fmt::Display for PacketType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
PacketType::Mix => write!(f, "Mix"),
#[allow(deprecated)]
PacketType::Vpn => write!(f, "Vpn"),
PacketType::Outfox => write!(f, "Outfox"),
}
+1
View File
@@ -251,6 +251,7 @@ pub trait FragmentPreparer {
&destination,
&delays,
)?,
#[allow(deprecated)]
PacketType::Vpn => NymPacket::sphinx_build(
packet_size.payload_size(),
packet_payload,
+32 -167
View File
@@ -1,88 +1,39 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
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};
pub use nym_client_core::config::Config as BaseClientConfig;
use nym_config::defaults::DEFAULT_SOCKS5_LISTENING_PORT;
use nym_config::{NymConfig, OptionalSet};
use nym_service_providers_common::interface::ProviderInterfaceVersion;
use nym_socks5_requests::Socks5ProtocolVersion;
use nym_config::OptionalSet;
use nym_sphinx::addressing::clients::Recipient;
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use std::path::{Path, PathBuf};
use std::str::FromStr;
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_PER_REQUEST_SURBS: u32 = 3;
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Config {
#[serde(flatten)]
base: BaseConfig<Config>,
pub base: BaseClientConfig,
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()
}
pub socks5: Socks5,
}
impl Config {
pub fn new<S: Into<String>>(id: S, provider_mix_address: S) -> Self {
Config {
base: BaseConfig::new(id),
base: BaseClientConfig::new(id),
socks5: Socks5::new(provider_mix_address),
}
}
#[must_use]
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 from_base(base: BaseClientConfig, socks5: Socks5) -> Self {
Config { base, socks5 }
}
pub fn validate(&self) -> bool {
@@ -90,63 +41,38 @@ impl Config {
self.base.validate()
}
// getters
pub fn get_base(&self) -> &BaseConfig<Self> {
&self.base
pub fn with_port(mut self, port: u16) -> Self {
self.socks5.listening_port = port;
self
}
pub fn get_base_mut(&mut self) -> &mut BaseConfig<Self> {
&mut self.base
}
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())
pub fn with_anonymous_replies(mut self, anonymous_replies: bool) -> Self {
self.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(BaseConfig<Self>, T) -> BaseConfig<Self>,
F: Fn(BaseClientConfig, T) -> BaseClientConfig,
{
self.base = f(self.base, val);
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
// (plz, lets refactor it)
pub fn with_optional_ext<F, T>(mut self, f: F, val: Option<T>) -> Self
pub fn with_optional_base<F, T>(mut self, f: F, val: Option<T>) -> Self
where
F: Fn(BaseConfig<Self>, T) -> BaseConfig<Self>,
F: Fn(BaseClientConfig, T) -> BaseClientConfig,
{
self.base = self.base.with_optional(f, val);
self
}
pub fn with_optional_env_ext<F, T>(mut self, f: F, val: Option<T>, env_var: &str) -> Self
pub fn with_optional_base_env<F, T>(mut self, f: F, val: Option<T>, env_var: &str) -> Self
where
F: Fn(BaseConfig<Self>, T) -> BaseConfig<Self>,
F: Fn(BaseClientConfig, T) -> BaseClientConfig,
T: FromStr,
<T as FromStr>::Err: Debug,
{
@@ -154,7 +80,7 @@ impl Config {
self
}
pub fn with_optional_custom_env_ext<F, T, G>(
pub fn with_optional_base_custom_env<F, T, G>(
mut self,
f: F,
val: Option<T>,
@@ -162,7 +88,7 @@ impl Config {
parser: G,
) -> Self
where
F: Fn(BaseConfig<Self>, T) -> BaseConfig<Self>,
F: Fn(BaseClientConfig, T) -> BaseClientConfig,
G: Fn(&str) -> T,
{
self.base = self.base.with_optional_custom_env(f, val, env_var, parser);
@@ -174,19 +100,19 @@ impl Config {
#[serde(deny_unknown_fields)]
pub struct Socks5 {
/// The port on which the client will be listening for incoming requests
listening_port: u16,
pub listening_port: u16,
/// The mix address of the provider to which all requests are going to be sent.
provider_mix_address: String,
pub provider_mix_address: String,
/// The version of the 'service provider' this client is going to use in its communication with the
/// specified socks5 provider.
// if in doubt, use the legacy version as initially nobody will be using the updated binaries
#[serde(default = "ProviderInterfaceVersion::new_legacy")]
provider_interface_version: ProviderInterfaceVersion,
pub provider_interface_version: ProviderInterfaceVersion,
#[serde(default = "Socks5ProtocolVersion::new_legacy")]
socks5_protocol_version: Socks5ProtocolVersion,
pub socks5_protocol_version: Socks5ProtocolVersion,
/// 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
@@ -194,10 +120,10 @@ pub struct Socks5 {
///
/// Note that some service providers might not support this.
#[serde(default)]
send_anonymously: bool,
pub send_anonymously: bool,
#[serde(default)]
socks5_debug: Socks5Debug,
pub socks5_debug: Socks5Debug,
}
impl Socks5 {
@@ -212,81 +138,20 @@ 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 {
Recipient::try_from_base58_string(&self.provider_mix_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
}
}
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)]
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Socks5Debug {
/// Number of reply SURBs attached to each `Request::Connect` message.
connection_start_surbs: u32,
pub connection_start_surbs: u32,
/// Number of reply SURBs attached to each `Request::Send` message.
per_request_surbs: u32,
pub per_request_surbs: u32,
}
impl Default for Socks5Debug {
@@ -3,60 +3,60 @@
use crate::config::{Config, Socks5};
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 std::path::PathBuf;
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct OldConfigV1_1_13 {
#[serde(flatten)]
base: OldBaseConfigV1_1_13<OldConfigV1_1_13>,
socks5: Socks5,
}
impl NymConfig for OldConfigV1_1_13 {
fn template() -> &'static str {
// not intended to be used
unimplemented!()
}
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.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 Config {
fn from(value: OldConfigV1_1_13) -> Self {
Config {
base: value.base.into(),
socks5: value.socks5,
}
}
}
// #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
// #[serde(deny_unknown_fields)]
// pub struct OldConfigV1_1_13 {
// #[serde(flatten)]
// base: OldBaseConfigV1_1_13<OldConfigV1_1_13>,
//
// socks5: Socks5,
// }
//
// //
// // impl NymConfig for OldConfigV1_1_13 {
// // fn template() -> &'static str {
// // // not intended to be used
// // unimplemented!()
// // }
// //
// // fn default_root_directory() -> PathBuf {
// // #[cfg(not(target_os = "android"))]
// // let base_dir = dirs::home_dir().expect("Failed to evaluate $HOME value");
// // #[cfg(target_os = "android")]
// // 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.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 Config {
// fn from(value: OldConfigV1_1_13) -> Self {
// Config {
// base: value.base.into(),
// socks5: value.socks5,
// }
// }
// }
+19 -23
View File
@@ -1,7 +1,7 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::{Config, Socks5};
use crate::config::Config;
use crate::error::Socks5ClientCoreError;
use crate::socks::{
authentication::{AuthenticationMethods, Authenticator, User},
@@ -67,8 +67,8 @@ where
#[allow(clippy::too_many_arguments)]
pub fn start_socks5_listener(
socks5_config: &Socks5,
debug_config: DebugConfig,
socks5_config: &config::Socks5,
base_debug: DebugConfig,
client_input: ClientInput,
client_output: ClientOutput,
client_status: ClientState,
@@ -94,25 +94,24 @@ where
..
} = client_status;
let packet_size = debug_config
let packet_size = base_debug
.traffic
.secondary_packet_size
.unwrap_or(debug_config.traffic.primary_packet_size);
.unwrap_or(base_debug.traffic.primary_packet_size);
let authenticator = Authenticator::new(auth_methods, allowed_users);
let mut sphinx_socks = NymSocksServer::new(
socks5_config.get_listening_port(),
socks5_config.listening_port,
authenticator,
socks5_config.get_provider_mix_address(),
self_address,
shared_lane_queue_lengths,
socks::client::Config::new(
packet_size,
socks5_config.get_provider_interface_version(),
socks5_config.get_socks5_protocol_version(),
socks5_config.get_send_anonymously(),
socks5_config.get_connection_start_surbs(),
socks5_config.get_per_request_surbs(),
socks5_config.provider_interface_version,
socks5_config.socks5_protocol_version,
socks5_config.send_anonymously,
socks5_config.socks5_debug,
),
shutdown.clone(),
packet_type,
@@ -191,43 +190,40 @@ where
let (key_store, reply_storage_backend, credential_store) = self.storage.into_split();
// don't create bandwidth controller if credentials are disabled
let bandwidth_controller = if self.config.get_base().get_disabled_credentials_mode() {
let bandwidth_controller = if self.config.base.client.disabled_credentials_mode {
None
} else {
Some(non_wasm_helpers::create_bandwidth_controller(
self.config.get_base(),
&self.config.base,
credential_store,
))
};
let base_builder = BaseClientBuilder::<_, S>::new_from_base_config(
self.config.get_base(),
&self.config.base,
key_store,
bandwidth_controller,
reply_storage_backend,
);
let packet_type = self.config.get_base().get_packet_type();
let mut started_client = base_builder.start_base(packet_type).await?;
let packet_type = self.config.base.debug.traffic.packet_type;
let mut started_client = base_builder.start_base().await?;
let self_address = started_client.address;
let client_input = started_client.client_input.register_producer();
let client_output = started_client.client_output.register_consumer();
let client_state = started_client.client_state;
info!(
"Running with {:?} packets",
self.config.get_base().get_packet_type()
);
info!("Running with {packet_type} packets",);
Self::start_socks5_listener(
self.config.get_socks5(),
*self.config.get_debug_settings(),
&self.config.socks5,
self.config.base.debug,
client_input,
client_output,
client_state,
self_address,
started_client.task_manager.subscribe(),
self.config.get_base().get_packet_type(),
packet_type,
);
info!("Client startup finished!");
@@ -4,6 +4,7 @@ use super::authentication::{AuthenticationMethods, Authenticator, User};
use super::request::{SocksCommand, SocksRequest};
use super::types::{ResponseCodeV4, ResponseCodeV5, SocksProxyError};
use super::{SocksVersion, RESERVED, SOCKS4_VERSION, SOCKS5_VERSION};
use crate::config;
use futures::channel::mpsc;
use futures::task::{Context, Poll};
use log::*;
@@ -147,16 +148,15 @@ impl Config {
provider_interface_version: ProviderInterfaceVersion,
socks5_protocol_version: Socks5ProtocolVersion,
use_surbs_for_responses: bool,
connection_start_surbs: u32,
per_request_surbs: u32,
debug_config: config::Socks5Debug,
) -> Self {
Self {
biggest_packet_size,
provider_interface_version,
socks5_protocol_version,
use_surbs_for_responses,
connection_start_surbs,
per_request_surbs,
connection_start_surbs: debug_config.connection_start_surbs,
per_request_surbs: debug_config.per_request_surbs,
}
}
-6
View File
@@ -81,7 +81,6 @@ impl GatewayBond {
pub struct GatewayNodeDetailsResponse {
pub identity_key: String,
pub sphinx_key: String,
pub announce_address: String,
pub bind_address: String,
pub version: String,
pub mix_port: u16,
@@ -93,11 +92,6 @@ impl fmt::Display for GatewayNodeDetailsResponse {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "Identity Key: {}", self.identity_key)?;
writeln!(f, "Sphinx Key: {}", self.sphinx_key)?;
writeln!(
f,
"Host: {} (bind address: {})",
self.announce_address, self.bind_address
)?;
writeln!(f, "Version: {}", self.version)?;
writeln!(
f,
+4 -11
View File
@@ -13,6 +13,7 @@ use nym_mixnet_contract_common::{
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::net::IpAddr;
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
#[cfg_attr(
@@ -167,31 +168,23 @@ impl MixNodeCostParams {
pub struct MixnodeNodeDetailsResponse {
pub identity_key: String,
pub sphinx_key: String,
pub announce_address: String,
pub bind_address: String,
pub bind_address: IpAddr,
pub version: String,
pub mix_port: u16,
pub http_api_port: u16,
pub verloc_port: u16,
pub wallet_address: Option<String>,
}
impl fmt::Display for MixnodeNodeDetailsResponse {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let wallet_address = self.wallet_address.clone().unwrap_or_default();
writeln!(f, "Identity Key: {}", self.identity_key)?;
writeln!(f, "Sphinx Key: {}", self.sphinx_key)?;
writeln!(
f,
"Host: {} (bind address: {})",
self.announce_address, self.bind_address
)?;
writeln!(f, "Host: {}", self.bind_address)?;
writeln!(f, "Version: {}", self.version)?;
writeln!(
f,
"Mix Port: {}, Verloc port: {}, Http Port: {}\n",
self.mix_port, self.verloc_port, self.http_api_port
)?;
writeln!(f, "You are bonding to wallet address: {wallet_address}\n\n")
)
}
}
+27 -31
View File
@@ -1,18 +1,18 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::{default_config_directory, default_config_filepath, default_data_directory};
use crate::{
commands::{override_config, OverrideConfig},
config::{persistence::pathfinder::GatewayPathfinder, Config},
config::Config,
OutputFormat,
};
use clap::Args;
use nym_config::NymConfig;
use nym_crypto::asymmetric::{encryption, identity};
use nym_validator_client::nyxd;
use std::error::Error;
use std::net::IpAddr;
use std::path::PathBuf;
use std::{fs, io};
#[derive(Args, Clone)]
pub struct Init {
@@ -24,10 +24,6 @@ pub struct Init {
#[clap(long)]
host: IpAddr,
/// The wallet address you will use to bond this gateway, e.g. nymt1z9egw0knv47nmur0p8vk4rcx59h9gg4zuxrrr9
#[clap(long)]
wallet_address: nyxd::AccountId,
/// The port on which the gateway will be listening for sphinx packets
#[clap(long)]
mix_port: Option<u16>,
@@ -36,11 +32,6 @@ pub struct Init {
#[clap(long)]
clients_port: Option<u16>,
/// The host that will be reported to the directory server
#[clap(long)]
// TODO: could this be changed to `Option<url::Url>`?
announce_host: Option<String>,
/// Path to sqlite database containing all gateway persistent data
#[clap(long)]
datastore: Option<PathBuf>,
@@ -86,11 +77,9 @@ impl From<Init> for OverrideConfig {
fn from(init_config: Init) -> Self {
OverrideConfig {
host: Some(init_config.host),
wallet_address: Some(init_config.wallet_address),
mix_port: init_config.mix_port,
clients_port: init_config.clients_port,
datastore: init_config.datastore,
announce_host: init_config.announce_host,
nym_apis: init_config.nym_apis,
mnemonic: init_config.mnemonic,
@@ -103,10 +92,15 @@ impl From<Init> for OverrideConfig {
}
}
fn init_paths(id: &str) -> io::Result<()> {
fs::create_dir_all(default_data_directory(id))?;
fs::create_dir_all(default_config_directory(id))
}
pub async fn execute(args: Init) -> Result<(), Box<dyn Error + Send + Sync>> {
eprintln!("Initialising gateway {}...", args.id);
let already_init = if Config::default_config_file_path(&args.id).exists() {
let already_init = if default_config_filepath(&args.id).exists() {
eprintln!(
"Gateway \"{}\" was already initialised before! Config information will be \
overwritten (but keys will be kept)!",
@@ -114,6 +108,7 @@ pub async fn execute(args: Init) -> Result<(), Box<dyn Error + Send + Sync>> {
);
true
} else {
init_paths(&args.id)?;
false
};
@@ -128,33 +123,36 @@ pub async fn execute(args: Init) -> Result<(), Box<dyn Error + Send + Sync>> {
let identity_keys = identity::KeyPair::new(&mut rng);
let sphinx_keys = encryption::KeyPair::new(&mut rng);
let pathfinder = GatewayPathfinder::new_from_config(&config);
nym_pemstore::store_keypair(
&sphinx_keys,
&nym_pemstore::KeyPairPath::new(
pathfinder.private_encryption_key().to_owned(),
pathfinder.public_encryption_key().to_owned(),
),
)
.expect("Failed to save sphinx keys");
nym_pemstore::store_keypair(
&identity_keys,
&nym_pemstore::KeyPairPath::new(
pathfinder.private_identity_key().to_owned(),
pathfinder.public_identity_key().to_owned(),
config.storage_paths.private_identity_key(),
config.storage_paths.public_identity_key(),
),
)
.expect("Failed to save identity keys");
nym_pemstore::store_keypair(
&sphinx_keys,
&nym_pemstore::KeyPairPath::new(
config.storage_paths.private_encryption_key(),
config.storage_paths.public_encryption_key(),
),
)
.expect("Failed to save sphinx keys");
eprintln!("Saved identity and mixnet sphinx keypairs");
}
let config_save_location = config.get_config_file_save_location();
let config_save_location = config.default_location();
config
.save_to_file(None)
.save_to_default_location()
.expect("Failed to save the config file");
eprintln!("Saved configuration file to {:?}", config_save_location);
eprintln!(
"Saved configuration file to {}",
config_save_location.display()
);
eprintln!("Gateway configuration completed.\n\n\n");
crate::node::create_gateway(config)
@@ -176,10 +174,8 @@ mod tests {
let args = Init {
id: "foo-id".to_string(),
host: "1.1.1.1".parse().unwrap(),
wallet_address: "n1z9egw0knv47nmur0p8vk4rcx59h9gg4zjx9ede".parse().unwrap(),
mix_port: Some(42),
clients_port: Some(43),
announce_host: Some("foo-announce-host".to_string()),
datastore: Some("/foo-datastore".parse().unwrap()),
nym_apis: None,
mnemonic: None,
+54 -23
View File
@@ -1,16 +1,19 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::default_config_filepath;
use crate::config::old_config_v1_1_19::ConfigV1_1_19;
use crate::error::GatewayError;
use crate::{config::Config, Cli};
use clap::CommandFactory;
use clap::Subcommand;
use log::{error, info};
use nym_bin_common::completions::{fig_generate, ArgShell};
use nym_bin_common::version_checker;
use nym_config::OptionalSet;
use nym_network_defaults::var_names::NYXD;
use nym_network_defaults::var_names::{BECH32_PREFIX, NYM_API, STATISTICS_SERVICE_DOMAIN_ADDRESS};
use nym_validator_client::nyxd::{self, AccountId};
use nym_validator_client::nyxd::AccountId;
use std::error::Error;
use std::net::IpAddr;
use std::path::PathBuf;
@@ -49,11 +52,9 @@ pub(crate) enum Commands {
#[derive(Default)]
pub(crate) struct OverrideConfig {
host: Option<IpAddr>,
wallet_address: Option<nyxd::AccountId>,
mix_port: Option<u16>,
clients_port: Option<u16>,
datastore: Option<PathBuf>,
announce_host: Option<String>,
enabled_statistics: Option<bool>,
statistics_service_url: Option<url::Url>,
nym_apis: Option<Vec<url::Url>>,
@@ -81,21 +82,8 @@ pub(crate) fn override_config(
mut config: Config,
args: OverrideConfig,
) -> Result<Config, GatewayError> {
// special case that I'm not sure could be easily handled with the trait
let mut was_host_overridden = false;
if let Some(host) = args.host {
config = config.with_listening_address(host);
was_host_overridden = true;
}
if let Some(announce_host) = args.announce_host {
config = config.with_announce_address(announce_host);
} else if was_host_overridden {
// make sure our 'mix-announce-host' always defaults to 'mix-host'
config = config.announce_host_from_listening_host();
}
config = config
.with_optional(Config::with_listening_address, args.host)
.with_optional(Config::with_mix_port, args.mix_port)
.with_optional(Config::with_clients_port, args.clients_port)
.with_optional_custom_env(
@@ -110,11 +98,6 @@ pub(crate) fn override_config(
args.statistics_service_url,
STATISTICS_SERVICE_DOMAIN_ADDRESS,
)
.with_validated_optional(
|cfg, wallet_address| cfg.with_wallet_address(wallet_address),
args.wallet_address,
ensure_correct_bech32_prefix,
)?
.with_optional(Config::with_custom_persistent_store, args.datastore)
.with_optional(Config::with_cosmos_mnemonic, args.mnemonic)
.with_optional_custom_env(
@@ -150,7 +133,7 @@ pub(crate) fn ensure_correct_bech32_prefix(address: &AccountId) -> Result<(), Ga
// network version. It might do so in the future.
pub(crate) fn ensure_config_version_compatibility(cfg: &Config) -> Result<(), GatewayError> {
let binary_version = env!("CARGO_PKG_VERSION");
let config_version = cfg.get_version();
let config_version = &cfg.gateway.version;
if binary_version == config_version {
Ok(())
@@ -169,3 +152,51 @@ pub(crate) fn ensure_config_version_compatibility(cfg: &Config) -> Result<(), Ga
})
}
}
fn try_upgrade_v1_1_19_config(id: &str) -> Result<(), GatewayError> {
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
// explicitly load it as v1.1.19 (which is incompatible with the current, i.e. 1.1.20+)
let Ok(old_config) = ConfigV1_1_19::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(());
};
info!("It seems the gateway is using <= v1.1.19 config template.");
info!("It is going to get updated to the current specification.");
let updated: Config = old_config.into();
updated
.save_to_default_location()
.map_err(|err| GatewayError::ConfigSaveFailure {
path: default_config_filepath(id),
id: id.to_string(),
source: err,
})
}
pub(crate) fn try_load_current_config(id: &str) -> Result<Config, GatewayError> {
try_upgrade_v1_1_19_config(id)?;
Config::read_from_default_path(id).map_err(|err| {
error!(
"Failed to load config for {id}. Are you sure you have run `init` before? (Error was: {err})",
);
GatewayError::ConfigLoadFailure {
path: default_config_filepath(id),
id: id.to_string(),
source: err,
}
})
}
#[cfg(test)]
mod tests {
use super::*;
use clap::CommandFactory;
#[test]
fn verify_cli() {
Cli::command().debug_assert();
}
}
+6 -22
View File
@@ -5,7 +5,7 @@ use crate::commands::{ensure_config_version_compatibility, OverrideConfig};
use crate::support::config::build_config;
use clap::Args;
use nym_bin_common::output_format::OutputFormat;
use nym_validator_client::nyxd;
use nym_config::helpers::SPECIAL_ADDRESSES;
use std::error::Error;
use std::net::IpAddr;
use std::path::PathBuf;
@@ -20,10 +20,6 @@ pub struct Run {
#[clap(long)]
host: Option<IpAddr>,
/// The wallet address you will use to bond this gateway, e.g. nymt1z9egw0knv47nmur0p8vk4rcx59h9gg4zuxrrr9
#[clap(long)]
wallet_address: Option<nyxd::AccountId>,
/// The port on which the gateway will be listening for sphinx packets
#[clap(long)]
mix_port: Option<u16>,
@@ -32,11 +28,6 @@ pub struct Run {
#[clap(long)]
clients_port: Option<u16>,
/// The host that will be reported to the directory server
#[clap(long)]
// TODO: could this be changed to `Option<url::Url>`?
announce_host: Option<String>,
/// Path to sqlite database containing all gateway persistent data
#[clap(long)]
datastore: Option<PathBuf>,
@@ -82,11 +73,9 @@ impl From<Run> for OverrideConfig {
fn from(run_config: Run) -> Self {
OverrideConfig {
host: run_config.host,
wallet_address: run_config.wallet_address,
mix_port: run_config.mix_port,
clients_port: run_config.clients_port,
datastore: run_config.datastore,
announce_host: run_config.announce_host,
nym_apis: run_config.nym_apis,
mnemonic: run_config.mnemonic,
@@ -98,21 +87,16 @@ impl From<Run> for OverrideConfig {
}
}
fn show_binding_warning(address: String) {
fn show_binding_warning(address: &str) {
eprintln!("\n##### NOTE #####");
eprintln!(
"\nYou are trying to bind to {} - you might not be accessible to other nodes\n\
"\nYou are trying to bind to {address} - you might not be accessible to other nodes\n\
You can ignore this warning if you're running setup on a local network \n\
or have set a custom 'announce-host'",
address
or have used different host when bonding your node"
);
eprintln!("\n\n");
}
fn special_addresses() -> Vec<&'static str> {
vec!["localhost", "127.0.0.1", "0.0.0.0", "::1", "[::1]"]
}
pub async fn execute(args: Run) -> Result<(), Box<dyn Error + Send + Sync>> {
let id = args.id.clone();
eprintln!("Starting gateway {id}...");
@@ -121,8 +105,8 @@ pub async fn execute(args: Run) -> Result<(), Box<dyn Error + Send + Sync>> {
let config = build_config(id, args)?;
ensure_config_version_compatibility(&config)?;
if special_addresses().contains(&&*config.get_listening_address().to_string()) {
show_binding_warning(config.get_listening_address().to_string());
if SPECIAL_ADDRESSES.contains(&config.gateway.listening_address) {
show_binding_warning(&config.gateway.listening_address.to_string());
}
let mut gateway = crate::node::create_gateway(config).await;
+6 -7
View File
@@ -5,8 +5,7 @@ use crate::commands::{ensure_correct_bech32_prefix, OverrideConfig};
use crate::error::GatewayError;
use crate::support::config::build_config;
use crate::{
commands::ensure_config_version_compatibility,
config::persistence::pathfinder::GatewayPathfinder,
commands::ensure_config_version_compatibility, config::persistence::paths::GatewayPaths,
};
use anyhow::{bail, Result};
use clap::{ArgGroup, Args};
@@ -65,11 +64,11 @@ impl TryFrom<Sign> for SignedTarget {
}
}
pub fn load_identity_keys(pathfinder: &GatewayPathfinder) -> identity::KeyPair {
pub fn load_identity_keys(paths: &GatewayPaths) -> identity::KeyPair {
let identity_keypair: identity::KeyPair =
nym_pemstore::load_keypair(&nym_pemstore::KeyPairPath::new(
pathfinder.private_identity_key().to_owned(),
pathfinder.public_identity_key().to_owned(),
paths.private_identity_key().to_owned(),
paths.public_identity_key().to_owned(),
))
.expect("Failed to read stored identity key files");
identity_keypair
@@ -139,8 +138,8 @@ pub fn execute(args: Sign) -> Result<(), Box<dyn Error + Send + Sync>> {
let output = args.output;
let signed_target = SignedTarget::try_from(args)?;
let pathfinder = GatewayPathfinder::new_from_config(&config);
let identity_keypair = load_identity_keys(&pathfinder);
let identity_keypair = load_identity_keys(&config.storage_paths);
match signed_target {
SignedTarget::Text(text) => {
+11 -10
View File
@@ -1,10 +1,9 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::{Config, MISSING_VALUE};
use crate::config::Config;
use clap::Args;
use nym_bin_common::version_checker::Version;
use nym_config::NymConfig;
use std::fmt::Display;
use std::process;
@@ -56,7 +55,7 @@ fn unsupported_upgrade(current_version: &Version, config_version: &Version) -> !
}
fn parse_config_version(config: &Config) -> Version {
let version = Version::parse(config.get_version()).unwrap_or_else(|err| {
let version = Version::parse(&config.gateway.version).unwrap_or_else(|err| {
eprintln!("failed to parse client version! - {err}");
process::exit(1)
});
@@ -105,11 +104,13 @@ fn minor_0_12_upgrade(
let upgraded_config = config.with_custom_version(to_version.to_string());
upgraded_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);
});
upgraded_config
.save_to_default_location()
.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);
@@ -139,12 +140,12 @@ fn do_upgrade(mut config: Config, args: &Upgrade, package_version: Version) {
pub async fn execute(args: &Upgrade) {
let package_version = parse_package_version();
let existing_config = Config::load_from_file(&args.id).unwrap_or_else(|err| {
let existing_config = Config::read_from_default_path(&args.id).unwrap_or_else(|err| {
eprintln!("failed to load existing config file! - {err}");
process::exit(1)
});
if existing_config.get_version() == MISSING_VALUE {
if existing_config.gateway.version.is_empty() {
eprintln!("the existing configuration file does not seem to contain version number.");
process::exit(1);
}
+101 -277
View File
@@ -1,22 +1,29 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::template::config_template;
use crate::config::persistence::paths::GatewayPaths;
use crate::config::template::CONFIG_TEMPLATE;
use nym_bin_common::logging::LoggingSettings;
use nym_config::defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT};
use nym_config::NymConfig;
use nym_network_defaults::mainnet::{NYM_API, NYXD_URL, STATISTICS_SERVICE_DOMAIN_ADDRESS};
use nym_validator_client::nyxd;
use nym_config::helpers::inaddr_any;
use nym_config::{
must_get_home, read_config_from_toml_file, save_formatted_config_to_file, NymConfigTemplate,
DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, DEFAULT_DATA_DIR, NYM_DIR,
};
use nym_network_defaults::mainnet;
use serde::{Deserialize, Serialize};
use std::io;
use std::net::IpAddr;
use std::path::PathBuf;
use std::str::FromStr;
use std::path::{Path, PathBuf};
use std::time::Duration;
use url::Url;
use zeroize::{Zeroize, ZeroizeOnDrop};
pub(crate) mod old_config_v1_1_19;
pub mod persistence;
mod template;
pub(crate) const MISSING_VALUE: &str = "MISSING VALUE";
const DEFAULT_GATEWAYS_DIR: &str = "gateways";
// 'DEBUG'
// where applicable, the below are defined in milliseconds
@@ -29,103 +36,77 @@ const DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE: usize = 2000;
const DEFAULT_STORED_MESSAGE_FILENAME_LENGTH: u16 = 16;
const DEFAULT_MESSAGE_RETRIEVAL_LIMIT: i64 = 100;
pub fn missing_string_value() -> String {
MISSING_VALUE.to_string()
/// Derive default path to gateway's config directory.
/// It should get resolved to `$HOME/.nym/gateways/<id>/config`
pub fn default_config_directory<P: AsRef<Path>>(id: P) -> PathBuf {
must_get_home()
.join(NYM_DIR)
.join(DEFAULT_GATEWAYS_DIR)
.join(id)
.join(DEFAULT_CONFIG_DIR)
}
fn bind_all_address() -> IpAddr {
"0.0.0.0".parse().unwrap()
/// Derive default path to gateways's config file.
/// It should get resolved to `$HOME/.nym/gateways/<id>/config/config.toml`
pub fn default_config_filepath<P: AsRef<Path>>(id: P) -> PathBuf {
default_config_directory(id).join(DEFAULT_CONFIG_FILENAME)
}
fn default_mix_port() -> u16 {
DEFAULT_MIX_LISTENING_PORT
/// Derive default path to gateways's data directory where files, such as keys, are stored.
/// It should get resolved to `$HOME/.nym/gateways/<id>/data`
pub fn default_data_directory<P: AsRef<Path>>(id: P) -> PathBuf {
must_get_home()
.join(NYM_DIR)
.join(DEFAULT_GATEWAYS_DIR)
.join(id)
.join(DEFAULT_DATA_DIR)
}
fn default_clients_port() -> u16 {
DEFAULT_CLIENT_LISTENING_PORT
}
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Config {
gateway: Gateway,
pub gateway: Gateway,
pub storage_paths: GatewayPaths,
#[serde(default)]
logging: Logging,
pub logging: LoggingSettings,
#[serde(default)]
debug: Debug,
pub debug: Debug,
}
impl NymConfig for Config {
impl NymConfigTemplate for Config {
fn template() -> &'static str {
config_template()
}
fn default_root_directory() -> PathBuf {
dirs::home_dir()
.expect("Failed to evaluate $HOME value")
.join(".nym")
.join("gateways")
}
fn try_default_root_directory() -> Option<PathBuf> {
dirs::home_dir().map(|path| path.join(".nym").join("gateways"))
}
fn root_directory(&self) -> PathBuf {
self.gateway.nym_root_directory.clone()
}
fn config_directory(&self) -> PathBuf {
self.gateway
.nym_root_directory
.join(&self.gateway.id)
.join("config")
}
fn data_directory(&self) -> PathBuf {
self.gateway
.nym_root_directory
.join(&self.gateway.id)
.join("data")
CONFIG_TEMPLATE
}
}
impl Config {
pub fn new<S: Into<String>>(id: S) -> Self {
Config::default().with_id(id)
pub fn new<S: AsRef<str>>(id: S) -> Self {
Config {
gateway: Gateway::new_default(id.as_ref()),
storage_paths: GatewayPaths::new_default(id.as_ref()),
logging: Default::default(),
debug: Default::default(),
}
}
// builder methods
pub fn with_id<S: Into<String>>(mut self, id: S) -> Self {
let id = id.into();
if self.gateway.private_sphinx_key_file.as_os_str().is_empty() {
self.gateway.private_sphinx_key_file =
self::Gateway::default_private_sphinx_key_file(&id);
}
if self.gateway.public_sphinx_key_file.as_os_str().is_empty() {
self.gateway.public_sphinx_key_file =
self::Gateway::default_public_sphinx_key_file(&id);
}
if self
.gateway
.private_identity_key_file
.as_os_str()
.is_empty()
{
self.gateway.private_identity_key_file =
self::Gateway::default_private_identity_key_file(&id);
}
if self.gateway.public_identity_key_file.as_os_str().is_empty() {
self.gateway.public_identity_key_file =
self::Gateway::default_public_identity_key_file(&id);
}
pub fn read_from_toml_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
read_config_from_toml_file(path)
}
if self.gateway.persistent_storage.as_os_str().is_empty() {
self.gateway.persistent_storage = self::Gateway::default_database_path(&id);
}
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
Self::read_from_toml_file(default_config_filepath(id))
}
self.gateway.id = id;
self
pub fn default_location(&self) -> PathBuf {
default_config_filepath(&self.gateway.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 with_only_coconut_credentials(mut self, only_coconut_credentials: bool) -> Self {
@@ -163,11 +144,6 @@ impl Config {
self
}
pub fn with_announce_address<S: Into<String>>(mut self, announce_address: S) -> Self {
self.gateway.announce_address = announce_address.into();
self
}
pub fn with_mix_port(mut self, port: u16) -> Self {
self.gateway.mix_port = port;
self
@@ -178,13 +154,8 @@ impl Config {
self
}
pub fn announce_host_from_listening_host(mut self) -> Self {
self.gateway.announce_address = self.gateway.listening_address.to_string();
self
}
pub fn with_custom_persistent_store(mut self, store_dir: PathBuf) -> Self {
self.gateway.persistent_storage = store_dir;
self.storage_paths.clients_storage = store_dir;
self
}
@@ -193,40 +164,6 @@ impl Config {
self
}
pub fn with_wallet_address(mut self, wallet_address: nyxd::AccountId) -> Self {
self.gateway.wallet_address = Some(wallet_address);
self
}
// getters
pub fn get_config_file_save_location(&self) -> PathBuf {
self.config_directory().join(Self::config_file_name())
}
pub fn get_only_coconut_credentials(&self) -> bool {
self.gateway.only_coconut_credentials
}
pub fn get_private_identity_key_file(&self) -> PathBuf {
self.gateway.private_identity_key_file.clone()
}
pub fn get_public_identity_key_file(&self) -> PathBuf {
self.gateway.public_identity_key_file.clone()
}
pub fn get_private_sphinx_key_file(&self) -> PathBuf {
self.gateway.private_sphinx_key_file.clone()
}
pub fn get_public_sphinx_key_file(&self) -> PathBuf {
self.gateway.public_sphinx_key_file.clone()
}
pub fn get_enabled_statistics(&self) -> bool {
self.gateway.enabled_statistics
}
pub fn get_statistics_service_url(&self) -> Url {
self.gateway.statistics_service_url.clone()
}
@@ -242,225 +179,112 @@ impl Config {
pub fn get_cosmos_mnemonic(&self) -> bip39::Mnemonic {
self.gateway.cosmos_mnemonic.clone()
}
pub fn get_listening_address(&self) -> IpAddr {
self.gateway.listening_address
}
pub fn get_announce_address(&self) -> String {
self.gateway.announce_address.clone()
}
pub fn get_mix_port(&self) -> u16 {
self.gateway.mix_port
}
pub fn get_clients_port(&self) -> u16 {
self.gateway.clients_port
}
pub fn get_persistent_store_path(&self) -> PathBuf {
self.gateway.persistent_storage.clone()
}
pub fn get_packet_forwarding_initial_backoff(&self) -> Duration {
self.debug.packet_forwarding_initial_backoff
}
pub fn get_packet_forwarding_maximum_backoff(&self) -> Duration {
self.debug.packet_forwarding_maximum_backoff
}
pub fn get_initial_connection_timeout(&self) -> Duration {
self.debug.initial_connection_timeout
}
pub fn get_maximum_connection_buffer_size(&self) -> usize {
self.debug.maximum_connection_buffer_size
}
pub fn get_use_legacy_sphinx_framing(&self) -> bool {
self.debug.use_legacy_framed_packet_version
}
pub fn get_message_retrieval_limit(&self) -> i64 {
self.debug.message_retrieval_limit
}
pub fn get_version(&self) -> &str {
&self.gateway.version
}
#[allow(unused)]
pub fn get_wallet_address(&self) -> Option<nyxd::AccountId> {
self.gateway.wallet_address.clone()
}
}
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
// we only really care about the mnemonic being zeroized
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Zeroize, ZeroizeOnDrop)]
pub struct Gateway {
/// Version of the gateway for which this configuration was created.
#[serde(default = "missing_string_value")]
version: String,
pub version: String,
/// ID specifies the human readable ID of this particular gateway.
id: String,
pub id: String,
/// Indicates whether this gateway is accepting only coconut credentials for accessing the
/// the mixnet, or if it also accepts non-paying clients
#[serde(default)]
only_coconut_credentials: bool,
pub only_coconut_credentials: bool,
/// Address to which this mixnode will bind to and will be listening for packets.
#[serde(default = "bind_all_address")]
listening_address: IpAddr,
/// Optional address announced to the validator for the clients to connect to.
/// It is useful, say, in NAT scenarios or wanting to more easily update actual IP address
/// later on by using name resolvable with a DNS query, such as `nymtech.net`.
#[serde(default = "missing_string_value")]
announce_address: String,
#[zeroize(skip)]
pub listening_address: IpAddr,
/// Port used for listening for all mixnet traffic.
/// (default: 1789)
#[serde(default = "default_mix_port")]
mix_port: u16,
pub mix_port: u16,
/// Port used for listening for all client-related traffic.
/// (default: 9000)
#[serde(default = "default_clients_port")]
clients_port: u16,
/// Path to file containing private identity key.
private_identity_key_file: PathBuf,
/// Path to file containing public identity key.
public_identity_key_file: PathBuf,
/// Path to file containing private sphinx key.
private_sphinx_key_file: PathBuf,
/// Path to file containing public sphinx key.
public_sphinx_key_file: PathBuf,
pub clients_port: u16,
/// Whether gateway collects and sends anonymized statistics
enabled_statistics: bool,
pub enabled_statistics: bool,
/// Domain address of the statistics service
statistics_service_url: Url,
#[zeroize(skip)]
pub statistics_service_url: Url,
/// Addresses to APIs from which the node gets the view of the network.
#[serde(alias = "validator_api_urls")]
nym_api_urls: Vec<Url>,
#[zeroize(skip)]
pub nym_api_urls: Vec<Url>,
/// Addresses to validators which the node uses to check for double spending of ERC20 tokens.
#[serde(alias = "validator_nymd_urls")]
nyxd_urls: Vec<Url>,
#[zeroize(skip)]
pub nyxd_urls: Vec<Url>,
/// Mnemonic of a cosmos wallet used in checking for double spending.
cosmos_mnemonic: bip39::Mnemonic,
/// nym_home_directory specifies absolute path to the home nym gateways directory.
/// It is expected to use default value and hence .toml file should not redefine this field.
nym_root_directory: PathBuf,
/// Path to sqlite database containing all persistent data: messages for offline clients,
/// derived shared keys and available client bandwidths.
persistent_storage: PathBuf,
/// The Cosmos wallet address that will control this gateway
// the only reason this is an Option is because of the lack of existence of a sane default value
wallet_address: Option<nyxd::AccountId>,
// #[deprecated(note = "move to storage")]
// TODO: I don't think this should be stored directly in the config...
pub cosmos_mnemonic: bip39::Mnemonic,
}
impl Gateway {
fn default_private_sphinx_key_file(id: &str) -> PathBuf {
Config::default_data_directory(id).join("private_sphinx.pem")
}
fn default_public_sphinx_key_file(id: &str) -> PathBuf {
Config::default_data_directory(id).join("public_sphinx.pem")
}
fn default_private_identity_key_file(id: &str) -> PathBuf {
Config::default_data_directory(id).join("private_identity.pem")
}
fn default_public_identity_key_file(id: &str) -> PathBuf {
Config::default_data_directory(id).join("public_identity.pem")
}
fn default_database_path(id: &str) -> PathBuf {
Config::default_data_directory(id).join("db.sqlite")
}
}
impl Default for Gateway {
fn default() -> Self {
pub fn new_default<S: Into<String>>(id: S) -> Self {
Gateway {
version: env!("CARGO_PKG_VERSION").to_string(),
id: "".to_string(),
id: id.into(),
only_coconut_credentials: false,
listening_address: bind_all_address(),
announce_address: "127.0.0.1".to_string(),
listening_address: inaddr_any(),
mix_port: DEFAULT_MIX_LISTENING_PORT,
clients_port: DEFAULT_CLIENT_LISTENING_PORT,
private_identity_key_file: Default::default(),
public_identity_key_file: Default::default(),
private_sphinx_key_file: Default::default(),
public_sphinx_key_file: Default::default(),
enabled_statistics: false,
statistics_service_url: Url::from_str(STATISTICS_SERVICE_DOMAIN_ADDRESS)
statistics_service_url: mainnet::STATISTICS_SERVICE_DOMAIN_ADDRESS
.parse()
.expect("Invalid default statistics service URL"),
nym_api_urls: vec![Url::from_str(NYM_API).expect("Invalid default API URL")],
nyxd_urls: vec![Url::from_str(NYXD_URL).expect("Invalid default nyxd URL")],
nym_api_urls: vec![mainnet::NYM_API.parse().expect("Invalid default API URL")],
nyxd_urls: vec![mainnet::NYXD_URL.parse().expect("Invalid default nyxd URL")],
cosmos_mnemonic: bip39::Mnemonic::generate(24).unwrap(),
nym_root_directory: Config::default_root_directory(),
persistent_storage: Default::default(),
wallet_address: None,
}
}
}
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
struct Logging {}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(default)]
struct Debug {
pub struct Debug {
/// Initial value of an exponential backoff to reconnect to dropped TCP connection when
/// forwarding sphinx packets.
#[serde(with = "humantime_serde")]
packet_forwarding_initial_backoff: Duration,
pub packet_forwarding_initial_backoff: Duration,
/// Maximum value of an exponential backoff to reconnect to dropped TCP connection when
/// forwarding sphinx packets.
#[serde(with = "humantime_serde")]
packet_forwarding_maximum_backoff: Duration,
pub packet_forwarding_maximum_backoff: Duration,
/// Timeout for establishing initial connection when trying to forward a sphinx packet.
#[serde(with = "humantime_serde")]
initial_connection_timeout: Duration,
pub initial_connection_timeout: Duration,
/// Maximum number of packets that can be stored waiting to get sent to a particular connection.
maximum_connection_buffer_size: usize,
pub maximum_connection_buffer_size: usize,
/// Delay between each subsequent presence data being sent.
#[serde(with = "humantime_serde")]
presence_sending_delay: Duration,
pub presence_sending_delay: Duration,
/// Length of filenames for new client messages.
stored_messages_filename_length: u16,
pub stored_messages_filename_length: u16,
/// Number of messages from offline client that can be pulled at once from the storage.
message_retrieval_limit: i64,
pub message_retrieval_limit: i64,
/// Specifies whether the mixnode should be using the legacy framing for the sphinx packets.
// it's set to true by default. The reason for that decision is to preserve compatibility with the
// existing nodes whilst everyone else is upgrading and getting the code for handling the new field.
// It shall be disabled in the subsequent releases.
use_legacy_framed_packet_version: bool,
pub use_legacy_framed_packet_version: bool,
}
impl Default for Debug {
+196
View File
@@ -0,0 +1,196 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::persistence::paths::{GatewayPaths, KeysPaths};
use crate::config::{Config, Debug, Gateway};
use nym_bin_common::logging::LoggingSettings;
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
use nym_validator_client::nyxd;
use serde::{Deserialize, Serialize};
use std::net::IpAddr;
use std::path::PathBuf;
use std::str::FromStr;
use std::time::Duration;
use url::Url;
const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "https://mainnet-stats.nymte.ch:8090/";
const NYXD_URL: &str = "https://rpc.nymtech.net";
const NYM_API: &str = "https://validator.nymtech.net/api/";
const DEFAULT_MIX_LISTENING_PORT: u16 = 1789;
const DEFAULT_CLIENT_LISTENING_PORT: u16 = 9000;
const DEFAULT_PRESENCE_SENDING_DELAY: Duration = Duration::from_millis(10_000);
const DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF: Duration = Duration::from_millis(10_000);
const DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF: Duration = Duration::from_millis(300_000);
const DEFAULT_INITIAL_CONNECTION_TIMEOUT: Duration = Duration::from_millis(1_500);
const DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE: usize = 2000;
const DEFAULT_STORED_MESSAGE_FILENAME_LENGTH: u16 = 16;
const DEFAULT_MESSAGE_RETRIEVAL_LIMIT: i64 = 100;
fn bind_all_address() -> IpAddr {
"0.0.0.0".parse().unwrap()
}
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
pub struct ConfigV1_1_19 {
gateway: GatewayV1_1_19,
#[serde(default)]
logging: LoggingV1_1_19,
#[serde(default)]
debug: DebugV1_1_19,
}
impl From<ConfigV1_1_19> for Config {
fn from(value: ConfigV1_1_19) -> Self {
Config {
gateway: Gateway {
version: value.gateway.version,
id: value.gateway.id,
only_coconut_credentials: value.gateway.only_coconut_credentials,
listening_address: value.gateway.listening_address,
mix_port: value.gateway.mix_port,
clients_port: value.gateway.clients_port,
enabled_statistics: value.gateway.enabled_statistics,
nym_api_urls: value.gateway.nym_api_urls,
nyxd_urls: value.gateway.nyxd_urls,
statistics_service_url: value.gateway.statistics_service_url,
cosmos_mnemonic: value.gateway.cosmos_mnemonic,
},
storage_paths: GatewayPaths {
keys: KeysPaths {
private_identity_key_file: value.gateway.private_identity_key_file,
public_identity_key_file: value.gateway.public_identity_key_file,
private_sphinx_key_file: value.gateway.private_sphinx_key_file,
public_sphinx_key_file: value.gateway.public_sphinx_key_file,
},
clients_storage: value.gateway.persistent_storage,
},
logging: value.logging.into(),
debug: value.debug.into(),
}
}
}
impl MigrationNymConfig for ConfigV1_1_19 {
fn default_root_directory() -> PathBuf {
dirs::home_dir()
.expect("Failed to evaluate $HOME value")
.join(".nym")
.join("gateways")
}
}
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
pub struct GatewayV1_1_19 {
version: String,
id: String,
#[serde(default)]
only_coconut_credentials: bool,
#[serde(default = "bind_all_address")]
listening_address: IpAddr,
announce_address: String,
mix_port: u16,
clients_port: u16,
private_identity_key_file: PathBuf,
public_identity_key_file: PathBuf,
private_sphinx_key_file: PathBuf,
public_sphinx_key_file: PathBuf,
enabled_statistics: bool,
statistics_service_url: Url,
#[serde(alias = "validator_api_urls")]
nym_api_urls: Vec<Url>,
#[serde(alias = "validator_nymd_urls")]
nyxd_urls: Vec<Url>,
cosmos_mnemonic: bip39::Mnemonic,
nym_root_directory: PathBuf,
persistent_storage: PathBuf,
wallet_address: Option<nyxd::AccountId>,
}
impl Default for GatewayV1_1_19 {
fn default() -> Self {
GatewayV1_1_19 {
version: env!("CARGO_PKG_VERSION").to_string(),
id: "".to_string(),
only_coconut_credentials: false,
listening_address: bind_all_address(),
announce_address: "127.0.0.1".to_string(),
mix_port: DEFAULT_MIX_LISTENING_PORT,
clients_port: DEFAULT_CLIENT_LISTENING_PORT,
private_identity_key_file: Default::default(),
public_identity_key_file: Default::default(),
private_sphinx_key_file: Default::default(),
public_sphinx_key_file: Default::default(),
enabled_statistics: false,
statistics_service_url: Url::from_str(STATISTICS_SERVICE_DOMAIN_ADDRESS)
.expect("Invalid default statistics service URL"),
nym_api_urls: vec![Url::from_str(NYM_API).expect("Invalid default API URL")],
nyxd_urls: vec![Url::from_str(NYXD_URL).expect("Invalid default nyxd URL")],
cosmos_mnemonic: bip39::Mnemonic::generate(24).unwrap(),
nym_root_directory: ConfigV1_1_19::default_root_directory(),
persistent_storage: Default::default(),
wallet_address: None,
}
}
}
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
struct LoggingV1_1_19 {}
impl From<LoggingV1_1_19> for LoggingSettings {
fn from(_value: LoggingV1_1_19) -> Self {
LoggingSettings {}
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(default)]
struct DebugV1_1_19 {
#[serde(with = "humantime_serde")]
packet_forwarding_initial_backoff: Duration,
#[serde(with = "humantime_serde")]
packet_forwarding_maximum_backoff: Duration,
#[serde(with = "humantime_serde")]
initial_connection_timeout: Duration,
maximum_connection_buffer_size: usize,
#[serde(with = "humantime_serde")]
presence_sending_delay: Duration,
stored_messages_filename_length: u16,
message_retrieval_limit: i64,
use_legacy_framed_packet_version: bool,
}
impl From<DebugV1_1_19> for Debug {
fn from(value: DebugV1_1_19) -> Self {
Debug {
packet_forwarding_initial_backoff: value.packet_forwarding_initial_backoff,
packet_forwarding_maximum_backoff: value.packet_forwarding_maximum_backoff,
initial_connection_timeout: value.initial_connection_timeout,
maximum_connection_buffer_size: value.maximum_connection_buffer_size,
presence_sending_delay: value.presence_sending_delay,
stored_messages_filename_length: value.stored_messages_filename_length,
message_retrieval_limit: value.message_retrieval_limit,
use_legacy_framed_packet_version: value.use_legacy_framed_packet_version,
}
}
}
impl Default for DebugV1_1_19 {
fn default() -> Self {
DebugV1_1_19 {
packet_forwarding_initial_backoff: DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF,
packet_forwarding_maximum_backoff: DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF,
initial_connection_timeout: DEFAULT_INITIAL_CONNECTION_TIMEOUT,
presence_sending_delay: DEFAULT_PRESENCE_SENDING_DELAY,
maximum_connection_buffer_size: DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE,
stored_messages_filename_length: DEFAULT_STORED_MESSAGE_FILENAME_LENGTH,
message_retrieval_limit: DEFAULT_MESSAGE_RETRIEVAL_LIMIT,
// TODO: remember to change it in one of future releases!!
use_legacy_framed_packet_version: true,
}
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod pathfinder;
pub mod paths;
@@ -1,42 +0,0 @@
// Copyright 2020 - 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 GatewayPathfinder {
pub config_dir: PathBuf,
pub private_sphinx_key: PathBuf,
pub public_sphinx_key: PathBuf,
pub private_identity_key: PathBuf,
pub public_identity_key: PathBuf,
}
impl GatewayPathfinder {
pub fn new_from_config(config: &Config) -> Self {
GatewayPathfinder {
config_dir: config.get_config_file_save_location(),
private_sphinx_key: config.get_private_sphinx_key_file(),
public_sphinx_key: config.get_public_sphinx_key_file(),
private_identity_key: config.get_private_identity_key_file(),
public_identity_key: config.get_public_identity_key_file(),
}
}
pub fn private_identity_key(&self) -> &Path {
&self.private_identity_key
}
pub fn public_identity_key(&self) -> &Path {
&self.public_identity_key
}
pub fn private_encryption_key(&self) -> &Path {
&self.private_sphinx_key
}
pub fn public_encryption_key(&self) -> &Path {
&self.public_sphinx_key
}
}
+99
View File
@@ -0,0 +1,99 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::default_data_directory;
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_SPHINX_KEY_FILENAME: &str = "private_sphinx.pem";
pub const DEFAULT_PUBLIC_SPHINX_KEY_FILENAME: &str = "public_sphinx.pem";
pub const DEFAULT_CLIENTS_STORAGE_FILENAME: &str = "db.sqlite";
// pub const DEFAULT_DESCRIPTION_FILENAME: &str = "description.toml";
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct GatewayPaths {
pub keys: KeysPaths,
/// Path to sqlite database containing all persistent data: messages for offline clients,
/// derived shared keys and available client bandwidths.
#[serde(alias = "persistent_storage")]
pub clients_storage: PathBuf,
// pub node_description: PathBuf,
// pub cosmos_bip39_mnemonic: PathBuf,
}
impl GatewayPaths {
pub fn new_default<P: AsRef<Path>>(id: P) -> Self {
GatewayPaths {
keys: KeysPaths::new_default(id.as_ref()),
clients_storage: default_data_directory(id).join(DEFAULT_CLIENTS_STORAGE_FILENAME),
// node_description: default_config_filepath(id).join(DEFAULT_DESCRIPTION_FILENAME),
}
}
pub fn private_identity_key(&self) -> &Path {
self.keys.private_identity_key()
}
pub fn public_identity_key(&self) -> &Path {
self.keys.public_identity_key()
}
pub fn private_encryption_key(&self) -> &Path {
self.keys.private_encryption_key()
}
pub fn public_encryption_key(&self) -> &Path {
self.keys.public_encryption_key()
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
pub struct KeysPaths {
/// 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 sphinx key.
pub private_sphinx_key_file: PathBuf,
/// Path to file containing public sphinx key.
pub public_sphinx_key_file: PathBuf,
}
impl KeysPaths {
pub fn new_default<P: AsRef<Path>>(id: P) -> Self {
let data_dir = default_data_directory(id);
KeysPaths {
private_identity_key_file: data_dir.join(DEFAULT_PRIVATE_IDENTITY_KEY_FILENAME),
public_identity_key_file: data_dir.join(DEFAULT_PUBLIC_IDENTITY_KEY_FILENAME),
private_sphinx_key_file: data_dir.join(DEFAULT_PRIVATE_SPHINX_KEY_FILENAME),
public_sphinx_key_file: data_dir.join(DEFAULT_PUBLIC_SPHINX_KEY_FILENAME),
}
}
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_sphinx_key_file
}
pub fn public_encryption_key(&self) -> &Path {
&self.public_sphinx_key_file
}
}
+19 -35
View File
@@ -1,12 +1,11 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub(crate) fn config_template() -> &'static str {
// 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 in verloc.
r#"
// 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
@@ -26,25 +25,6 @@ only_coconut_credentials = {{ gateway.only_coconut_credentials }}
# Socket address to which this gateway will bind to and will be listening for packets.
listening_address = '{{ gateway.listening_address }}'
# Path to file containing private identity key.
private_identity_key_file = '{{ gateway.private_identity_key_file }}'
# Path to file containing public identity key.
public_identity_key_file = '{{ gateway.public_identity_key_file }}'
# Path to file containing private sphinx key.
private_sphinx_key_file = '{{ gateway.private_sphinx_key_file }}'
# Path to file containing public sphinx key.
public_sphinx_key_file = '{{ gateway.public_sphinx_key_file }}'
##### additional gateway config options #####
# Optional address announced to the directory server for the clients to connect to.
# It is useful, say, in NAT scenarios or wanting to more easily update actual IP address
# later on by using name resolvable with a DNS query, such as `nymtech.net`.
announce_address = '{{ gateway.announce_address }}'
# Port used for listening for all mixnet traffic.
# (default: 1789)
mix_port = {{ gateway.mix_port }}
@@ -75,18 +55,23 @@ nyxd_urls = [
cosmos_mnemonic = "{{ gateway.cosmos_mnemonic }}"
# Nym wallet address on the blockchain that should control this gateway
wallet_address = '{{ gateway.wallet_address }}'
[storage_paths]
##### advanced configuration options #####
# Path to file containing private identity key.
keys.private_identity_key_file = '{{ storage_paths.keys.private_identity_key_file }}'
# nym_home_directory specifies absolute path to the home nym gateway directory.
# It is expected to use default value and hence .toml file should not redefine this field.
nym_root_directory = '{{ gateway.nym_root_directory }}'
# Path to file containing public identity key.
keys.public_identity_key_file = '{{ storage_paths.keys.public_identity_key_file }}'
# Path to file containing private identity key.
keys.private_sphinx_key_file = '{{ storage_paths.keys.private_sphinx_key_file }}'
# Path to file containing public sphinx key.
keys.public_sphinx_key_file = '{{ storage_paths.keys.public_sphinx_key_file }}'
# Path to sqlite database containing all persistent data: messages for offline clients,
# derived shared keys and available client bandwidths.
persistent_storage = '{{ gateway.persistent_storage }}'
clients_storage = '{{ storage_paths.clients_storage }}'
##### logging configuration options #####
@@ -94,5 +79,4 @@ persistent_storage = '{{ gateway.persistent_storage }}'
# TODO
"#
}
"#;
+10 -7
View File
@@ -19,19 +19,22 @@ pub(crate) enum GatewayError {
source: io::Error,
},
#[error(
"failed to save config file for id {id} using path {path}. detailed message: {source}"
)]
ConfigSaveFailure {
id: String,
path: PathBuf,
#[source]
source: io::Error,
},
#[error("the configured version of the gateway ({config_version}) is incompatible with the binary version ({binary_version})")]
LocalVersionCheckFailure {
binary_version: String,
config_version: String,
},
#[error("another node on the network seems to have identical announce-host ({host})! Their identity is {remote_identity}, while ours is {local_identity}")]
DuplicateNodeHost {
host: String,
local_identity: String,
remote_identity: String,
},
#[error("could not obtain the information about current gateways on the network: {source}")]
NetworkGatewaysQueryFailure {
#[source]
+55 -73
View File
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
use self::storage::PersistentStorage;
use crate::config::persistence::pathfinder::GatewayPathfinder;
use crate::config::Config;
use crate::error::GatewayError;
use crate::node::client_handling::active_clients::ActiveClientsStore;
@@ -37,15 +36,15 @@ pub(crate) async fn create_gateway(config: Config) -> Gateway<PersistentStorage>
}
async fn initialise_storage(config: &Config) -> PersistentStorage {
let path = config.get_persistent_store_path();
let retrieval_limit = config.get_message_retrieval_limit();
let path = &config.storage_paths.clients_storage;
let retrieval_limit = config.debug.message_retrieval_limit;
match PersistentStorage::init(path, retrieval_limit).await {
Err(err) => panic!("failed to initialise gateway storage - {err}"),
Err(err) => panic!("failed to initialise gateway storage: {err}"),
Ok(storage) => storage,
}
}
pub(crate) struct Gateway<St: Storage> {
pub(crate) struct Gateway<St> {
config: Config,
/// ed25519 keypair used to assert one's identity.
identity_keypair: Arc<identity::KeyPair>,
@@ -54,20 +53,14 @@ pub(crate) struct Gateway<St: Storage> {
storage: St,
}
impl<St> Gateway<St>
where
St: Storage + Clone + 'static,
{
impl<St> Gateway<St> {
/// Construct from the given `Config` instance.
pub async fn new(config: Config, storage: St) -> Self {
let pathfinder = GatewayPathfinder::new_from_config(&config);
// let storage = Self::initialise_storage(&config).await;
Gateway {
config,
identity_keypair: Arc::new(Self::load_identity_keys(&pathfinder)),
sphinx_keypair: Arc::new(Self::load_sphinx_keys(&pathfinder)),
storage,
identity_keypair: Arc::new(Self::load_identity_keys(&config)),
sphinx_keypair: Arc::new(Self::load_sphinx_keys(&config)),
config,
}
}
@@ -86,21 +79,23 @@ where
}
}
fn load_identity_keys(pathfinder: &GatewayPathfinder) -> identity::KeyPair {
/// Loads identity keys stored on disk
pub(crate) fn load_identity_keys(config: &Config) -> identity::KeyPair {
let identity_keypair: identity::KeyPair =
nym_pemstore::load_keypair(&nym_pemstore::KeyPairPath::new(
pathfinder.private_identity_key().to_owned(),
pathfinder.public_identity_key().to_owned(),
config.storage_paths.keys.private_identity_key(),
config.storage_paths.keys.public_identity_key(),
))
.expect("Failed to read stored identity key files");
identity_keypair
}
fn load_sphinx_keys(pathfinder: &GatewayPathfinder) -> encryption::KeyPair {
/// Loads Sphinx keys stored on disk
fn load_sphinx_keys(config: &Config) -> encryption::KeyPair {
let sphinx_keypair: encryption::KeyPair =
nym_pemstore::load_keypair(&nym_pemstore::KeyPairPath::new(
pathfinder.private_encryption_key().to_owned(),
pathfinder.public_encryption_key().to_owned(),
config.storage_paths.keys.private_encryption_key(),
config.storage_paths.keys.public_encryption_key(),
))
.expect("Failed to read stored sphinx key files");
sphinx_keypair
@@ -110,16 +105,15 @@ where
let node_details = nym_types::gateway::GatewayNodeDetailsResponse {
identity_key: self.identity_keypair.public_key().to_base58_string(),
sphinx_key: self.sphinx_keypair.public_key().to_base58_string(),
announce_address: self.config.get_announce_address(),
bind_address: self.config.get_listening_address().to_string(),
version: self.config.get_version().to_string(),
mix_port: self.config.get_mix_port(),
clients_port: self.config.get_clients_port(),
bind_address: self.config.gateway.listening_address.to_string(),
version: self.config.gateway.version.clone(),
mix_port: self.config.gateway.mix_port,
clients_port: self.config.gateway.clients_port,
data_store: self
.config
.get_persistent_store_path()
.to_str()
.unwrap_or(".")
.storage_paths
.clients_storage
.display()
.to_string(),
};
@@ -131,7 +125,9 @@ where
ack_sender: MixForwardingSender,
active_clients_store: ActiveClientsStore,
shutdown: TaskClient,
) {
) where
St: Storage + Clone + 'static,
{
info!("Starting mix socket listener...");
let packet_processor =
@@ -145,8 +141,8 @@ where
);
let listening_address = SocketAddr::new(
self.config.get_listening_address(),
self.config.get_mix_port(),
self.config.gateway.listening_address,
self.config.gateway.mix_port,
);
mixnet_handling::Listener::new(listening_address, shutdown).start(connection_handler);
@@ -158,18 +154,20 @@ where
active_clients_store: ActiveClientsStore,
shutdown: TaskClient,
coconut_verifier: Arc<CoconutVerifier>,
) {
) where
St: Storage + Clone + 'static,
{
info!("Starting client [web]socket listener...");
let listening_address = SocketAddr::new(
self.config.get_listening_address(),
self.config.get_clients_port(),
self.config.gateway.listening_address,
self.config.gateway.clients_port,
);
websocket::Listener::new(
listening_address,
Arc::clone(&self.identity_keypair),
self.config.get_only_coconut_credentials(),
self.config.gateway.only_coconut_credentials,
coconut_verifier,
)
.start(
@@ -184,11 +182,11 @@ where
info!("Starting mix packet forwarder...");
let (mut packet_forwarder, packet_sender) = PacketForwarder::new(
self.config.get_packet_forwarding_initial_backoff(),
self.config.get_packet_forwarding_maximum_backoff(),
self.config.get_initial_connection_timeout(),
self.config.get_maximum_connection_buffer_size(),
self.config.get_use_legacy_sphinx_framing(),
self.config.debug.packet_forwarding_initial_backoff,
self.config.debug.packet_forwarding_maximum_backoff,
self.config.debug.initial_connection_timeout,
self.config.debug.maximum_connection_buffer_size,
self.config.debug.use_legacy_framed_packet_version,
shutdown,
);
@@ -236,48 +234,32 @@ where
client
}
async fn check_if_same_ip_gateway_exists(&self) -> Result<Option<String>, GatewayError> {
async fn check_if_bonded(&self) -> Result<bool, GatewayError> {
// TODO: if anything, this should be getting data directly from the contract
// as opposed to the validator API
let validator_client = self.random_api_client();
let existing_gateways = match validator_client.get_cached_gateways().await {
Ok(gateways) => gateways,
let existing_nodes = match validator_client.get_cached_gateways().await {
Ok(nodes) => nodes,
Err(err) => {
error!("failed to grab initial network gateways - {err}\n Please try to startup again in few minutes");
return Err(GatewayError::NetworkGatewaysQueryFailure { source: err });
}
};
let our_host = self.config.get_announce_address();
Ok(existing_gateways
.iter()
.find(|node| node.gateway.host == our_host)
.map(|node| node.gateway().identity_key.clone()))
Ok(existing_nodes.iter().any(|node| {
node.gateway.identity_key == self.identity_keypair.public_key().to_base58_string()
}))
}
async fn ensure_no_duplicate_host_exists(&self) -> Result<(), GatewayError> {
let local_identity = self.identity_keypair.public_key().to_base58_string();
if let Some(remote_identity) = self.check_if_same_ip_gateway_exists().await? {
if remote_identity == local_identity {
warn!("We seem to have not unregistered after going offline - there's a node with identical identity and announce-host as us registered.")
} else {
error!(
"Our announce-host is identical to an existing node's announce-host! (its key is {remote_identity})",
);
return Err(GatewayError::DuplicateNodeHost {
host: self.config.get_announce_address(),
local_identity,
remote_identity,
});
}
}
Ok(())
}
pub async fn run(&mut self) -> Result<(), Box<dyn Error + Send + Sync>> {
pub async fn run(&mut self) -> Result<(), Box<dyn Error + Send + Sync>>
where
St: Storage + Clone + 'static,
{
info!("Starting nym gateway!");
self.ensure_no_duplicate_host_exists().await?;
if self.check_if_bonded().await? {
warn!("You seem to have bonded your gateway before starting it - that's highly unrecommended as in the future it might result in slashing");
}
let shutdown = TaskManager::new(10);
@@ -295,7 +277,7 @@ where
shutdown.subscribe(),
);
if self.config.get_enabled_statistics() {
if self.config.gateway.enabled_statistics {
let statistics_service_url = self.config.get_statistics_service_url();
let stats_collector = GatewayStatisticsCollector::new(
self.identity_keypair.public_key().to_base58_string(),
+2 -16
View File
@@ -1,29 +1,15 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::{override_config, OverrideConfig};
use crate::commands::{override_config, try_load_current_config, OverrideConfig};
use crate::config::Config;
use crate::error::GatewayError;
use log::error;
use nym_config::NymConfig;
pub(crate) fn build_config<O: Into<OverrideConfig>>(
id: String,
override_args: O,
) -> Result<Config, GatewayError> {
let config = match Config::load_from_file(&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(GatewayError::ConfigLoadFailure {
path: Config::default_config_file_path(&id),
id,
source: err,
});
}
};
let config = try_load_current_config(&id)?;
override_config(config, override_args.into())
}
+5 -15
View File
@@ -1,11 +1,10 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::Config;
use crate::commands::try_load_current_config;
use crate::node::node_description::NodeDescription;
use clap::Args;
use colored::Colorize;
use nym_config::NymConfig;
use std::io;
use std::io::Write;
@@ -39,15 +38,9 @@ fn read_user_input() -> String {
buf.trim().to_string()
}
pub(crate) fn execute(args: Describe) {
pub(crate) fn execute(args: Describe) -> anyhow::Result<()> {
// ensure that the mixnode has in fact been initialized
match Config::load_from_file(&args.id) {
Ok(cfg) => cfg,
Err(err) => {
error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {err})", &args.id);
return;
}
};
let config = try_load_current_config(&args.id)?;
let example_url = "https://mixnode.yourdomain.com".bright_cyan();
let example_location = "City: London, Country: UK";
@@ -81,9 +74,6 @@ pub(crate) fn execute(args: Describe) {
};
// save the struct
NodeDescription::save_to_file(
&node_description,
Config::default_config_directory(&args.id),
)
.unwrap()
NodeDescription::save_to_file(&node_description, config.storage_paths.node_description)?;
Ok(())
}
+30 -27
View File
@@ -2,15 +2,16 @@
// SPDX-License-Identifier: Apache-2.0
use super::OverrideConfig;
use crate::config::Config;
use crate::commands::override_config;
use crate::config::{
default_config_directory, default_config_filepath, default_data_directory, Config,
};
use crate::node::MixNode;
use crate::{commands::override_config, config::persistence::pathfinder::MixNodePathfinder};
use clap::Args;
use nym_bin_common::output_format::OutputFormat;
use nym_config::NymConfig;
use nym_crypto::asymmetric::{encryption, identity};
use nym_validator_client::nyxd;
use std::net::IpAddr;
use std::{fs, io};
#[derive(Args, Clone)]
pub(crate) struct Init {
@@ -22,10 +23,6 @@ pub(crate) struct Init {
#[clap(long)]
host: IpAddr,
/// The wallet address you will use to bond this mixnode, e.g. nymt1z9egw0knv47nmur0p8vk4rcx59h9gg4zuxrrr9
#[clap(long)]
wallet_address: nyxd::AccountId,
/// The port on which the mixnode will be listening for mix packets
#[clap(long)]
mix_port: Option<u16>,
@@ -38,10 +35,6 @@ pub(crate) struct Init {
#[clap(long)]
http_api_port: Option<u16>,
/// The custom host that will be reported to the directory server
#[clap(long)]
announce_host: Option<String>,
/// Comma separated list of nym-api endpoints of the validators
// the alias here is included for backwards compatibility (1.1.4 and before)
#[clap(long, alias = "validators", value_delimiter = ',')]
@@ -56,29 +49,33 @@ impl From<Init> for OverrideConfig {
OverrideConfig {
id: init_config.id,
host: Some(init_config.host),
wallet_address: Some(init_config.wallet_address),
mix_port: init_config.mix_port,
verloc_port: init_config.verloc_port,
http_api_port: init_config.http_api_port,
announce_host: init_config.announce_host,
nym_apis: init_config.nym_apis,
}
}
}
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) fn execute(args: &Init) {
let override_config_fields = OverrideConfig::from(args.clone());
let id = &override_config_fields.id;
let id = override_config_fields.id.clone();
eprintln!("Initialising mixnode {id}...");
let already_init = if Config::default_config_file_path(id).exists() {
let already_init = if default_config_filepath(&id).exists() {
eprintln!("Mixnode \"{id}\" was already initialised before! Config information will be overwritten (but keys will be kept)!");
true
} else {
init_paths(&id).expect("failed to initialise storage paths");
false
};
let mut config = Config::new(id);
let mut config = Config::new(&id);
config = override_config(config, override_config_fields);
// if node was already initialised, don't generate new keys
@@ -87,12 +84,12 @@ pub(crate) fn execute(args: &Init) {
let identity_keys = identity::KeyPair::new(&mut rng);
let sphinx_keys = encryption::KeyPair::new(&mut rng);
let pathfinder = MixNodePathfinder::new_from_config(&config);
nym_pemstore::store_keypair(
&identity_keys,
&nym_pemstore::KeyPairPath::new(
pathfinder.private_identity_key().to_owned(),
pathfinder.public_identity_key().to_owned(),
config.storage_paths.private_identity_key(),
config.storage_paths.public_identity_key(),
),
)
.expect("Failed to save identity keys");
@@ -100,19 +97,25 @@ pub(crate) fn execute(args: &Init) {
nym_pemstore::store_keypair(
&sphinx_keys,
&nym_pemstore::KeyPairPath::new(
pathfinder.private_encryption_key().to_owned(),
pathfinder.public_encryption_key().to_owned(),
config.storage_paths.private_encryption_key(),
config.storage_paths.public_encryption_key(),
),
)
.expect("Failed to save sphinx keys");
eprintln!("Saved mixnet identity and sphinx keypairs");
}
let config_save_location = config.get_config_file_save_location();
config
.save_to_file(None)
.expect("Failed to save the config file");
eprintln!("Saved configuration file to {config_save_location:?}");
let config_save_location = config.default_location();
config.save_to_default_location().unwrap_or_else(|_| {
panic!(
"Failed to save the config file to {}",
config_save_location.display()
)
});
eprintln!(
"Saved configuration file to {}",
config_save_location.display()
);
eprintln!("Mixnode configuration completed.\n\n\n");
MixNode::new(config).print_node_details(args.output)
+52 -32
View File
@@ -1,7 +1,9 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::old_config_v1_1_20::ConfigV1_1_20;
use crate::{config::Config, Cli};
use anyhow::anyhow;
use clap::CommandFactory;
use clap::Subcommand;
use colored::Colorize;
@@ -10,7 +12,6 @@ use nym_bin_common::version_checker;
use nym_config::defaults::var_names::{BECH32_PREFIX, NYM_API};
use nym_config::OptionalSet;
use nym_crypto::bech32_address_validation;
use nym_validator_client::nyxd;
use std::net::IpAddr;
use std::process;
@@ -52,45 +53,31 @@ pub(crate) enum Commands {
struct OverrideConfig {
id: String,
host: Option<IpAddr>,
wallet_address: Option<nyxd::AccountId>,
mix_port: Option<u16>,
verloc_port: Option<u16>,
http_api_port: Option<u16>,
announce_host: Option<String>,
nym_apis: Option<Vec<url::Url>>,
}
pub(crate) async fn execute(args: Cli) {
pub(crate) async fn execute(args: Cli) -> anyhow::Result<()> {
let bin_name = "nym-mixnode";
match args.command {
Commands::Describe(m) => describe::execute(m),
Commands::Describe(m) => describe::execute(m)?,
Commands::Init(m) => init::execute(&m),
Commands::Run(m) => run::execute(&m).await,
Commands::Sign(m) => sign::execute(&m),
Commands::Upgrade(m) => upgrade::execute(&m),
Commands::NodeDetails(m) => node_details::execute(&m),
Commands::Run(m) => run::execute(&m).await?,
Commands::Sign(m) => sign::execute(&m)?,
Commands::Upgrade(m) => upgrade::execute(&m)?,
Commands::NodeDetails(m) => node_details::execute(&m)?,
Commands::Completions(s) => s.generate(&mut crate::Cli::command(), bin_name),
Commands::GenerateFigSpec => fig_generate(&mut crate::Cli::command(), bin_name),
}
Ok(())
}
fn override_config(mut config: Config, args: OverrideConfig) -> Config {
// special case that I'm not sure could be easily handled with the trait
let mut was_host_overridden = false;
if let Some(host) = args.host {
config = config.with_listening_address(host);
was_host_overridden = true;
}
if let Some(announce_host) = args.announce_host {
config = config.with_announce_address(announce_host);
} else if was_host_overridden {
// make sure our 'announce-host' always defaults to 'host'
config = config.announce_address_from_listening_address()
}
fn override_config(config: Config, args: OverrideConfig) -> Config {
config
.with_optional(Config::with_listening_address, args.host)
.with_optional(Config::with_mix_port, args.mix_port)
.with_optional(Config::with_verloc_port, args.verloc_port)
.with_optional(Config::with_http_api_port, args.http_api_port)
@@ -100,13 +87,6 @@ fn override_config(mut config: Config, args: OverrideConfig) -> Config {
NYM_API,
nym_config::parse_urls,
)
.with_optional(
|cfg, wallet_address| {
validate_bech32_address_or_exit(wallet_address.as_ref());
cfg.with_wallet_address(wallet_address)
},
args.wallet_address,
)
}
/// Ensures that a given bech32 address is valid, or exits
@@ -135,7 +115,7 @@ pub(crate) fn validate_bech32_address_or_exit(address: &str) {
// network version. It might do so in the future.
pub(crate) fn version_check(cfg: &Config) -> bool {
let binary_version = env!("CARGO_PKG_VERSION");
let config_version = cfg.get_version();
let config_version = &cfg.mixnode.version;
if binary_version == config_version {
true
} else {
@@ -149,3 +129,43 @@ pub(crate) fn version_check(cfg: &Config) -> bool {
}
}
}
fn try_upgrade_v1_1_20_config(id: &str) -> std::io::Result<()> {
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
// explicitly load it as v1.1.20 (which is incompatible with the current, 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(());
};
info!("It seems the mixnode is using <= v1.1.20 config template.");
info!("It is going to get updated to the current specification.");
let updated: Config = old_config.into();
updated.save_to_default_location()
}
fn try_load_current_config(id: &str) -> anyhow::Result<Config> {
try_upgrade_v1_1_20_config(id)?;
Config::read_from_default_path(id).map_err(|err| {
let error_msg =
format!(
"Failed to load config for {id}. Are you sure you have run `init` before? (Error was: {err})",
);
error!("{error_msg}");
anyhow!(error_msg)
})
}
#[cfg(test)]
mod tests {
use super::*;
use clap::CommandFactory;
#[test]
fn verify_cli() {
Cli::command().debug_assert();
}
}
+5 -15
View File
@@ -1,11 +1,10 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::Config;
use crate::commands::try_load_current_config;
use crate::node::MixNode;
use clap::Args;
use nym_bin_common::output_format::OutputFormat;
use nym_config::NymConfig;
#[derive(Args)]
pub(crate) struct NodeDetails {
@@ -17,18 +16,9 @@ pub(crate) struct NodeDetails {
output: OutputFormat,
}
pub(crate) fn execute(args: &NodeDetails) {
let config = match Config::load_from_file(&args.id) {
Ok(cfg) => cfg,
Err(err) => {
error!(
"Failed to load config for {}. Are you sure you have run `init` before? (Error was: {})",
args.id,
err,
);
return;
}
};
pub(crate) fn execute(args: &NodeDetails) -> anyhow::Result<()> {
let config = try_load_current_config(&args.id)?;
MixNode::new(config).print_node_details(args.output)
MixNode::new(config).print_node_details(args.output);
Ok(())
}
+11 -31
View File
@@ -2,12 +2,12 @@
// SPDX-License-Identifier: Apache-2.0
use super::OverrideConfig;
use crate::commands::{override_config, version_check};
use crate::config::Config;
use crate::commands::{override_config, try_load_current_config, version_check};
use crate::node::MixNode;
use anyhow::bail;
use clap::Args;
use nym_bin_common::output_format::OutputFormat;
use nym_config::NymConfig;
use nym_config::helpers::SPECIAL_ADDRESSES;
use nym_validator_client::nyxd;
use std::net::IpAddr;
@@ -37,10 +37,6 @@ pub(crate) struct Run {
#[clap(long)]
http_api_port: Option<u16>,
/// The host that will be reported to the directory server
#[clap(long)]
announce_host: Option<String>,
/// Comma separated list of nym-api endpoints of the validators
// the alias here is included for backwards compatibility (1.1.4 and before)
#[clap(long, alias = "validators", value_delimiter = ',')]
@@ -55,11 +51,9 @@ impl From<Run> for OverrideConfig {
OverrideConfig {
id: run_config.id,
host: run_config.host,
wallet_address: run_config.wallet_address,
mix_port: run_config.mix_port,
verloc_port: run_config.verloc_port,
http_api_port: run_config.http_api_port,
announce_host: run_config.announce_host,
nym_apis: run_config.nym_apis,
}
}
@@ -70,39 +64,24 @@ fn show_binding_warning(address: &str) {
eprintln!(
"\nYou are trying to bind to {address} - you might not be accessible to other nodes\n\
You can ignore this note if you're running setup on a local network \n\
or have set a custom 'announce-host'"
or have used different host when bonding your node"
);
eprintln!("\n\n");
}
fn special_addresses() -> Vec<&'static str> {
vec!["localhost", "127.0.0.1", "0.0.0.0", "::1", "[::1]"]
}
pub(crate) async fn execute(args: &Run) {
pub(crate) async fn execute(args: &Run) -> anyhow::Result<()> {
eprintln!("Starting mixnode {}...", args.id);
let mut config = match Config::load_from_file(&args.id) {
Ok(cfg) => cfg,
Err(err) => {
error!(
"Failed to load config for {}. Are you sure you have run `init` before? (Error was: {})",
args.id,
err
);
return;
}
};
let mut config = try_load_current_config(&args.id)?;
config = override_config(config, OverrideConfig::from(args.clone()));
if !version_check(&config) {
error!("failed the local version check");
return;
bail!("failed the local version check")
}
if special_addresses().contains(&&*config.get_listening_address().to_string()) {
show_binding_warning(&config.get_listening_address().to_string());
if SPECIAL_ADDRESSES.contains(&config.mixnode.listening_address) {
show_binding_warning(&config.mixnode.listening_address.to_string());
}
let mut mixnode = MixNode::new(config);
@@ -112,5 +91,6 @@ pub(crate) async fn execute(args: &Run) {
Select the correct version and install it to your machine. You will need to provide the following: \n ");
mixnode.print_node_details(args.output);
mixnode.run().await
mixnode.run().await;
Ok(())
}
+10 -21
View File
@@ -1,18 +1,16 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::convert::TryFrom;
use crate::commands::validate_bech32_address_or_exit;
use crate::config::{persistence::pathfinder::MixNodePathfinder, Config};
use crate::commands::{try_load_current_config, validate_bech32_address_or_exit};
use crate::node::MixNode;
use anyhow::{bail, Result};
use clap::{ArgGroup, Args};
use nym_bin_common::output_format::OutputFormat;
use nym_config::NymConfig;
use nym_crypto::asymmetric::identity;
use nym_types::helpers::ConsoleSigningOutput;
use nym_validator_client::nyxd;
use std::convert::TryFrom;
#[cfg(feature = "cpucycles")]
use tracing::error;
@@ -116,32 +114,22 @@ fn print_signed_contract_msg(
println!("{}", output.format(&sign_output));
}
pub(crate) fn execute(args: &Sign) {
let config = match Config::load_from_file(&args.id) {
Ok(cfg) => cfg,
Err(err) => {
error!(
"Failed to load config for {}. Are you sure you have run `init` before? (Error was: {err})",
args.id,
);
return;
}
};
pub(crate) fn execute(args: &Sign) -> anyhow::Result<()> {
let config = try_load_current_config(&args.id)?;
if !version_check(&config) {
error!("Failed the local version check");
return;
error!("failed the local version check");
bail!("failed the local version check")
}
let signed_target = match SignedTarget::try_from(args.clone()) {
Ok(s) => s,
Err(err) => {
error!("{err}");
return;
bail!(err);
}
};
let pathfinder = MixNodePathfinder::new_from_config(&config);
let identity_keypair = MixNode::load_identity_keys(&pathfinder);
let identity_keypair = MixNode::load_identity_keys(&config);
match signed_target {
SignedTarget::Text(text) => {
@@ -154,4 +142,5 @@ pub(crate) fn execute(args: &Sign) {
print_signed_contract_msg(identity_keypair.private_key(), &raw_msg, args.output)
}
}
Ok(())
}
+18 -13
View File
@@ -1,10 +1,10 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::{missing_string_value, Config};
use crate::commands::try_upgrade_v1_1_20_config;
use crate::config::Config;
use clap::Args;
use nym_bin_common::version_checker::Version;
use nym_config::NymConfig;
use std::fmt::Display;
use std::process;
@@ -46,7 +46,7 @@ fn unsupported_upgrade(config_version: &Version, package_version: &Version) -> !
}
fn parse_config_version(config: &Config) -> Version {
let version = Version::parse(config.get_version()).unwrap_or_else(|err| {
let version = Version::parse(&config.mixnode.version).unwrap_or_else(|err| {
eprintln!("failed to parse client version! - {err}");
process::exit(1)
});
@@ -89,13 +89,15 @@ fn minor_0_12_upgrade(
print_start_upgrade(config_version, &to_version);
let upgraded_config = config.with_custom_version(to_version.to_string().as_ref());
let upgraded_config = config.with_custom_version(to_version.to_string());
upgraded_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);
});
upgraded_config
.save_to_default_location()
.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);
@@ -122,18 +124,21 @@ fn do_upgrade(mut config: Config, args: &Upgrade, package_version: Version) {
}
}
pub(crate) fn execute(args: &Upgrade) {
pub(crate) fn execute(args: &Upgrade) -> anyhow::Result<()> {
try_upgrade_v1_1_20_config(&args.id)?;
let package_version = parse_package_version();
let existing_config = Config::load_from_file(&args.id).unwrap_or_else(|err| {
let existing_config = Config::read_from_default_path(&args.id).unwrap_or_else(|err| {
eprintln!("failed to load existing config file! - {err}");
process::exit(1)
});
if existing_config.get_version() == missing_string_value::<String>() {
if existing_config.mixnode.version.is_empty() {
eprintln!("the existing configuration file does not seem to contain version number.");
process::exit(1);
}
do_upgrade(existing_config, args, package_version)
do_upgrade(existing_config, args, package_version);
Ok(())
}
+108 -322
View File
@@ -1,24 +1,31 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::template::config_template;
use nym_config::defaults::mainnet::NYM_API;
use crate::config::persistence::paths::MixNodePaths;
use crate::config::template::CONFIG_TEMPLATE;
use nym_bin_common::logging::LoggingSettings;
use nym_config::defaults::{
DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT,
mainnet, DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT,
DEFAULT_VERLOC_LISTENING_PORT,
};
use nym_config::NymConfig;
use nym_validator_client::nyxd;
use serde::{Deserialize, Deserializer, Serialize};
use std::net::{IpAddr, SocketAddr};
use std::path::PathBuf;
use nym_config::helpers::inaddr_any;
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::io;
use std::net::IpAddr;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::time::Duration;
use url::Url;
pub(crate) mod old_config_v1_1_20;
pub mod persistence;
mod template;
pub(crate) const MISSING_VALUE: &str = "MISSING VALUE";
const DEFAULT_MIXNODES_DIR: &str = "mixnodes";
// 'RTT MEASUREMENT'
const DEFAULT_PACKETS_PER_NODE: usize = 100;
@@ -37,125 +44,84 @@ const DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF: Duration = Duration::from_milli
const DEFAULT_INITIAL_CONNECTION_TIMEOUT: Duration = Duration::from_millis(1_500);
const DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE: usize = 2000;
pub fn missing_string_value<T: From<String>>() -> T {
MISSING_VALUE.to_string().into()
/// Derive default path to mixnodes's config directory.
/// It should get resolved to `$HOME/.nym/mixnodes/<id>/config`
pub fn default_config_directory<P: AsRef<Path>>(id: P) -> PathBuf {
must_get_home()
.join(NYM_DIR)
.join(DEFAULT_MIXNODES_DIR)
.join(id)
.join(DEFAULT_CONFIG_DIR)
}
fn bind_all_address() -> IpAddr {
"0.0.0.0".parse().unwrap()
/// Derive default path to mixnodes's config file.
/// It should get resolved to `$HOME/.nym/mixnodes/<id>/config/config.toml`
pub fn default_config_filepath<P: AsRef<Path>>(id: P) -> PathBuf {
default_config_directory(id).join(DEFAULT_CONFIG_FILENAME)
}
fn default_mix_port() -> u16 {
DEFAULT_MIX_LISTENING_PORT
/// Derive default path to mixnodes's data directory where files, such as keys, are stored.
/// It should get resolved to `$HOME/.nym/mixnodes/<id>/data`
pub fn default_data_directory<P: AsRef<Path>>(id: P) -> PathBuf {
must_get_home()
.join(NYM_DIR)
.join(DEFAULT_MIXNODES_DIR)
.join(id)
.join(DEFAULT_DATA_DIR)
}
fn default_verloc_port() -> u16 {
DEFAULT_VERLOC_LISTENING_PORT
}
fn default_http_api_port() -> u16 {
DEFAULT_HTTP_API_LISTENING_PORT
}
// basically a migration helper that deserialises string representation of a maybe socket addr (like "1.1.1.1:1234")
// into just the ipaddr (like "1.1.1.1")
pub(super) fn de_ipaddr_from_maybe_str_socks_addr<'de, D>(
deserializer: D,
) -> Result<IpAddr, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
if let Ok(socket_addr) = SocketAddr::from_str(&s) {
Ok(socket_addr.ip())
} else {
IpAddr::from_str(&s).map_err(serde::de::Error::custom)
}
}
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Config {
mixnode: MixNode,
pub mixnode: MixNode,
pub storage_paths: MixNodePaths,
#[serde(default)]
verloc: Verloc,
pub verloc: Verloc,
#[serde(default)]
logging: Logging,
pub logging: LoggingSettings,
#[serde(default)]
debug: Debug,
pub debug: Debug,
}
impl NymConfig for Config {
impl NymConfigTemplate for Config {
fn template() -> &'static str {
config_template()
}
fn default_root_directory() -> PathBuf {
dirs::home_dir()
.expect("Failed to evaluate $HOME value")
.join(".nym")
.join("mixnodes")
}
fn try_default_root_directory() -> Option<PathBuf> {
dirs::home_dir().map(|path| path.join(".nym").join("mixnodes"))
}
fn root_directory(&self) -> PathBuf {
self.mixnode.nym_root_directory.clone()
}
fn config_directory(&self) -> PathBuf {
self.mixnode
.nym_root_directory
.join(&self.mixnode.id)
.join("config")
}
fn data_directory(&self) -> PathBuf {
self.mixnode
.nym_root_directory
.join(&self.mixnode.id)
.join("data")
CONFIG_TEMPLATE
}
}
impl Config {
pub fn new<S: Into<String>>(id: S) -> Self {
Config::default().with_id(id)
pub fn new<S: AsRef<str>>(id: S) -> Self {
Config {
mixnode: MixNode::new_default(id.as_ref()),
storage_paths: MixNodePaths::new_default(id.as_ref()),
verloc: Default::default(),
logging: Default::default(),
debug: 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.mixnode.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)
}
// builder methods
pub fn with_id<S: Into<String>>(mut self, id: S) -> Self {
let id = id.into();
if self
.mixnode
.private_identity_key_file
.as_os_str()
.is_empty()
{
self.mixnode.private_identity_key_file =
self::MixNode::default_private_identity_key_file(&id);
}
if self.mixnode.public_identity_key_file.as_os_str().is_empty() {
self.mixnode.public_identity_key_file =
self::MixNode::default_public_identity_key_file(&id);
}
if self.mixnode.private_sphinx_key_file.as_os_str().is_empty() {
self.mixnode.private_sphinx_key_file =
self::MixNode::default_private_sphinx_key_file(&id);
}
if self.mixnode.public_sphinx_key_file.as_os_str().is_empty() {
self.mixnode.public_sphinx_key_file =
self::MixNode::default_public_sphinx_key_file(&id);
}
self.mixnode.id = id;
self
}
pub fn with_custom_nym_apis(mut self, nym_api_urls: Vec<Url>) -> Self {
self.mixnode.nym_api_urls = nym_api_urls;
self
@@ -166,11 +132,6 @@ impl Config {
self
}
pub fn with_announce_address<S: Into<String>>(mut self, announce_address: S) -> Self {
self.mixnode.announce_address = announce_address.into();
self
}
pub fn with_mix_port(mut self, port: u16) -> Self {
self.mixnode.mix_port = port;
self
@@ -186,261 +147,86 @@ impl Config {
self
}
pub fn announce_address_from_listening_address(mut self) -> Self {
self.mixnode.announce_address = self.mixnode.listening_address.to_string();
pub fn with_custom_version<S: Into<String>>(mut self, version: S) -> Self {
self.mixnode.version = version.into();
self
}
pub fn with_custom_version(mut self, version: &str) -> Self {
self.mixnode.version = version.to_string();
self
}
pub fn with_wallet_address(mut self, wallet_address: nyxd::AccountId) -> Self {
self.mixnode.wallet_address = Some(wallet_address);
self
}
// getters
pub fn get_id(&self) -> String {
self.mixnode.id.clone()
}
pub fn get_config_file_save_location(&self) -> PathBuf {
self.config_directory().join(Self::config_file_name())
}
pub fn get_private_identity_key_file(&self) -> PathBuf {
self.mixnode.private_identity_key_file.clone()
}
pub fn get_public_identity_key_file(&self) -> PathBuf {
self.mixnode.public_identity_key_file.clone()
}
pub fn get_private_sphinx_key_file(&self) -> PathBuf {
self.mixnode.private_sphinx_key_file.clone()
}
pub fn get_public_sphinx_key_file(&self) -> PathBuf {
self.mixnode.public_sphinx_key_file.clone()
}
pub fn get_nym_api_endpoints(&self) -> Vec<Url> {
self.mixnode.nym_api_urls.clone()
}
pub fn get_node_stats_logging_delay(&self) -> Duration {
self.debug.node_stats_logging_delay
}
pub fn get_node_stats_updating_delay(&self) -> Duration {
self.debug.node_stats_updating_delay
}
pub fn get_listening_address(&self) -> IpAddr {
self.mixnode.listening_address
}
pub fn get_announce_address(&self) -> String {
self.mixnode.announce_address.clone()
}
pub fn get_mix_port(&self) -> u16 {
self.mixnode.mix_port
}
pub fn get_verloc_port(&self) -> u16 {
self.mixnode.verloc_port
}
pub fn get_http_api_port(&self) -> u16 {
self.mixnode.http_api_port
}
pub fn get_packet_forwarding_initial_backoff(&self) -> Duration {
self.debug.packet_forwarding_initial_backoff
}
pub fn get_packet_forwarding_maximum_backoff(&self) -> Duration {
self.debug.packet_forwarding_maximum_backoff
}
pub fn get_initial_connection_timeout(&self) -> Duration {
self.debug.initial_connection_timeout
}
pub fn get_maximum_connection_buffer_size(&self) -> usize {
self.debug.maximum_connection_buffer_size
}
pub fn get_use_legacy_sphinx_framing(&self) -> bool {
self.debug.use_legacy_framed_packet_version
}
pub fn get_version(&self) -> &str {
&self.mixnode.version
}
pub fn get_measurement_packets_per_node(&self) -> usize {
self.verloc.packets_per_node
}
pub fn get_measurement_packet_timeout(&self) -> Duration {
self.verloc.packet_timeout
}
pub fn get_measurement_connection_timeout(&self) -> Duration {
self.verloc.connection_timeout
}
pub fn get_measurement_delay_between_packets(&self) -> Duration {
self.verloc.delay_between_packets
}
pub fn get_measurement_tested_nodes_batch_size(&self) -> usize {
self.verloc.tested_nodes_batch_size
}
pub fn get_measurement_testing_interval(&self) -> Duration {
self.verloc.testing_interval
}
pub fn get_measurement_retry_timeout(&self) -> Duration {
self.verloc.retry_timeout
}
pub fn get_wallet_address(&self) -> Option<nyxd::AccountId> {
self.mixnode.wallet_address.clone()
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
struct MixNode {
pub struct MixNode {
/// Version of the mixnode for which this configuration was created.
#[serde(default = "missing_string_value")]
version: String,
pub version: String,
/// ID specifies the human readable ID of this particular mixnode.
id: String,
pub id: String,
/// Address to which this mixnode will bind to and will be listening for packets.
#[serde(deserialize_with = "de_ipaddr_from_maybe_str_socks_addr")]
listening_address: IpAddr,
/// Optional address announced to the validator for the clients to connect to.
/// It is useful, say, in NAT scenarios or wanting to more easily update actual IP address
/// later on by using name resolvable with a DNS query, such as `nymtech.net`.
announce_address: String,
pub listening_address: IpAddr,
/// Port used for listening for all mixnet traffic.
/// (default: 1789)
#[serde(default = "default_mix_port")]
mix_port: u16,
pub mix_port: u16,
/// Port used for listening for verloc traffic.
/// (default: 1790)
#[serde(default = "default_verloc_port")]
verloc_port: u16,
pub verloc_port: u16,
/// Port used for listening for http requests.
/// (default: 8000)
#[serde(default = "default_http_api_port")]
http_api_port: u16,
/// Path to file containing private identity key.
#[serde(default = "missing_string_value")]
private_identity_key_file: PathBuf,
/// Path to file containing public identity key.
#[serde(default = "missing_string_value")]
public_identity_key_file: PathBuf,
/// Path to file containing private sphinx key.
private_sphinx_key_file: PathBuf,
/// Path to file containing public sphinx key.
public_sphinx_key_file: PathBuf,
pub http_api_port: u16,
/// Addresses to nym APIs from which the node gets the view of the network.
nym_api_urls: Vec<Url>,
/// nym_home_directory specifies absolute path to the home nym MixNodes directory.
/// It is expected to use default value and hence .toml file should not redefine this field.
nym_root_directory: PathBuf,
/// The Cosmos wallet address that will control this mixnode
// the only reason this is an Option is because of the lack of existence of a sane default value
wallet_address: Option<nyxd::AccountId>,
pub nym_api_urls: Vec<Url>,
}
impl MixNode {
fn default_private_identity_key_file(id: &str) -> PathBuf {
Config::default_data_directory(id).join("private_identity.pem")
}
fn default_public_identity_key_file(id: &str) -> PathBuf {
Config::default_data_directory(id).join("public_identity.pem")
}
fn default_private_sphinx_key_file(id: &str) -> PathBuf {
Config::default_data_directory(id).join("private_sphinx.pem")
}
fn default_public_sphinx_key_file(id: &str) -> PathBuf {
Config::default_data_directory(id).join("public_sphinx.pem")
}
}
impl Default for MixNode {
fn default() -> Self {
pub fn new_default<S: Into<String>>(id: S) -> Self {
MixNode {
version: env!("CARGO_PKG_VERSION").to_string(),
id: "".to_string(),
listening_address: bind_all_address(),
announce_address: "127.0.0.1".to_string(),
id: id.into(),
listening_address: inaddr_any(),
mix_port: DEFAULT_MIX_LISTENING_PORT,
verloc_port: DEFAULT_VERLOC_LISTENING_PORT,
http_api_port: DEFAULT_HTTP_API_LISTENING_PORT,
private_identity_key_file: Default::default(),
public_identity_key_file: Default::default(),
private_sphinx_key_file: Default::default(),
public_sphinx_key_file: Default::default(),
nym_api_urls: vec![Url::from_str(NYM_API).expect("Invalid default API URL")],
nym_root_directory: Config::default_root_directory(),
wallet_address: None,
nym_api_urls: vec![Url::from_str(mainnet::NYM_API).expect("Invalid default API URL")],
}
}
}
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
struct Logging {}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
struct Verloc {
pub struct Verloc {
/// Specifies number of echo packets sent to each node during a measurement run.
packets_per_node: usize,
pub packets_per_node: usize,
/// Specifies maximum amount of time to wait for the connection to get established.
connection_timeout: Duration,
#[serde(with = "humantime_serde")]
pub connection_timeout: Duration,
/// Specifies maximum amount of time to wait for the reply packet to arrive before abandoning the test.
packet_timeout: Duration,
#[serde(with = "humantime_serde")]
pub packet_timeout: Duration,
/// Specifies delay between subsequent test packets being sent (after receiving a reply).
delay_between_packets: Duration,
#[serde(with = "humantime_serde")]
pub delay_between_packets: Duration,
/// Specifies number of nodes being tested at once.
tested_nodes_batch_size: usize,
pub tested_nodes_batch_size: usize,
/// Specifies delay between subsequent test runs.
testing_interval: Duration,
#[serde(with = "humantime_serde")]
pub testing_interval: Duration,
/// Specifies delay between attempting to run the measurement again if the previous run failed
/// due to being unable to get the list of nodes.
retry_timeout: Duration,
#[serde(with = "humantime_serde")]
pub retry_timeout: Duration,
}
impl Default for Verloc {
@@ -459,37 +245,37 @@ impl Default for Verloc {
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(default)]
struct Debug {
pub struct Debug {
/// Delay between each subsequent node statistics being logged to the console
#[serde(with = "humantime_serde")]
node_stats_logging_delay: Duration,
pub node_stats_logging_delay: Duration,
/// Delay between each subsequent node statistics being updated
#[serde(with = "humantime_serde")]
node_stats_updating_delay: Duration,
pub node_stats_updating_delay: Duration,
/// Initial value of an exponential backoff to reconnect to dropped TCP connection when
/// forwarding sphinx packets.
#[serde(with = "humantime_serde")]
packet_forwarding_initial_backoff: Duration,
pub packet_forwarding_initial_backoff: Duration,
/// Maximum value of an exponential backoff to reconnect to dropped TCP connection when
/// forwarding sphinx packets.
#[serde(with = "humantime_serde")]
packet_forwarding_maximum_backoff: Duration,
pub packet_forwarding_maximum_backoff: Duration,
/// Timeout for establishing initial connection when trying to forward a sphinx packet.
#[serde(with = "humantime_serde")]
initial_connection_timeout: Duration,
pub initial_connection_timeout: Duration,
/// Maximum number of packets that can be stored waiting to get sent to a particular connection.
maximum_connection_buffer_size: usize,
pub maximum_connection_buffer_size: usize,
/// Specifies whether the mixnode should be using the legacy framing for the sphinx packets.
// it's set to true by default. The reason for that decision is to preserve compatibility with the
// existing nodes whilst everyone else is upgrading and getting the code for handling the new field.
// It shall be disabled in the subsequent releases.
use_legacy_framed_packet_version: bool,
pub use_legacy_framed_packet_version: bool,
}
impl Default for Debug {
+249
View File
@@ -0,0 +1,249 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::persistence::paths::{KeysPaths, MixNodePaths};
use crate::config::{Config, Debug, MixNode, Verloc};
use nym_bin_common::logging::LoggingSettings;
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
use nym_validator_client::nyxd;
use serde::{Deserialize, Deserializer, Serialize};
use std::net::{IpAddr, SocketAddr};
use std::path::PathBuf;
use std::str::FromStr;
use std::time::Duration;
use url::Url;
const DEFAULT_MIX_LISTENING_PORT: u16 = 1789;
const DEFAULT_VERLOC_LISTENING_PORT: u16 = 1790;
const DEFAULT_HTTP_API_LISTENING_PORT: u16 = 8000;
const NYM_API: &str = "https://validator.nymtech.net/api/";
const DESCRIPTION_FILE: &str = "description.toml";
// 'RTT MEASUREMENT'
const DEFAULT_PACKETS_PER_NODE: usize = 100;
const DEFAULT_CONNECTION_TIMEOUT: Duration = Duration::from_millis(5000);
const DEFAULT_PACKET_TIMEOUT: Duration = Duration::from_millis(1500);
const DEFAULT_DELAY_BETWEEN_PACKETS: Duration = Duration::from_millis(50);
const DEFAULT_BATCH_SIZE: usize = 50;
const DEFAULT_TESTING_INTERVAL: Duration = Duration::from_secs(60 * 60 * 12);
const DEFAULT_RETRY_TIMEOUT: Duration = Duration::from_secs(60 * 30);
// 'DEBUG'
const DEFAULT_NODE_STATS_LOGGING_DELAY: Duration = Duration::from_millis(60_000);
const DEFAULT_NODE_STATS_UPDATING_DELAY: Duration = Duration::from_millis(30_000);
const DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF: Duration = Duration::from_millis(10_000);
const DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF: Duration = Duration::from_millis(300_000);
const DEFAULT_INITIAL_CONNECTION_TIMEOUT: Duration = Duration::from_millis(1_500);
const DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE: usize = 2000;
pub(super) fn de_ipaddr_from_maybe_str_socks_addr<'de, D>(
deserializer: D,
) -> Result<IpAddr, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
if let Ok(socket_addr) = SocketAddr::from_str(&s) {
Ok(socket_addr.ip())
} else {
IpAddr::from_str(&s).map_err(serde::de::Error::custom)
}
}
fn bind_all_address() -> IpAddr {
"0.0.0.0".parse().unwrap()
}
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_20 {
mixnode: MixNodeV1_1_20,
#[serde(default)]
verloc: VerlocV1_1_20,
#[serde(default)]
logging: LoggingV1_1_20,
#[serde(default)]
debug: DebugV1_1_20,
}
impl From<ConfigV1_1_20> for Config {
fn from(value: ConfigV1_1_20) -> Self {
let node_description =
ConfigV1_1_20::default_config_directory(&value.mixnode.id).join(DESCRIPTION_FILE);
Config {
mixnode: MixNode {
version: value.mixnode.version,
id: value.mixnode.id,
listening_address: value.mixnode.listening_address,
mix_port: value.mixnode.mix_port,
verloc_port: value.mixnode.verloc_port,
http_api_port: value.mixnode.http_api_port,
nym_api_urls: value.mixnode.nym_api_urls,
},
storage_paths: MixNodePaths {
keys: KeysPaths {
private_identity_key_file: value.mixnode.private_identity_key_file,
public_identity_key_file: value.mixnode.public_identity_key_file,
private_sphinx_key_file: value.mixnode.private_sphinx_key_file,
public_sphinx_key_file: value.mixnode.public_sphinx_key_file,
},
node_description,
},
verloc: value.verloc.into(),
logging: value.logging.into(),
debug: value.debug.into(),
}
}
}
impl MigrationNymConfig for ConfigV1_1_20 {
fn default_root_directory() -> PathBuf {
dirs::home_dir()
.expect("Failed to evaluate $HOME value")
.join(".nym")
.join("mixnodes")
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
struct MixNodeV1_1_20 {
version: String,
id: String,
#[serde(deserialize_with = "de_ipaddr_from_maybe_str_socks_addr")]
listening_address: IpAddr,
announce_address: String,
mix_port: u16,
verloc_port: u16,
http_api_port: u16,
private_identity_key_file: PathBuf,
public_identity_key_file: PathBuf,
private_sphinx_key_file: PathBuf,
public_sphinx_key_file: PathBuf,
nym_api_urls: Vec<Url>,
nym_root_directory: PathBuf,
wallet_address: Option<nyxd::AccountId>,
}
impl Default for MixNodeV1_1_20 {
fn default() -> Self {
MixNodeV1_1_20 {
version: env!("CARGO_PKG_VERSION").to_string(),
id: "".to_string(),
listening_address: bind_all_address(),
announce_address: "127.0.0.1".to_string(),
mix_port: DEFAULT_MIX_LISTENING_PORT,
verloc_port: DEFAULT_VERLOC_LISTENING_PORT,
http_api_port: DEFAULT_HTTP_API_LISTENING_PORT,
private_identity_key_file: Default::default(),
public_identity_key_file: Default::default(),
private_sphinx_key_file: Default::default(),
public_sphinx_key_file: Default::default(),
nym_api_urls: vec![Url::from_str(NYM_API).expect("Invalid default API URL")],
nym_root_directory: ConfigV1_1_20::default_root_directory(),
wallet_address: None,
}
}
}
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
struct LoggingV1_1_20 {}
impl From<LoggingV1_1_20> for LoggingSettings {
fn from(_value: LoggingV1_1_20) -> Self {
LoggingSettings {}
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
struct VerlocV1_1_20 {
packets_per_node: usize,
connection_timeout: Duration,
packet_timeout: Duration,
delay_between_packets: Duration,
tested_nodes_batch_size: usize,
testing_interval: Duration,
retry_timeout: Duration,
}
impl From<VerlocV1_1_20> for Verloc {
fn from(value: VerlocV1_1_20) -> Self {
Verloc {
packets_per_node: value.packets_per_node,
connection_timeout: value.connection_timeout,
packet_timeout: value.packet_timeout,
delay_between_packets: value.delay_between_packets,
tested_nodes_batch_size: value.tested_nodes_batch_size,
testing_interval: value.testing_interval,
retry_timeout: value.retry_timeout,
}
}
}
impl Default for VerlocV1_1_20 {
fn default() -> Self {
VerlocV1_1_20 {
packets_per_node: DEFAULT_PACKETS_PER_NODE,
connection_timeout: DEFAULT_CONNECTION_TIMEOUT,
packet_timeout: DEFAULT_PACKET_TIMEOUT,
delay_between_packets: DEFAULT_DELAY_BETWEEN_PACKETS,
tested_nodes_batch_size: DEFAULT_BATCH_SIZE,
testing_interval: DEFAULT_TESTING_INTERVAL,
retry_timeout: DEFAULT_RETRY_TIMEOUT,
}
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(default)]
struct DebugV1_1_20 {
#[serde(with = "humantime_serde")]
node_stats_logging_delay: Duration,
#[serde(with = "humantime_serde")]
node_stats_updating_delay: Duration,
#[serde(with = "humantime_serde")]
packet_forwarding_initial_backoff: Duration,
#[serde(with = "humantime_serde")]
packet_forwarding_maximum_backoff: Duration,
#[serde(with = "humantime_serde")]
initial_connection_timeout: Duration,
maximum_connection_buffer_size: usize,
use_legacy_framed_packet_version: bool,
}
impl From<DebugV1_1_20> for Debug {
fn from(value: DebugV1_1_20) -> Self {
Debug {
node_stats_logging_delay: value.node_stats_logging_delay,
node_stats_updating_delay: value.node_stats_updating_delay,
packet_forwarding_initial_backoff: value.packet_forwarding_initial_backoff,
packet_forwarding_maximum_backoff: value.packet_forwarding_maximum_backoff,
initial_connection_timeout: value.initial_connection_timeout,
maximum_connection_buffer_size: value.maximum_connection_buffer_size,
use_legacy_framed_packet_version: value.use_legacy_framed_packet_version,
}
}
}
impl Default for DebugV1_1_20 {
fn default() -> Self {
DebugV1_1_20 {
node_stats_logging_delay: DEFAULT_NODE_STATS_LOGGING_DELAY,
node_stats_updating_delay: DEFAULT_NODE_STATS_UPDATING_DELAY,
packet_forwarding_initial_backoff: DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF,
packet_forwarding_maximum_backoff: DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF,
initial_connection_timeout: DEFAULT_INITIAL_CONNECTION_TIMEOUT,
maximum_connection_buffer_size: DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE,
use_legacy_framed_packet_version: true,
}
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod pathfinder;
pub mod paths;
@@ -1,40 +0,0 @@
// Copyright 2020 - 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 MixNodePathfinder {
identity_private_key: PathBuf,
identity_public_key: PathBuf,
private_sphinx_key: PathBuf,
public_sphinx_key: PathBuf,
}
impl MixNodePathfinder {
pub fn new_from_config(config: &Config) -> Self {
MixNodePathfinder {
identity_private_key: config.get_private_identity_key_file(),
identity_public_key: config.get_public_identity_key_file(),
private_sphinx_key: config.get_private_sphinx_key_file(),
public_sphinx_key: config.get_public_sphinx_key_file(),
}
}
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.private_sphinx_key
}
pub fn public_encryption_key(&self) -> &Path {
&self.public_sphinx_key
}
}
+90
View File
@@ -0,0 +1,90 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::{default_config_directory, default_data_directory};
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_SPHINX_KEY_FILENAME: &str = "private_sphinx.pem";
pub const DEFAULT_PUBLIC_SPHINX_KEY_FILENAME: &str = "public_sphinx.pem";
pub const DEFAULT_DESCRIPTION_FILENAME: &str = "description.toml";
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct MixNodePaths {
pub keys: KeysPaths,
pub node_description: PathBuf,
}
impl MixNodePaths {
pub fn new_default<P: AsRef<Path>>(id: P) -> Self {
MixNodePaths {
keys: KeysPaths::new_default(id.as_ref()),
node_description: default_config_directory(id).join(DEFAULT_DESCRIPTION_FILENAME),
}
}
pub fn private_identity_key(&self) -> &Path {
self.keys.private_identity_key()
}
pub fn public_identity_key(&self) -> &Path {
self.keys.public_identity_key()
}
pub fn private_encryption_key(&self) -> &Path {
self.keys.private_encryption_key()
}
pub fn public_encryption_key(&self) -> &Path {
self.keys.public_encryption_key()
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
pub struct KeysPaths {
/// 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 sphinx key.
pub private_sphinx_key_file: PathBuf,
/// Path to file containing public sphinx key.
pub public_sphinx_key_file: PathBuf,
}
impl KeysPaths {
pub fn new_default<P: AsRef<Path>>(id: P) -> Self {
let data_dir = default_data_directory(id);
KeysPaths {
private_identity_key_file: data_dir.join(DEFAULT_PRIVATE_IDENTITY_KEY_FILENAME),
public_identity_key_file: data_dir.join(DEFAULT_PUBLIC_IDENTITY_KEY_FILENAME),
private_sphinx_key_file: data_dir.join(DEFAULT_PRIVATE_SPHINX_KEY_FILENAME),
public_sphinx_key_file: data_dir.join(DEFAULT_PUBLIC_SPHINX_KEY_FILENAME),
}
}
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_sphinx_key_file
}
pub fn public_encryption_key(&self) -> &Path {
&self.public_sphinx_key_file
}
}
+19 -32
View File
@@ -1,12 +1,11 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub(crate) fn config_template() -> &'static str {
// 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 in verloc.
r#"
// 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
@@ -22,25 +21,6 @@ id = '{{ mixnode.id }}'
# Socket address to which this mixnode will bind to and will be listening for packets.
listening_address = '{{ mixnode.listening_address }}'
# Path to file containing private identity key.
private_identity_key_file = '{{ mixnode.private_identity_key_file }}'
# Path to file containing public identity key.
public_identity_key_file = '{{ mixnode.public_identity_key_file }}'
# Path to file containing private identity key.
private_sphinx_key_file = '{{ mixnode.private_sphinx_key_file }}'
# Path to file containing public sphinx key.
public_sphinx_key_file = '{{ mixnode.public_sphinx_key_file }}'
##### additional mixnode config options #####
# Optional address announced to the directory server for the clients to connect to.
# It is useful, say, in NAT scenarios or wanting to more easily update actual IP address
# later on by using name resolvable with a DNS query, such as `nymtech.net`.
announce_address = '{{ mixnode.announce_address }}'
# Port used for listening for all mixnet traffic.
# (default: 1789)
mix_port = {{ mixnode.mix_port }}
@@ -60,14 +40,22 @@ nym_api_urls = [
{{/each}}
]
# Nym wallet address on the blockchain that should control this mixnode
wallet_address = '{{ mixnode.wallet_address }}'
[storage_paths]
##### advanced configuration options #####
# Path to file containing private identity key.
keys.private_identity_key_file = '{{ storage_paths.keys.private_identity_key_file }}'
# Absolute path to the home Nym Clients directory.
nym_root_directory = '{{ mixnode.nym_root_directory }}'
# Path to file containing public identity key.
keys.public_identity_key_file = '{{ storage_paths.keys.public_identity_key_file }}'
# Path to file containing private identity key.
keys.private_sphinx_key_file = '{{ storage_paths.keys.private_sphinx_key_file }}'
# Path to file containing public sphinx key.
keys.public_sphinx_key_file = '{{ storage_paths.keys.public_sphinx_key_file }}'
# Path to file containing description of this node.
node_description = '{{ storage_paths.node_description }}'
##### logging configuration options #####
@@ -75,5 +63,4 @@ nym_root_directory = '{{ mixnode.nym_root_directory }}'
# TODO
"#
}
"#;
+4 -2
View File
@@ -48,7 +48,7 @@ fn test_function() {
}
#[tokio::main]
async fn main() {
async fn main() -> anyhow::Result<()> {
cfg_if::cfg_if! {
if #[cfg(feature = "cpucycles")] {
let home_dir = dirs::home_dir().expect("Could not get $HOME");
@@ -66,12 +66,14 @@ async fn main() {
let args = Cli::parse();
setup_env(args.config_env_file.as_ref());
commands::execute(args).await;
commands::execute(args).await?;
cfg_if::cfg_if! {
if #[cfg(feature = "cpucycles")] {
opentelemetry::global::shutdown_tracer_provider();
}}
Ok(())
}
#[cfg(test)]
+51 -65
View File
@@ -1,14 +1,13 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::persistence::pathfinder::MixNodePathfinder;
use crate::config::Config;
use crate::node::http::{
description::description,
hardware::hardware,
not_found,
stats::stats,
verloc::{verloc as verlocRoute, VerlocState},
verloc::{verloc as verloc_route, VerlocState},
};
use crate::node::listener::connection_handler::packet_processing::PacketProcessor;
use crate::node::listener::connection_handler::ConnectionHandler;
@@ -18,7 +17,6 @@ use crate::node::node_statistics::SharedNodeStats;
use crate::node::packet_delayforwarder::{DelayForwarder, PacketDelayForwardSender};
use nym_bin_common::output_format::OutputFormat;
use nym_bin_common::version_checker::parse_version;
use nym_config::NymConfig;
use nym_crypto::asymmetric::{encryption, identity};
use nym_mixnode_common::verloc::{self, AtomicVerlocResult, VerlocMeasurer};
use nym_task::{TaskClient, TaskManager};
@@ -27,6 +25,7 @@ use rand::thread_rng;
use std::net::SocketAddr;
use std::process;
use std::sync::Arc;
#[cfg(feature = "cpucycles")]
use tracing::{error, info, warn};
@@ -46,38 +45,35 @@ pub struct MixNode {
impl MixNode {
pub fn new(config: Config) -> Self {
let pathfinder = MixNodePathfinder::new_from_config(&config);
MixNode {
descriptor: Self::load_node_description(&config),
identity_keypair: Arc::new(Self::load_identity_keys(&pathfinder)),
sphinx_keypair: Arc::new(Self::load_sphinx_keys(&pathfinder)),
identity_keypair: Arc::new(Self::load_identity_keys(&config)),
sphinx_keypair: Arc::new(Self::load_sphinx_keys(&config)),
config,
}
}
fn load_node_description(config: &Config) -> NodeDescription {
NodeDescription::load_from_file(Config::default_config_directory(&config.get_id()))
.unwrap_or_default()
NodeDescription::load_from_file(&config.storage_paths.node_description).unwrap_or_default()
}
/// Loads identity keys stored on disk
pub(crate) fn load_identity_keys(pathfinder: &MixNodePathfinder) -> identity::KeyPair {
pub(crate) fn load_identity_keys(config: &Config) -> identity::KeyPair {
let identity_keypair: identity::KeyPair =
nym_pemstore::load_keypair(&nym_pemstore::KeyPairPath::new(
pathfinder.private_identity_key().to_owned(),
pathfinder.public_identity_key().to_owned(),
config.storage_paths.keys.private_identity_key(),
config.storage_paths.keys.public_identity_key(),
))
.expect("Failed to read stored identity key files");
identity_keypair
}
/// Loads Sphinx keys stored on disk
fn load_sphinx_keys(pathfinder: &MixNodePathfinder) -> encryption::KeyPair {
fn load_sphinx_keys(config: &Config) -> encryption::KeyPair {
let sphinx_keypair: encryption::KeyPair =
nym_pemstore::load_keypair(&nym_pemstore::KeyPairPath::new(
pathfinder.private_encryption_key().to_owned(),
pathfinder.public_encryption_key().to_owned(),
config.storage_paths.keys.private_encryption_key(),
config.storage_paths.keys.public_encryption_key(),
))
.expect("Failed to read stored sphinx key files");
sphinx_keypair
@@ -88,13 +84,11 @@ impl MixNode {
let node_details = nym_types::mixnode::MixnodeNodeDetailsResponse {
identity_key: self.identity_keypair.public_key().to_base58_string(),
sphinx_key: self.sphinx_keypair.public_key().to_base58_string(),
announce_address: self.config.get_announce_address(),
bind_address: self.config.get_listening_address().to_string(),
version: self.config.get_version().to_string(),
mix_port: self.config.get_mix_port(),
http_api_port: self.config.get_http_api_port(),
verloc_port: self.config.get_verloc_port(),
wallet_address: self.config.get_wallet_address().map(|x| x.to_string()),
bind_address: self.config.mixnode.listening_address,
version: self.config.mixnode.version.clone(),
mix_port: self.config.mixnode.mix_port,
http_api_port: self.config.mixnode.http_api_port,
verloc_port: self.config.mixnode.verloc_port,
};
println!("{}", output.format(&node_details));
@@ -105,13 +99,16 @@ impl MixNode {
atomic_verloc_result: AtomicVerlocResult,
node_stats_pointer: SharedNodeStats,
) {
info!("Starting HTTP API on http://localhost:8000");
info!(
"Starting HTTP API on http://{}:{}",
self.config.mixnode.listening_address, self.config.mixnode.http_api_port
);
let mut config = rocket::config::Config::release_default();
// bind to the same address as we are using for mixnodes
config.address = self.config.get_listening_address();
config.port = self.config.get_http_api_port();
config.address = self.config.mixnode.listening_address;
config.port = self.config.mixnode.http_api_port;
let verloc_state = VerlocState::new(atomic_verloc_result);
let descriptor = self.descriptor.clone();
@@ -119,7 +116,7 @@ impl MixNode {
tokio::spawn(async move {
rocket::build()
.configure(config)
.mount("/", routes![verlocRoute, description, stats, hardware])
.mount("/", routes![verloc_route, description, stats, hardware])
.register("/", catchers![not_found])
.manage(verloc_state)
.manage(descriptor)
@@ -135,8 +132,8 @@ impl MixNode {
) -> (SharedNodeStats, node_statistics::UpdateSender) {
info!("Starting node stats controller...");
let controller = node_statistics::Controller::new(
self.config.get_node_stats_logging_delay(),
self.config.get_node_stats_updating_delay(),
self.config.debug.node_stats_logging_delay,
self.config.debug.node_stats_updating_delay,
shutdown,
);
let node_stats_pointer = controller.get_node_stats_data_pointer();
@@ -159,8 +156,8 @@ impl MixNode {
let connection_handler = ConnectionHandler::new(packet_processor, delay_forwarding_channel);
let listening_address = SocketAddr::new(
self.config.get_listening_address(),
self.config.get_mix_port(),
self.config.mixnode.listening_address,
self.config.mixnode.mix_port,
);
Listener::new(listening_address, shutdown).start(connection_handler);
@@ -174,11 +171,11 @@ impl MixNode {
info!("Starting packet delay-forwarder...");
let client_config = nym_mixnet_client::Config::new(
self.config.get_packet_forwarding_initial_backoff(),
self.config.get_packet_forwarding_maximum_backoff(),
self.config.get_initial_connection_timeout(),
self.config.get_maximum_connection_buffer_size(),
self.config.get_use_legacy_sphinx_framing(),
self.config.debug.packet_forwarding_initial_backoff,
self.config.debug.packet_forwarding_maximum_backoff,
self.config.debug.initial_connection_timeout,
self.config.debug.maximum_connection_buffer_size,
self.config.debug.use_legacy_framed_packet_version,
);
let mut packet_forwarder = DelayForwarder::new(
@@ -199,8 +196,8 @@ impl MixNode {
// this is a sanity check to make sure we didn't mess up with the minimum version at some point
// and whether the user has run update if they're using old config
// if this code exists in the node, it MUST BE compatible
let config_version =
parse_version(self.config.get_version()).expect("malformed version in the config file");
let config_version = parse_version(&self.config.mixnode.version)
.expect("malformed version in the config file");
let minimum_version = parse_version(verloc::MINIMUM_NODE_VERSION).unwrap();
if config_version < minimum_version {
error!("You seem to have not updated your mixnode configuration file - please run `upgrade` before attempting again");
@@ -210,19 +207,19 @@ impl MixNode {
// use the same binding address with the HARDCODED port for time being (I don't like that approach personally)
let listening_address = SocketAddr::new(
self.config.get_listening_address(),
self.config.get_verloc_port(),
self.config.mixnode.listening_address,
self.config.mixnode.verloc_port,
);
let config = verloc::ConfigBuilder::new()
.listening_address(listening_address)
.packets_per_node(self.config.get_measurement_packets_per_node())
.connection_timeout(self.config.get_measurement_connection_timeout())
.packet_timeout(self.config.get_measurement_packet_timeout())
.delay_between_packets(self.config.get_measurement_delay_between_packets())
.tested_nodes_batch_size(self.config.get_measurement_tested_nodes_batch_size())
.testing_interval(self.config.get_measurement_testing_interval())
.retry_timeout(self.config.get_measurement_retry_timeout())
.packets_per_node(self.config.verloc.packets_per_node)
.connection_timeout(self.config.verloc.connection_timeout)
.packet_timeout(self.config.verloc.packet_timeout)
.delay_between_packets(self.config.verloc.delay_between_packets)
.tested_nodes_batch_size(self.config.verloc.tested_nodes_batch_size)
.testing_interval(self.config.verloc.testing_interval)
.retry_timeout(self.config.verloc.retry_timeout)
.nym_api_urls(self.config.get_nym_api_endpoints())
.build();
@@ -242,8 +239,7 @@ impl MixNode {
nym_validator_client::NymApiClient::new(nym_api.clone())
}
// TODO: ask DH whether this function still makes sense in ^0.10
async fn check_if_same_ip_node_exists(&mut self) -> Option<String> {
async fn check_if_bonded(&self) -> bool {
// TODO: if anything, this should be getting data directly from the contract
// as opposed to the validator API
let validator_client = self.random_api_client();
@@ -258,12 +254,10 @@ impl MixNode {
}
};
let our_host = self.config.get_announce_address();
existing_nodes
.iter()
.find(|node| node.bond_information.mix_node.host == our_host)
.map(|node| node.bond_information.mix_node.identity_key.clone())
existing_nodes.iter().any(|node| {
node.bond_information.mix_node.identity_key
== self.identity_keypair.public_key().to_base58_string()
})
}
async fn wait_for_interrupt(&self, shutdown: TaskManager) {
@@ -274,16 +268,8 @@ impl MixNode {
pub async fn run(&mut self) {
info!("Starting nym mixnode");
if let Some(duplicate_node_key) = self.check_if_same_ip_node_exists().await {
if duplicate_node_key == self.identity_keypair.public_key().to_base58_string() {
warn!("You seem to have bonded your mixnode before starting it - that's highly unrecommended as in the future it might result in slashing");
} else {
log::error!(
"Our announce-host is identical to an existing node's announce-host! (its key is {:?})",
duplicate_node_key
);
return;
}
if self.check_if_bonded().await {
warn!("You seem to have bonded your mixnode before starting it - that's highly unrecommended as in the future it might result in slashing");
}
let shutdown = TaskManager::default();
+14 -14
View File
@@ -1,10 +1,8 @@
use serde::Deserialize;
use serde::Serialize;
use std::path::PathBuf;
use std::path::Path;
use std::{fs, io};
pub(crate) const DESCRIPTION_FILE: &str = "description.toml";
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
pub struct NodeDescription {
pub(crate) name: String,
@@ -25,24 +23,26 @@ impl Default for NodeDescription {
}
impl NodeDescription {
pub(crate) fn load_from_file(config_path: PathBuf) -> io::Result<NodeDescription> {
let description_file_path: PathBuf = [config_path.to_str().unwrap(), DESCRIPTION_FILE]
.iter()
.collect();
let toml = fs::read_to_string(description_file_path)?;
pub(crate) fn load_from_file<P: AsRef<Path>>(path: P) -> io::Result<NodeDescription> {
// let description_file_path: PathBuf = [config_path.to_str().unwrap(), DESCRIPTION_FILE]
// .iter()
// .collect();
// let toml = fs::read_to_string(description_file_path)?;
// toml::from_str(&toml).map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err))
let toml = fs::read_to_string(path)?;
toml::from_str(&toml).map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err))
}
pub(crate) fn save_to_file(
pub(crate) fn save_to_file<P: AsRef<Path>>(
description: &NodeDescription,
config_path: PathBuf,
path: P,
) -> io::Result<()> {
let description_file_path: PathBuf = [config_path.to_str().unwrap(), DESCRIPTION_FILE]
.iter()
.collect();
// let description_file_path: PathBuf = [config_path.to_str().unwrap(), DESCRIPTION_FILE]
// .iter()
// .collect();
let description_toml =
toml::to_string(description).expect("could not encode description to toml");
fs::write(description_file_path, description_toml)?;
fs::write(path, description_toml)?;
Ok(())
}
}
+1
View File
@@ -62,6 +62,7 @@ sqlx = { version = "0.6.2", features = [
okapi = { version = "0.7.0-rc.1", features = ["impl_json_schema"] }
rocket_okapi = { version = "0.8.0-rc.2", features = ["swagger"] }
schemars = { version = "0.8", features = ["preserve_order"] }
zeroize = { workspace = true }
## internal
nym-bandwidth-controller = { path = "../common/bandwidth-controller" }
+4 -4
View File
@@ -6,7 +6,7 @@ use okapi::openapi3::OpenApi;
use rocket::Route;
use rocket_okapi::{openapi_get_routes_spec, settings::OpenApiSettings};
use crate::support::{config::Config, nyxd};
use crate::support::{config, nyxd};
use self::cache::refresher::CirculatingSupplyCacheRefresher;
@@ -24,16 +24,16 @@ pub(crate) fn circulating_supply_routes(settings: &OpenApiSettings) -> (Vec<Rout
/// Spawn the circulating supply cache refresher.
pub(crate) fn start_cache_refresh(
config: &Config,
config: &config::CirculatingSupplyCacher,
nyxd_client: nyxd::Client,
circulating_supply_cache: &cache::CirculatingSupplyCache,
shutdown: &TaskManager,
) {
if config.get_circulating_supply_enabled() {
if config.enabled {
let refresher = CirculatingSupplyCacheRefresher::new(
nyxd_client,
circulating_supply_cache.to_owned(),
config.get_circulating_supply_caching_interval(),
config.debug.caching_interval,
);
let shutdown_listener = shutdown.subscribe();
tokio::spawn(async move { refresher.run(shutdown_listener).await });
+17 -16
View File
@@ -12,7 +12,7 @@ use crate::coconut::dkg::{
};
use crate::coconut::keypair::KeyPair as CoconutKeyPair;
use crate::nyxd;
use crate::support::config::Config;
use crate::support::config;
use anyhow::Result;
use nym_coconut_dkg_common::types::EpochState;
use nym_dkg::bte::keys::KeyPair as DkgKeyPair;
@@ -23,15 +23,15 @@ use std::path::PathBuf;
use std::time::{Duration, SystemTime};
use tokio::time::interval;
pub(crate) fn init_keypair(config: &Config) -> Result<()> {
pub(crate) fn init_keypair(config: &config::CoconutSigner) -> Result<()> {
let mut rng = OsRng;
let dkg_params = nym_dkg::bte::setup();
let kp = DkgKeyPair::new(&dkg_params, &mut rng);
nym_pemstore::store_keypair(
&kp,
&nym_pemstore::KeyPairPath::new(
config.decryption_key_path(),
config.public_key_with_proof_path(),
&config.storage_paths.decryption_key_path,
&config.storage_paths.public_key_with_proof_path,
),
)?;
Ok(())
@@ -48,39 +48,40 @@ pub(crate) struct DkgController<R> {
impl<R: RngCore + CryptoRng + Clone> DkgController<R> {
pub(crate) async fn new(
config: &Config,
config: &config::CoconutSigner,
nyxd_client: nyxd::Client,
coconut_keypair: CoconutKeyPair,
rng: R,
) -> Result<Self> {
let dkg_keypair = nym_pemstore::load_keypair(&nym_pemstore::KeyPairPath::new(
config.decryption_key_path(),
config.public_key_with_proof_path(),
&config.storage_paths.decryption_key_path,
&config.storage_paths.public_key_with_proof_path,
))?;
if let Ok(coconut_keypair_value) =
nym_pemstore::load_keypair(&nym_pemstore::KeyPairPath::new(
config.secret_key_path(),
config.verification_key_path(),
&config.storage_paths.secret_key_path,
&config.storage_paths.verification_key_path,
))
{
coconut_keypair.set(Some(coconut_keypair_value)).await;
}
let persistent_state =
PersistentState::load_from_file(config.persistent_state_path()).unwrap_or_default();
PersistentState::load_from_file(&config.storage_paths.dkg_persistent_state_path)
.unwrap_or_default();
Ok(DkgController {
dkg_client: DkgClient::new(nyxd_client),
secret_key_path: config.secret_key_path(),
verification_key_path: config.verification_key_path(),
secret_key_path: config.storage_paths.secret_key_path.clone(),
verification_key_path: config.storage_paths.verification_key_path.clone(),
state: State::new(
config.persistent_state_path(),
config.storage_paths.dkg_persistent_state_path.clone(),
persistent_state,
config.get_announce_address(),
config.announce_address.clone(),
dkg_keypair,
coconut_keypair,
),
rng,
polling_rate: config.get_dkg_contract_polling_rate(),
polling_rate: config.debug.dkg_contract_polling_rate,
})
}
@@ -199,7 +200,7 @@ impl<R: RngCore + CryptoRng + Clone> DkgController<R> {
// TODO: can we make it non-async? it seems we'd have to modify `coconut_keypair.set(coconut_keypair_value)` in new
// could we do it?
pub(crate) async fn start(
config: &Config,
config: &config::CoconutSigner,
nyxd_client: nyxd::Client,
coconut_keypair: CoconutKeyPair,
rng: R,
+3 -3
View File
@@ -14,7 +14,7 @@ use nym_dkg::{NodeIndex, RecoveredVerificationKeys, Threshold};
use serde::de::Error;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::collections::BTreeMap;
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use url::Url;
fn bte_pk_serialize<S: Serializer>(
@@ -192,12 +192,12 @@ impl From<&State> for PersistentState {
}
impl PersistentState {
pub fn save_to_file(&self, path: PathBuf) -> Result<(), CoconutError> {
pub fn save_to_file<P: AsRef<Path>>(&self, path: P) -> Result<(), CoconutError> {
std::fs::write(path, serde_json::to_string(self)?)?;
Ok(())
}
pub fn load_from_file(path: PathBuf) -> Result<Self, CoconutError> {
pub fn load_from_file<P: AsRef<Path>>(path: P) -> Result<Self, CoconutError> {
Ok(serde_json::from_str(&std::fs::read_to_string(path)?)?)
}
}
+9 -10
View File
@@ -19,7 +19,6 @@ use coconut::dkg::controller::DkgController;
use log::info;
use node_status_api::NodeStatusCache;
use nym_bin_common::logging::setup_logging;
use nym_config::NymConfig;
use nym_contract_cache::cache::NymContractCache;
use nym_sphinx::receiver::SphinxMessageReceiver;
use nym_task::TaskManager;
@@ -88,13 +87,13 @@ async fn start_nym_api_tasks(
// start all the caches first
let nym_contract_cache_listener = nym_contract_cache::start_refresher(
&config,
&config.node_status_api,
nym_contract_cache_state,
nyxd_client.clone(),
&shutdown,
);
node_status_api::start_cache_refresh(
&config,
&config.node_status_api,
nym_contract_cache_state,
node_status_cache_state,
maybe_storage,
@@ -102,16 +101,16 @@ async fn start_nym_api_tasks(
&shutdown,
);
circulating_supply_api::start_cache_refresh(
&config,
&config.circulating_supply_cacher,
nyxd_client.clone(),
circulating_supply_cache_state,
&shutdown,
);
// start dkg task
if config.get_coconut_signer_enabled() {
if config.coconut_signer.enabled {
DkgController::start(
&config,
&config.coconut_signer,
nyxd_client.clone(),
coconut_keypair,
OsRng,
@@ -122,12 +121,12 @@ async fn start_nym_api_tasks(
// and then only start the uptime updater (and the monitor itself, duh)
// if the monitoring if it's enabled
if config.get_network_monitor_enabled() {
if config.network_monitor.enabled {
// if network monitor is enabled, the storage MUST BE available
let storage = maybe_storage.unwrap();
network_monitor::start::<SphinxMessageReceiver>(
&config,
&config.network_monitor,
nym_contract_cache_state,
storage,
nyxd_client.clone(),
@@ -138,7 +137,7 @@ async fn start_nym_api_tasks(
HistoricalUptimeUpdater::start(storage, &shutdown);
// start 'rewarding' if its enabled
if config.get_rewarding_enabled() {
if config.rewarding.enabled {
epoch_operations::ensure_rewarding_permission(&nyxd_client).await?;
RewardedSetUpdater::start(nyxd_client, nym_contract_cache_state, storage, &shutdown);
}
@@ -160,7 +159,7 @@ async fn run_nym_api(cli_args: CliArgs) -> Result<(), Box<dyn Error + Send + Syn
// if we just wanted to write data to the config, exit, don't start any tasks
if save_to_file {
info!("Saving the configuration to a file");
config.save_to_file(None)?;
config.save_to_default_location()?;
return Ok(());
}
+17 -20
View File
@@ -1,7 +1,6 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::network_monitor;
use crate::network_monitor::monitor::preparer::PacketPreparer;
use crate::network_monitor::monitor::processor::{
ReceivedProcessor, ReceivedProcessorReceiver, ReceivedProcessorSender,
@@ -14,8 +13,7 @@ use crate::network_monitor::monitor::summary_producer::SummaryProducer;
use crate::network_monitor::monitor::Monitor;
use crate::nym_contract_cache::cache::NymContractCache;
use crate::storage::NymApiStorage;
use crate::support::config::Config;
use crate::support::nyxd;
use crate::support::{config, nyxd};
use futures::channel::mpsc;
use nym_bandwidth_controller::BandwidthController;
use nym_credential_storage::persistent_storage::PersistentStorage;
@@ -34,7 +32,7 @@ pub(crate) mod test_route;
pub(crate) const ROUTE_TESTING_TEST_NONCE: u64 = 0;
pub(crate) fn setup<'a>(
config: &'a Config,
config: &'a config::NetworkMonitor,
nym_contract_cache_state: &NymContractCache,
storage: &NymApiStorage,
nyxd_client: nyxd::Client,
@@ -48,7 +46,7 @@ pub(crate) fn setup<'a>(
}
pub(crate) struct NetworkMonitorBuilder<'a> {
config: &'a Config,
config: &'a config::NetworkMonitor,
nyxd_client: nyxd::Client,
node_status_storage: NymApiStorage,
validator_cache: NymContractCache,
@@ -56,7 +54,7 @@ pub(crate) struct NetworkMonitorBuilder<'a> {
impl<'a> NetworkMonitorBuilder<'a> {
pub(crate) fn new(
config: &'a Config,
config: &'a config::NetworkMonitor,
nyxd_client: nyxd::Client,
node_status_storage: NymApiStorage,
validator_cache: NymContractCache,
@@ -87,7 +85,7 @@ impl<'a> NetworkMonitorBuilder<'a> {
let packet_preparer = new_packet_preparer(
self.validator_cache,
self.config.get_per_node_test_packets(),
self.config.debug.per_node_test_packets,
Arc::clone(&ack_key),
*identity_keypair.public_key(),
*encryption_keypair.public_key(),
@@ -96,7 +94,7 @@ impl<'a> NetworkMonitorBuilder<'a> {
let bandwidth_controller = {
BandwidthController::new(
nym_credential_storage::initialise_persistent_storage(
self.config.get_credentials_database_path(),
&self.config.storage_paths.credentials_database_path,
)
.await,
self.nyxd_client.clone(),
@@ -107,9 +105,9 @@ impl<'a> NetworkMonitorBuilder<'a> {
self.config,
gateway_status_update_sender,
Arc::clone(&identity_keypair),
self.config.get_gateway_sending_rate(),
self.config.debug.gateway_sending_rate,
bandwidth_controller,
self.config.get_disabled_credentials_mode(),
self.config.debug.disabled_credentials_mode,
);
let received_processor = new_received_processor(
@@ -117,13 +115,13 @@ impl<'a> NetworkMonitorBuilder<'a> {
Arc::clone(&encryption_keypair),
ack_key,
);
let summary_producer = new_summary_producer(self.config.get_per_node_test_packets());
let summary_producer = new_summary_producer(self.config.debug.per_node_test_packets);
let packet_receiver = new_packet_receiver(
gateway_status_update_receiver,
received_processor_sender_channel,
);
let monitor = monitor::Monitor::new(
let monitor = Monitor::new(
self.config,
packet_preparer,
packet_sender,
@@ -176,7 +174,7 @@ fn new_packet_preparer(
}
fn new_packet_sender(
config: &Config,
config: &config::NetworkMonitor,
gateways_status_updater: GatewayClientUpdateSender,
local_identity: Arc<identity::KeyPair>,
max_sending_rate: usize,
@@ -186,9 +184,9 @@ fn new_packet_sender(
PacketSender::new(
gateways_status_updater,
local_identity,
config.get_gateway_response_timeout(),
config.get_gateway_connection_timeout(),
config.get_max_concurrent_gateway_clients(),
config.debug.gateway_response_timeout,
config.debug.gateway_connection_timeout,
config.debug.max_concurrent_gateway_clients,
max_sending_rate,
bandwidth_controller,
disabled_credentials_mode,
@@ -219,14 +217,13 @@ fn new_packet_receiver(
// TODO: 1) does it still have to have separate builder or could we get rid of it now?
// TODO: 2) how do we make it non-async as other 'start' methods?
pub(crate) async fn start<R: MessageReceiver + Send + 'static>(
config: &Config,
config: &config::NetworkMonitor,
nym_contract_cache_state: &NymContractCache,
storage: &NymApiStorage,
nyxd_client: nyxd::Client,
shutdown: &TaskManager,
) {
let monitor_builder =
network_monitor::setup(config, nym_contract_cache_state, storage, nyxd_client);
let monitor_builder = setup(config, nym_contract_cache_state, storage, nyxd_client);
info!("Starting network monitor...");
let runnables: NetworkMonitorRunnables<R> = monitor_builder.build().await;
runnables.spawn_tasks(shutdown);

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