Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8c5f264cbf | |||
| 73b74ad4d2 | |||
| 4b4f211cba | |||
| 7a26e2ef57 | |||
| 9782945c92 | |||
| 573a91e015 | |||
| eb06285653 | |||
| 728542181e | |||
| 8f52c2a229 | |||
| 3916bbf632 | |||
| f8ce87a205 | |||
| c9b5ac2abf | |||
| bd5577c0a4 | |||
| 3e9178a664 | |||
| ac822164af | |||
| edc404d6d7 | |||
| e0e5317b44 | |||
| 6711a3bb3c | |||
| 20130fb2a2 | |||
| 59560ac125 | |||
| 893648cad0 | |||
| 27502b9c75 | |||
| 2ab4a445b7 | |||
| 7f5e2f2909 | |||
| d5bc26d2e3 | |||
| aaa965937b | |||
| b3f883e82b |
@@ -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
@@ -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"
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 }}'
|
||||
|
||||
"#
|
||||
}
|
||||
|
||||
"#;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
+42
-48
@@ -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 }}'
|
||||
|
||||
"#
|
||||
}
|
||||
"#;
|
||||
@@ -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]
|
||||
|
||||
Generated
+85
-5
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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)
|
||||
|
||||
@@ -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"]
|
||||
@@ -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 {}
|
||||
@@ -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
@@ -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,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()
|
||||
|
||||
@@ -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..])?,
|
||||
};
|
||||
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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"),
|
||||
}
|
||||
|
||||
@@ -251,6 +251,7 @@ pub trait FragmentPreparer {
|
||||
&destination,
|
||||
&delays,
|
||||
)?,
|
||||
#[allow(deprecated)]
|
||||
PacketType::Vpn => NymPacket::sphinx_build(
|
||||
packet_size.payload_size(),
|
||||
packet_payload,
|
||||
|
||||
@@ -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,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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,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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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(),
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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,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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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();
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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(());
|
||||
}
|
||||
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user