Feature/extract gateway config (#3517)
* wip client core * hashing shared key in persisted details * native client using on-disk gateway details * ibid for socks5 * ibid for NR * nym sdk * non-wasm fixes * wasm * missed cargo fmt * fixed nym-connect build * changed serialization of the key hash to be more human readable * allowing some dead code * fixed gateway details deserializtion * removed needless borrow in wasm client * removed deadcode * exhaustive match on GatewaySetup after having loaded the keys
This commit is contained in:
committed by
GitHub
parent
c5ad4006ae
commit
42a43a3709
Generated
+13
-11
@@ -373,9 +373,9 @@ checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.0"
|
||||
version = "0.21.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
|
||||
checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
@@ -3562,6 +3562,7 @@ name = "nym-client-core"
|
||||
version = "1.1.14"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.21.2",
|
||||
"dashmap 5.4.0",
|
||||
"dirs 4.0.0",
|
||||
"futures",
|
||||
@@ -3584,6 +3585,7 @@ dependencies = [
|
||||
"rand 0.7.3",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.10.6",
|
||||
"sqlx 0.6.3",
|
||||
"tap",
|
||||
"tempfile",
|
||||
@@ -4855,6 +4857,12 @@ dependencies = [
|
||||
"tokio-stream",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "option-ext"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||
|
||||
[[package]]
|
||||
name = "ordered-float"
|
||||
version = "2.10.0"
|
||||
@@ -4864,12 +4872,6 @@ dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[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"
|
||||
@@ -5718,7 +5720,7 @@ version = "0.11.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55"
|
||||
dependencies = [
|
||||
"base64 0.21.0",
|
||||
"base64 0.21.2",
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
@@ -5988,7 +5990,7 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b"
|
||||
dependencies = [
|
||||
"base64 0.21.0",
|
||||
"base64 0.21.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7300,7 +7302,7 @@ checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum",
|
||||
"base64 0.21.0",
|
||||
"base64 0.21.2",
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
|
||||
@@ -21,6 +21,7 @@ pub use nym_client_core::config::{DebugConfig, GatewayEndpointConfig};
|
||||
|
||||
pub mod old_config_v1_1_13;
|
||||
pub mod old_config_v1_1_20;
|
||||
pub mod old_config_v1_1_20_2;
|
||||
mod persistence;
|
||||
mod template;
|
||||
|
||||
@@ -52,19 +53,6 @@ pub fn default_data_directory<P: AsRef<Path>>(id: P) -> PathBuf {
|
||||
.join(DEFAULT_DATA_DIR)
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone, Copy)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub enum SocketType {
|
||||
WebSocket,
|
||||
None,
|
||||
}
|
||||
|
||||
impl SocketType {
|
||||
pub fn is_websocket(&self) -> bool {
|
||||
matches!(self, SocketType::WebSocket)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
pub struct Config {
|
||||
#[serde(flatten)]
|
||||
@@ -187,6 +175,19 @@ impl Config {
|
||||
|
||||
// define_optional_set_inner!(Config, base, BaseClientConfig);
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone, Copy)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub enum SocketType {
|
||||
WebSocket,
|
||||
None,
|
||||
}
|
||||
|
||||
impl SocketType {
|
||||
pub fn is_websocket(&self) -> bool {
|
||||
matches!(self, SocketType::WebSocket)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct Socket {
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
// 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 crate::client::config::old_config_v1_1_20_2::{
|
||||
ClientPathsV1_1_20_2, ConfigV1_1_20_2, SocketTypeV1_1_20_2, SocketV1_1_20_2,
|
||||
};
|
||||
use nym_bin_common::logging::LoggingSettings;
|
||||
use nym_client_core::config::disk_persistence::keys_paths::ClientKeysPaths;
|
||||
use nym_client_core::config::disk_persistence::CommonClientPaths;
|
||||
use nym_client_core::config::disk_persistence::old_v1_1_20_2::CommonClientPathsV1_1_20_2;
|
||||
use nym_client_core::config::old_config_v1_1_20::ConfigV1_1_20 as BaseConfigV1_1_20;
|
||||
use nym_client_core::config::{Client, Config as BaseConfig};
|
||||
use nym_client_core::config::old_config_v1_1_20_2::{
|
||||
ClientV1_1_20_2, ConfigV1_1_20_2 as BaseConfigV1_1_20_2,
|
||||
};
|
||||
use nym_config::defaults::DEFAULT_WEBSOCKET_LISTENING_PORT;
|
||||
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -22,11 +25,11 @@ pub enum SocketTypeV1_1_20 {
|
||||
None,
|
||||
}
|
||||
|
||||
impl From<SocketTypeV1_1_20> for SocketType {
|
||||
impl From<SocketTypeV1_1_20> for SocketTypeV1_1_20_2 {
|
||||
fn from(value: SocketTypeV1_1_20) -> Self {
|
||||
match value {
|
||||
SocketTypeV1_1_20::WebSocket => SocketType::WebSocket,
|
||||
SocketTypeV1_1_20::None => SocketType::None,
|
||||
SocketTypeV1_1_20::WebSocket => SocketTypeV1_1_20_2::WebSocket,
|
||||
SocketTypeV1_1_20::None => SocketTypeV1_1_20_2::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,11 +43,11 @@ pub struct ConfigV1_1_20 {
|
||||
pub socket: SocketV1_1_20,
|
||||
}
|
||||
|
||||
impl From<ConfigV1_1_20> for Config {
|
||||
impl From<ConfigV1_1_20> for ConfigV1_1_20_2 {
|
||||
fn from(value: ConfigV1_1_20) -> Self {
|
||||
Config {
|
||||
base: BaseConfig {
|
||||
client: Client {
|
||||
ConfigV1_1_20_2 {
|
||||
base: BaseConfigV1_1_20_2 {
|
||||
client: ClientV1_1_20_2 {
|
||||
version: value.base.client.version,
|
||||
id: value.base.client.id,
|
||||
disabled_credentials_mode: value.base.client.disabled_credentials_mode,
|
||||
@@ -55,8 +58,8 @@ impl From<ConfigV1_1_20> for Config {
|
||||
debug: value.base.debug.into(),
|
||||
},
|
||||
socket: value.socket.into(),
|
||||
storage_paths: ClientPaths {
|
||||
common_paths: CommonClientPaths {
|
||||
storage_paths: ClientPathsV1_1_20_2 {
|
||||
common_paths: CommonClientPathsV1_1_20_2 {
|
||||
keys: ClientKeysPaths {
|
||||
private_identity_key_file: value.base.client.private_identity_key_file,
|
||||
public_identity_key_file: value.base.client.public_identity_key_file,
|
||||
@@ -91,9 +94,9 @@ pub struct SocketV1_1_20 {
|
||||
listening_port: u16,
|
||||
}
|
||||
|
||||
impl From<SocketV1_1_20> for Socket {
|
||||
impl From<SocketV1_1_20> for SocketV1_1_20_2 {
|
||||
fn from(value: SocketV1_1_20) -> Self {
|
||||
Socket {
|
||||
SocketV1_1_20_2 {
|
||||
socket_type: value.socket_type.into(),
|
||||
host: value.host,
|
||||
listening_port: value.listening_port,
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::config::persistence::ClientPaths;
|
||||
use crate::client::config::{default_config_filepath, Config, Socket, SocketType};
|
||||
use nym_bin_common::logging::LoggingSettings;
|
||||
use nym_client_core::config::disk_persistence::old_v1_1_20_2::CommonClientPathsV1_1_20_2;
|
||||
use nym_client_core::config::old_config_v1_1_20_2::ConfigV1_1_20_2 as BaseConfigV1_1_20_2;
|
||||
use nym_client_core::config::GatewayEndpointConfig;
|
||||
use nym_config::read_config_from_toml_file;
|
||||
use nym_network_defaults::DEFAULT_WEBSOCKET_LISTENING_PORT;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::io;
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone)]
|
||||
pub struct ClientPathsV1_1_20_2 {
|
||||
#[serde(flatten)]
|
||||
pub common_paths: CommonClientPathsV1_1_20_2,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
pub struct ConfigV1_1_20_2 {
|
||||
#[serde(flatten)]
|
||||
pub base: BaseConfigV1_1_20_2,
|
||||
|
||||
pub socket: SocketV1_1_20_2,
|
||||
|
||||
pub storage_paths: ClientPathsV1_1_20_2,
|
||||
|
||||
pub logging: LoggingSettings,
|
||||
}
|
||||
|
||||
impl ConfigV1_1_20_2 {
|
||||
pub fn read_from_toml_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
|
||||
read_config_from_toml_file(path)
|
||||
}
|
||||
|
||||
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
|
||||
Self::read_from_toml_file(default_config_filepath(id))
|
||||
}
|
||||
|
||||
// in this upgrade, gateway endpoint configuration was moved out of the config file,
|
||||
// so its returned to be stored elsewhere.
|
||||
pub fn upgrade(self) -> (Config, GatewayEndpointConfig) {
|
||||
let gateway_details = self.base.client.gateway_endpoint.clone().into();
|
||||
let config = Config {
|
||||
base: self.base.into(),
|
||||
socket: self.socket.into(),
|
||||
storage_paths: ClientPaths {
|
||||
common_paths: self.storage_paths.common_paths.upgrade_default(),
|
||||
},
|
||||
logging: self.logging,
|
||||
};
|
||||
|
||||
(config, gateway_details)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone, Copy)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub enum SocketTypeV1_1_20_2 {
|
||||
WebSocket,
|
||||
None,
|
||||
}
|
||||
|
||||
impl From<SocketTypeV1_1_20_2> for SocketType {
|
||||
fn from(value: SocketTypeV1_1_20_2) -> Self {
|
||||
match value {
|
||||
SocketTypeV1_1_20_2::WebSocket => SocketType::WebSocket,
|
||||
SocketTypeV1_1_20_2::None => SocketType::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct SocketV1_1_20_2 {
|
||||
pub socket_type: SocketTypeV1_1_20_2,
|
||||
pub host: IpAddr,
|
||||
pub listening_port: u16,
|
||||
}
|
||||
|
||||
impl From<SocketV1_1_20_2> for Socket {
|
||||
fn from(value: SocketV1_1_20_2) -> Self {
|
||||
Socket {
|
||||
socket_type: value.socket_type.into(),
|
||||
host: value.host,
|
||||
listening_port: value.listening_port,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SocketV1_1_20_2 {
|
||||
fn default() -> Self {
|
||||
SocketV1_1_20_2 {
|
||||
socket_type: SocketTypeV1_1_20_2::WebSocket,
|
||||
host: IpAddr::V4(Ipv4Addr::LOCALHOST),
|
||||
listening_port: DEFAULT_WEBSOCKET_LISTENING_PORT,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,18 +64,9 @@ 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 }}'
|
||||
|
||||
# Address of the gateway owner to which the client should send messages.
|
||||
gateway_owner = '{{ client.gateway_endpoint.gateway_owner }}'
|
||||
|
||||
# Address of the gateway listener to which all client requests should be sent.
|
||||
gateway_listener = '{{ client.gateway_endpoint.gateway_listener }}'
|
||||
|
||||
|
||||
# Path to the file containing information about gateway used by this client,
|
||||
# i.e. details such as its public key, owner address or the network information.
|
||||
gateway_details = '{{ storage_paths.gateway_details }}'
|
||||
|
||||
##### socket config options #####
|
||||
|
||||
|
||||
@@ -9,15 +9,12 @@ use log::*;
|
||||
use nym_client_core::client::base_client::non_wasm_helpers::default_query_dkg_client_from_config;
|
||||
use nym_client_core::client::base_client::storage::OnDiskPersistent;
|
||||
use nym_client_core::client::base_client::{
|
||||
non_wasm_helpers, BaseClientBuilder, ClientInput, ClientOutput, ClientState,
|
||||
BaseClientBuilder, ClientInput, ClientOutput, ClientState,
|
||||
};
|
||||
use nym_client_core::client::inbound_messages::InputMessage;
|
||||
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
|
||||
use nym_client_core::client::received_buffer::{
|
||||
ReceivedBufferMessage, ReceivedBufferRequestSender, ReconstructedMessagesReceiver,
|
||||
};
|
||||
use nym_client_core::client::replies::reply_storage::fs_backend;
|
||||
use nym_credential_storage::persistent_storage::PersistentStorage as PersistentCredentialStorage;
|
||||
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
|
||||
use nym_sphinx::params::PacketType;
|
||||
use nym_task::connections::TransmissionLane;
|
||||
@@ -94,32 +91,12 @@ impl SocketClient {
|
||||
res
|
||||
}
|
||||
|
||||
fn initialise_key_store(&self) -> OnDiskKeys {
|
||||
OnDiskKeys::new(self.config.storage_paths.common_paths.keys.clone())
|
||||
}
|
||||
|
||||
async fn initialise_credential_store(&self) -> PersistentCredentialStorage {
|
||||
nym_credential_storage::initialise_persistent_storage(
|
||||
&self.config.storage_paths.common_paths.credentials_database,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn initialise_reply_surb_store(&self) -> Result<fs_backend::Backend, ClientError> {
|
||||
non_wasm_helpers::setup_fs_reply_surb_backend(
|
||||
&self.config.storage_paths.common_paths.reply_surb_database,
|
||||
&self.config.base.debug.reply_surbs,
|
||||
)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
async fn initialise_storage(&self) -> Result<OnDiskPersistent, ClientError> {
|
||||
Ok(OnDiskPersistent::new(
|
||||
self.initialise_key_store(),
|
||||
self.initialise_reply_surb_store().await?,
|
||||
self.initialise_credential_store().await,
|
||||
))
|
||||
Ok(OnDiskPersistent::from_paths(
|
||||
self.config.storage_paths.common_paths.clone(),
|
||||
&self.config.base.debug,
|
||||
)
|
||||
.await?)
|
||||
}
|
||||
|
||||
// TODO: see if this could also be shared with socks5 client / nym-sdk maybe
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use crate::client::config::{
|
||||
default_config_directory, default_config_filepath, default_data_directory,
|
||||
};
|
||||
use crate::commands::try_load_current_config;
|
||||
use crate::commands::try_upgrade_config;
|
||||
use crate::{
|
||||
client::config::Config,
|
||||
commands::{override_config, OverrideConfig},
|
||||
@@ -12,7 +12,10 @@ use crate::{
|
||||
};
|
||||
use clap::Args;
|
||||
use nym_bin_common::output_format::OutputFormat;
|
||||
use nym_client_core::client::base_client::storage::gateway_details::OnDiskGatewayDetails;
|
||||
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
|
||||
use nym_client_core::config::GatewayEndpointConfig;
|
||||
use nym_client_core::init::GatewaySetup;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use serde::Serialize;
|
||||
@@ -105,9 +108,9 @@ pub struct InitResults {
|
||||
}
|
||||
|
||||
impl InitResults {
|
||||
fn new(config: &Config, address: &Recipient) -> Self {
|
||||
fn new(config: &Config, address: &Recipient, gateway: &GatewayEndpointConfig) -> Self {
|
||||
Self {
|
||||
client_core: nym_client_core::init::InitResults::new(&config.base, address),
|
||||
client_core: nym_client_core::init::InitResults::new(&config.base, address, gateway),
|
||||
client_listening_port: config.socket.listening_port,
|
||||
client_address: address.to_string(),
|
||||
}
|
||||
@@ -132,13 +135,15 @@ pub(crate) async fn execute(args: &Init) -> Result<(), ClientError> {
|
||||
|
||||
let id = &args.id;
|
||||
|
||||
let old_config = if default_config_filepath(id).exists() {
|
||||
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_config(id)?;
|
||||
eprintln!("Client \"{id}\" was already initialised before");
|
||||
// if the file exist, try to load it (with checking for errors)
|
||||
Some(try_load_current_config(&args.id)?)
|
||||
true
|
||||
} else {
|
||||
init_paths(&args.id)?;
|
||||
None
|
||||
init_paths(id)?;
|
||||
false
|
||||
};
|
||||
|
||||
// Usually you only register with the gateway on the first init, however you can force
|
||||
@@ -151,30 +156,33 @@ pub(crate) async fn execute(args: &Init) -> Result<(), ClientError> {
|
||||
// If the client was already initialized, don't generate new keys and don't re-register with
|
||||
// the gateway (because this would create a new shared key).
|
||||
// Unless the user really wants to.
|
||||
let register_gateway = old_config.is_none() || user_wants_force_register;
|
||||
let register_gateway = !already_init || user_wants_force_register;
|
||||
|
||||
// Attempt to use a user-provided gateway, if possible
|
||||
let user_chosen_gateway_id = args.gateway;
|
||||
let gateway_setup = GatewaySetup::new_fresh(
|
||||
user_chosen_gateway_id.map(|id| id.to_base58_string()),
|
||||
Some(args.latency_based_selection),
|
||||
);
|
||||
|
||||
// Load and potentially override config
|
||||
let mut config = override_config(Config::new(id), OverrideConfig::from(args.clone()));
|
||||
let config = override_config(Config::new(id), OverrideConfig::from(args.clone()));
|
||||
|
||||
// Setup gateway by either registering a new one, or creating a new config from the selected
|
||||
// one but with keys kept, or reusing the gateway configuration.
|
||||
let key_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone());
|
||||
let gateway = nym_client_core::init::setup_gateway_from_config::<_>(
|
||||
let details_store =
|
||||
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
|
||||
let init_details = nym_client_core::init::setup_gateway(
|
||||
&gateway_setup,
|
||||
&key_store,
|
||||
&details_store,
|
||||
register_gateway,
|
||||
user_chosen_gateway_id,
|
||||
&config.base,
|
||||
old_config.map(|cfg| cfg.base.client.gateway_endpoint),
|
||||
args.latency_based_selection,
|
||||
Some(&config.base.client.nym_api_urls),
|
||||
)
|
||||
.await
|
||||
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?;
|
||||
|
||||
config.base.set_gateway_endpoint(gateway);
|
||||
|
||||
let config_save_location = config.default_location();
|
||||
config.save_to_default_location().tap_err(|_| {
|
||||
log::error!("Failed to save the config file");
|
||||
@@ -184,14 +192,11 @@ pub(crate) async fn execute(args: &Init) -> Result<(), ClientError> {
|
||||
config_save_location.display()
|
||||
);
|
||||
|
||||
let address = nym_client_core::init::get_client_address_from_stored_ondisk_keys(
|
||||
&config.storage_paths.common_paths.keys,
|
||||
&config.base.client.gateway_endpoint,
|
||||
)?;
|
||||
let address = init_details.client_address()?;
|
||||
|
||||
eprintln!("Client configuration completed.\n");
|
||||
|
||||
let init_results = InitResults::new(&config, &address);
|
||||
let init_results = InitResults::new(&config, &address, &init_details.gateway_details);
|
||||
println!("{}", args.output.format(&init_results));
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
use crate::client::config::old_config_v1_1_13::OldConfigV1_1_13;
|
||||
use crate::client::config::old_config_v1_1_20::ConfigV1_1_20;
|
||||
use crate::client::config::old_config_v1_1_20_2::ConfigV1_1_20_2;
|
||||
use crate::client::config::{BaseClientConfig, Config};
|
||||
use crate::error::ClientError;
|
||||
use clap::CommandFactory;
|
||||
@@ -11,6 +12,12 @@ use lazy_static::lazy_static;
|
||||
use log::{error, info};
|
||||
use nym_bin_common::build_information::BinaryBuildInformation;
|
||||
use nym_bin_common::completions::{fig_generate, ArgShell};
|
||||
use nym_client_core::client::base_client::storage::gateway_details::{
|
||||
OnDiskGatewayDetails, PersistedGatewayDetails,
|
||||
};
|
||||
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
|
||||
use nym_client_core::config::GatewayEndpointConfig;
|
||||
use nym_client_core::error::ClientCoreError;
|
||||
use nym_config::OptionalSet;
|
||||
use std::error::Error;
|
||||
use std::net::IpAddr;
|
||||
@@ -109,6 +116,28 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
|
||||
)
|
||||
}
|
||||
|
||||
fn persist_gateway_details(
|
||||
config: &Config,
|
||||
details: GatewayEndpointConfig,
|
||||
) -> Result<(), ClientError> {
|
||||
let details_store =
|
||||
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
|
||||
let keys_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone());
|
||||
let shared_keys = keys_store.ephemeral_load_gateway_keys().map_err(|source| {
|
||||
ClientError::ClientCoreError(ClientCoreError::KeyStoreError {
|
||||
source: Box::new(source),
|
||||
})
|
||||
})?;
|
||||
let persisted_details = PersistedGatewayDetails::new(details, &shared_keys);
|
||||
details_store
|
||||
.store_to_disk(&persisted_details)
|
||||
.map_err(|source| {
|
||||
ClientError::ClientCoreError(ClientCoreError::GatewayDetailsStoreError {
|
||||
source: Box::new(source),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, ClientError> {
|
||||
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
|
||||
|
||||
@@ -122,7 +151,9 @@ fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, ClientError> {
|
||||
info!("It is going to get updated to the current specification.");
|
||||
|
||||
let updated_step1: ConfigV1_1_20 = old_config.into();
|
||||
let updated: Config = updated_step1.into();
|
||||
let updated_step2: ConfigV1_1_20_2 = updated_step1.into();
|
||||
let (updated, gateway_config) = updated_step2.upgrade();
|
||||
persist_gateway_details(&updated, gateway_config)?;
|
||||
|
||||
updated.save_to_default_location()?;
|
||||
Ok(true)
|
||||
@@ -140,21 +171,56 @@ fn try_upgrade_v1_1_20_config(id: &str) -> Result<bool, ClientError> {
|
||||
info!("It seems the client is using <= v1.1.20 config template.");
|
||||
info!("It is going to get updated to the current specification.");
|
||||
|
||||
let updated: Config = old_config.into();
|
||||
let updated_step1: ConfigV1_1_20_2 = old_config.into();
|
||||
let (updated, gateway_config) = updated_step1.upgrade();
|
||||
persist_gateway_details(&updated, gateway_config)?;
|
||||
|
||||
updated.save_to_default_location()?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn try_upgrade_v1_1_20_2_config(id: &str) -> Result<bool, ClientError> {
|
||||
// explicitly load it as v1.1.20_2 (which is incompatible with the current one, i.e. +1.1.21)
|
||||
let Ok(old_config) = ConfigV1_1_20_2::read_from_default_path(id) else {
|
||||
// if we failed to load it, there might have been nothing to upgrade
|
||||
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
|
||||
return Ok(false);
|
||||
};
|
||||
info!("It seems the client is using <= v1.1.20_2 config template.");
|
||||
info!("It is going to get updated to the current specification.");
|
||||
|
||||
let (updated, gateway_config) = old_config.upgrade();
|
||||
persist_gateway_details(&updated, gateway_config)?;
|
||||
|
||||
updated.save_to_default_location()?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn try_upgrade_config(id: &str) -> Result<(), ClientError> {
|
||||
let upgraded = try_upgrade_v1_1_13_config(id)?;
|
||||
if !upgraded {
|
||||
try_upgrade_v1_1_20_config(id)?;
|
||||
if try_upgrade_v1_1_13_config(id)? {
|
||||
return Ok(());
|
||||
}
|
||||
if try_upgrade_v1_1_20_config(id)? {
|
||||
return Ok(());
|
||||
}
|
||||
if try_upgrade_v1_1_20_2_config(id)? {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn try_load_current_config(id: &str) -> Result<Config, ClientError> {
|
||||
// try to load the config as is
|
||||
if let Ok(cfg) = Config::read_from_default_path(id) {
|
||||
return if !cfg.validate() {
|
||||
Err(ClientError::ConfigValidationFailure)
|
||||
} else {
|
||||
Ok(cfg)
|
||||
};
|
||||
}
|
||||
|
||||
// we couldn't load it - try upgrading it from older revisions
|
||||
try_upgrade_config(id)?;
|
||||
|
||||
let config = match Config::read_from_default_path(id) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::commands::try_load_current_config;
|
||||
use crate::commands::try_upgrade_config;
|
||||
use crate::config::{
|
||||
default_config_directory, default_config_filepath, default_data_directory, Config,
|
||||
};
|
||||
@@ -11,7 +11,10 @@ use crate::{
|
||||
};
|
||||
use clap::Args;
|
||||
use nym_bin_common::output_format::OutputFormat;
|
||||
use nym_client_core::client::base_client::storage::gateway_details::OnDiskGatewayDetails;
|
||||
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
|
||||
use nym_client_core::config::GatewayEndpointConfig;
|
||||
use nym_client_core::init::GatewaySetup;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use serde::Serialize;
|
||||
@@ -107,9 +110,13 @@ pub struct InitResults {
|
||||
}
|
||||
|
||||
impl InitResults {
|
||||
fn new(config: &Config, address: &Recipient) -> Self {
|
||||
fn new(config: &Config, address: &Recipient, gateway: &GatewayEndpointConfig) -> Self {
|
||||
Self {
|
||||
client_core: nym_client_core::init::InitResults::new(&config.core.base, address),
|
||||
client_core: nym_client_core::init::InitResults::new(
|
||||
&config.core.base,
|
||||
address,
|
||||
gateway,
|
||||
),
|
||||
socks5_listening_port: config.core.socks5.listening_port,
|
||||
client_address: address.to_string(),
|
||||
}
|
||||
@@ -135,13 +142,15 @@ pub(crate) async fn execute(args: &Init) -> Result<(), Socks5ClientError> {
|
||||
let id = &args.id;
|
||||
let provider_address = &args.provider;
|
||||
|
||||
let old_config = if default_config_filepath(id).exists() {
|
||||
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_config(id)?;
|
||||
eprintln!("SOCKS5 client \"{id}\" was already initialised before");
|
||||
// if the file exist, try to load it (with checking for errors)
|
||||
Some(try_load_current_config(&args.id)?)
|
||||
true
|
||||
} else {
|
||||
init_paths(&args.id)?;
|
||||
None
|
||||
init_paths(id)?;
|
||||
false
|
||||
};
|
||||
|
||||
// Usually you only register with the gateway on the first init, however you can force
|
||||
@@ -154,13 +163,17 @@ pub(crate) async fn execute(args: &Init) -> Result<(), Socks5ClientError> {
|
||||
// If the client was already initialized, don't generate new keys and don't re-register with
|
||||
// the gateway (because this would create a new shared key).
|
||||
// Unless the user really wants to.
|
||||
let register_gateway = old_config.is_none() || user_wants_force_register;
|
||||
let register_gateway = !already_init || user_wants_force_register;
|
||||
|
||||
// Attempt to use a user-provided gateway, if possible
|
||||
let user_chosen_gateway_id = args.gateway;
|
||||
let gateway_setup = GatewaySetup::new_fresh(
|
||||
user_chosen_gateway_id.map(|id| id.to_base58_string()),
|
||||
Some(args.latency_based_selection),
|
||||
);
|
||||
|
||||
// Load and potentially override config
|
||||
let mut config = override_config(
|
||||
let config = override_config(
|
||||
Config::new(id, &provider_address.to_string()),
|
||||
OverrideConfig::from(args.clone()),
|
||||
);
|
||||
@@ -168,19 +181,18 @@ 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::new(config.storage_paths.common_paths.keys.clone());
|
||||
let gateway = nym_client_core::init::setup_gateway_from_config::<_>(
|
||||
let details_store =
|
||||
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
|
||||
let init_details = nym_client_core::init::setup_gateway(
|
||||
&gateway_setup,
|
||||
&key_store,
|
||||
&details_store,
|
||||
register_gateway,
|
||||
user_chosen_gateway_id,
|
||||
&config.core.base,
|
||||
old_config.map(|cfg| cfg.core.base.client.gateway_endpoint),
|
||||
args.latency_based_selection,
|
||||
Some(&config.core.base.client.nym_api_urls),
|
||||
)
|
||||
.await
|
||||
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?;
|
||||
|
||||
config.core.base.set_gateway_endpoint(gateway);
|
||||
|
||||
// TODO: ask the service provider we specified for its interface version and set it in the config
|
||||
|
||||
let config_save_location = config.default_location();
|
||||
@@ -192,11 +204,9 @@ pub(crate) async fn execute(args: &Init) -> Result<(), Socks5ClientError> {
|
||||
config_save_location.display()
|
||||
);
|
||||
|
||||
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);
|
||||
let address = init_details.client_address()?;
|
||||
|
||||
let init_results = InitResults::new(&config, &address, &init_details.gateway_details);
|
||||
println!("{}", args.output.format(&init_results));
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
use crate::config::old_config_v1_1_13::OldConfigV1_1_13;
|
||||
use crate::config::old_config_v1_1_20::ConfigV1_1_20;
|
||||
use crate::config::old_config_v1_1_20_2::ConfigV1_1_20_2;
|
||||
use crate::config::{BaseClientConfig, Config};
|
||||
use crate::error::Socks5ClientError;
|
||||
use clap::CommandFactory;
|
||||
@@ -11,6 +12,12 @@ use lazy_static::lazy_static;
|
||||
use log::{error, info};
|
||||
use nym_bin_common::build_information::BinaryBuildInformation;
|
||||
use nym_bin_common::completions::{fig_generate, ArgShell};
|
||||
use nym_client_core::client::base_client::storage::gateway_details::{
|
||||
OnDiskGatewayDetails, PersistedGatewayDetails,
|
||||
};
|
||||
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
|
||||
use nym_client_core::config::GatewayEndpointConfig;
|
||||
use nym_client_core::error::ClientCoreError;
|
||||
use nym_config::OptionalSet;
|
||||
use nym_sphinx::params::PacketType;
|
||||
use std::error::Error;
|
||||
@@ -116,6 +123,28 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
|
||||
)
|
||||
}
|
||||
|
||||
fn persist_gateway_details(
|
||||
config: &Config,
|
||||
details: GatewayEndpointConfig,
|
||||
) -> Result<(), Socks5ClientError> {
|
||||
let details_store =
|
||||
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
|
||||
let keys_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone());
|
||||
let shared_keys = keys_store.ephemeral_load_gateway_keys().map_err(|source| {
|
||||
Socks5ClientError::ClientCoreError(ClientCoreError::KeyStoreError {
|
||||
source: Box::new(source),
|
||||
})
|
||||
})?;
|
||||
let persisted_details = PersistedGatewayDetails::new(details, &shared_keys);
|
||||
details_store
|
||||
.store_to_disk(&persisted_details)
|
||||
.map_err(|source| {
|
||||
Socks5ClientError::ClientCoreError(ClientCoreError::GatewayDetailsStoreError {
|
||||
source: Box::new(source),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, Socks5ClientError> {
|
||||
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
|
||||
|
||||
@@ -129,7 +158,9 @@ fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, Socks5ClientError> {
|
||||
info!("It is going to get updated to the current specification.");
|
||||
|
||||
let updated_step1: ConfigV1_1_20 = old_config.into();
|
||||
let updated: Config = updated_step1.into();
|
||||
let updated_step2: ConfigV1_1_20_2 = updated_step1.into();
|
||||
let (updated, gateway_config) = updated_step2.upgrade();
|
||||
persist_gateway_details(&updated, gateway_config)?;
|
||||
|
||||
updated.save_to_default_location()?;
|
||||
Ok(true)
|
||||
@@ -147,21 +178,56 @@ fn try_upgrade_v1_1_20_config(id: &str) -> Result<bool, Socks5ClientError> {
|
||||
info!("It seems the client is using <= v1.1.20 config template.");
|
||||
info!("It is going to get updated to the current specification.");
|
||||
|
||||
let updated: Config = old_config.into();
|
||||
let updated_step1: ConfigV1_1_20_2 = old_config.into();
|
||||
let (updated, gateway_config) = updated_step1.upgrade();
|
||||
persist_gateway_details(&updated, gateway_config)?;
|
||||
|
||||
updated.save_to_default_location()?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn try_upgrade_v1_1_20_2_config(id: &str) -> Result<bool, Socks5ClientError> {
|
||||
// explicitly load it as v1.1.20_2 (which is incompatible with the current one, i.e. +1.1.21)
|
||||
let Ok(old_config) = ConfigV1_1_20_2::read_from_default_path(id) else {
|
||||
// if we failed to load it, there might have been nothing to upgrade
|
||||
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
|
||||
return Ok(false);
|
||||
};
|
||||
info!("It seems the client is using <= v1.1.20_2 config template.");
|
||||
info!("It is going to get updated to the current specification.");
|
||||
|
||||
let (updated, gateway_config) = old_config.upgrade();
|
||||
persist_gateway_details(&updated, gateway_config)?;
|
||||
|
||||
updated.save_to_default_location()?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn try_upgrade_config(id: &str) -> Result<(), Socks5ClientError> {
|
||||
let upgraded = try_upgrade_v1_1_13_config(id)?;
|
||||
if !upgraded {
|
||||
try_upgrade_v1_1_20_config(id)?;
|
||||
if try_upgrade_v1_1_13_config(id)? {
|
||||
return Ok(());
|
||||
}
|
||||
if try_upgrade_v1_1_20_config(id)? {
|
||||
return Ok(());
|
||||
}
|
||||
if try_upgrade_v1_1_20_2_config(id)? {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn try_load_current_config(id: &str) -> Result<Config, Socks5ClientError> {
|
||||
// try to load the config as is
|
||||
if let Ok(cfg) = Config::read_from_default_path(id) {
|
||||
return if !cfg.validate() {
|
||||
Err(Socks5ClientError::ConfigValidationFailure)
|
||||
} else {
|
||||
Ok(cfg)
|
||||
};
|
||||
}
|
||||
|
||||
// we couldn't load it - try upgrading it from older revisions
|
||||
try_upgrade_config(id)?;
|
||||
|
||||
let config = match Config::read_from_default_path(id) {
|
||||
|
||||
@@ -19,6 +19,7 @@ pub use nym_socks5_client_core::config::Config as CoreConfig;
|
||||
|
||||
pub mod old_config_v1_1_13;
|
||||
pub mod old_config_v1_1_20;
|
||||
pub mod old_config_v1_1_20_2;
|
||||
mod persistence;
|
||||
mod template;
|
||||
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
// 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 crate::config::old_config_v1_1_20_2::{
|
||||
ConfigV1_1_20_2, CoreConfigV1_1_20_2, SocksClientPathsV1_1_20_2,
|
||||
};
|
||||
use nym_bin_common::logging::LoggingSettings;
|
||||
use nym_client_core::config::disk_persistence::keys_paths::ClientKeysPaths;
|
||||
use nym_client_core::config::disk_persistence::CommonClientPaths;
|
||||
use nym_client_core::config::disk_persistence::old_v1_1_20_2::CommonClientPathsV1_1_20_2;
|
||||
use nym_client_core::config::old_config_v1_1_20::ConfigV1_1_20 as BaseConfigV1_1_20;
|
||||
use nym_client_core::config::Client;
|
||||
use nym_client_core::config::old_config_v1_1_20_2::ClientV1_1_20_2;
|
||||
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
|
||||
use nym_config::must_get_home;
|
||||
use nym_socks5_client_core::config::{
|
||||
ProviderInterfaceVersion, Socks5, Socks5Debug, Socks5ProtocolVersion,
|
||||
use nym_socks5_client_core::config::old_config_v1_1_20_2::{
|
||||
BaseClientConfigV1_1_20_2, Socks5DebugV1_1_20_2, Socks5V1_1_20_2,
|
||||
};
|
||||
use nym_socks5_client_core::config::{ProviderInterfaceVersion, Socks5ProtocolVersion};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Debug;
|
||||
use std::path::PathBuf;
|
||||
@@ -29,12 +31,12 @@ pub struct ConfigV1_1_20 {
|
||||
pub socks5: Socks5V1_1_20,
|
||||
}
|
||||
|
||||
impl From<ConfigV1_1_20> for Config {
|
||||
impl From<ConfigV1_1_20> for ConfigV1_1_20_2 {
|
||||
fn from(value: ConfigV1_1_20) -> Self {
|
||||
Config {
|
||||
core: CoreConfig {
|
||||
base: BaseClientConfig {
|
||||
client: Client {
|
||||
ConfigV1_1_20_2 {
|
||||
core: CoreConfigV1_1_20_2 {
|
||||
base: BaseClientConfigV1_1_20_2 {
|
||||
client: ClientV1_1_20_2 {
|
||||
version: value.base.client.version,
|
||||
id: value.base.client.id,
|
||||
disabled_credentials_mode: value.base.client.disabled_credentials_mode,
|
||||
@@ -46,8 +48,8 @@ impl From<ConfigV1_1_20> for Config {
|
||||
},
|
||||
socks5: value.socks5.into(),
|
||||
},
|
||||
storage_paths: SocksClientPaths {
|
||||
common_paths: CommonClientPaths {
|
||||
storage_paths: SocksClientPathsV1_1_20_2 {
|
||||
common_paths: CommonClientPathsV1_1_20_2 {
|
||||
keys: ClientKeysPaths {
|
||||
private_identity_key_file: value.base.client.private_identity_key_file,
|
||||
public_identity_key_file: value.base.client.public_identity_key_file,
|
||||
@@ -96,9 +98,9 @@ pub struct Socks5V1_1_20 {
|
||||
pub socks5_debug: Socks5DebugV1_1_20,
|
||||
}
|
||||
|
||||
impl From<Socks5V1_1_20> for Socks5 {
|
||||
impl From<Socks5V1_1_20> for Socks5V1_1_20_2 {
|
||||
fn from(value: Socks5V1_1_20) -> Self {
|
||||
Socks5 {
|
||||
Socks5V1_1_20_2 {
|
||||
listening_port: value.listening_port,
|
||||
provider_mix_address: value.provider_mix_address,
|
||||
provider_interface_version: value.provider_interface_version,
|
||||
@@ -116,9 +118,9 @@ pub struct Socks5DebugV1_1_20 {
|
||||
per_request_surbs: u32,
|
||||
}
|
||||
|
||||
impl From<Socks5DebugV1_1_20> for Socks5Debug {
|
||||
impl From<Socks5DebugV1_1_20> for Socks5DebugV1_1_20_2 {
|
||||
fn from(value: Socks5DebugV1_1_20) -> Self {
|
||||
Socks5Debug {
|
||||
Socks5DebugV1_1_20_2 {
|
||||
connection_start_surbs: value.connection_start_surbs,
|
||||
per_request_surbs: value.per_request_surbs,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::persistence::SocksClientPaths;
|
||||
use crate::config::{default_config_filepath, Config};
|
||||
use nym_bin_common::logging::LoggingSettings;
|
||||
use nym_client_core::config::disk_persistence::old_v1_1_20_2::CommonClientPathsV1_1_20_2;
|
||||
use nym_client_core::config::GatewayEndpointConfig;
|
||||
use nym_config::read_config_from_toml_file;
|
||||
pub use nym_socks5_client_core::config::old_config_v1_1_20_2::ConfigV1_1_20_2 as CoreConfigV1_1_20_2;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone)]
|
||||
pub struct SocksClientPathsV1_1_20_2 {
|
||||
#[serde(flatten)]
|
||||
pub common_paths: CommonClientPathsV1_1_20_2,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ConfigV1_1_20_2 {
|
||||
pub core: CoreConfigV1_1_20_2,
|
||||
|
||||
pub storage_paths: SocksClientPathsV1_1_20_2,
|
||||
|
||||
pub logging: LoggingSettings,
|
||||
}
|
||||
|
||||
impl ConfigV1_1_20_2 {
|
||||
pub fn read_from_toml_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
|
||||
read_config_from_toml_file(path)
|
||||
}
|
||||
|
||||
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
|
||||
Self::read_from_toml_file(default_config_filepath(id))
|
||||
}
|
||||
|
||||
// in this upgrade, gateway endpoint configuration was moved out of the config file,
|
||||
// so its returned to be stored elsewhere.
|
||||
pub fn upgrade(self) -> (Config, GatewayEndpointConfig) {
|
||||
let gateway_details = self.core.base.client.gateway_endpoint.clone().into();
|
||||
let config = Config {
|
||||
core: self.core.into(),
|
||||
storage_paths: SocksClientPaths {
|
||||
common_paths: self.storage_paths.common_paths.upgrade_default(),
|
||||
},
|
||||
logging: self.logging,
|
||||
};
|
||||
|
||||
(config, gateway_details)
|
||||
}
|
||||
}
|
||||
@@ -64,17 +64,9 @@ 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
|
||||
[core.client.gateway_endpoint]
|
||||
# ID of the gateway from which the client should be fetching messages.
|
||||
gateway_id = '{{ core.client.gateway_endpoint.gateway_id }}'
|
||||
|
||||
# Address of the gateway owner to which the client should send messages.
|
||||
gateway_owner = '{{ core.client.gateway_endpoint.gateway_owner }}'
|
||||
|
||||
# Address of the gateway listener to which all client requests should be sent.
|
||||
gateway_listener = '{{ core.client.gateway_endpoint.gateway_listener }}'
|
||||
|
||||
# Path to the file containing information about gateway used by this client,
|
||||
# i.e. details such as its public key, owner address or the network information.
|
||||
gateway_details = '{{ storage_paths.gateway_details }}'
|
||||
|
||||
##### socket config options #####
|
||||
|
||||
|
||||
Generated
+6
-4
@@ -225,9 +225,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.0"
|
||||
version = "0.21.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
|
||||
checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
@@ -2258,6 +2258,7 @@ name = "nym-client-core"
|
||||
version = "1.1.14"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.21.2",
|
||||
"dashmap",
|
||||
"dirs 4.0.0",
|
||||
"futures",
|
||||
@@ -2280,6 +2281,7 @@ dependencies = [
|
||||
"rand 0.7.3",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.10.6",
|
||||
"sqlx 0.6.3",
|
||||
"tap",
|
||||
"thiserror",
|
||||
@@ -3496,7 +3498,7 @@ version = "0.11.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55"
|
||||
dependencies = [
|
||||
"base64 0.21.0",
|
||||
"base64 0.21.2",
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
@@ -3630,7 +3632,7 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b"
|
||||
dependencies = [
|
||||
"base64 0.21.0",
|
||||
"base64 0.21.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::client::response_pusher::ResponsePusher;
|
||||
use crate::constants::NODE_TESTER_CLIENT_ID;
|
||||
use crate::error::WasmClientError;
|
||||
use crate::helpers::{
|
||||
choose_gateway, gateway_from_topology, parse_recipient, parse_sender_tag,
|
||||
parse_recipient, parse_sender_tag, setup_from_topology, setup_gateway_from_api,
|
||||
setup_reply_surb_storage_backend,
|
||||
};
|
||||
use crate::storage::traits::FullWasmClientStorage;
|
||||
@@ -27,7 +27,7 @@ use nym_topology::provider_trait::{HardcodedTopologyProvider, TopologyProvider};
|
||||
use nym_topology::NymTopology;
|
||||
use nym_validator_client::client::IdentityKey;
|
||||
use rand::rngs::OsRng;
|
||||
use rand::{thread_rng, RngCore};
|
||||
use rand::RngCore;
|
||||
use std::sync::Arc;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen_futures::future_to_promise;
|
||||
@@ -122,7 +122,7 @@ impl NymClientBuilder {
|
||||
|
||||
fn initialise_storage(config: &Config, base_storage: ClientStorage) -> FullWasmClientStorage {
|
||||
FullWasmClientStorage {
|
||||
key_store: base_storage,
|
||||
keys_and_gateway_store: base_storage,
|
||||
reply_storage: setup_reply_surb_storage_backend(config.base.debug.reply_surbs),
|
||||
credential_storage: EphemeralCredentialStorage::default(),
|
||||
}
|
||||
@@ -138,30 +138,17 @@ impl NymClientBuilder {
|
||||
ClientStorage::new_async(&self.config.base.client.id, self.storage_passphrase.take())
|
||||
.await?;
|
||||
|
||||
let user_chosen = self.preferred_gateway.clone();
|
||||
|
||||
// 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.preferred_gateway.as_deref(),
|
||||
topology,
|
||||
&client_store,
|
||||
)
|
||||
.await?
|
||||
if let Some(topology) = &self.custom_topology {
|
||||
setup_from_topology(user_chosen, topology, &client_store).await?
|
||||
} else {
|
||||
choose_gateway(
|
||||
&client_store,
|
||||
self.preferred_gateway.clone(),
|
||||
&nym_api_endpoints,
|
||||
)
|
||||
.await?
|
||||
setup_gateway_from_api(&client_store, user_chosen, &nym_api_endpoints).await?
|
||||
};
|
||||
|
||||
let packet_type = self.config.base.debug.traffic.packet_type;
|
||||
|
||||
// temporary workaround until it's properly moved into storage
|
||||
self.config.base = self.config.base.with_gateway_endpoint(gateway_endpoint);
|
||||
let storage = Self::initialise_storage(&self.config, client_store);
|
||||
|
||||
let maybe_topology_provider = self.topology_provider();
|
||||
|
||||
let mut base_builder: BaseClientBuilder<_, FullWasmClientStorage> =
|
||||
|
||||
@@ -7,19 +7,18 @@ use crate::topology::WasmNymTopology;
|
||||
use js_sys::Promise;
|
||||
use nym_client_core::client::replies::reply_storage::browser_backend;
|
||||
use nym_client_core::config;
|
||||
use nym_client_core::config::GatewayEndpointConfig;
|
||||
use nym_client_core::init::GatewaySetup;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_client_core::init::helpers::current_gateways;
|
||||
use nym_client_core::init::{setup_gateway_from, GatewaySetup, InitialisationDetails};
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
|
||||
use nym_topology::NymTopology;
|
||||
use nym_validator_client::client::{IdentityKey, IdentityKeyRef};
|
||||
use nym_topology::{gateway, NymTopology};
|
||||
use nym_validator_client::client::IdentityKey;
|
||||
use nym_validator_client::NymApiClient;
|
||||
use rand::{CryptoRng, Rng};
|
||||
use rand::thread_rng;
|
||||
use url::Url;
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
use wasm_bindgen_futures::future_to_promise;
|
||||
use wasm_utils::{console_log, PromisableResult};
|
||||
use wasm_utils::PromisableResult;
|
||||
|
||||
// don't get too excited about the name, under the hood it's just a big fat placeholder
|
||||
// with no disk_persistence
|
||||
@@ -79,87 +78,37 @@ pub fn current_network_topology(nym_api_url: String) -> Promise {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) async fn choose_gateway(
|
||||
async fn setup_gateway(
|
||||
client_store: &ClientStorage,
|
||||
chosen_gateway: Option<IdentityKey>,
|
||||
gateways: &[gateway::Node],
|
||||
) -> Result<InitialisationDetails, WasmClientError> {
|
||||
let setup = if client_store.has_full_gateway_info().await? {
|
||||
GatewaySetup::MustLoad
|
||||
} else {
|
||||
GatewaySetup::new_fresh(chosen_gateway.clone(), None)
|
||||
};
|
||||
|
||||
setup_gateway_from(&setup, client_store, client_store, false, Some(gateways))
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) async fn setup_gateway_from_api(
|
||||
client_store: &ClientStorage,
|
||||
chosen_gateway: Option<IdentityKey>,
|
||||
nym_apis: &[Url],
|
||||
) -> Result<GatewayEndpointConfig, WasmClientError> {
|
||||
let existing_gateway_config = client_store.read_gateway_config().await?;
|
||||
|
||||
console_log!("loaded: {:?}", existing_gateway_config);
|
||||
|
||||
if let Some(existing) = existing_gateway_config {
|
||||
if let Some(provided) = &chosen_gateway {
|
||||
if provided != &existing.gateway_id {
|
||||
return Err(WasmClientError::AlreadyRegistered {
|
||||
gateway_config: existing,
|
||||
});
|
||||
}
|
||||
}
|
||||
return Ok(existing);
|
||||
};
|
||||
|
||||
// if NOTHING is specified nor available, choose gateway randomly.
|
||||
let setup = GatewaySetup::new(None, chosen_gateway, None);
|
||||
let config = setup.try_get_gateway_details(nym_apis).await?;
|
||||
|
||||
// perform registration + persist the new gateway info
|
||||
// TODO: this is actually quite bad. we shouldn't be persisting gateway info here since we did not have persisted
|
||||
// the shared key yet. this will only happen when we start the base client itself.
|
||||
// but unfortunately, we can't do much more until we do a bit more refactoring.
|
||||
client_store.store_gateway_config(&config).await?;
|
||||
|
||||
console_log!("stored: {:?}", config);
|
||||
|
||||
Ok(config)
|
||||
) -> Result<InitialisationDetails, WasmClientError> {
|
||||
let mut rng = thread_rng();
|
||||
let gateways = current_gateways(&mut rng, nym_apis).await?;
|
||||
setup_gateway(client_store, chosen_gateway, &gateways).await
|
||||
}
|
||||
|
||||
pub(crate) async fn gateway_from_topology<R: Rng + CryptoRng>(
|
||||
rng: &mut R,
|
||||
explicit_gateway: Option<IdentityKeyRef<'_>>,
|
||||
pub(crate) async fn setup_from_topology(
|
||||
explicit_gateway: Option<IdentityKey>,
|
||||
topology: &NymTopology,
|
||||
client_store: &ClientStorage,
|
||||
) -> Result<GatewayEndpointConfig, WasmClientError> {
|
||||
let existing_gateway_config = client_store.read_gateway_config().await?;
|
||||
console_log!("loaded: {:?}", existing_gateway_config);
|
||||
|
||||
let new_gateway: GatewayEndpointConfig = if let Some(provided) = explicit_gateway {
|
||||
if let Some(existing) = existing_gateway_config {
|
||||
// we have stored gateway info and explicitly provided identity key
|
||||
//
|
||||
// check if they match, otherwise return an error
|
||||
return if provided != existing.gateway_id {
|
||||
Err(WasmClientError::AlreadyRegistered {
|
||||
gateway_config: existing,
|
||||
})
|
||||
} else {
|
||||
Ok(existing)
|
||||
};
|
||||
} else {
|
||||
// we have explicitly provided identity key and didn't have any prior stored data
|
||||
//
|
||||
// try to grab details from the topology
|
||||
let gateway_identity = identity::PublicKey::from_base58_string(provided)
|
||||
.map_err(|source| WasmClientError::InvalidGatewayIdentity { source })?;
|
||||
if let Some(gateway) = topology.get_gateway(&gateway_identity) {
|
||||
gateway.clone().into()
|
||||
} else {
|
||||
return Err(WasmClientError::NonExistentGateway {
|
||||
gateway_identity: gateway_identity.to_base58_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if let Some(existing) = existing_gateway_config {
|
||||
// we have stored data and didn't provide anything separately - use what's stored!
|
||||
return Ok(existing);
|
||||
} else {
|
||||
// we don't have anything stored nor we have provided anything
|
||||
//
|
||||
// just grab random gateway from our topology
|
||||
topology.random_gateway(rng)?.clone().into()
|
||||
};
|
||||
|
||||
console_log!("storing: {:?}", new_gateway);
|
||||
client_store.store_gateway_config(&new_gateway).await?;
|
||||
Ok(new_gateway)
|
||||
) -> Result<InitialisationDetails, WasmClientError> {
|
||||
let gateways = topology.gateways();
|
||||
setup_gateway(client_store, explicit_gateway, gateways).await
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@ pub enum ClientStorageError {
|
||||
|
||||
#[error("{typ} cryptographic key is not available in storage")]
|
||||
CryptoKeyNotInStorage { typ: String },
|
||||
|
||||
#[error("the prior gateway details are not available in the storage")]
|
||||
GatewayDetailsNotInStorage,
|
||||
}
|
||||
|
||||
impl From<ClientStorageError> for JsValue {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::config::Config;
|
||||
use crate::storage::errors::ClientStorageError;
|
||||
use js_sys::Promise;
|
||||
use nym_client_core::config::GatewayEndpointConfig;
|
||||
use nym_client_core::client::base_client::storage::gateway_details::PersistedGatewayDetails;
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
use nym_gateway_client::SharedKeys;
|
||||
use nym_sphinx::acknowledgements::AckKey;
|
||||
@@ -27,8 +28,8 @@ mod v1 {
|
||||
pub const CORE_STORE: &str = "core";
|
||||
|
||||
// keys
|
||||
// TODO: to replace with FULL config
|
||||
pub const GATEWAY_CONFIG: &str = "gateway_config";
|
||||
pub const CONFIG: &str = "config";
|
||||
pub const GATEWAY_DETAILS: &str = "gateway_details";
|
||||
|
||||
pub const ED25519_IDENTITY_KEYPAIR: &str = "ed25519_identity_keypair";
|
||||
pub const X25519_ENCRYPTION_KEYPAIR: &str = "x25519_encryption_keypair";
|
||||
@@ -110,15 +111,32 @@ impl ClientStorage {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) async fn read_gateway_config(
|
||||
&self,
|
||||
) -> Result<Option<GatewayEndpointConfig>, ClientStorageError> {
|
||||
// TODO: persist client's config
|
||||
#[allow(dead_code)]
|
||||
pub(crate) async fn read_config(&self) -> Result<Option<Config>, ClientStorageError> {
|
||||
self.inner
|
||||
.read_value(v1::CORE_STORE, JsValue::from_str(v1::GATEWAY_CONFIG))
|
||||
.read_value(v1::CORE_STORE, JsValue::from_str(v1::CONFIG))
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) async fn may_read_gateway_details(
|
||||
&self,
|
||||
) -> Result<Option<PersistedGatewayDetails>, ClientStorageError> {
|
||||
self.inner
|
||||
.read_value(v1::CORE_STORE, JsValue::from_str(v1::GATEWAY_DETAILS))
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) async fn must_read_gateway_details(
|
||||
&self,
|
||||
) -> Result<PersistedGatewayDetails, ClientStorageError> {
|
||||
self.may_read_gateway_details()
|
||||
.await?
|
||||
.ok_or(ClientStorageError::GatewayDetailsNotInStorage)
|
||||
}
|
||||
|
||||
async fn may_read_identity_keypair(
|
||||
&self,
|
||||
) -> Result<Option<identity::KeyPair>, ClientStorageError> {
|
||||
@@ -244,17 +262,33 @@ impl ClientStorage {
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) async fn store_gateway_config(
|
||||
pub(crate) async fn store_gateway_details(
|
||||
&self,
|
||||
gateway_endpoint: &GatewayEndpointConfig,
|
||||
gateway_endpoint: &PersistedGatewayDetails,
|
||||
) -> Result<(), ClientStorageError> {
|
||||
self.inner
|
||||
.store_value(
|
||||
v1::CORE_STORE,
|
||||
JsValue::from_str(v1::GATEWAY_CONFIG),
|
||||
JsValue::from_str(v1::GATEWAY_DETAILS),
|
||||
gateway_endpoint,
|
||||
)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
// TODO: persist client's config
|
||||
#[allow(dead_code)]
|
||||
pub(crate) async fn store_config(&self, config: &Config) -> Result<(), ClientStorageError> {
|
||||
self.inner
|
||||
.store_value(v1::CORE_STORE, JsValue::from_str(v1::CONFIG), config)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) async fn has_full_gateway_info(&self) -> Result<bool, ClientStorageError> {
|
||||
let has_keys = self.may_read_gateway_shared_key().await?.is_some();
|
||||
let has_details = self.may_read_gateway_details().await?.is_some();
|
||||
|
||||
Ok(has_keys && has_details)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
use crate::storage::errors::ClientStorageError;
|
||||
use crate::storage::ClientStorage;
|
||||
use async_trait::async_trait;
|
||||
use nym_client_core::client::base_client::storage::gateway_details::{
|
||||
GatewayDetailsStore, PersistedGatewayDetails,
|
||||
};
|
||||
use nym_client_core::client::base_client::storage::MixnetClientStorage;
|
||||
use nym_client_core::client::key_manager::persistence::KeyStore;
|
||||
use nym_client_core::client::key_manager::KeyManager;
|
||||
@@ -14,7 +17,7 @@ use wasm_utils::console_log;
|
||||
// temporary until other variants are properly implemented (probably it should get changed into `ClientStorage`
|
||||
// implementing all traits and everything getting combined
|
||||
pub struct FullWasmClientStorage {
|
||||
pub(crate) key_store: ClientStorage,
|
||||
pub(crate) keys_and_gateway_store: ClientStorage,
|
||||
pub(crate) reply_storage: browser_backend::Backend,
|
||||
pub(crate) credential_storage: EphemeralCredentialStorage,
|
||||
}
|
||||
@@ -24,12 +27,14 @@ impl MixnetClientStorage for FullWasmClientStorage {
|
||||
type ReplyStore = browser_backend::Backend;
|
||||
type CredentialStore = EphemeralCredentialStorage;
|
||||
|
||||
fn into_split(self) -> (Self::KeyStore, Self::ReplyStore, Self::CredentialStore) {
|
||||
(self.key_store, self.reply_storage, self.credential_storage)
|
||||
type GatewayDetailsStore = ClientStorage;
|
||||
|
||||
fn into_runtime_stores(self) -> (Self::ReplyStore, Self::CredentialStore) {
|
||||
(self.reply_storage, self.credential_storage)
|
||||
}
|
||||
|
||||
fn key_store(&self) -> &Self::KeyStore {
|
||||
&self.key_store
|
||||
&self.keys_and_gateway_store
|
||||
}
|
||||
|
||||
fn reply_store(&self) -> &Self::ReplyStore {
|
||||
@@ -39,6 +44,10 @@ impl MixnetClientStorage for FullWasmClientStorage {
|
||||
fn credential_store(&self) -> &Self::CredentialStore {
|
||||
&self.credential_storage
|
||||
}
|
||||
|
||||
fn gateway_details_store(&self) -> &Self::GatewayDetailsStore {
|
||||
&self.keys_and_gateway_store
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
@@ -74,3 +83,19 @@ impl KeyStore for ClientStorage {
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl GatewayDetailsStore for ClientStorage {
|
||||
type StorageError = ClientStorageError;
|
||||
|
||||
async fn load_gateway_details(&self) -> Result<PersistedGatewayDetails, Self::StorageError> {
|
||||
self.must_read_gateway_details().await
|
||||
}
|
||||
|
||||
async fn store_gateway_details(
|
||||
&self,
|
||||
details: &PersistedGatewayDetails,
|
||||
) -> Result<(), Self::StorageError> {
|
||||
self.store_gateway_details(details).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use crate::constants::NODE_TESTER_ID;
|
||||
use crate::error::WasmClientError;
|
||||
use crate::helpers::{current_network_topology_async, gateway_from_topology};
|
||||
use crate::helpers::{current_network_topology_async, setup_from_topology};
|
||||
use crate::storage::ClientStorage;
|
||||
use crate::tester::ephemeral_receiver::EphemeralTestReceiver;
|
||||
use crate::tester::helpers::{
|
||||
@@ -15,7 +15,7 @@ use js_sys::Promise;
|
||||
use nym_bandwidth_controller::wasm_mockups::{Client as FakeClient, DirectSigningNyxdClient};
|
||||
use nym_bandwidth_controller::BandwidthController;
|
||||
use nym_client_core::client::key_manager::ManagedKeys;
|
||||
use nym_client_core::config::GatewayEndpointConfig;
|
||||
use nym_client_core::init::InitialisationDetails;
|
||||
use nym_credential_storage::ephemeral_storage::EphemeralStorage;
|
||||
use nym_gateway_client::GatewayClient;
|
||||
use nym_node_tester_utils::receiver::SimpleMessageReceiver;
|
||||
@@ -28,7 +28,6 @@ use nym_task::TaskManager;
|
||||
use nym_topology::NymTopology;
|
||||
use nym_validator_client::client::IdentityKey;
|
||||
use rand::rngs::OsRng;
|
||||
use rand::{CryptoRng, Rng};
|
||||
use std::collections::HashSet;
|
||||
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
|
||||
use std::sync::{Arc, Mutex as SyncMutex};
|
||||
@@ -120,29 +119,26 @@ impl NymNodeTesterBuilder {
|
||||
})
|
||||
}
|
||||
|
||||
async fn gateway_info<R: Rng + CryptoRng>(
|
||||
async fn gateway_info(
|
||||
&self,
|
||||
rng: &mut R,
|
||||
client_store: &ClientStorage,
|
||||
) -> Result<GatewayEndpointConfig, WasmClientError> {
|
||||
gateway_from_topology(
|
||||
rng,
|
||||
self.gateway.as_deref(),
|
||||
&self.base_topology,
|
||||
client_store,
|
||||
)
|
||||
.await
|
||||
) -> Result<InitialisationDetails, WasmClientError> {
|
||||
if let Ok(loaded) = InitialisationDetails::try_load(client_store, client_store).await {
|
||||
Ok(loaded)
|
||||
} else {
|
||||
setup_from_topology(self.gateway.clone(), &self.base_topology, client_store).await
|
||||
}
|
||||
}
|
||||
|
||||
async fn _setup_client(mut self) -> Result<NymNodeTester, WasmClientError> {
|
||||
let mut rng = OsRng;
|
||||
let task_manager = TaskManager::default();
|
||||
|
||||
let client_store = ClientStorage::new_async(NODE_TESTER_ID, None).await?;
|
||||
|
||||
let gateway_endpoint = self.gateway_info(&mut rng, &client_store).await?;
|
||||
let init_details = self.gateway_info(&client_store).await?;
|
||||
let gateway_endpoint = init_details.gateway_details;
|
||||
let gateway_identity = gateway_endpoint.try_get_gateway_identity_key()?;
|
||||
let mut managed_keys = ManagedKeys::load_or_generate(&mut rng, &client_store).await;
|
||||
let managed_keys = init_details.managed_keys;
|
||||
|
||||
let (mixnet_message_sender, mixnet_message_receiver) = mpsc::unbounded();
|
||||
let (ack_sender, ack_receiver) = mpsc::unbounded();
|
||||
@@ -151,7 +147,7 @@ impl NymNodeTesterBuilder {
|
||||
gateway_endpoint.gateway_listener,
|
||||
managed_keys.identity_keypair(),
|
||||
gateway_identity,
|
||||
managed_keys.gateway_shared_key(),
|
||||
Some(managed_keys.must_get_gateway_shared_key()),
|
||||
mixnet_message_sender,
|
||||
ack_sender,
|
||||
Duration::from_secs(10),
|
||||
@@ -160,14 +156,11 @@ impl NymNodeTesterBuilder {
|
||||
);
|
||||
|
||||
gateway_client.set_disabled_credentials_mode(true);
|
||||
let shared_keys = gateway_client.authenticate_and_start().await?;
|
||||
managed_keys
|
||||
.deal_with_gateway_key(shared_keys, &client_store)
|
||||
.await?;
|
||||
gateway_client.authenticate_and_start().await?;
|
||||
|
||||
// TODO: make those values configurable later
|
||||
let tester = NodeTester::new(
|
||||
rng,
|
||||
OsRng,
|
||||
self.base_topology,
|
||||
Some(address(&managed_keys, gateway_identity)),
|
||||
PacketSize::default(),
|
||||
|
||||
@@ -9,6 +9,7 @@ rust-version = "1.66"
|
||||
|
||||
[dependencies]
|
||||
async-trait = { workspace = true }
|
||||
base64 = "0.21.2"
|
||||
dirs = "4.0"
|
||||
dashmap = "5.4.0"
|
||||
futures = "0.3"
|
||||
@@ -17,6 +18,7 @@ log = { workspace = true }
|
||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
sha2 = "0.10.6"
|
||||
tap = "1.0.1"
|
||||
thiserror = "1.0.34"
|
||||
url = { version ="2.2", features = ["serde"] }
|
||||
|
||||
@@ -42,7 +42,6 @@ use nym_sphinx::receiver::{ReconstructedMessage, SphinxMessageReceiver};
|
||||
use nym_task::connections::{ConnectionCommandReceiver, ConnectionCommandSender, LaneQueueLengths};
|
||||
use nym_task::{TaskClient, TaskManager};
|
||||
use nym_topology::provider_trait::TopologyProvider;
|
||||
use rand::rngs::OsRng;
|
||||
use std::sync::Arc;
|
||||
use tap::TapFallible;
|
||||
use url::Url;
|
||||
@@ -50,6 +49,8 @@ use url::Url;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use nym_bandwidth_controller::wasm_mockups::DkgQueryClient;
|
||||
|
||||
use crate::client::base_client::storage::gateway_details::GatewayDetailsStore;
|
||||
use crate::init::{setup_gateway, GatewaySetup, InitialisationDetails};
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use nym_validator_client::nyxd::traits::DkgQueryClient;
|
||||
|
||||
@@ -157,13 +158,11 @@ impl From<bool> for CredentialsToggle {
|
||||
}
|
||||
|
||||
pub struct BaseClientBuilder<'a, C, S: MixnetClientStorage> {
|
||||
// due to wasm limitations I had to split it like this : (
|
||||
gateway_config: &'a GatewayEndpointConfig,
|
||||
config: &'a Config,
|
||||
nym_api_endpoints: Vec<Url>,
|
||||
client_store: S,
|
||||
dkg_query_client: Option<C>,
|
||||
custom_topology_provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
|
||||
setup_method: GatewaySetup,
|
||||
}
|
||||
|
||||
impl<'a, C, S> BaseClientBuilder<'a, C, S>
|
||||
@@ -177,15 +176,19 @@ where
|
||||
dkg_query_client: Option<C>,
|
||||
) -> BaseClientBuilder<'a, C, S> {
|
||||
BaseClientBuilder {
|
||||
gateway_config: base_config.get_gateway_endpoint_config(),
|
||||
config: base_config,
|
||||
nym_api_endpoints: base_config.get_nym_api_endpoints(),
|
||||
client_store,
|
||||
dkg_query_client,
|
||||
custom_topology_provider: None,
|
||||
setup_method: GatewaySetup::MustLoad,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_gateway_setup(mut self, setup: GatewaySetup) -> Self {
|
||||
self.setup_method = setup;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_topology_provider(
|
||||
mut self,
|
||||
provider: Box<dyn TopologyProvider + Send + Sync>,
|
||||
@@ -288,12 +291,10 @@ where
|
||||
controller.start_with_shutdown(shutdown)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn start_gateway_client(
|
||||
managed_keys: &mut ManagedKeys,
|
||||
key_store: &S::KeyStore,
|
||||
gateway_details: &GatewayEndpointConfig,
|
||||
config: &Config,
|
||||
gateway_config: GatewayEndpointConfig,
|
||||
managed_keys: &ManagedKeys,
|
||||
bandwidth_controller: Option<BandwidthController<C, S::CredentialStore>>,
|
||||
mixnet_message_sender: MixnetMessageSender,
|
||||
ack_sender: AcknowledgementSender,
|
||||
@@ -303,15 +304,10 @@ where
|
||||
<S::KeyStore as KeyStore>::StorageError: Send + Sync + 'static,
|
||||
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static,
|
||||
{
|
||||
let gateway_id = gateway_details.gateway_id.clone();
|
||||
if gateway_id.is_empty() {
|
||||
return Err(ClientCoreError::GatewayIdUnknown);
|
||||
}
|
||||
let gateway_address = gateway_details.gateway_listener.clone();
|
||||
if gateway_address.is_empty() {
|
||||
return Err(ClientCoreError::GatewayAddressUnknown);
|
||||
}
|
||||
let gateway_address = gateway_config.gateway_listener.clone();
|
||||
let gateway_id = gateway_config.gateway_id;
|
||||
|
||||
// TODO: in theory, at this point, this should be infallible
|
||||
let gateway_identity = identity::PublicKey::from_base58_string(gateway_id)
|
||||
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
|
||||
|
||||
@@ -319,7 +315,7 @@ where
|
||||
gateway_address,
|
||||
managed_keys.identity_keypair(),
|
||||
gateway_identity,
|
||||
managed_keys.gateway_shared_key(),
|
||||
Some(managed_keys.must_get_gateway_shared_key()),
|
||||
mixnet_message_sender,
|
||||
ack_sender,
|
||||
config.debug.gateway_connection.gateway_response_timeout,
|
||||
@@ -336,12 +332,7 @@ where
|
||||
log::error!("Could not authenticate and start up the gateway connection - {err}")
|
||||
})?;
|
||||
|
||||
managed_keys
|
||||
.deal_with_gateway_key(shared_key, key_store)
|
||||
.await
|
||||
.map_err(|source| ClientCoreError::KeyStoreError {
|
||||
source: Box::new(source),
|
||||
})?;
|
||||
managed_keys.ensure_gateway_key(shared_key);
|
||||
|
||||
Ok(gateway_client)
|
||||
}
|
||||
@@ -447,22 +438,38 @@ where
|
||||
Ok(mem_store)
|
||||
}
|
||||
|
||||
async fn initial_key_setup(key_store: &S::KeyStore) -> ManagedKeys {
|
||||
let mut rng = OsRng;
|
||||
ManagedKeys::load_or_generate(&mut rng, key_store).await
|
||||
async fn initialise_keys_and_gateway(&self) -> Result<InitialisationDetails, ClientCoreError>
|
||||
where
|
||||
<S::KeyStore as KeyStore>::StorageError: Sync + Send,
|
||||
<S::GatewayDetailsStore as GatewayDetailsStore>::StorageError: Sync + Send,
|
||||
{
|
||||
setup_gateway(
|
||||
&self.setup_method,
|
||||
self.client_store.key_store(),
|
||||
self.client_store.gateway_details_store(),
|
||||
false,
|
||||
Some(&self.config.client.nym_api_urls),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn start_base(mut self) -> Result<BaseClient, ClientCoreError>
|
||||
where
|
||||
<S::ReplyStore as ReplyStorageBackend>::StorageError: Sync + Send,
|
||||
S::ReplyStore: Send + Sync,
|
||||
<S::KeyStore as KeyStore>::StorageError: Send + Sync,
|
||||
<S::ReplyStore as ReplyStorageBackend>::StorageError: Sync + Send,
|
||||
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static,
|
||||
<S::GatewayDetailsStore as GatewayDetailsStore>::StorageError: Sync + Send,
|
||||
{
|
||||
info!("Starting nym client");
|
||||
let (key_store, reply_storage_backend, credential_store) = self.client_store.into_split();
|
||||
|
||||
let mut managed_keys = Self::initial_key_setup(&key_store).await;
|
||||
// derive (or load) client keys and gateway configuration
|
||||
let details = self.initialise_keys_and_gateway().await?;
|
||||
let gateway_config = details.gateway_details;
|
||||
let managed_keys = details.managed_keys;
|
||||
|
||||
let (reply_storage_backend, credential_store) = self.client_store.into_runtime_stores();
|
||||
|
||||
let bandwidth_controller = self
|
||||
.dkg_query_client
|
||||
.map(|client| BandwidthController::new(credential_store, client));
|
||||
@@ -493,15 +500,14 @@ where
|
||||
let (reply_controller_sender, reply_controller_receiver) =
|
||||
reply_controller::requests::new_control_channels();
|
||||
|
||||
let self_address = Self::mix_address(&managed_keys, self.gateway_config);
|
||||
let self_address = Self::mix_address(&managed_keys, &gateway_config);
|
||||
|
||||
// the components are started in very specific order. Unless you know what you are doing,
|
||||
// do not change that.
|
||||
let gateway_client = Self::start_gateway_client(
|
||||
&mut managed_keys,
|
||||
&key_store,
|
||||
self.gateway_config,
|
||||
self.config,
|
||||
gateway_config,
|
||||
&managed_keys,
|
||||
bandwidth_controller,
|
||||
mixnet_messages_sender,
|
||||
ack_sender,
|
||||
@@ -515,7 +521,7 @@ where
|
||||
|
||||
let topology_provider = Self::setup_topology_provider(
|
||||
self.custom_topology_provider.take(),
|
||||
self.nym_api_endpoints,
|
||||
self.config.get_nym_api_endpoints(),
|
||||
);
|
||||
Self::start_topology_refresher(
|
||||
topology_provider,
|
||||
|
||||
@@ -5,10 +5,12 @@ use crate::config::GatewayEndpointConfig;
|
||||
use async_trait::async_trait;
|
||||
use nym_gateway_requests::registration::handshake::SharedKeys;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::error::Error;
|
||||
use std::sync::Arc;
|
||||
use std::ops::Deref;
|
||||
use tokio::sync::Mutex;
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
// TODO: to incorporate into `MixnetClientStorage`
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
pub trait GatewayDetailsStore {
|
||||
@@ -22,22 +24,178 @@ pub trait GatewayDetailsStore {
|
||||
) -> Result<(), Self::StorageError>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PersistedGatewayDetails {
|
||||
/// (to be determined), hash, ciphertext or tag derived from the details and the shared key
|
||||
/// to ensure they correspond to the same instance.
|
||||
magic_crypto_field: (),
|
||||
// TODO: should we also verify correctness of the details themselves?
|
||||
// i.e. we could include a checksum or tag (via the shared keys)
|
||||
// counterargument: if we wanted to modify, say, the host information in the stored file on disk,
|
||||
// in order to actually use it, we'd have to recompute the whole checksum which would be a huge pain.
|
||||
/// The hash of the shared keys to ensure the correct ones are used with those gateway details.
|
||||
#[serde(with = "base64")]
|
||||
key_hash: Vec<u8>,
|
||||
|
||||
/// Actual gateway details being persisted.
|
||||
details: GatewayEndpointConfig,
|
||||
pub(crate) details: GatewayEndpointConfig,
|
||||
}
|
||||
|
||||
impl From<PersistedGatewayDetails> for GatewayEndpointConfig {
|
||||
fn from(value: PersistedGatewayDetails) -> Self {
|
||||
value.details
|
||||
}
|
||||
}
|
||||
|
||||
impl PersistedGatewayDetails {
|
||||
pub fn new(details: GatewayEndpointConfig, shared_key: Arc<SharedKeys>) -> Self {
|
||||
todo!()
|
||||
pub fn new(details: GatewayEndpointConfig, shared_key: &SharedKeys) -> Self {
|
||||
let key_bytes = Zeroizing::new(shared_key.to_bytes());
|
||||
|
||||
let mut key_hasher = Sha256::new();
|
||||
key_hasher.update(&key_bytes);
|
||||
let key_hash = key_hasher.finalize().to_vec();
|
||||
|
||||
PersistedGatewayDetails { key_hash, details }
|
||||
}
|
||||
|
||||
pub fn verify(&self, shared_key: Arc<SharedKeys>) -> bool {
|
||||
todo!()
|
||||
pub fn verify(&self, shared_key: &SharedKeys) -> bool {
|
||||
let key_bytes = Zeroizing::new(shared_key.to_bytes());
|
||||
|
||||
let mut key_hasher = Sha256::new();
|
||||
key_hasher.update(&key_bytes);
|
||||
let key_hash = key_hasher.finalize();
|
||||
|
||||
self.key_hash == key_hash.deref()
|
||||
}
|
||||
}
|
||||
|
||||
// helper to make Vec<u8> serialization use base64 representation to make it human readable
|
||||
// so that it would be easier for users to copy contents from the disk if they wanted to use it elsewhere
|
||||
mod base64 {
|
||||
use base64::{engine::general_purpose::STANDARD, Engine as _};
|
||||
use serde::{Deserialize, Deserializer, Serializer};
|
||||
|
||||
pub fn serialize<S: Serializer>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error> {
|
||||
serializer.serialize_str(&STANDARD.encode(bytes))
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Vec<u8>, D::Error> {
|
||||
let s = <String>::deserialize(deserializer)?;
|
||||
STANDARD.decode(s).map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum OnDiskGatewayDetailsError {
|
||||
#[error("JSON failure: {0}")]
|
||||
SerializationFailure(#[from] serde_json::Error),
|
||||
|
||||
#[error("failed to store gateway details to {path}: {err}")]
|
||||
StoreFailure {
|
||||
path: String,
|
||||
#[source]
|
||||
err: std::io::Error,
|
||||
},
|
||||
|
||||
#[error("failed to load gateway details from {path}: {err}")]
|
||||
LoadFailure {
|
||||
path: String,
|
||||
#[source]
|
||||
err: std::io::Error,
|
||||
},
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub struct OnDiskGatewayDetails {
|
||||
file_location: std::path::PathBuf,
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
impl OnDiskGatewayDetails {
|
||||
pub fn new<P: AsRef<std::path::Path>>(path: P) -> Self {
|
||||
OnDiskGatewayDetails {
|
||||
file_location: path.as_ref().to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_from_disk(&self) -> Result<PersistedGatewayDetails, OnDiskGatewayDetailsError> {
|
||||
let file = std::fs::File::open(&self.file_location).map_err(|err| {
|
||||
OnDiskGatewayDetailsError::LoadFailure {
|
||||
path: self.file_location.display().to_string(),
|
||||
err,
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(serde_json::from_reader(file)?)
|
||||
}
|
||||
|
||||
pub fn store_to_disk(
|
||||
&self,
|
||||
details: &PersistedGatewayDetails,
|
||||
) -> Result<(), OnDiskGatewayDetailsError> {
|
||||
// ensure the whole directory structure exists
|
||||
if let Some(parent_dir) = &self.file_location.parent() {
|
||||
std::fs::create_dir_all(parent_dir).map_err(|err| {
|
||||
OnDiskGatewayDetailsError::StoreFailure {
|
||||
path: self.file_location.display().to_string(),
|
||||
err,
|
||||
}
|
||||
})?
|
||||
}
|
||||
|
||||
let file = std::fs::File::create(&self.file_location).map_err(|err| {
|
||||
OnDiskGatewayDetailsError::StoreFailure {
|
||||
path: self.file_location.display().to_string(),
|
||||
err,
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(serde_json::to_writer_pretty(file, details)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl GatewayDetailsStore for OnDiskGatewayDetails {
|
||||
type StorageError = OnDiskGatewayDetailsError;
|
||||
|
||||
async fn load_gateway_details(&self) -> Result<PersistedGatewayDetails, Self::StorageError> {
|
||||
self.load_from_disk()
|
||||
}
|
||||
|
||||
async fn store_gateway_details(
|
||||
&self,
|
||||
gateway_details: &PersistedGatewayDetails,
|
||||
) -> Result<(), Self::StorageError> {
|
||||
self.store_to_disk(gateway_details)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct InMemGatewayDetails {
|
||||
details: Mutex<Option<PersistedGatewayDetails>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("old ephemeral gateway details can't be loaded from storage")]
|
||||
pub struct EphemeralGatewayDetailsError;
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl GatewayDetailsStore for InMemGatewayDetails {
|
||||
type StorageError = EphemeralGatewayDetailsError;
|
||||
|
||||
async fn load_gateway_details(&self) -> Result<PersistedGatewayDetails, Self::StorageError> {
|
||||
self.details
|
||||
.lock()
|
||||
.await
|
||||
.clone()
|
||||
.ok_or(EphemeralGatewayDetailsError)
|
||||
}
|
||||
|
||||
async fn store_gateway_details(
|
||||
&self,
|
||||
gateway_details: &PersistedGatewayDetails,
|
||||
) -> Result<(), Self::StorageError> {
|
||||
*self.details.lock().await = Some(gateway_details.clone());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,17 +4,20 @@
|
||||
// TODO: combine those more closely. Perhaps into a single underlying store.
|
||||
// Like for persistent, on-disk, storage, what's the point of having 3 different databases?
|
||||
|
||||
use crate::client::base_client::storage::gateway_details::{
|
||||
GatewayDetailsStore, InMemGatewayDetails,
|
||||
};
|
||||
use crate::client::key_manager::persistence::{InMemEphemeralKeys, KeyStore};
|
||||
use crate::client::replies::reply_storage;
|
||||
use crate::client::replies::reply_storage::ReplyStorageBackend;
|
||||
use nym_credential_storage::ephemeral_storage::{
|
||||
EphemeralStorage as EphemeralCredentialStorage, EphemeralStorage,
|
||||
};
|
||||
use nym_credential_storage::ephemeral_storage::EphemeralStorage as EphemeralCredentialStorage;
|
||||
use nym_credential_storage::storage::Storage as CredentialStorage;
|
||||
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
|
||||
use crate::client::base_client::non_wasm_helpers;
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
|
||||
use crate::client::base_client::storage::gateway_details::OnDiskGatewayDetails;
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
|
||||
use crate::client::key_manager::persistence::OnDiskKeys;
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
|
||||
use crate::client::replies::reply_storage::fs_backend;
|
||||
@@ -25,24 +28,33 @@ use crate::error::ClientCoreError;
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
|
||||
use nym_credential_storage::persistent_storage::PersistentStorage as PersistentCredentialStorage;
|
||||
|
||||
pub mod gateway_details;
|
||||
|
||||
// TODO: ideally this should be changed into
|
||||
// `MixnetClientStorage: KeyStore + ReplyStorageBackend + CredentialStorage + GatewayDetailsStore`
|
||||
pub trait MixnetClientStorage {
|
||||
type KeyStore: KeyStore;
|
||||
type ReplyStore: ReplyStorageBackend;
|
||||
type CredentialStore: CredentialStorage;
|
||||
type GatewayDetailsStore: GatewayDetailsStore;
|
||||
|
||||
// this is a TERRIBLE name...
|
||||
fn into_split(self) -> (Self::KeyStore, Self::ReplyStore, Self::CredentialStore);
|
||||
// fn into_split(self) -> (Self::KeyStore, Self::ReplyStore, Self::CredentialStore, Self::GatewayDetailsStore);
|
||||
|
||||
fn into_runtime_stores(self) -> (Self::ReplyStore, Self::CredentialStore);
|
||||
|
||||
fn key_store(&self) -> &Self::KeyStore;
|
||||
fn reply_store(&self) -> &Self::ReplyStore;
|
||||
fn credential_store(&self) -> &Self::CredentialStore;
|
||||
fn gateway_details_store(&self) -> &Self::GatewayDetailsStore;
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Ephemeral {
|
||||
key_store: InMemEphemeralKeys,
|
||||
reply_store: reply_storage::Empty,
|
||||
credential_store: EphemeralStorage,
|
||||
credential_store: EphemeralCredentialStorage,
|
||||
gateway_details_store: InMemGatewayDetails,
|
||||
}
|
||||
|
||||
impl Ephemeral {
|
||||
@@ -55,9 +67,10 @@ impl MixnetClientStorage for Ephemeral {
|
||||
type KeyStore = InMemEphemeralKeys;
|
||||
type ReplyStore = reply_storage::Empty;
|
||||
type CredentialStore = EphemeralCredentialStorage;
|
||||
type GatewayDetailsStore = InMemGatewayDetails;
|
||||
|
||||
fn into_split(self) -> (Self::KeyStore, Self::ReplyStore, Self::CredentialStore) {
|
||||
(self.key_store, self.reply_store, self.credential_store)
|
||||
fn into_runtime_stores(self) -> (Self::ReplyStore, Self::CredentialStore) {
|
||||
(self.reply_store, self.credential_store)
|
||||
}
|
||||
|
||||
fn key_store(&self) -> &Self::KeyStore {
|
||||
@@ -71,6 +84,10 @@ impl MixnetClientStorage for Ephemeral {
|
||||
fn credential_store(&self) -> &Self::CredentialStore {
|
||||
&self.credential_store
|
||||
}
|
||||
|
||||
fn gateway_details_store(&self) -> &Self::GatewayDetailsStore {
|
||||
&self.gateway_details_store
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
|
||||
@@ -78,6 +95,7 @@ pub struct OnDiskPersistent {
|
||||
pub(crate) key_store: OnDiskKeys,
|
||||
pub(crate) reply_store: fs_backend::Backend,
|
||||
pub(crate) credential_store: PersistentCredentialStorage,
|
||||
pub(crate) gateway_details_store: OnDiskGatewayDetails,
|
||||
}
|
||||
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
|
||||
@@ -86,11 +104,13 @@ impl OnDiskPersistent {
|
||||
key_store: OnDiskKeys,
|
||||
reply_store: fs_backend::Backend,
|
||||
credential_store: PersistentCredentialStorage,
|
||||
gateway_details_store: OnDiskGatewayDetails,
|
||||
) -> Self {
|
||||
Self {
|
||||
key_store,
|
||||
reply_store,
|
||||
credential_store,
|
||||
gateway_details_store,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,10 +129,13 @@ impl OnDiskPersistent {
|
||||
let credential_store =
|
||||
nym_credential_storage::initialise_persistent_storage(paths.credentials_database).await;
|
||||
|
||||
let gateway_details_store = OnDiskGatewayDetails::new(paths.gateway_details);
|
||||
|
||||
Ok(OnDiskPersistent {
|
||||
key_store,
|
||||
reply_store,
|
||||
credential_store,
|
||||
gateway_details_store,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -122,9 +145,10 @@ impl MixnetClientStorage for OnDiskPersistent {
|
||||
type KeyStore = OnDiskKeys;
|
||||
type ReplyStore = fs_backend::Backend;
|
||||
type CredentialStore = PersistentCredentialStorage;
|
||||
type GatewayDetailsStore = OnDiskGatewayDetails;
|
||||
|
||||
fn into_split(self) -> (Self::KeyStore, Self::ReplyStore, Self::CredentialStore) {
|
||||
(self.key_store, self.reply_store, self.credential_store)
|
||||
fn into_runtime_stores(self) -> (Self::ReplyStore, Self::CredentialStore) {
|
||||
(self.reply_store, self.credential_store)
|
||||
}
|
||||
|
||||
fn key_store(&self) -> &Self::KeyStore {
|
||||
@@ -138,4 +162,8 @@ impl MixnetClientStorage for OnDiskPersistent {
|
||||
fn credential_store(&self) -> &Self::CredentialStore {
|
||||
&self.credential_store
|
||||
}
|
||||
|
||||
fn gateway_details_store(&self) -> &Self::GatewayDetailsStore {
|
||||
&self.gateway_details_store
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ use nym_crypto::asymmetric::{encryption, identity};
|
||||
use nym_gateway_requests::registration::handshake::SharedKeys;
|
||||
use nym_sphinx::acknowledgements::AckKey;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use std::sync::Arc;
|
||||
use zeroize::ZeroizeOnDrop;
|
||||
|
||||
@@ -20,6 +21,16 @@ pub enum ManagedKeys {
|
||||
Invalidated,
|
||||
}
|
||||
|
||||
impl Debug for ManagedKeys {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ManagedKeys::Initial(_) => write!(f, "initial"),
|
||||
ManagedKeys::FullyDerived(_) => write!(f, "fully derived"),
|
||||
ManagedKeys::Invalidated => write!(f, "invalidated"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<KeyManagerBuilder> for ManagedKeys {
|
||||
fn from(value: KeyManagerBuilder) -> Self {
|
||||
ManagedKeys::Initial(value)
|
||||
@@ -84,6 +95,11 @@ impl ManagedKeys {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn must_get_gateway_shared_key(&self) -> Arc<SharedKeys> {
|
||||
self.gateway_shared_key()
|
||||
.expect("failed to extract gateway shared key")
|
||||
}
|
||||
|
||||
pub fn gateway_shared_key(&self) -> Option<Arc<SharedKeys>> {
|
||||
match self {
|
||||
ManagedKeys::Initial(_) => None,
|
||||
@@ -108,6 +124,17 @@ impl ManagedKeys {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ensure_gateway_key(&self, gateway_shared_key: Arc<SharedKeys>) {
|
||||
if let ManagedKeys::FullyDerived(key_manager) = &self {
|
||||
if !Arc::ptr_eq(&key_manager.gateway_shared_key, &gateway_shared_key)
|
||||
|| key_manager.gateway_shared_key != gateway_shared_key
|
||||
{
|
||||
// this should NEVER happen thus panic here
|
||||
panic!("derived fresh gateway shared key whilst already holding one!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn deal_with_gateway_key<S: KeyStore>(
|
||||
&mut self,
|
||||
gateway_shared_key: Arc<SharedKeys>,
|
||||
@@ -120,12 +147,7 @@ impl ManagedKeys {
|
||||
key_manager
|
||||
}
|
||||
ManagedKeys::FullyDerived(key_manager) => {
|
||||
if !Arc::ptr_eq(&key_manager.gateway_shared_key, &gateway_shared_key)
|
||||
|| key_manager.gateway_shared_key != gateway_shared_key
|
||||
{
|
||||
// this should NEVER happen thus panic here
|
||||
panic!("derived fresh gateway shared key whilst already holding one!")
|
||||
}
|
||||
self.ensure_gateway_key(gateway_shared_key);
|
||||
key_manager
|
||||
}
|
||||
ManagedKeys::Invalidated => unreachable!("the managed keys got invalidated"),
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
use crate::client::key_manager::KeyManager;
|
||||
use async_trait::async_trait;
|
||||
use std::error::Error;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use crate::config::disk_persistence::keys_paths::ClientKeysPaths;
|
||||
@@ -36,6 +37,7 @@ pub enum OnDiskKeysError {
|
||||
KeyPairLoadFailure {
|
||||
keys: String,
|
||||
paths: nym_pemstore::KeyPairPath,
|
||||
#[source]
|
||||
err: std::io::Error,
|
||||
},
|
||||
|
||||
@@ -43,6 +45,7 @@ pub enum OnDiskKeysError {
|
||||
KeyPairStoreFailure {
|
||||
keys: String,
|
||||
paths: nym_pemstore::KeyPairPath,
|
||||
#[source]
|
||||
err: std::io::Error,
|
||||
},
|
||||
|
||||
@@ -50,6 +53,7 @@ pub enum OnDiskKeysError {
|
||||
KeyLoadFailure {
|
||||
key: String,
|
||||
path: String,
|
||||
#[source]
|
||||
err: std::io::Error,
|
||||
},
|
||||
|
||||
@@ -57,6 +61,7 @@ pub enum OnDiskKeysError {
|
||||
KeyStoreFailure {
|
||||
key: String,
|
||||
path: String,
|
||||
#[source]
|
||||
err: std::io::Error,
|
||||
},
|
||||
}
|
||||
@@ -79,11 +84,21 @@ impl OnDiskKeys {
|
||||
OnDiskKeys { paths }
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn ephemeral_load_gateway_keys(
|
||||
&self,
|
||||
) -> Result<zeroize::Zeroizing<SharedKeys>, OnDiskKeysError> {
|
||||
self.load_key(self.paths.gateway_shared_key(), "gateway shared keys")
|
||||
.map(zeroize::Zeroizing::new)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn load_encryption_keypair(&self) -> Result<encryption::KeyPair, OnDiskKeysError> {
|
||||
let encryption_paths = self.paths.encryption_key_pair_path();
|
||||
self.load_keypair(encryption_paths, "encryption keys")
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn load_identity_keypair(&self) -> Result<identity::KeyPair, OnDiskKeysError> {
|
||||
let identity_paths = self.paths.identity_key_pair_path();
|
||||
self.load_keypair(identity_paths, "identity keys")
|
||||
@@ -198,10 +213,12 @@ impl KeyStore for OnDiskKeys {
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct InMemEphemeralKeys;
|
||||
pub struct InMemEphemeralKeys {
|
||||
keys: Mutex<Option<KeyManager>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("ephemeral keys can't be loaded from storage")]
|
||||
#[error("old ephemeral keys can't be loaded from storage")]
|
||||
pub struct EphemeralKeysError;
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
@@ -210,10 +227,11 @@ impl KeyStore for InMemEphemeralKeys {
|
||||
type StorageError = EphemeralKeysError;
|
||||
|
||||
async fn load_keys(&self) -> Result<KeyManager, Self::StorageError> {
|
||||
Err(EphemeralKeysError)
|
||||
self.keys.lock().await.clone().ok_or(EphemeralKeysError)
|
||||
}
|
||||
|
||||
async fn store_keys(&self, _keys: &KeyManager) -> Result<(), Self::StorageError> {
|
||||
async fn store_keys(&self, keys: &KeyManager) -> Result<(), Self::StorageError> {
|
||||
*self.keys.lock().await = Some(keys.clone());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,16 +6,21 @@ use serde::{Deserialize, Serialize};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
pub mod keys_paths;
|
||||
pub mod old_v1_1_20_2;
|
||||
|
||||
pub const DEFAULT_GATEWAY_DETAILS_FILENAME: &str = "gateway_details.json";
|
||||
pub const DEFAULT_REPLY_SURB_DB_FILENAME: &str = "persistent_reply_store.sqlite";
|
||||
pub const DEFAULT_CREDENTIALS_DB_FILENAME: &str = "credentials_database.db";
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct CommonClientPaths {
|
||||
pub keys: ClientKeysPaths,
|
||||
|
||||
// TODO:
|
||||
// pub gateway_config_pathfinder: (),
|
||||
/// Path to the file containing information about gateway used by this client,
|
||||
/// i.e. details such as its public key, owner address or the network information.
|
||||
pub gateway_details: PathBuf,
|
||||
|
||||
/// Path to the database containing bandwidth credentials of this client.
|
||||
pub credentials_database: PathBuf,
|
||||
|
||||
@@ -30,6 +35,7 @@ impl CommonClientPaths {
|
||||
CommonClientPaths {
|
||||
credentials_database: base_dir.join(DEFAULT_CREDENTIALS_DB_FILENAME),
|
||||
reply_surb_database: base_dir.join(DEFAULT_REPLY_SURB_DB_FILENAME),
|
||||
gateway_details: base_dir.join(DEFAULT_GATEWAY_DETAILS_FILENAME),
|
||||
keys: ClientKeysPaths::new_default(base_data_directory),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::disk_persistence::keys_paths::ClientKeysPaths;
|
||||
use crate::config::disk_persistence::{CommonClientPaths, DEFAULT_GATEWAY_DETAILS_FILENAME};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct CommonClientPathsV1_1_20_2 {
|
||||
pub keys: ClientKeysPaths,
|
||||
pub credentials_database: PathBuf,
|
||||
pub reply_surb_database: PathBuf,
|
||||
}
|
||||
|
||||
impl CommonClientPathsV1_1_20_2 {
|
||||
pub fn upgrade_default(self) -> CommonClientPaths {
|
||||
let data_dir = self
|
||||
.reply_surb_database
|
||||
.parent()
|
||||
.expect("client paths upgrade failure");
|
||||
CommonClientPaths {
|
||||
keys: self.keys,
|
||||
gateway_details: data_dir.join(DEFAULT_GATEWAY_DETAILS_FILENAME),
|
||||
credentials_database: self.credentials_database,
|
||||
reply_surb_database: self.reply_surb_database,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ use wasm_bindgen::prelude::*;
|
||||
pub mod disk_persistence;
|
||||
pub mod old_config_v1_1_13;
|
||||
pub mod old_config_v1_1_20;
|
||||
pub mod old_config_v1_1_20_2;
|
||||
|
||||
// 'DEBUG'
|
||||
const DEFAULT_ACK_WAIT_MULTIPLIER: f64 = 1.5;
|
||||
@@ -77,7 +78,7 @@ impl Config {
|
||||
}
|
||||
|
||||
pub fn validate(&self) -> bool {
|
||||
self.client.validate() && self.debug.validate()
|
||||
self.debug.validate()
|
||||
}
|
||||
|
||||
pub fn with_debug_config(mut self, debug: DebugConfig) -> Self {
|
||||
@@ -90,19 +91,6 @@ impl Config {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_gateway_endpoint(&mut self, gateway_endpoint: GatewayEndpointConfig) {
|
||||
self.client.gateway_endpoint = gateway_endpoint;
|
||||
}
|
||||
|
||||
pub fn with_gateway_endpoint(mut self, gateway_endpoint: GatewayEndpointConfig) -> Self {
|
||||
self.client.gateway_endpoint = gateway_endpoint;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_gateway_id<S: Into<String>>(&mut self, id: S) {
|
||||
self.client.gateway_endpoint.gateway_id = id.into();
|
||||
}
|
||||
|
||||
pub fn with_custom_nyxd(mut self, urls: Vec<Url>) -> Self {
|
||||
self.client.nyxd_urls = urls;
|
||||
self
|
||||
@@ -178,22 +166,6 @@ impl Config {
|
||||
pub fn get_nym_api_endpoints(&self) -> Vec<Url> {
|
||||
self.client.nym_api_urls.clone()
|
||||
}
|
||||
|
||||
pub fn get_gateway_id(&self) -> String {
|
||||
self.client.gateway_endpoint.gateway_id.clone()
|
||||
}
|
||||
|
||||
pub fn get_gateway_owner(&self) -> String {
|
||||
self.client.gateway_endpoint.gateway_owner.clone()
|
||||
}
|
||||
|
||||
pub fn get_gateway_listener(&self) -> String {
|
||||
self.client.gateway_endpoint.gateway_listener.clone()
|
||||
}
|
||||
|
||||
pub fn get_gateway_endpoint_config(&self) -> &GatewayEndpointConfig {
|
||||
&self.client.gateway_endpoint
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
|
||||
@@ -246,6 +218,8 @@ impl From<nym_topology::gateway::Node> for GatewayEndpointConfig {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
|
||||
// note: the deny_unknown_fields is VITAL here to allow upgrades from v1.1.20_2
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Client {
|
||||
/// Version of the client for which this configuration was created.
|
||||
pub version: String,
|
||||
@@ -266,11 +240,6 @@ pub struct Client {
|
||||
/// Addresses to APIs running on validator from which the client gets the view of the network.
|
||||
#[serde(alias = "validator_api_urls")]
|
||||
pub nym_api_urls: Vec<Url>,
|
||||
|
||||
/// 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,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
@@ -293,7 +262,6 @@ impl Client {
|
||||
disabled_credentials_mode: true,
|
||||
nyxd_urls,
|
||||
nym_api_urls,
|
||||
gateway_endpoint: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,7 +270,6 @@ impl Client {
|
||||
disabled_credentials_mode: bool,
|
||||
nyxd_urls: Vec<Url>,
|
||||
nym_api_urls: Vec<Url>,
|
||||
gateway_endpoint: GatewayEndpointConfig,
|
||||
) -> Self {
|
||||
Client {
|
||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||
@@ -310,15 +277,8 @@ impl Client {
|
||||
disabled_credentials_mode,
|
||||
nyxd_urls,
|
||||
nym_api_urls,
|
||||
gateway_endpoint,
|
||||
}
|
||||
}
|
||||
|
||||
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, Copy, Deserialize, PartialEq, Serialize)]
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
// 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 crate::config::old_config_v1_1_20_2::{
|
||||
AcknowledgementsV1_1_20_2, CoverTrafficV1_1_20_2, DebugConfigV1_1_20_2,
|
||||
GatewayConnectionV1_1_20_2, GatewayEndpointConfigV1_1_20_2, ReplySurbsV1_1_20_2,
|
||||
TopologyV1_1_20_2, TrafficV1_1_20_2,
|
||||
};
|
||||
use nym_sphinx::params::{PacketSize, PacketType};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -71,9 +72,9 @@ pub struct GatewayEndpointConfigV1_1_20 {
|
||||
pub gateway_listener: String,
|
||||
}
|
||||
|
||||
impl From<GatewayEndpointConfigV1_1_20> for GatewayEndpointConfig {
|
||||
impl From<GatewayEndpointConfigV1_1_20> for GatewayEndpointConfigV1_1_20_2 {
|
||||
fn from(value: GatewayEndpointConfigV1_1_20) -> Self {
|
||||
GatewayEndpointConfig {
|
||||
GatewayEndpointConfigV1_1_20_2 {
|
||||
gateway_id: value.gateway_id,
|
||||
gateway_owner: value.gateway_owner,
|
||||
gateway_listener: value.gateway_listener,
|
||||
@@ -123,9 +124,9 @@ pub struct TrafficV1_1_20 {
|
||||
pub secondary_packet_size: Option<PacketSize>,
|
||||
}
|
||||
|
||||
impl From<TrafficV1_1_20> for Traffic {
|
||||
impl From<TrafficV1_1_20> for TrafficV1_1_20_2 {
|
||||
fn from(value: TrafficV1_1_20) -> Self {
|
||||
Traffic {
|
||||
TrafficV1_1_20_2 {
|
||||
average_packet_delay: value.average_packet_delay,
|
||||
message_sending_average_delay: value.message_sending_average_delay,
|
||||
disable_main_poisson_packet_distribution: value
|
||||
@@ -158,9 +159,9 @@ pub struct CoverTrafficV1_1_20 {
|
||||
pub disable_loop_cover_traffic_stream: bool,
|
||||
}
|
||||
|
||||
impl From<CoverTrafficV1_1_20> for CoverTraffic {
|
||||
impl From<CoverTrafficV1_1_20> for CoverTrafficV1_1_20_2 {
|
||||
fn from(value: CoverTrafficV1_1_20) -> Self {
|
||||
CoverTraffic {
|
||||
CoverTrafficV1_1_20_2 {
|
||||
loop_cover_traffic_average_delay: value.loop_cover_traffic_average_delay,
|
||||
cover_traffic_primary_size_ratio: value.cover_traffic_primary_size_ratio,
|
||||
disable_loop_cover_traffic_stream: value.disable_loop_cover_traffic_stream,
|
||||
@@ -185,9 +186,9 @@ pub struct GatewayConnectionV1_1_20 {
|
||||
pub gateway_response_timeout: Duration,
|
||||
}
|
||||
|
||||
impl From<GatewayConnectionV1_1_20> for GatewayConnection {
|
||||
impl From<GatewayConnectionV1_1_20> for GatewayConnectionV1_1_20_2 {
|
||||
fn from(value: GatewayConnectionV1_1_20) -> Self {
|
||||
GatewayConnection {
|
||||
GatewayConnectionV1_1_20_2 {
|
||||
gateway_response_timeout: value.gateway_response_timeout,
|
||||
}
|
||||
}
|
||||
@@ -211,9 +212,9 @@ pub struct AcknowledgementsV1_1_20 {
|
||||
pub ack_wait_addition: Duration,
|
||||
}
|
||||
|
||||
impl From<AcknowledgementsV1_1_20> for Acknowledgements {
|
||||
impl From<AcknowledgementsV1_1_20> for AcknowledgementsV1_1_20_2 {
|
||||
fn from(value: AcknowledgementsV1_1_20) -> Self {
|
||||
Acknowledgements {
|
||||
AcknowledgementsV1_1_20_2 {
|
||||
average_ack_delay: value.average_ack_delay,
|
||||
ack_wait_multiplier: value.ack_wait_multiplier,
|
||||
ack_wait_addition: value.ack_wait_addition,
|
||||
@@ -241,9 +242,9 @@ pub struct TopologyV1_1_20 {
|
||||
pub disable_refreshing: bool,
|
||||
}
|
||||
|
||||
impl From<TopologyV1_1_20> for Topology {
|
||||
impl From<TopologyV1_1_20> for TopologyV1_1_20_2 {
|
||||
fn from(value: TopologyV1_1_20) -> Self {
|
||||
Topology {
|
||||
TopologyV1_1_20_2 {
|
||||
topology_refresh_rate: value.topology_refresh_rate,
|
||||
topology_resolution_timeout: value.topology_resolution_timeout,
|
||||
disable_refreshing: value.disable_refreshing,
|
||||
@@ -279,9 +280,9 @@ pub struct ReplySurbsV1_1_20 {
|
||||
pub maximum_reply_key_age: Duration,
|
||||
}
|
||||
|
||||
impl From<ReplySurbsV1_1_20> for ReplySurbs {
|
||||
impl From<ReplySurbsV1_1_20> for ReplySurbsV1_1_20_2 {
|
||||
fn from(value: ReplySurbsV1_1_20) -> Self {
|
||||
ReplySurbs {
|
||||
ReplySurbsV1_1_20_2 {
|
||||
minimum_reply_surb_storage_threshold: value.minimum_reply_surb_storage_threshold,
|
||||
maximum_reply_surb_storage_threshold: value.maximum_reply_surb_storage_threshold,
|
||||
minimum_reply_surb_request_size: value.minimum_reply_surb_request_size,
|
||||
@@ -313,7 +314,7 @@ impl Default for ReplySurbsV1_1_20 {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||
#[derive(Debug, Default, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct DebugConfigV1_1_20 {
|
||||
pub traffic: TrafficV1_1_20,
|
||||
@@ -324,9 +325,9 @@ pub struct DebugConfigV1_1_20 {
|
||||
pub reply_surbs: ReplySurbsV1_1_20,
|
||||
}
|
||||
|
||||
impl From<DebugConfigV1_1_20> for DebugConfig {
|
||||
impl From<DebugConfigV1_1_20> for DebugConfigV1_1_20_2 {
|
||||
fn from(value: DebugConfigV1_1_20) -> Self {
|
||||
DebugConfig {
|
||||
DebugConfigV1_1_20_2 {
|
||||
traffic: value.traffic.into(),
|
||||
cover_traffic: value.cover_traffic.into(),
|
||||
gateway_connection: value.gateway_connection.into(),
|
||||
@@ -336,19 +337,3 @@ impl From<DebugConfigV1_1_20> for DebugConfig {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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_20 {
|
||||
fn default() -> Self {
|
||||
DebugConfigV1_1_20 {
|
||||
traffic: Default::default(),
|
||||
cover_traffic: Default::default(),
|
||||
gateway_connection: Default::default(),
|
||||
acknowledgements: Default::default(),
|
||||
topology: Default::default(),
|
||||
reply_surbs: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,348 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::{
|
||||
Acknowledgements, Client, Config, CoverTraffic, DebugConfig, GatewayConnection,
|
||||
GatewayEndpointConfig, ReplySurbs, Topology, Traffic,
|
||||
};
|
||||
use nym_sphinx::params::{PacketSize, PacketType};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
use url::Url;
|
||||
|
||||
// 'DEBUG'
|
||||
const DEFAULT_ACK_WAIT_MULTIPLIER: f64 = 1.5;
|
||||
|
||||
const DEFAULT_ACK_WAIT_ADDITION: Duration = Duration::from_millis(1_500);
|
||||
const DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(200);
|
||||
const DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(20);
|
||||
const DEFAULT_AVERAGE_PACKET_DELAY: Duration = Duration::from_millis(50);
|
||||
const DEFAULT_TOPOLOGY_REFRESH_RATE: Duration = Duration::from_secs(5 * 60); // every 5min
|
||||
const DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT: Duration = Duration::from_millis(5_000);
|
||||
// Set this to a high value for now, so that we don't risk sporadic timeouts that might cause
|
||||
// bought bandwidth tokens to not have time to be spent; Once we remove the gateway from the
|
||||
// bandwidth bridging protocol, we can come back to a smaller timeout value
|
||||
const DEFAULT_GATEWAY_RESPONSE_TIMEOUT: Duration = Duration::from_secs(5 * 60);
|
||||
|
||||
const DEFAULT_COVER_TRAFFIC_PRIMARY_SIZE_RATIO: f64 = 0.70;
|
||||
|
||||
// reply-surbs related:
|
||||
|
||||
// define when to request
|
||||
// clients/client-core/src/client/replies/reply_storage/surb_storage.rs
|
||||
const DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD: usize = 10;
|
||||
const DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD: usize = 200;
|
||||
|
||||
// define how much to request at once
|
||||
// clients/client-core/src/client/replies/reply_controller.rs
|
||||
const DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE: u32 = 10;
|
||||
const DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE: u32 = 100;
|
||||
|
||||
const DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE: u32 = 500;
|
||||
|
||||
const DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD: Duration = Duration::from_secs(10);
|
||||
const DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD: Duration = Duration::from_secs(5 * 60);
|
||||
|
||||
// 12 hours
|
||||
const DEFAULT_MAXIMUM_REPLY_SURB_AGE: Duration = Duration::from_secs(12 * 60 * 60);
|
||||
|
||||
// 24 hours
|
||||
const DEFAULT_MAXIMUM_REPLY_KEY_AGE: Duration = Duration::from_secs(24 * 60 * 60);
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ConfigV1_1_20_2 {
|
||||
pub client: ClientV1_1_20_2,
|
||||
|
||||
#[serde(default)]
|
||||
pub debug: DebugConfigV1_1_20_2,
|
||||
}
|
||||
|
||||
impl From<ConfigV1_1_20_2> for Config {
|
||||
fn from(value: ConfigV1_1_20_2) -> Self {
|
||||
Config {
|
||||
client: value.client.into(),
|
||||
debug: value.debug.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
|
||||
pub struct GatewayEndpointConfigV1_1_20_2 {
|
||||
/// gateway_id specifies ID of the gateway to which the client should send messages.
|
||||
/// If initially omitted, a random gateway will be chosen from the available topology.
|
||||
pub gateway_id: String,
|
||||
|
||||
/// Address of the gateway owner to which the client should send messages.
|
||||
pub gateway_owner: String,
|
||||
|
||||
/// Address of the gateway listener to which all client requests should be sent.
|
||||
pub gateway_listener: String,
|
||||
}
|
||||
|
||||
impl From<GatewayEndpointConfigV1_1_20_2> for GatewayEndpointConfig {
|
||||
fn from(value: GatewayEndpointConfigV1_1_20_2) -> Self {
|
||||
GatewayEndpointConfig {
|
||||
gateway_id: value.gateway_id,
|
||||
gateway_owner: value.gateway_owner,
|
||||
gateway_listener: value.gateway_listener,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
|
||||
pub struct ClientV1_1_20_2 {
|
||||
pub version: String,
|
||||
|
||||
pub id: String,
|
||||
|
||||
#[serde(default)]
|
||||
pub disabled_credentials_mode: bool,
|
||||
|
||||
#[serde(alias = "validator_urls")]
|
||||
pub nyxd_urls: Vec<Url>,
|
||||
|
||||
#[serde(alias = "validator_api_urls")]
|
||||
pub nym_api_urls: Vec<Url>,
|
||||
pub gateway_endpoint: GatewayEndpointConfigV1_1_20_2,
|
||||
}
|
||||
|
||||
impl From<ClientV1_1_20_2> for Client {
|
||||
fn from(value: ClientV1_1_20_2) -> Self {
|
||||
Client {
|
||||
version: value.version,
|
||||
id: value.id,
|
||||
disabled_credentials_mode: value.disabled_credentials_mode,
|
||||
nyxd_urls: value.nyxd_urls,
|
||||
nym_api_urls: value.nym_api_urls,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct TrafficV1_1_20_2 {
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub average_packet_delay: Duration,
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub message_sending_average_delay: Duration,
|
||||
pub disable_main_poisson_packet_distribution: bool,
|
||||
pub primary_packet_size: PacketSize,
|
||||
pub secondary_packet_size: Option<PacketSize>,
|
||||
pub packet_type: PacketType,
|
||||
}
|
||||
|
||||
impl From<TrafficV1_1_20_2> for Traffic {
|
||||
fn from(value: TrafficV1_1_20_2) -> Self {
|
||||
Traffic {
|
||||
average_packet_delay: value.average_packet_delay,
|
||||
message_sending_average_delay: value.message_sending_average_delay,
|
||||
disable_main_poisson_packet_distribution: value
|
||||
.disable_main_poisson_packet_distribution,
|
||||
primary_packet_size: value.primary_packet_size,
|
||||
secondary_packet_size: value.secondary_packet_size,
|
||||
packet_type: PacketType::Mix,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for TrafficV1_1_20_2 {
|
||||
fn default() -> Self {
|
||||
TrafficV1_1_20_2 {
|
||||
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
|
||||
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
|
||||
disable_main_poisson_packet_distribution: false,
|
||||
primary_packet_size: PacketSize::RegularPacket,
|
||||
secondary_packet_size: None,
|
||||
packet_type: PacketType::Mix,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct CoverTrafficV1_1_20_2 {
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub loop_cover_traffic_average_delay: Duration,
|
||||
pub cover_traffic_primary_size_ratio: f64,
|
||||
pub disable_loop_cover_traffic_stream: bool,
|
||||
}
|
||||
|
||||
impl From<CoverTrafficV1_1_20_2> for CoverTraffic {
|
||||
fn from(value: CoverTrafficV1_1_20_2) -> Self {
|
||||
CoverTraffic {
|
||||
loop_cover_traffic_average_delay: value.loop_cover_traffic_average_delay,
|
||||
cover_traffic_primary_size_ratio: value.cover_traffic_primary_size_ratio,
|
||||
disable_loop_cover_traffic_stream: value.disable_loop_cover_traffic_stream,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CoverTrafficV1_1_20_2 {
|
||||
fn default() -> Self {
|
||||
CoverTrafficV1_1_20_2 {
|
||||
loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
|
||||
cover_traffic_primary_size_ratio: DEFAULT_COVER_TRAFFIC_PRIMARY_SIZE_RATIO,
|
||||
disable_loop_cover_traffic_stream: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct GatewayConnectionV1_1_20_2 {
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub gateway_response_timeout: Duration,
|
||||
}
|
||||
|
||||
impl From<GatewayConnectionV1_1_20_2> for GatewayConnection {
|
||||
fn from(value: GatewayConnectionV1_1_20_2) -> Self {
|
||||
GatewayConnection {
|
||||
gateway_response_timeout: value.gateway_response_timeout,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for GatewayConnectionV1_1_20_2 {
|
||||
fn default() -> Self {
|
||||
GatewayConnectionV1_1_20_2 {
|
||||
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct AcknowledgementsV1_1_20_2 {
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub average_ack_delay: Duration,
|
||||
pub ack_wait_multiplier: f64,
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub ack_wait_addition: Duration,
|
||||
}
|
||||
|
||||
impl From<AcknowledgementsV1_1_20_2> for Acknowledgements {
|
||||
fn from(value: AcknowledgementsV1_1_20_2) -> Self {
|
||||
Acknowledgements {
|
||||
average_ack_delay: value.average_ack_delay,
|
||||
ack_wait_multiplier: value.ack_wait_multiplier,
|
||||
ack_wait_addition: value.ack_wait_addition,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AcknowledgementsV1_1_20_2 {
|
||||
fn default() -> Self {
|
||||
AcknowledgementsV1_1_20_2 {
|
||||
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
|
||||
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
|
||||
ack_wait_addition: DEFAULT_ACK_WAIT_ADDITION,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct TopologyV1_1_20_2 {
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub topology_refresh_rate: Duration,
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub topology_resolution_timeout: Duration,
|
||||
pub disable_refreshing: bool,
|
||||
}
|
||||
|
||||
impl Default for TopologyV1_1_20_2 {
|
||||
fn default() -> Self {
|
||||
TopologyV1_1_20_2 {
|
||||
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
|
||||
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
|
||||
disable_refreshing: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TopologyV1_1_20_2> for Topology {
|
||||
fn from(value: TopologyV1_1_20_2) -> Self {
|
||||
Topology {
|
||||
topology_refresh_rate: value.topology_refresh_rate,
|
||||
topology_resolution_timeout: value.topology_resolution_timeout,
|
||||
disable_refreshing: value.disable_refreshing,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct ReplySurbsV1_1_20_2 {
|
||||
pub minimum_reply_surb_storage_threshold: usize,
|
||||
pub maximum_reply_surb_storage_threshold: usize,
|
||||
pub minimum_reply_surb_request_size: u32,
|
||||
pub maximum_reply_surb_request_size: u32,
|
||||
pub maximum_allowed_reply_surb_request_size: u32,
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub maximum_reply_surb_rerequest_waiting_period: Duration,
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub maximum_reply_surb_drop_waiting_period: Duration,
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub maximum_reply_surb_age: Duration,
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub maximum_reply_key_age: Duration,
|
||||
}
|
||||
|
||||
impl Default for ReplySurbsV1_1_20_2 {
|
||||
fn default() -> Self {
|
||||
ReplySurbsV1_1_20_2 {
|
||||
minimum_reply_surb_storage_threshold: DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
|
||||
maximum_reply_surb_storage_threshold: DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD,
|
||||
minimum_reply_surb_request_size: DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE,
|
||||
maximum_reply_surb_request_size: DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE,
|
||||
maximum_allowed_reply_surb_request_size: DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE,
|
||||
maximum_reply_surb_rerequest_waiting_period:
|
||||
DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD,
|
||||
maximum_reply_surb_drop_waiting_period: DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD,
|
||||
maximum_reply_surb_age: DEFAULT_MAXIMUM_REPLY_SURB_AGE,
|
||||
maximum_reply_key_age: DEFAULT_MAXIMUM_REPLY_KEY_AGE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ReplySurbsV1_1_20_2> for ReplySurbs {
|
||||
fn from(value: ReplySurbsV1_1_20_2) -> Self {
|
||||
ReplySurbs {
|
||||
minimum_reply_surb_storage_threshold: value.minimum_reply_surb_storage_threshold,
|
||||
maximum_reply_surb_storage_threshold: value.maximum_reply_surb_storage_threshold,
|
||||
minimum_reply_surb_request_size: value.minimum_reply_surb_request_size,
|
||||
maximum_reply_surb_request_size: value.maximum_reply_surb_request_size,
|
||||
maximum_allowed_reply_surb_request_size: value.maximum_allowed_reply_surb_request_size,
|
||||
maximum_reply_surb_rerequest_waiting_period: value
|
||||
.maximum_reply_surb_rerequest_waiting_period,
|
||||
maximum_reply_surb_drop_waiting_period: value.maximum_reply_surb_drop_waiting_period,
|
||||
maximum_reply_surb_age: value.maximum_reply_surb_age,
|
||||
maximum_reply_key_age: value.maximum_reply_key_age,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct DebugConfigV1_1_20_2 {
|
||||
pub traffic: TrafficV1_1_20_2,
|
||||
pub cover_traffic: CoverTrafficV1_1_20_2,
|
||||
pub gateway_connection: GatewayConnectionV1_1_20_2,
|
||||
pub acknowledgements: AcknowledgementsV1_1_20_2,
|
||||
pub topology: TopologyV1_1_20_2,
|
||||
pub reply_surbs: ReplySurbsV1_1_20_2,
|
||||
}
|
||||
|
||||
impl From<DebugConfigV1_1_20_2> for DebugConfig {
|
||||
fn from(value: DebugConfigV1_1_20_2) -> Self {
|
||||
DebugConfig {
|
||||
traffic: value.traffic.into(),
|
||||
cover_traffic: value.cover_traffic.into(),
|
||||
gateway_connection: value.gateway_connection.into(),
|
||||
acknowledgements: value.acknowledgements.into(),
|
||||
topology: value.topology.into(),
|
||||
reply_surbs: value.reply_surbs.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,11 @@ pub enum ClientCoreError {
|
||||
source: Box<dyn Error + Send + Sync>,
|
||||
},
|
||||
|
||||
#[error("experienced a failure with our gateway details storage: {source}")]
|
||||
GatewayDetailsStoreError {
|
||||
source: Box<dyn Error + Send + Sync>,
|
||||
},
|
||||
|
||||
#[error("The gateway id is invalid - {0}")]
|
||||
UnableToCreatePublicKeyFromGatewayId(Ed25519RecoveryError),
|
||||
|
||||
@@ -97,6 +102,20 @@ pub enum ClientCoreError {
|
||||
"This operation would have resulted in clients keys being overwritten without permission"
|
||||
)]
|
||||
ForbiddenKeyOverwrite,
|
||||
|
||||
#[error("gateway details are unavailable")]
|
||||
UnavailableGatewayDetails {
|
||||
source: Box<dyn Error + Send + Sync>,
|
||||
},
|
||||
|
||||
#[error("gateway shared key is unavailable whilst we have full node information")]
|
||||
UnavailableSharedKey,
|
||||
|
||||
#[error("attempted to obtain fresh gateway details whilst already knowing about one")]
|
||||
UnexpectedGatewayDetails,
|
||||
|
||||
#[error("the provided gateway details (for gateway {gateway_id}) do not correspond to the shared keys")]
|
||||
MismatchedGatewayDetails { gateway_id: String },
|
||||
}
|
||||
|
||||
/// Set of messages that the client can send to listeners via the task manager
|
||||
|
||||
@@ -44,18 +44,18 @@ const MEASUREMENTS: usize = 3;
|
||||
const CONN_TIMEOUT: Duration = Duration::from_millis(1500);
|
||||
const PING_TIMEOUT: Duration = Duration::from_millis(1000);
|
||||
|
||||
struct GatewayWithLatency {
|
||||
gateway: gateway::Node,
|
||||
struct GatewayWithLatency<'a> {
|
||||
gateway: &'a gateway::Node,
|
||||
latency: Duration,
|
||||
}
|
||||
|
||||
impl GatewayWithLatency {
|
||||
fn new(gateway: gateway::Node, latency: Duration) -> Self {
|
||||
impl<'a> GatewayWithLatency<'a> {
|
||||
fn new(gateway: &'a gateway::Node, latency: Duration) -> Self {
|
||||
GatewayWithLatency { gateway, latency }
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) async fn current_gateways<R: Rng>(
|
||||
pub async fn current_gateways<R: Rng>(
|
||||
rng: &mut R,
|
||||
nym_apis: &[Url],
|
||||
) -> Result<Vec<gateway::Node>, ClientCoreError> {
|
||||
@@ -64,7 +64,7 @@ pub(super) async fn current_gateways<R: Rng>(
|
||||
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
|
||||
let client = nym_validator_client::client::NymApiClient::new(nym_api.clone());
|
||||
|
||||
log::trace!("Fetching list of gateways from: {}", nym_api);
|
||||
log::trace!("Fetching list of gateways from: {nym_api}");
|
||||
|
||||
let gateways = client.get_cached_gateways().await?;
|
||||
let valid_gateways = gateways
|
||||
@@ -91,7 +91,7 @@ async fn connect(endpoint: &str) -> Result<WsConn, ClientCoreError> {
|
||||
JSWebsocket::new(endpoint).map_err(|_| ClientCoreError::GatewayJsConnectionFailure)
|
||||
}
|
||||
|
||||
async fn measure_latency(gateway: gateway::Node) -> Result<GatewayWithLatency, ClientCoreError> {
|
||||
async fn measure_latency(gateway: &gateway::Node) -> Result<GatewayWithLatency, ClientCoreError> {
|
||||
let addr = gateway.clients_address();
|
||||
trace!(
|
||||
"establishing connection to {} ({addr})...",
|
||||
@@ -156,7 +156,7 @@ async fn measure_latency(gateway: gateway::Node) -> Result<GatewayWithLatency, C
|
||||
|
||||
pub(super) async fn choose_gateway_by_latency<R: Rng>(
|
||||
rng: &mut R,
|
||||
gateways: Vec<gateway::Node>,
|
||||
gateways: &[gateway::Node],
|
||||
) -> Result<gateway::Node, ClientCoreError> {
|
||||
info!("choosing gateway by latency...");
|
||||
|
||||
@@ -189,7 +189,7 @@ pub(super) async fn choose_gateway_by_latency<R: Rng>(
|
||||
|
||||
pub(super) fn uniformly_random_gateway<R: Rng>(
|
||||
rng: &mut R,
|
||||
gateways: Vec<gateway::Node>,
|
||||
gateways: &[gateway::Node],
|
||||
) -> Result<gateway::Node, ClientCoreError> {
|
||||
gateways
|
||||
.choose(rng)
|
||||
|
||||
+272
-145
@@ -3,26 +3,82 @@
|
||||
|
||||
//! Collection of initialization steps used by client implementations
|
||||
|
||||
use crate::client::base_client::storage::MixnetClientStorage;
|
||||
use crate::client::base_client::storage::gateway_details::{
|
||||
GatewayDetailsStore, PersistedGatewayDetails,
|
||||
};
|
||||
use crate::client::key_manager::persistence::KeyStore;
|
||||
use crate::client::key_manager::{KeyManager, ManagedKeys};
|
||||
use crate::client::key_manager::ManagedKeys;
|
||||
use crate::init::helpers::{choose_gateway_by_latency, current_gateways, uniformly_random_gateway};
|
||||
use crate::{
|
||||
config::{disk_persistence::keys_paths::ClientKeysPaths, Config, GatewayEndpointConfig},
|
||||
config::{Config, GatewayEndpointConfig},
|
||||
error::ClientCoreError,
|
||||
};
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_sphinx::addressing::{clients::Recipient, nodes::NodeIdentity};
|
||||
use nym_topology::gateway;
|
||||
use nym_validator_client::client::IdentityKey;
|
||||
use rand::rngs::OsRng;
|
||||
use serde::Serialize;
|
||||
use std::fmt::{Debug, Display};
|
||||
use url::Url;
|
||||
|
||||
mod helpers;
|
||||
pub mod helpers;
|
||||
|
||||
#[derive(Clone)]
|
||||
// TODO: rename to something better...
|
||||
#[derive(Debug)]
|
||||
pub struct InitialisationDetails {
|
||||
pub gateway_details: GatewayEndpointConfig,
|
||||
pub managed_keys: ManagedKeys,
|
||||
}
|
||||
|
||||
impl InitialisationDetails {
|
||||
pub fn new(gateway_details: GatewayEndpointConfig, managed_keys: ManagedKeys) -> Self {
|
||||
InitialisationDetails {
|
||||
gateway_details,
|
||||
managed_keys,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn try_load<K, D>(key_store: &K, details_store: &D) -> Result<Self, ClientCoreError>
|
||||
where
|
||||
K: KeyStore,
|
||||
D: GatewayDetailsStore,
|
||||
K::StorageError: Send + Sync + 'static,
|
||||
D::StorageError: Send + Sync + 'static,
|
||||
{
|
||||
let loaded_details = _load_gateway_details(details_store).await?;
|
||||
let loaded_keys = _load_managed_keys(key_store).await?;
|
||||
|
||||
if !loaded_details.verify(&loaded_keys.must_get_gateway_shared_key()) {
|
||||
return Err(ClientCoreError::MismatchedGatewayDetails {
|
||||
gateway_id: loaded_details.details.gateway_id,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(InitialisationDetails {
|
||||
gateway_details: loaded_details.into(),
|
||||
managed_keys: loaded_keys,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn client_address(&self) -> Result<Recipient, ClientCoreError> {
|
||||
let client_recipient = Recipient::new(
|
||||
*self.managed_keys.identity_public_key(),
|
||||
*self.managed_keys.encryption_public_key(),
|
||||
// TODO: below only works under assumption that gateway address == gateway id
|
||||
// (which currently is true)
|
||||
NodeIdentity::from_base58_string(&self.gateway_details.gateway_id)?,
|
||||
);
|
||||
|
||||
Ok(client_recipient)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum GatewaySetup {
|
||||
/// The gateway specification MUST BE loaded from the underlying storage.
|
||||
MustLoad,
|
||||
|
||||
/// Specifies usage of a new, random, gateway.
|
||||
New {
|
||||
/// Should the new gateway be selected based on latency.
|
||||
@@ -34,13 +90,13 @@ pub enum GatewaySetup {
|
||||
},
|
||||
Predefined {
|
||||
/// Full gateway configuration
|
||||
config: GatewayEndpointConfig,
|
||||
details: PersistedGatewayDetails,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<GatewayEndpointConfig> for GatewaySetup {
|
||||
fn from(config: GatewayEndpointConfig) -> Self {
|
||||
GatewaySetup::Predefined { config }
|
||||
impl From<PersistedGatewayDetails> for GatewaySetup {
|
||||
fn from(details: PersistedGatewayDetails) -> Self {
|
||||
GatewaySetup::Predefined { details }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,14 +113,11 @@ impl Default for GatewaySetup {
|
||||
}
|
||||
|
||||
impl GatewaySetup {
|
||||
pub fn new(
|
||||
full_config: Option<GatewayEndpointConfig>,
|
||||
gateway_identity: Option<IdentityKey>,
|
||||
pub fn new_fresh(
|
||||
gateway_identity: Option<String>,
|
||||
latency_based_selection: Option<bool>,
|
||||
) -> Self {
|
||||
if let Some(config) = full_config {
|
||||
GatewaySetup::Predefined { config }
|
||||
} else if let Some(gateway_identity) = gateway_identity {
|
||||
if let Some(gateway_identity) = gateway_identity {
|
||||
GatewaySetup::Specified { gateway_identity }
|
||||
} else {
|
||||
GatewaySetup::New {
|
||||
@@ -73,15 +126,22 @@ impl GatewaySetup {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn try_get_gateway_details(
|
||||
self,
|
||||
validator_servers: &[Url],
|
||||
pub fn is_must_load(&self) -> bool {
|
||||
matches!(self, GatewaySetup::MustLoad)
|
||||
}
|
||||
|
||||
pub fn has_full_details(&self) -> bool {
|
||||
matches!(self, GatewaySetup::Predefined { .. }) || self.is_must_load()
|
||||
}
|
||||
|
||||
pub async fn choose_gateway(
|
||||
&self,
|
||||
gateways: &[gateway::Node],
|
||||
) -> Result<GatewayEndpointConfig, ClientCoreError> {
|
||||
match self {
|
||||
GatewaySetup::New { by_latency } => {
|
||||
let mut rng = OsRng;
|
||||
let gateways = current_gateways(&mut rng, validator_servers).await?;
|
||||
if by_latency {
|
||||
if *by_latency {
|
||||
choose_gateway_by_latency(&mut rng, gateways).await
|
||||
} else {
|
||||
uniformly_random_gateway(&mut rng, gateways)
|
||||
@@ -89,20 +149,28 @@ impl GatewaySetup {
|
||||
}
|
||||
.map(Into::into),
|
||||
GatewaySetup::Specified { gateway_identity } => {
|
||||
let user_gateway = identity::PublicKey::from_base58_string(&gateway_identity)
|
||||
let user_gateway = identity::PublicKey::from_base58_string(gateway_identity)
|
||||
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
|
||||
|
||||
let mut rng = OsRng;
|
||||
let gateways = current_gateways(&mut rng, validator_servers).await?;
|
||||
gateways
|
||||
.into_iter()
|
||||
.iter()
|
||||
.find(|gateway| gateway.identity_key == user_gateway)
|
||||
.ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_identity.to_string()))
|
||||
.cloned()
|
||||
}
|
||||
.map(Into::into),
|
||||
GatewaySetup::Predefined { config } => Ok(config),
|
||||
_ => Err(ClientCoreError::UnexpectedGatewayDetails),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn try_get_new_gateway_details(
|
||||
&self,
|
||||
validator_servers: &[Url],
|
||||
) -> Result<GatewayEndpointConfig, ClientCoreError> {
|
||||
let mut rng = OsRng;
|
||||
let gateways = current_gateways(&mut rng, validator_servers).await?;
|
||||
self.choose_gateway(&gateways).await
|
||||
}
|
||||
}
|
||||
|
||||
/// Struct describing the results of the client initialization procedure.
|
||||
@@ -117,14 +185,14 @@ pub struct InitResults {
|
||||
}
|
||||
|
||||
impl InitResults {
|
||||
pub fn new(config: &Config, address: &Recipient) -> Self {
|
||||
pub fn new(config: &Config, address: &Recipient, gateway: &GatewayEndpointConfig) -> Self {
|
||||
Self {
|
||||
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(),
|
||||
gateway_listener: config.get_gateway_listener(),
|
||||
gateway_id: gateway.gateway_id.clone(),
|
||||
gateway_listener: gateway.gateway_listener.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -140,38 +208,164 @@ impl Display for InitResults {
|
||||
}
|
||||
}
|
||||
|
||||
/// Recovers the already present gateway information or attempts to register with new gateway
|
||||
/// and stores the newly obtained key
|
||||
pub async fn get_registered_gateway<S>(
|
||||
validator_servers: Vec<Url>,
|
||||
key_store: &S::KeyStore,
|
||||
setup: GatewaySetup,
|
||||
overwrite_keys: bool,
|
||||
) -> Result<(GatewayEndpointConfig, ManagedKeys), ClientCoreError>
|
||||
// helpers for error wrapping
|
||||
async fn _store_gateway_details<D>(
|
||||
details_store: &D,
|
||||
details: &PersistedGatewayDetails,
|
||||
) -> Result<(), ClientCoreError>
|
||||
where
|
||||
S: MixnetClientStorage,
|
||||
<S::KeyStore as KeyStore>::StorageError: Send + Sync + 'static,
|
||||
D: GatewayDetailsStore,
|
||||
D::StorageError: Send + Sync + 'static,
|
||||
{
|
||||
details_store
|
||||
.store_gateway_details(details)
|
||||
.await
|
||||
.map_err(|source| ClientCoreError::GatewayDetailsStoreError {
|
||||
source: Box::new(source),
|
||||
})
|
||||
}
|
||||
|
||||
async fn _load_gateway_details<D>(
|
||||
details_store: &D,
|
||||
) -> Result<PersistedGatewayDetails, ClientCoreError>
|
||||
where
|
||||
D: GatewayDetailsStore,
|
||||
D::StorageError: Send + Sync + 'static,
|
||||
{
|
||||
details_store
|
||||
.load_gateway_details()
|
||||
.await
|
||||
.map_err(|source| ClientCoreError::UnavailableGatewayDetails {
|
||||
source: Box::new(source),
|
||||
})
|
||||
}
|
||||
|
||||
async fn _load_managed_keys<K>(key_store: &K) -> Result<ManagedKeys, ClientCoreError>
|
||||
where
|
||||
K: KeyStore,
|
||||
K::StorageError: Send + Sync + 'static,
|
||||
{
|
||||
ManagedKeys::try_load(key_store)
|
||||
.await
|
||||
.map_err(|source| ClientCoreError::KeyStoreError {
|
||||
source: Box::new(source),
|
||||
})
|
||||
}
|
||||
|
||||
fn ensure_valid_details(
|
||||
details: &PersistedGatewayDetails,
|
||||
loaded_keys: &ManagedKeys,
|
||||
) -> Result<(), ClientCoreError> {
|
||||
if !details.verify(&loaded_keys.must_get_gateway_shared_key()) {
|
||||
Err(ClientCoreError::MismatchedGatewayDetails {
|
||||
gateway_id: details.details.gateway_id.clone(),
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn setup_gateway_from<K, D>(
|
||||
setup: &GatewaySetup,
|
||||
key_store: &K,
|
||||
details_store: &D,
|
||||
overwrite_data: bool,
|
||||
gateways: Option<&[gateway::Node]>,
|
||||
) -> Result<InitialisationDetails, ClientCoreError>
|
||||
where
|
||||
K: KeyStore,
|
||||
D: GatewayDetailsStore,
|
||||
K::StorageError: Send + Sync + 'static,
|
||||
D::StorageError: Send + Sync + 'static,
|
||||
{
|
||||
let mut rng = OsRng;
|
||||
|
||||
// try load keys
|
||||
// try load gateway details
|
||||
let loaded_details = _load_gateway_details(details_store).await;
|
||||
|
||||
// try load keys and decide what to do based on the GatewaySetup
|
||||
let mut managed_keys = match ManagedKeys::try_load(key_store).await {
|
||||
Ok(loaded_keys) => {
|
||||
// if we loaded something and we don't have full gateway details, check if we can overwrite the data
|
||||
if let GatewaySetup::Predefined { config } = setup {
|
||||
// we already have defined gateway details AND a shared key, so nothing more for us to do
|
||||
return Ok((config, loaded_keys));
|
||||
} else if overwrite_keys {
|
||||
ManagedKeys::generate_new(&mut rng)
|
||||
} else {
|
||||
return Err(ClientCoreError::ForbiddenKeyOverwrite);
|
||||
match setup {
|
||||
GatewaySetup::MustLoad => {
|
||||
// get EVERYTHING from the storage
|
||||
let details = loaded_details?;
|
||||
ensure_valid_details(&details, &loaded_keys)?;
|
||||
|
||||
// no need to persist anything as we got everything from the storage
|
||||
return Ok(InitialisationDetails::new(details.into(), loaded_keys));
|
||||
}
|
||||
GatewaySetup::Predefined { details } => {
|
||||
// we already have defined gateway details AND a shared key
|
||||
ensure_valid_details(details, &loaded_keys)?;
|
||||
|
||||
// if nothing was stored or we're allowed to overwrite what's there, just persist the passed data
|
||||
if overwrite_data || loaded_details.is_err() {
|
||||
_store_gateway_details(details_store, details).await?;
|
||||
}
|
||||
|
||||
return Ok(InitialisationDetails::new(
|
||||
details.clone().into(),
|
||||
loaded_keys,
|
||||
));
|
||||
}
|
||||
GatewaySetup::Specified { gateway_identity } => {
|
||||
// if that data was already stored...
|
||||
if let Ok(existing_gateway) = loaded_details {
|
||||
ensure_valid_details(&existing_gateway, &loaded_keys)?;
|
||||
if &existing_gateway.details.gateway_id != gateway_identity
|
||||
&& !overwrite_data
|
||||
{
|
||||
// if our loaded details don't match requested value and we CANT overwrite it...
|
||||
return Err(ClientCoreError::UnexpectedGatewayDetails);
|
||||
} else if &existing_gateway.details.gateway_id == gateway_identity {
|
||||
// if they do match up, just return it
|
||||
return Ok(InitialisationDetails::new(
|
||||
existing_gateway.into(),
|
||||
loaded_keys,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// we didn't get full details from the store and we have loaded some keys
|
||||
// so we can only continue if we're allowed to overwrite keys
|
||||
if overwrite_data {
|
||||
ManagedKeys::generate_new(&mut rng)
|
||||
} else {
|
||||
return Err(ClientCoreError::ForbiddenKeyOverwrite);
|
||||
}
|
||||
}
|
||||
GatewaySetup::New { .. } => {
|
||||
if let Ok(existing_gateway) = loaded_details {
|
||||
ensure_valid_details(&existing_gateway, &loaded_keys)?;
|
||||
return Ok(InitialisationDetails::new(
|
||||
existing_gateway.into(),
|
||||
loaded_keys,
|
||||
));
|
||||
}
|
||||
|
||||
// we didn't get full details from the store and we have loaded some keys
|
||||
// so we can only continue if we're allowed to overwrite keys
|
||||
if overwrite_data {
|
||||
ManagedKeys::generate_new(&mut rng)
|
||||
} else {
|
||||
return Err(ClientCoreError::ForbiddenKeyOverwrite);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => ManagedKeys::generate_new(&mut rng),
|
||||
Err(_) => {
|
||||
// if we failed to load the keys, ensure we didn't provide gateway details in some form
|
||||
// (in that case we CAN'T generate new keys
|
||||
if setup.has_full_details() {
|
||||
return Err(ClientCoreError::UnavailableSharedKey);
|
||||
}
|
||||
ManagedKeys::generate_new(&mut rng)
|
||||
}
|
||||
};
|
||||
|
||||
// choose gateway
|
||||
let gateway_details = setup.try_get_gateway_details(&validator_servers).await?;
|
||||
let gateway_details = setup.choose_gateway(gateways.unwrap_or_default()).await?;
|
||||
|
||||
// get our identity key
|
||||
let our_identity = managed_keys.identity_keypair();
|
||||
@@ -179,6 +373,9 @@ where
|
||||
// Establish connection, authenticate and generate keys for talking with the gateway
|
||||
let shared_keys = helpers::register_with_gateway(&gateway_details, our_identity).await?;
|
||||
|
||||
let persisted_details = PersistedGatewayDetails::new(gateway_details, &shared_keys);
|
||||
|
||||
// persist gateway keys
|
||||
managed_keys
|
||||
.deal_with_gateway_key(shared_keys, key_store)
|
||||
.await
|
||||
@@ -186,109 +383,39 @@ where
|
||||
source: Box::new(source),
|
||||
})?;
|
||||
|
||||
// TODO: here we should be probably persisting gateway details as opposed to returning them
|
||||
// persist gateway config
|
||||
_store_gateway_details(details_store, &persisted_details).await?;
|
||||
|
||||
Ok((gateway_details, managed_keys))
|
||||
Ok(InitialisationDetails::new(
|
||||
persisted_details.into(),
|
||||
managed_keys,
|
||||
))
|
||||
}
|
||||
|
||||
/// Convenience function for setting up the gateway for a client given a `Config`. Depending on the
|
||||
/// arguments given it will do the sensible thing. Either it will
|
||||
///
|
||||
/// a. Reuse existing gateway configuration from storage.
|
||||
/// 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<KSt>(
|
||||
key_store: &KSt,
|
||||
register_gateway: bool,
|
||||
user_chosen_gateway_id: Option<identity::PublicKey>,
|
||||
config: &Config,
|
||||
old_gateway_config: Option<GatewayEndpointConfig>,
|
||||
by_latency: bool,
|
||||
) -> Result<GatewayEndpointConfig, ClientCoreError>
|
||||
pub async fn setup_gateway<K, D>(
|
||||
setup: &GatewaySetup,
|
||||
key_store: &K,
|
||||
details_store: &D,
|
||||
overwrite_data: bool,
|
||||
validator_servers: Option<&[Url]>,
|
||||
) -> Result<InitialisationDetails, ClientCoreError>
|
||||
where
|
||||
KSt: KeyStore,
|
||||
<KSt as KeyStore>::StorageError: Send + Sync + 'static,
|
||||
K: KeyStore,
|
||||
D: GatewayDetailsStore,
|
||||
K::StorageError: Send + Sync + 'static,
|
||||
D::StorageError: Send + Sync + 'static,
|
||||
{
|
||||
// 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 old_gateway_config.ok_or(ClientCoreError::FailedToSetupGateway);
|
||||
}
|
||||
|
||||
let gateway_setup = GatewaySetup::new(
|
||||
None,
|
||||
user_chosen_gateway_id.map(|id| id.to_base58_string()),
|
||||
Some(by_latency),
|
||||
);
|
||||
// Else, we proceed by querying the nym-api
|
||||
let gateway = gateway_setup
|
||||
.try_get_gateway_details(&config.get_nym_api_endpoints())
|
||||
.await?;
|
||||
log::debug!("Querying gateway gives: {:?}", gateway);
|
||||
|
||||
// If we are not registering, just return this and assume the caller has the keys already and
|
||||
// wants to keep the,
|
||||
if !register_gateway && user_chosen_gateway_id.is_some() {
|
||||
eprintln!("Using gateway provided by user, keeping existing keys");
|
||||
return Ok(gateway);
|
||||
}
|
||||
|
||||
let mut rng = OsRng;
|
||||
let mut managed_keys =
|
||||
crate::client::key_manager::ManagedKeys::load_or_generate(&mut rng, key_store).await;
|
||||
let gateways = current_gateways(&mut rng, validator_servers.unwrap_or_default()).await?;
|
||||
|
||||
// Create new keys and derive our identity
|
||||
let our_identity = managed_keys.identity_keypair();
|
||||
|
||||
// Establish connection, authenticate and generate keys for talking with the gateway
|
||||
eprintln!("Registering with new gateway");
|
||||
let shared_keys = helpers::register_with_gateway(&gateway, our_identity).await?;
|
||||
managed_keys
|
||||
.deal_with_gateway_key(shared_keys, key_store)
|
||||
.await
|
||||
.map_err(|source| ClientCoreError::KeyStoreError {
|
||||
source: Box::new(source),
|
||||
})?;
|
||||
|
||||
Ok(gateway)
|
||||
}
|
||||
|
||||
/// Get the full client address from the client keys and the gateway identity
|
||||
pub fn get_client_address(
|
||||
key_manager: &KeyManager,
|
||||
gateway_config: &GatewayEndpointConfig,
|
||||
) -> Recipient {
|
||||
Recipient::new(
|
||||
*key_manager.identity_keypair().public_key(),
|
||||
*key_manager.encryption_keypair().public_key(),
|
||||
// TODO: below only works under assumption that gateway address == gateway id
|
||||
// (which currently is true)
|
||||
NodeIdentity::from_base58_string(&gateway_config.gateway_id).unwrap(),
|
||||
setup_gateway_from(
|
||||
setup,
|
||||
key_store,
|
||||
details_store,
|
||||
overwrite_data,
|
||||
Some(&gateways),
|
||||
)
|
||||
}
|
||||
|
||||
/// Get the client address by loading the keys from stored files.
|
||||
// TODO: rethink that sucker
|
||||
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(
|
||||
public_identity,
|
||||
public_encryption,
|
||||
// TODO: below only works under assumption that gateway address == gateway id
|
||||
// (which currently is true)
|
||||
NodeIdentity::from_base58_string(&gateway_config.gateway_id)?,
|
||||
);
|
||||
|
||||
Ok(client_recipient)
|
||||
.await
|
||||
}
|
||||
|
||||
pub fn output_to_json<T: Serialize>(init_results: &T, output_file: &str) {
|
||||
|
||||
@@ -9,6 +9,8 @@ use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Debug;
|
||||
use std::str::FromStr;
|
||||
|
||||
pub mod old_config_v1_1_20_2;
|
||||
|
||||
pub use nym_service_providers_common::interface::ProviderInterfaceVersion;
|
||||
pub use nym_socks5_requests::Socks5ProtocolVersion;
|
||||
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub use nym_client_core::config::old_config_v1_1_20_2::ConfigV1_1_20_2 as BaseClientConfigV1_1_20_2;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::config::{Config, Socks5, Socks5Debug};
|
||||
pub use nym_service_providers_common::interface::ProviderInterfaceVersion;
|
||||
pub use nym_socks5_requests::Socks5ProtocolVersion;
|
||||
|
||||
const DEFAULT_CONNECTION_START_SURBS: u32 = 20;
|
||||
const DEFAULT_PER_REQUEST_SURBS: u32 = 3;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ConfigV1_1_20_2 {
|
||||
#[serde(flatten)]
|
||||
pub base: BaseClientConfigV1_1_20_2,
|
||||
|
||||
pub socks5: Socks5V1_1_20_2,
|
||||
}
|
||||
|
||||
impl From<ConfigV1_1_20_2> for Config {
|
||||
fn from(value: ConfigV1_1_20_2) -> Self {
|
||||
Config {
|
||||
base: value.base.into(),
|
||||
socks5: value.socks5.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Socks5V1_1_20_2 {
|
||||
pub listening_port: u16,
|
||||
pub provider_mix_address: String,
|
||||
#[serde(default = "ProviderInterfaceVersion::new_legacy")]
|
||||
pub provider_interface_version: ProviderInterfaceVersion,
|
||||
#[serde(default = "Socks5ProtocolVersion::new_legacy")]
|
||||
pub socks5_protocol_version: Socks5ProtocolVersion,
|
||||
#[serde(default)]
|
||||
pub send_anonymously: bool,
|
||||
#[serde(default)]
|
||||
pub socks5_debug: Socks5DebugV1_1_20_2,
|
||||
}
|
||||
|
||||
impl From<Socks5V1_1_20_2> for Socks5 {
|
||||
fn from(value: Socks5V1_1_20_2) -> Self {
|
||||
Socks5 {
|
||||
listening_port: value.listening_port,
|
||||
provider_mix_address: value.provider_mix_address,
|
||||
provider_interface_version: value.provider_interface_version,
|
||||
socks5_protocol_version: value.socks5_protocol_version,
|
||||
send_anonymously: value.send_anonymously,
|
||||
socks5_debug: value.socks5_debug.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Socks5DebugV1_1_20_2 {
|
||||
/// Number of reply SURBs attached to each `Request::Connect` message.
|
||||
pub connection_start_surbs: u32,
|
||||
|
||||
/// Number of reply SURBs attached to each `Request::Send` message.
|
||||
pub per_request_surbs: u32,
|
||||
}
|
||||
|
||||
impl From<Socks5DebugV1_1_20_2> for Socks5Debug {
|
||||
fn from(value: Socks5DebugV1_1_20_2) -> Self {
|
||||
Socks5Debug {
|
||||
connection_start_surbs: value.connection_start_surbs,
|
||||
per_request_surbs: value.per_request_surbs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Socks5DebugV1_1_20_2 {
|
||||
fn default() -> Self {
|
||||
Socks5DebugV1_1_20_2 {
|
||||
connection_start_surbs: DEFAULT_CONNECTION_START_SURBS,
|
||||
per_request_surbs: DEFAULT_PER_REQUEST_SURBS,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ use futures::channel::mpsc;
|
||||
use futures::StreamExt;
|
||||
use log::*;
|
||||
use nym_client_core::client::base_client::non_wasm_helpers::default_query_dkg_client_from_config;
|
||||
use nym_client_core::client::base_client::storage::gateway_details::GatewayDetailsStore;
|
||||
use nym_client_core::client::base_client::storage::MixnetClientStorage;
|
||||
use nym_client_core::client::base_client::{
|
||||
BaseClientBuilder, ClientInput, ClientOutput, ClientState,
|
||||
@@ -60,6 +61,7 @@ where
|
||||
S::ReplyStore: Send + Sync,
|
||||
<S::ReplyStore as ReplyStorageBackend>::StorageError: Sync + Send,
|
||||
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync,
|
||||
<S::GatewayDetailsStore as GatewayDetailsStore>::StorageError: Sync + Send,
|
||||
<S::KeyStore as KeyStore>::StorageError: Send + Sync,
|
||||
{
|
||||
pub fn new(config: Config, storage: S) -> Self {
|
||||
|
||||
Generated
+7
-5
@@ -207,9 +207,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.0"
|
||||
version = "0.21.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
|
||||
checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
@@ -3238,6 +3238,7 @@ name = "nym-client-core"
|
||||
version = "1.1.14"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.21.2",
|
||||
"dashmap",
|
||||
"dirs 4.0.0",
|
||||
"futures",
|
||||
@@ -3260,6 +3261,7 @@ dependencies = [
|
||||
"rand 0.7.3",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.10.6",
|
||||
"sqlx 0.6.3",
|
||||
"tap",
|
||||
"thiserror",
|
||||
@@ -4427,7 +4429,7 @@ version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9bd9647b268a3d3e14ff09c23201133a62589c658db02bb7388c7246aafe0590"
|
||||
dependencies = [
|
||||
"base64 0.21.0",
|
||||
"base64 0.21.2",
|
||||
"indexmap",
|
||||
"line-wrap",
|
||||
"quick-xml 0.28.1",
|
||||
@@ -4778,7 +4780,7 @@ version = "0.11.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ba30cc2c0cd02af1222ed216ba659cdb2f879dfe3181852fe7c50b1d0005949"
|
||||
dependencies = [
|
||||
"base64 0.21.0",
|
||||
"base64 0.21.2",
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
@@ -4972,7 +4974,7 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b"
|
||||
dependencies = [
|
||||
"base64 0.21.0",
|
||||
"base64 0.21.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -4,7 +4,10 @@
|
||||
use crate::config::persistence::NymConnectPaths;
|
||||
use crate::config::template::CONFIG_TEMPLATE;
|
||||
use crate::error::{BackendError, Result};
|
||||
use nym_client_core::client::base_client::storage::gateway_details::OnDiskGatewayDetails;
|
||||
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
|
||||
use nym_client_core::config::GatewayEndpointConfig;
|
||||
use nym_client_core::init::GatewaySetup;
|
||||
use nym_config_common::{
|
||||
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,
|
||||
@@ -127,13 +130,15 @@ pub async fn init_socks5_config(provider_address: String, chosen_gateway_id: Str
|
||||
|
||||
// Append the gateway id to the name id that we store the config under
|
||||
let id = socks5_config_id_appended_with(&chosen_gateway_id);
|
||||
let _validated = identity::PublicKey::from_base58_string(&chosen_gateway_id)
|
||||
.map_err(|_| BackendError::UnableToParseGateway)?;
|
||||
|
||||
let old_config = if default_config_filepath(&id).exists() {
|
||||
let already_init = if default_config_filepath(&id).exists() {
|
||||
eprintln!("SOCKS5 client \"{id}\" was already initialised before");
|
||||
Some(Config::read_from_default_path(&id)?)
|
||||
true
|
||||
} else {
|
||||
init_paths(&id)?;
|
||||
None
|
||||
false
|
||||
};
|
||||
|
||||
// Future proofing. This flag exists for the other clients
|
||||
@@ -142,7 +147,7 @@ pub async fn init_socks5_config(provider_address: String, chosen_gateway_id: Str
|
||||
// If the client was already initialized, don't generate new keys and don't re-register with
|
||||
// the gateway (because this would create a new shared key).
|
||||
// Unless the user really wants to.
|
||||
let register_gateway = old_config.is_none() || user_wants_force_register;
|
||||
let register_gateway = !already_init || user_wants_force_register;
|
||||
|
||||
log::trace!("Creating config for id: {id}");
|
||||
let mut config = Config::new(&id, &provider_address);
|
||||
@@ -151,49 +156,40 @@ pub async fn init_socks5_config(provider_address: String, chosen_gateway_id: Str
|
||||
config.core.base.client.nym_api_urls = nym_config_common::parse_urls(&raw_validators);
|
||||
}
|
||||
|
||||
let chosen_gateway_id = identity::PublicKey::from_base58_string(chosen_gateway_id)
|
||||
.map_err(|_| BackendError::UnableToParseGateway)?;
|
||||
let gateway_setup = GatewaySetup::new_fresh(Some(chosen_gateway_id), None);
|
||||
|
||||
// Setup gateway by either registering a new one, or reusing exiting keys
|
||||
let key_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone());
|
||||
let gateway = nym_client_core::init::setup_gateway_from_config::<_>(
|
||||
let details_store =
|
||||
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
|
||||
let init_details = nym_client_core::init::setup_gateway(
|
||||
&gateway_setup,
|
||||
&key_store,
|
||||
&details_store,
|
||||
register_gateway,
|
||||
Some(chosen_gateway_id),
|
||||
&config.core.base,
|
||||
old_config.map(|cfg| cfg.core.base.client.gateway_endpoint),
|
||||
// TODO: another instance where this setting should probably get used
|
||||
false,
|
||||
Some(&config.core.base.client.nym_api_urls),
|
||||
)
|
||||
.await?;
|
||||
|
||||
config.core.base.set_gateway_endpoint(gateway);
|
||||
|
||||
config.save_to_default_location().tap_err(|_| {
|
||||
log::error!("Failed to save the config file");
|
||||
})?;
|
||||
|
||||
print_saved_config(&config);
|
||||
print_saved_config(&config, &init_details.gateway_details);
|
||||
|
||||
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,
|
||||
)?;
|
||||
log::info!("The address of this client is: {}", address);
|
||||
let address = init_details.client_address()?;
|
||||
log::info!("The address of this client is: {address}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_saved_config(config: &Config) {
|
||||
fn print_saved_config(config: &Config, gateway_details: &GatewayEndpointConfig) {
|
||||
log::info!(
|
||||
"Saved configuration file to {}",
|
||||
config.default_location().display()
|
||||
);
|
||||
log::info!("Gateway id: {}", config.core.base.get_gateway_id());
|
||||
log::info!("Gateway owner: {}", config.core.base.get_gateway_owner());
|
||||
log::info!(
|
||||
"Gateway listener: {}",
|
||||
config.core.base.get_gateway_listener()
|
||||
);
|
||||
log::info!("Gateway id: {}", gateway_details.gateway_id);
|
||||
log::info!("Gateway owner: {}", gateway_details.gateway_owner);
|
||||
log::info!("Gateway listener: {}", gateway_details.gateway_listener);
|
||||
log::info!(
|
||||
"Service provider address: {}",
|
||||
config.core.socks5.provider_mix_address
|
||||
|
||||
@@ -64,16 +64,9 @@ 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
|
||||
[core.client.gateway_endpoint]
|
||||
# ID of the gateway from which the client should be fetching messages.
|
||||
gateway_id = '{{ core.client.gateway_endpoint.gateway_id }}'
|
||||
|
||||
# Address of the gateway owner to which the client should send messages.
|
||||
gateway_owner = '{{ core.client.gateway_endpoint.gateway_owner }}'
|
||||
|
||||
# Address of the gateway listener to which all client requests should be sent.
|
||||
gateway_listener = '{{ core.client.gateway_endpoint.gateway_listener }}'
|
||||
# Path to the file containing information about gateway used by this client,
|
||||
# i.e. details such as its public key, owner address or the network information.
|
||||
gateway_details = '{{ storage_paths.gateway_details }}'
|
||||
|
||||
|
||||
##### socket config options #####
|
||||
|
||||
@@ -176,7 +176,7 @@ impl State {
|
||||
}
|
||||
|
||||
// Kick off the main task and get the channel for controlling it
|
||||
self.start_nym_socks5_client()
|
||||
self.start_nym_socks5_client().await
|
||||
}
|
||||
|
||||
/// Create a configuration file
|
||||
@@ -196,12 +196,12 @@ impl State {
|
||||
}
|
||||
|
||||
/// Spawn a new thread running the SOCKS5 client
|
||||
fn start_nym_socks5_client(
|
||||
async fn start_nym_socks5_client(
|
||||
&mut self,
|
||||
) -> Result<(nym_task::StatusReceiver, ExitStatusReceiver)> {
|
||||
let id = self.get_config_id()?;
|
||||
let (control_tx, msg_rx, exit_status_rx, used_gateway) =
|
||||
tasks::start_nym_socks5_client(&id)?;
|
||||
tasks::start_nym_socks5_client(&id).await?;
|
||||
self.socks5_client_sender = Some(control_tx);
|
||||
self.gateway = Some(used_gateway.gateway_id);
|
||||
Ok((msg_rx, exit_status_rx))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use futures::{channel::mpsc, StreamExt};
|
||||
use nym_client_core::client::base_client::storage::OnDiskPersistent;
|
||||
use nym_client_core::client::base_client::storage::gateway_details::GatewayDetailsStore;
|
||||
use nym_client_core::client::base_client::storage::{MixnetClientStorage, OnDiskPersistent};
|
||||
use nym_client_core::{config::GatewayEndpointConfig, error::ClientCoreStatusMessage};
|
||||
use nym_socks5_client_core::NymClient as Socks5NymClient;
|
||||
use nym_socks5_client_core::Socks5ControlMessageSender;
|
||||
@@ -29,7 +30,7 @@ pub enum Socks5ExitStatusMessage {
|
||||
}
|
||||
|
||||
/// The main SOCKS5 client task. It loads the configuration from file determined by the `id`.
|
||||
pub fn start_nym_socks5_client(
|
||||
pub async fn start_nym_socks5_client(
|
||||
id: &str,
|
||||
) -> Result<(
|
||||
Socks5ControlMessageSender,
|
||||
@@ -40,7 +41,17 @@ pub fn start_nym_socks5_client(
|
||||
log::info!("Loading config from file: {id}");
|
||||
let config = Config::read_from_default_path(id)
|
||||
.tap_err(|_| log::warn!("Failed to load configuration file"))?;
|
||||
let used_gateway = config.core.base.client.gateway_endpoint.clone();
|
||||
|
||||
let storage =
|
||||
OnDiskPersistent::from_paths(config.storage_paths.common_paths, &config.core.base.debug)
|
||||
.await?;
|
||||
|
||||
let used_gateway = storage
|
||||
.gateway_details_store()
|
||||
.load_gateway_details()
|
||||
.await
|
||||
.expect("failed to load gateway details")
|
||||
.into();
|
||||
|
||||
log::info!("Starting socks5 client");
|
||||
|
||||
@@ -61,11 +72,6 @@ pub fn start_nym_socks5_client(
|
||||
let result = tokio::runtime::Runtime::new()
|
||||
.expect("Failed to create runtime for SOCKS5 client")
|
||||
.block_on(async move {
|
||||
let storage = OnDiskPersistent::from_paths(
|
||||
config.storage_paths.common_paths,
|
||||
&config.core.base.debug,
|
||||
)
|
||||
.await?;
|
||||
let socks5_client = Socks5NymClient::new(config.core, storage);
|
||||
|
||||
socks5_client
|
||||
|
||||
@@ -8,8 +8,6 @@ use anyhow::{anyhow, Result};
|
||||
use lazy_static::lazy_static;
|
||||
use log::{info, warn};
|
||||
use nym_bin_common::logging::setup_logging;
|
||||
use nym_client_core::client::key_manager::persistence::InMemEphemeralKeys;
|
||||
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
|
||||
use nym_config_common::defaults::setup_env;
|
||||
use nym_socks5_client_core::NymClient as Socks5NymClient;
|
||||
use safer_ffi::char_p::char_p_boxed;
|
||||
@@ -288,30 +286,10 @@ async fn setup_new_client_config(
|
||||
.set_custom_nym_apis(nym_config_common::parse_urls(&raw_validators));
|
||||
}
|
||||
|
||||
let gateway = if let Some(storage_paths) = &new_config.storage_paths {
|
||||
nym_client_core::init::setup_gateway_from_config::<_>(
|
||||
&OnDiskKeys::new(storage_paths.common_paths.keys.clone()),
|
||||
true,
|
||||
None,
|
||||
&new_config.core.base,
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
nym_client_core::init::setup_gateway_from_config::<_>(
|
||||
&InMemEphemeralKeys,
|
||||
true,
|
||||
None,
|
||||
&new_config.core.base,
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.await?
|
||||
if let Some(_storage_paths) = &new_config.storage_paths {
|
||||
println!("persistent storage is not implemented");
|
||||
};
|
||||
|
||||
new_config.core.base.set_gateway_endpoint(gateway);
|
||||
|
||||
if let Some(storage_dir) = storage_dir {
|
||||
new_config.save_to_default_location(storage_dir)?;
|
||||
}
|
||||
|
||||
@@ -1,40 +1,30 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::Config;
|
||||
use nym_client_core::client::base_client::storage::gateway_details::InMemGatewayDetails;
|
||||
use nym_client_core::client::base_client::storage::MixnetClientStorage;
|
||||
use nym_client_core::client::key_manager::persistence::InMemEphemeralKeys;
|
||||
use nym_client_core::client::replies::reply_storage;
|
||||
use nym_credential_storage::ephemeral_storage::EphemeralStorage as EphemeralCredentialStorage;
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
use nym_client_core::client::key_manager::persistence::InMemEphemeralKeys;
|
||||
|
||||
use crate::config::Config;
|
||||
#[cfg(not(target_os = "android"))]
|
||||
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
|
||||
|
||||
pub struct MobileClientStorage {
|
||||
#[cfg(not(target_os = "android"))]
|
||||
key_store: OnDiskKeys,
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
// the key storage is now useless without gateway details store. so use ephemeral for everything.
|
||||
key_store: InMemEphemeralKeys,
|
||||
gateway_details_store: InMemGatewayDetails,
|
||||
|
||||
reply_store: reply_storage::Empty,
|
||||
credential_store: EphemeralCredentialStorage,
|
||||
}
|
||||
|
||||
impl MixnetClientStorage for MobileClientStorage {
|
||||
#[cfg(not(target_os = "android"))]
|
||||
type KeyStore = OnDiskKeys;
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
type KeyStore = InMemEphemeralKeys;
|
||||
|
||||
type ReplyStore = reply_storage::Empty;
|
||||
type CredentialStore = EphemeralCredentialStorage;
|
||||
type GatewayDetailsStore = InMemGatewayDetails;
|
||||
|
||||
fn into_split(self) -> (Self::KeyStore, Self::ReplyStore, Self::CredentialStore) {
|
||||
(self.key_store, self.reply_store, self.credential_store)
|
||||
fn into_runtime_stores(self) -> (Self::ReplyStore, Self::CredentialStore) {
|
||||
(self.reply_store, self.credential_store)
|
||||
}
|
||||
|
||||
fn key_store(&self) -> &Self::KeyStore {
|
||||
@@ -48,28 +38,17 @@ impl MixnetClientStorage for MobileClientStorage {
|
||||
fn credential_store(&self) -> &Self::CredentialStore {
|
||||
&self.credential_store
|
||||
}
|
||||
|
||||
fn gateway_details_store(&self) -> &Self::GatewayDetailsStore {
|
||||
&self.gateway_details_store
|
||||
}
|
||||
}
|
||||
|
||||
impl MobileClientStorage {
|
||||
pub fn new(config: &Config) -> Self {
|
||||
#[cfg(target_os = "android")]
|
||||
let key_store = {
|
||||
let _ = config;
|
||||
InMemEphemeralKeys
|
||||
};
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
let key_store = OnDiskKeys::new(
|
||||
config
|
||||
.storage_paths
|
||||
.clone()
|
||||
.expect("storage paths unavailable")
|
||||
.common_paths
|
||||
.keys,
|
||||
);
|
||||
|
||||
pub fn new(_config: &Config) -> Self {
|
||||
MobileClientStorage {
|
||||
key_store,
|
||||
key_store: Default::default(),
|
||||
gateway_details_store: Default::default(),
|
||||
reply_store: Default::default(),
|
||||
credential_store: Default::default(),
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
use nym_client_core::client::base_client::storage::gateway_details::{
|
||||
GatewayDetailsStore, PersistedGatewayDetails,
|
||||
};
|
||||
use nym_sdk::mixnet::{
|
||||
self, EmptyReplyStorage, EphemeralCredentialStorage, KeyManager, KeyStore, MixnetClientStorage,
|
||||
};
|
||||
@@ -10,37 +13,13 @@ async fn main() {
|
||||
// Just some plain data to pretend we have some external storage that the application
|
||||
// implementer is using.
|
||||
let mock_storage = MockClientStorage::empty();
|
||||
let mut mock_gw_storage = MockGatewayConfigStorage::empty();
|
||||
|
||||
let first_run = true;
|
||||
|
||||
let client = if first_run {
|
||||
// Create a client without a storage backend
|
||||
let mut client = mixnet::MixnetClientBuilder::new_with_storage(mock_storage)
|
||||
.build()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// In this we want to provide our own gateway config struct, and handle persisting this info to disk
|
||||
// ourselves (e.g., as part of our own configuration file).
|
||||
// during registration, our key storage will be automatically called to persist the keys
|
||||
client.register_and_authenticate_gateway().await.unwrap();
|
||||
mock_gw_storage.write_config(client.get_gateway_endpoint().unwrap());
|
||||
client
|
||||
} else {
|
||||
let gateway_config = mock_gw_storage.read_config();
|
||||
|
||||
// Create a client with a storage backend, so that our keys could be loaded.
|
||||
// This creates the client in a registered state.
|
||||
mixnet::MixnetClientBuilder::new_with_storage(mock_storage)
|
||||
.registered_gateway(gateway_config)
|
||||
.build()
|
||||
.await
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
// Connect to the mixnet, now we're listening for incoming
|
||||
let mut client = client.connect_to_mixnet().await.unwrap();
|
||||
let mut client = mixnet::MixnetClientBuilder::new_with_storage(mock_storage)
|
||||
.build()
|
||||
.await
|
||||
.unwrap()
|
||||
.connect_to_mixnet()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Be able to get our client address
|
||||
let our_address = client.nym_address();
|
||||
@@ -59,30 +38,10 @@ async fn main() {
|
||||
client.disconnect().await;
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
struct MockGatewayConfigStorage {
|
||||
pub gateway_config: Option<mixnet::GatewayEndpointConfig>,
|
||||
}
|
||||
|
||||
impl MockGatewayConfigStorage {
|
||||
fn read_config(&self) -> mixnet::GatewayEndpointConfig {
|
||||
todo!();
|
||||
}
|
||||
|
||||
fn write_config(&mut self, _gateway_config: &mixnet::GatewayEndpointConfig) {
|
||||
log::info!("todo");
|
||||
}
|
||||
|
||||
fn empty() -> Self {
|
||||
Self {
|
||||
gateway_config: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
struct MockClientStorage {
|
||||
pub key_store: MockKeyStore,
|
||||
pub gateway_details_store: MockGatewayDetailsStore,
|
||||
pub reply_store: EmptyReplyStorage,
|
||||
pub credential_store: EphemeralCredentialStorage,
|
||||
}
|
||||
@@ -91,6 +50,7 @@ impl MockClientStorage {
|
||||
fn empty() -> Self {
|
||||
Self {
|
||||
key_store: MockKeyStore,
|
||||
gateway_details_store: MockGatewayDetailsStore,
|
||||
reply_store: EmptyReplyStorage::default(),
|
||||
credential_store: EphemeralCredentialStorage::default(),
|
||||
}
|
||||
@@ -101,9 +61,10 @@ impl MixnetClientStorage for MockClientStorage {
|
||||
type KeyStore = MockKeyStore;
|
||||
type ReplyStore = EmptyReplyStorage;
|
||||
type CredentialStore = EphemeralCredentialStorage;
|
||||
type GatewayDetailsStore = MockGatewayDetailsStore;
|
||||
|
||||
fn into_split(self) -> (Self::KeyStore, Self::ReplyStore, Self::CredentialStore) {
|
||||
(self.key_store, self.reply_store, self.credential_store)
|
||||
fn into_runtime_stores(self) -> (Self::ReplyStore, Self::CredentialStore) {
|
||||
(self.reply_store, self.credential_store)
|
||||
}
|
||||
|
||||
fn key_store(&self) -> &Self::KeyStore {
|
||||
@@ -117,6 +78,10 @@ impl MixnetClientStorage for MockClientStorage {
|
||||
fn credential_store(&self) -> &Self::CredentialStore {
|
||||
&self.credential_store
|
||||
}
|
||||
|
||||
fn gateway_details_store(&self) -> &Self::GatewayDetailsStore {
|
||||
&self.gateway_details_store
|
||||
}
|
||||
}
|
||||
|
||||
struct MockKeyStore;
|
||||
@@ -137,6 +102,29 @@ impl KeyStore for MockKeyStore {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct MockGatewayDetailsStore;
|
||||
|
||||
#[async_trait]
|
||||
impl GatewayDetailsStore for MockGatewayDetailsStore {
|
||||
type StorageError = MyError;
|
||||
|
||||
async fn load_gateway_details(&self) -> Result<PersistedGatewayDetails, Self::StorageError> {
|
||||
println!("loading stored gateway details");
|
||||
|
||||
Err(MyError)
|
||||
}
|
||||
|
||||
async fn store_gateway_details(
|
||||
&self,
|
||||
_details: &PersistedGatewayDetails,
|
||||
) -> Result<(), Self::StorageError> {
|
||||
println!("storing gateway details");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// struct MockReplyStore;
|
||||
//
|
||||
|
||||
@@ -8,25 +8,24 @@ use crate::mixnet::{CredentialStorage, MixnetClient, Recipient};
|
||||
use crate::{Error, Result};
|
||||
use futures::channel::mpsc;
|
||||
use futures::StreamExt;
|
||||
use nym_client_core::client::base_client::storage::gateway_details::GatewayDetailsStore;
|
||||
use nym_client_core::client::base_client::storage::{
|
||||
Ephemeral, MixnetClientStorage, OnDiskPersistent,
|
||||
};
|
||||
use nym_client_core::client::base_client::BaseClient;
|
||||
use nym_client_core::client::key_manager::persistence::KeyStore;
|
||||
use nym_client_core::client::key_manager::ManagedKeys;
|
||||
use nym_client_core::config::{Client as ClientConfig, DebugConfig};
|
||||
use nym_client_core::config::DebugConfig;
|
||||
use nym_client_core::init::GatewaySetup;
|
||||
use nym_client_core::{
|
||||
client::{base_client::BaseClientBuilder, replies::reply_storage::ReplyStorageBackend},
|
||||
config::GatewayEndpointConfig,
|
||||
};
|
||||
use nym_network_defaults::NymNetworkDetails;
|
||||
use nym_socks5_client_core::config::{BaseClientConfig, Socks5};
|
||||
use nym_socks5_client_core::config::Socks5;
|
||||
use nym_task::manager::TaskStatus;
|
||||
use nym_topology::provider_trait::TopologyProvider;
|
||||
use nym_validator_client::nyxd::QueryNyxdClient;
|
||||
use nym_validator_client::Client;
|
||||
use rand::thread_rng;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use url::Url;
|
||||
@@ -34,8 +33,6 @@ use url::Url;
|
||||
// The number of surbs to include in a message by default
|
||||
const DEFAULT_NUMBER_OF_SURBS: u32 = 5;
|
||||
|
||||
const DEFAULT_SDK_CLIENT_ID: &str = "_default-nym-sdk-client";
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MixnetClientBuilder<S: MixnetClientStorage = Ephemeral> {
|
||||
config: Config,
|
||||
@@ -85,10 +82,11 @@ impl MixnetClientBuilder<OnDiskPersistent> {
|
||||
impl<S> MixnetClientBuilder<S>
|
||||
where
|
||||
S: MixnetClientStorage + 'static,
|
||||
S::ReplyStore: Send + Sync,
|
||||
<S::ReplyStore as ReplyStorageBackend>::StorageError: Sync + Send,
|
||||
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync,
|
||||
S::ReplyStore: Send + Sync,
|
||||
<S::KeyStore as KeyStore>::StorageError: Send + Sync,
|
||||
<S::GatewayDetailsStore as GatewayDetailsStore>::StorageError: Send + Sync,
|
||||
{
|
||||
/// Creates a client builder with the provided client storage implementation.
|
||||
#[must_use]
|
||||
@@ -155,13 +153,6 @@ where
|
||||
self
|
||||
}
|
||||
|
||||
/// Use a gateway that you previously registered with.
|
||||
#[must_use]
|
||||
pub fn registered_gateway(mut self, gateway_config: GatewayEndpointConfig) -> Self {
|
||||
self.gateway_config = Some(gateway_config);
|
||||
self
|
||||
}
|
||||
|
||||
/// Configure the SOCKS5 mode.
|
||||
#[must_use]
|
||||
pub fn socks5_config(mut self, socks5_config: Socks5) -> Self {
|
||||
@@ -187,21 +178,14 @@ where
|
||||
|
||||
/// Construct a [`DisconnectedMixnetClient`] from the setup specified.
|
||||
pub async fn build(self) -> Result<DisconnectedMixnetClient<S>> {
|
||||
let mut client = DisconnectedMixnetClient::new(
|
||||
let client = DisconnectedMixnetClient::new(
|
||||
self.config,
|
||||
self.socks5_config,
|
||||
self.storage,
|
||||
self.custom_topology_provider,
|
||||
self.gateway_endpoint_config_path,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// If we have a gateway config, we can move the client into a registered state. This will
|
||||
// fail if no gateway key is set.
|
||||
if let Some(gateway_config) = self.gateway_config {
|
||||
client.register_gateway_with_config(gateway_config)?;
|
||||
}
|
||||
|
||||
Ok(client)
|
||||
}
|
||||
}
|
||||
@@ -232,13 +216,6 @@ where
|
||||
/// dkg and coconut contracts
|
||||
dkg_query_client: Option<Client<QueryNyxdClient>>,
|
||||
|
||||
/// Keys handled by the client
|
||||
managed_keys: ManagedKeys,
|
||||
|
||||
// TODO: incorporate it properly into `MixnetClientStorage` (I will need it in wasm anyway)
|
||||
/// Path to optionally persist gateway configuration. Note that it's required if one were to use persistent keys.
|
||||
gateway_endpoint_config_path: Option<PathBuf>,
|
||||
|
||||
/// Alternative provider of network topology used for constructing sphinx packets.
|
||||
custom_topology_provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
|
||||
}
|
||||
@@ -246,10 +223,11 @@ where
|
||||
impl<S> DisconnectedMixnetClient<S>
|
||||
where
|
||||
S: MixnetClientStorage + 'static,
|
||||
S::ReplyStore: Send + Sync,
|
||||
<S::ReplyStore as ReplyStorageBackend>::StorageError: Sync + Send,
|
||||
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync,
|
||||
S::ReplyStore: Send + Sync,
|
||||
<S::KeyStore as KeyStore>::StorageError: Send + Sync,
|
||||
<S::GatewayDetailsStore as GatewayDetailsStore>::StorageError: Send + Sync,
|
||||
{
|
||||
/// Create a new mixnet client in a disconnected state. The default configuration,
|
||||
/// creates a new mainnet client with ephemeral keys stored in RAM, which will be discarded at
|
||||
@@ -263,7 +241,6 @@ where
|
||||
socks5_config: Option<Socks5>,
|
||||
storage: S,
|
||||
custom_topology_provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
|
||||
gateway_endpoint_config_path: Option<PathBuf>,
|
||||
) -> Result<DisconnectedMixnetClient<S>> {
|
||||
// don't create dkg client for the bandwidth controller if credentials are disabled
|
||||
let dkg_query_client = if config.enabled_credentials_mode {
|
||||
@@ -276,9 +253,6 @@ where
|
||||
None
|
||||
};
|
||||
|
||||
let mut rng = thread_rng();
|
||||
let managed_keys = ManagedKeys::load_or_generate(&mut rng, storage.key_store()).await;
|
||||
|
||||
Ok(DisconnectedMixnetClient {
|
||||
config,
|
||||
socks5_config,
|
||||
@@ -286,8 +260,6 @@ where
|
||||
dkg_query_client,
|
||||
storage,
|
||||
custom_topology_provider,
|
||||
managed_keys,
|
||||
gateway_endpoint_config_path,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -314,35 +286,17 @@ where
|
||||
/// Client keys are generated at client creation if none were found. The gateway shared
|
||||
/// key, however, is created during the gateway registration handshake so it might not
|
||||
/// necessarily be available.
|
||||
fn has_gateway_key(&self) -> bool {
|
||||
matches!(self.managed_keys, ManagedKeys::FullyDerived(..))
|
||||
}
|
||||
/// Furthermore, it has to be coupled with particular gateway's config.
|
||||
async fn has_gateway_info(&self) -> bool {
|
||||
let has_keys = self.storage.key_store().load_keys().await.is_ok();
|
||||
let has_gateway_details = self
|
||||
.storage
|
||||
.gateway_details_store()
|
||||
.load_gateway_details()
|
||||
.await
|
||||
.is_ok();
|
||||
|
||||
fn remove_gateway_key(&mut self) {
|
||||
assert!(self.has_gateway_key());
|
||||
let ManagedKeys::FullyDerived(keys) = std::mem::replace(&mut self.managed_keys, ManagedKeys::Invalidated) else {
|
||||
unreachable!()
|
||||
};
|
||||
self.managed_keys = ManagedKeys::Initial(keys.remove_gateway_key())
|
||||
}
|
||||
|
||||
/// Sets the gateway endpoint of this [`MixnetClientBuilder`].
|
||||
///
|
||||
/// NOTE: this will mark this builder as `Registered`, and the it is assumed that the keys are
|
||||
/// also explicitly set.
|
||||
pub fn register_gateway_with_config(
|
||||
&mut self,
|
||||
gateway_endpoint_config: GatewayEndpointConfig,
|
||||
) -> Result<()> {
|
||||
if !self.has_gateway_key() {
|
||||
return Err(Error::NoGatewayKeySet);
|
||||
}
|
||||
|
||||
self.state = BuilderState::Registered {
|
||||
gateway_endpoint_config,
|
||||
};
|
||||
|
||||
Ok(())
|
||||
has_keys && has_gateway_details
|
||||
}
|
||||
|
||||
/// Register with a gateway. If a gateway is provided in the config then that will try to be
|
||||
@@ -353,59 +307,31 @@ where
|
||||
/// This function will return an error if you try to re-register when in an already registered
|
||||
/// state.
|
||||
pub async fn register_and_authenticate_gateway(&mut self) -> Result<()> {
|
||||
if self.state != BuilderState::New {
|
||||
if !matches!(self.state, BuilderState::New) {
|
||||
return Err(Error::ReregisteringGatewayNotSupported);
|
||||
}
|
||||
|
||||
log::debug!("Registering with gateway");
|
||||
|
||||
let api_endpoints = self.get_api_endpoints();
|
||||
let gateway_setup = GatewaySetup::new(None, self.config.user_chosen_gateway.clone(), None);
|
||||
|
||||
let (gateway_config, managed_keys) = nym_client_core::init::get_registered_gateway::<S>(
|
||||
api_endpoints,
|
||||
let gateway_setup = if self.has_gateway_info().await {
|
||||
GatewaySetup::MustLoad
|
||||
} else {
|
||||
GatewaySetup::new_fresh(self.config.user_chosen_gateway.clone(), None)
|
||||
};
|
||||
|
||||
// this will perform necessary key and details load and optional store
|
||||
let _init_result = nym_client_core::init::setup_gateway(
|
||||
&gateway_setup,
|
||||
self.storage.key_store(),
|
||||
gateway_setup,
|
||||
self.storage.gateway_details_store(),
|
||||
!self.config.key_mode.is_keep(),
|
||||
Some(&api_endpoints),
|
||||
)
|
||||
.await?;
|
||||
|
||||
self.state = BuilderState::Registered {
|
||||
gateway_endpoint_config: gateway_config,
|
||||
};
|
||||
self.managed_keys = managed_keys;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the get gateway endpoint of this [`MixnetClientBuilder`].
|
||||
pub fn get_gateway_endpoint(&self) -> Option<&GatewayEndpointConfig> {
|
||||
self.state.gateway_endpoint_config()
|
||||
}
|
||||
|
||||
fn write_gateway_endpoint_config(&self, gateway_endpoint_config_path: &Path) -> Result<()> {
|
||||
let gateway_endpoint_config = toml::to_string(
|
||||
self.get_gateway_endpoint()
|
||||
.ok_or(Error::GatewayNotAvailableForWriting)?,
|
||||
)?;
|
||||
|
||||
// Ensure the whole directory structure exists
|
||||
if let Some(parent_dir) = gateway_endpoint_config_path.parent() {
|
||||
std::fs::create_dir_all(parent_dir)?;
|
||||
}
|
||||
std::fs::write(gateway_endpoint_config_path, gateway_endpoint_config)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_gateway_endpoint_config<P: AsRef<Path>>(
|
||||
&mut self,
|
||||
gateway_endpoint_config_path: P,
|
||||
) -> Result<()> {
|
||||
let gateway_endpoint_config: GatewayEndpointConfig =
|
||||
std::fs::read_to_string(gateway_endpoint_config_path)
|
||||
.map(|str| toml::from_str(&str))??;
|
||||
|
||||
self.state = BuilderState::Registered {
|
||||
gateway_endpoint_config,
|
||||
};
|
||||
self.state = BuilderState::Registered {};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -426,61 +352,40 @@ where
|
||||
}
|
||||
|
||||
async fn connect_to_mixnet_common(mut self) -> Result<(BaseClient, Recipient)> {
|
||||
// For some simple cases we can figure how to setup gateway without it having to have been
|
||||
// called in advance.
|
||||
if matches!(self.state, BuilderState::New) {
|
||||
let already_registered = self.has_gateway_key();
|
||||
if already_registered {
|
||||
if let Some(gateway_endpoint_path) = self.gateway_endpoint_config_path.clone() {
|
||||
self.read_gateway_endpoint_config(gateway_endpoint_path)?;
|
||||
} else if !self.config.key_mode.is_keep() {
|
||||
// if we don't have gateway configuration available and we're not keeping the keys,
|
||||
// purge them
|
||||
self.remove_gateway_key();
|
||||
}
|
||||
} else {
|
||||
// TODO: that is redundant since the base client will perform gateway registration
|
||||
self.register_and_authenticate_gateway().await?;
|
||||
if let Some(gateway_endpoint_path) = &self.gateway_endpoint_config_path {
|
||||
self.write_gateway_endpoint_config(gateway_endpoint_path)?;
|
||||
}
|
||||
}
|
||||
// if we don't care about our keys, explicitly register
|
||||
if !self.config.key_mode.is_keep() {
|
||||
self.register_and_authenticate_gateway().await?;
|
||||
}
|
||||
|
||||
// otherwise, the whole key setup and gateway selection dance will be done for us
|
||||
// when we start the base client
|
||||
|
||||
let nyxd_endpoints = self.get_nyxd_endpoints();
|
||||
let nym_api_endpoints = self.get_api_endpoints();
|
||||
|
||||
// If the gateway is in a registered state, but without the gateway key set.
|
||||
if matches!(self.state, BuilderState::Registered { .. }) && !self.has_gateway_key() {
|
||||
return Err(Error::NoGatewayKeySet);
|
||||
}
|
||||
|
||||
// At this point we should be in a registered state, either at function entry or by the
|
||||
// above convenience logic.
|
||||
let BuilderState::Registered { gateway_endpoint_config } = self.state else {
|
||||
return Err(Error::FailedToTransitionToRegisteredState);
|
||||
};
|
||||
|
||||
// a temporary workaround
|
||||
let base_config = BaseClientConfig::from_client_config(
|
||||
ClientConfig::new(
|
||||
DEFAULT_SDK_CLIENT_ID,
|
||||
!self.config.enabled_credentials_mode,
|
||||
nyxd_endpoints,
|
||||
nym_api_endpoints,
|
||||
gateway_endpoint_config,
|
||||
),
|
||||
self.config.debug_config,
|
||||
);
|
||||
let base_config = self
|
||||
.config
|
||||
.as_base_client_config(nyxd_endpoints, nym_api_endpoints);
|
||||
|
||||
let known_gateway = self.has_gateway_info().await;
|
||||
|
||||
let mut base_builder: BaseClientBuilder<_, _> =
|
||||
BaseClientBuilder::new(&base_config, self.storage, self.dkg_query_client);
|
||||
|
||||
if !known_gateway {
|
||||
base_builder = base_builder.with_gateway_setup(GatewaySetup::new_fresh(
|
||||
self.config.user_chosen_gateway,
|
||||
None,
|
||||
))
|
||||
}
|
||||
|
||||
if let Some(topology_provider) = self.custom_topology_provider {
|
||||
base_builder = base_builder.with_topology_provider(topology_provider);
|
||||
}
|
||||
|
||||
let started_client = base_builder.start_base().await?;
|
||||
self.state = BuilderState::Registered {};
|
||||
let nym_address = started_client.address;
|
||||
|
||||
Ok((started_client, nym_address))
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
use nym_client_core::config::DebugConfig;
|
||||
use nym_client_core::config::{Client as ClientConfig, DebugConfig};
|
||||
use nym_network_defaults::NymNetworkDetails;
|
||||
use nym_socks5_client_core::config::BaseClientConfig;
|
||||
use url::Url;
|
||||
|
||||
const DEFAULT_SDK_CLIENT_ID: &str = "_default-nym-sdk-client";
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub enum KeyMode {
|
||||
@@ -35,3 +39,22 @@ pub struct Config {
|
||||
/// Changing these risk compromising network anonymity!
|
||||
pub debug_config: DebugConfig,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
// I really dislike this workaround.
|
||||
pub fn as_base_client_config(
|
||||
&self,
|
||||
nyxd_endpoints: Vec<Url>,
|
||||
nym_api_endpoints: Vec<Url>,
|
||||
) -> BaseClientConfig {
|
||||
BaseClientConfig::from_client_config(
|
||||
ClientConfig::new(
|
||||
DEFAULT_SDK_CLIENT_ID,
|
||||
!self.enabled_credentials_mode,
|
||||
nyxd_endpoints,
|
||||
nym_api_endpoints,
|
||||
),
|
||||
self.debug_config,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,8 @@
|
||||
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_client_core::config::GatewayEndpointConfig;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug)]
|
||||
pub(super) enum BuilderState {
|
||||
New,
|
||||
Registered {
|
||||
gateway_endpoint_config: GatewayEndpointConfig,
|
||||
},
|
||||
}
|
||||
|
||||
impl BuilderState {
|
||||
pub(super) fn gateway_endpoint_config(&self) -> Option<&GatewayEndpointConfig> {
|
||||
match self {
|
||||
BuilderState::New => None,
|
||||
BuilderState::Registered {
|
||||
gateway_endpoint_config,
|
||||
..
|
||||
} => Some(gateway_endpoint_config),
|
||||
}
|
||||
}
|
||||
Registered {},
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use nym_client_core::client::base_client::storage::gateway_details::OnDiskGatewayDetails;
|
||||
use nym_client_core::client::base_client::{non_wasm_helpers, storage};
|
||||
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
|
||||
use nym_client_core::client::replies::reply_storage::fs_backend;
|
||||
@@ -38,6 +39,9 @@ pub struct StoragePaths {
|
||||
|
||||
/// The database storing reply surbs in-between sessions
|
||||
pub reply_surb_database_path: PathBuf,
|
||||
|
||||
/// Details of the used gateway
|
||||
pub gateway_details_path: PathBuf,
|
||||
}
|
||||
|
||||
impl StoragePaths {
|
||||
@@ -61,6 +65,7 @@ impl StoragePaths {
|
||||
gateway_shared_key: dir.join("gateway_shared.pem"),
|
||||
credential_database_path: dir.join("db.sqlite"),
|
||||
reply_surb_database_path: dir.join("persistent_reply_store.sqlite"),
|
||||
gateway_details_path: dir.join("gateway_details.json"),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -72,6 +77,7 @@ impl StoragePaths {
|
||||
self.on_disk_key_storage_spec(),
|
||||
self.default_persistent_fs_reply_backend().await?,
|
||||
self.persistent_credential_storage().await?,
|
||||
self.on_disk_gateway_details_storage(),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -85,6 +91,7 @@ impl StoragePaths {
|
||||
self.persistent_fs_reply_backend(&config.reply_surbs)
|
||||
.await?,
|
||||
self.persistent_credential_storage().await?,
|
||||
self.on_disk_gateway_details_storage(),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -121,6 +128,10 @@ impl StoragePaths {
|
||||
OnDiskKeys::new(self.client_keys_paths())
|
||||
}
|
||||
|
||||
pub fn on_disk_gateway_details_storage(&self) -> OnDiskGatewayDetails {
|
||||
OnDiskGatewayDetails::new(&self.gateway_details_path)
|
||||
}
|
||||
|
||||
fn client_keys_paths(&self) -> ClientKeysPaths {
|
||||
ClientKeysPaths {
|
||||
private_identity_key_file: self.private_identity.clone(),
|
||||
@@ -144,6 +155,7 @@ impl From<StoragePaths> for CommonClientPaths {
|
||||
gateway_shared_key_file: value.gateway_shared_key,
|
||||
ack_key_file: value.ack_key,
|
||||
},
|
||||
gateway_details: value.gateway_details_path,
|
||||
credentials_database: value.credential_database_path,
|
||||
reply_surb_database: value.reply_surb_database_path,
|
||||
}
|
||||
@@ -161,6 +173,7 @@ impl From<CommonClientPaths> for StoragePaths {
|
||||
gateway_shared_key: value.keys.gateway_shared_key_file,
|
||||
credential_database_path: value.credentials_database,
|
||||
reply_surb_database_path: value.reply_surb_database,
|
||||
gateway_details_path: value.gateway_details,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::cli::try_load_current_config;
|
||||
use crate::cli::try_upgrade_config;
|
||||
use crate::config::{default_config_directory, default_config_filepath, default_data_directory};
|
||||
use crate::{
|
||||
cli::{override_config, OverrideConfig},
|
||||
@@ -10,7 +10,10 @@ use crate::{
|
||||
};
|
||||
use clap::Args;
|
||||
use nym_bin_common::output_format::OutputFormat;
|
||||
use nym_client_core::client::base_client::storage::gateway_details::OnDiskGatewayDetails;
|
||||
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
|
||||
use nym_client_core::config::GatewayEndpointConfig;
|
||||
use nym_client_core::init::GatewaySetup;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use serde::Serialize;
|
||||
@@ -77,9 +80,9 @@ pub struct InitResults {
|
||||
}
|
||||
|
||||
impl InitResults {
|
||||
fn new(config: &Config, address: &Recipient) -> Self {
|
||||
fn new(config: &Config, address: &Recipient, gateway: &GatewayEndpointConfig) -> Self {
|
||||
Self {
|
||||
client_core: nym_client_core::init::InitResults::new(&config.base, address),
|
||||
client_core: nym_client_core::init::InitResults::new(&config.base, address, gateway),
|
||||
client_address: address.to_string(),
|
||||
}
|
||||
}
|
||||
@@ -106,13 +109,15 @@ pub(crate) async fn execute(args: &Init) -> Result<(), NetworkRequesterError> {
|
||||
|
||||
let id = &args.id;
|
||||
|
||||
let old_config = if default_config_filepath(id).exists() {
|
||||
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_config(id)?;
|
||||
eprintln!("Client \"{id}\" was already initialised before");
|
||||
// if the file exist, try to load it (with checking for errors)
|
||||
Some(try_load_current_config(&args.id)?)
|
||||
true
|
||||
} else {
|
||||
init_paths(&args.id)?;
|
||||
None
|
||||
false
|
||||
};
|
||||
|
||||
// Usually you only register with the gateway on the first init, however you can force
|
||||
@@ -125,32 +130,32 @@ pub(crate) async fn execute(args: &Init) -> Result<(), NetworkRequesterError> {
|
||||
// If the client was already initialized, don't generate new keys and don't re-register with
|
||||
// the gateway (because this would create a new shared key).
|
||||
// Unless the user really wants to.
|
||||
let register_gateway = old_config.is_none() || user_wants_force_register;
|
||||
let register_gateway = !already_init || user_wants_force_register;
|
||||
|
||||
// Attempt to use a user-provided gateway, if possible
|
||||
let user_chosen_gateway_id = args.gateway;
|
||||
let gateway_setup = GatewaySetup::new_fresh(
|
||||
user_chosen_gateway_id.map(|id| id.to_base58_string()),
|
||||
Some(args.latency_based_selection),
|
||||
);
|
||||
|
||||
// Load and potentially override config
|
||||
let mut config = override_config(Config::new(id), OverrideConfig::from(args.clone()));
|
||||
let config = override_config(Config::new(id), OverrideConfig::from(args.clone()));
|
||||
|
||||
// Setup gateway by either registering a new one, or creating a new config from the selected
|
||||
// one but with keys kept, or reusing the gateway configuration.
|
||||
let key_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone());
|
||||
let gateway = nym_client_core::init::setup_gateway_from_config::<_>(
|
||||
let details_store =
|
||||
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
|
||||
let init_details = nym_client_core::init::setup_gateway(
|
||||
&gateway_setup,
|
||||
&key_store,
|
||||
&details_store,
|
||||
register_gateway,
|
||||
user_chosen_gateway_id,
|
||||
&config.base,
|
||||
old_config.map(|cfg| cfg.base.client.gateway_endpoint),
|
||||
args.latency_based_selection,
|
||||
Some(&config.base.client.nym_api_urls),
|
||||
)
|
||||
.await
|
||||
.map_err(|source| {
|
||||
eprintln!("Failed to setup gateway\nError: {source}");
|
||||
NetworkRequesterError::FailedToSetupGateway { source }
|
||||
})?;
|
||||
|
||||
config.base.set_gateway_endpoint(gateway);
|
||||
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?;
|
||||
|
||||
let config_save_location = config.default_location();
|
||||
config.save_to_default_location().tap_err(|_| {
|
||||
@@ -161,14 +166,11 @@ pub(crate) async fn execute(args: &Init) -> Result<(), NetworkRequesterError> {
|
||||
config_save_location.display()
|
||||
);
|
||||
|
||||
let address = nym_client_core::init::get_client_address_from_stored_ondisk_keys(
|
||||
&config.storage_paths.common_paths.keys,
|
||||
&config.base.client.gateway_endpoint,
|
||||
)?;
|
||||
let address = init_details.client_address()?;
|
||||
|
||||
eprintln!("Client configuration completed.\n");
|
||||
|
||||
let init_results = InitResults::new(&config, &address);
|
||||
let init_results = InitResults::new(&config, &address, &init_details.gateway_details);
|
||||
println!("{}", args.output.format(&init_results));
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
use crate::config::old_config_v1_1_13::OldConfigV1_1_13;
|
||||
use crate::config::old_config_v1_1_20::ConfigV1_1_20;
|
||||
use crate::config::old_config_v1_1_20_2::ConfigV1_1_20_2;
|
||||
use crate::{
|
||||
config::{BaseClientConfig, Config},
|
||||
error::NetworkRequesterError,
|
||||
@@ -12,6 +13,12 @@ use log::{error, info};
|
||||
use nym_bin_common::build_information::BinaryBuildInformation;
|
||||
use nym_bin_common::completions::{fig_generate, ArgShell};
|
||||
use nym_bin_common::version_checker;
|
||||
use nym_client_core::client::base_client::storage::gateway_details::{
|
||||
OnDiskGatewayDetails, PersistedGatewayDetails,
|
||||
};
|
||||
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
|
||||
use nym_client_core::config::GatewayEndpointConfig;
|
||||
use nym_client_core::error::ClientCoreError;
|
||||
|
||||
mod init;
|
||||
mod run;
|
||||
@@ -105,6 +112,28 @@ pub(crate) async fn execute(args: Cli) -> Result<(), NetworkRequesterError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn persist_gateway_details(
|
||||
config: &Config,
|
||||
details: GatewayEndpointConfig,
|
||||
) -> Result<(), NetworkRequesterError> {
|
||||
let details_store =
|
||||
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
|
||||
let keys_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone());
|
||||
let shared_keys = keys_store.ephemeral_load_gateway_keys().map_err(|source| {
|
||||
NetworkRequesterError::ClientCoreError(ClientCoreError::KeyStoreError {
|
||||
source: Box::new(source),
|
||||
})
|
||||
})?;
|
||||
let persisted_details = PersistedGatewayDetails::new(details, &shared_keys);
|
||||
details_store
|
||||
.store_to_disk(&persisted_details)
|
||||
.map_err(|source| {
|
||||
NetworkRequesterError::ClientCoreError(ClientCoreError::GatewayDetailsStoreError {
|
||||
source: Box::new(source),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, NetworkRequesterError> {
|
||||
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
|
||||
|
||||
@@ -118,7 +147,9 @@ fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, NetworkRequesterError> {
|
||||
info!("It is going to get updated to the current specification.");
|
||||
|
||||
let updated_step1: ConfigV1_1_20 = old_config.into();
|
||||
let updated: Config = updated_step1.into();
|
||||
let updated_step2: ConfigV1_1_20_2 = updated_step1.into();
|
||||
let (updated, gateway_config) = updated_step2.upgrade();
|
||||
persist_gateway_details(&updated, gateway_config)?;
|
||||
|
||||
updated.save_to_default_location()?;
|
||||
Ok(true)
|
||||
@@ -137,21 +168,56 @@ fn try_upgrade_v1_1_20_config(id: &str) -> Result<bool, NetworkRequesterError> {
|
||||
info!("It seems the client is using <= v1.1.20 config template.");
|
||||
info!("It is going to get updated to the current specification.");
|
||||
|
||||
let updated: Config = old_config.into();
|
||||
let updated_step1: ConfigV1_1_20_2 = old_config.into();
|
||||
let (updated, gateway_config) = updated_step1.upgrade();
|
||||
persist_gateway_details(&updated, gateway_config)?;
|
||||
|
||||
updated.save_to_default_location()?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn try_upgrade_v1_1_20_2_config(id: &str) -> Result<bool, NetworkRequesterError> {
|
||||
// explicitly load it as v1.1.20_2 (which is incompatible with the current one, i.e. +1.1.21)
|
||||
let Ok(old_config) = ConfigV1_1_20_2::read_from_default_path(id) else {
|
||||
// if we failed to load it, there might have been nothing to upgrade
|
||||
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
|
||||
return Ok(false);
|
||||
};
|
||||
info!("It seems the client is using <= v1.1.20_2 config template.");
|
||||
info!("It is going to get updated to the current specification.");
|
||||
|
||||
let (updated, gateway_config) = old_config.upgrade();
|
||||
persist_gateway_details(&updated, gateway_config)?;
|
||||
|
||||
updated.save_to_default_location()?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn try_upgrade_config(id: &str) -> Result<(), NetworkRequesterError> {
|
||||
let upgraded = try_upgrade_v1_1_13_config(id)?;
|
||||
if !upgraded {
|
||||
try_upgrade_v1_1_20_config(id)?;
|
||||
if try_upgrade_v1_1_13_config(id)? {
|
||||
return Ok(());
|
||||
}
|
||||
if try_upgrade_v1_1_20_config(id)? {
|
||||
return Ok(());
|
||||
}
|
||||
if try_upgrade_v1_1_20_2_config(id)? {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn try_load_current_config(id: &str) -> Result<Config, NetworkRequesterError> {
|
||||
// try to load the config as is
|
||||
if let Ok(cfg) = Config::read_from_default_path(id) {
|
||||
return if !cfg.validate() {
|
||||
Err(NetworkRequesterError::ConfigValidationFailure)
|
||||
} else {
|
||||
Ok(cfg)
|
||||
};
|
||||
}
|
||||
|
||||
// we couldn't load it - try upgrading it from older revisions
|
||||
try_upgrade_config(id)?;
|
||||
|
||||
let config = match Config::read_from_default_path(id) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::persistence::NetworkRequesterPaths;
|
||||
use crate::config::template::CONFIG_TEMPLATE;
|
||||
use nym_bin_common::logging::LoggingSettings;
|
||||
use nym_config::{
|
||||
@@ -14,12 +15,12 @@ use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::config::persistence::NetworkRequesterPaths;
|
||||
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_20;
|
||||
pub mod old_config_v1_1_20_2;
|
||||
mod persistence;
|
||||
mod template;
|
||||
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::old_config_v1_1_20::{
|
||||
ConfigV1_1_20, DebugV1_1_20, NetworkRequesterPathsV1_1_20,
|
||||
};
|
||||
use nym_client_core::config::disk_persistence::keys_paths::ClientKeysPaths;
|
||||
use nym_client_core::config::disk_persistence::old_v1_1_20::CommonClientPathsV1_1_20;
|
||||
use nym_client_core::config::old_config_v1_1_19::ConfigV1_1_19 as BaseConfigV1_1_19;
|
||||
use nym_client_core::config::old_config_v1_1_20::{
|
||||
ClientV1_1_20, ConfigV1_1_20 as BaseClientConfigV1_1_20,
|
||||
};
|
||||
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
|
||||
const DEFAULT_STANDARD_LIST_UPDATE_INTERVAL: Duration = Duration::from_secs(30 * 60);
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ConfigV1_1_19 {
|
||||
#[serde(flatten)]
|
||||
pub base: BaseConfigV1_1_19<ConfigV1_1_19>,
|
||||
|
||||
#[serde(default)]
|
||||
pub network_requester: NetworkRequster,
|
||||
|
||||
#[serde(default)]
|
||||
pub network_requester_debug: DebugV1_1_19,
|
||||
}
|
||||
|
||||
impl From<ConfigV1_1_19> for ConfigV1_1_20 {
|
||||
fn from(value: ConfigV1_1_19) -> Self {
|
||||
ConfigV1_1_20 {
|
||||
base: BaseClientConfigV1_1_20 {
|
||||
client: ClientV1_1_20 {
|
||||
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: Default::default(),
|
||||
},
|
||||
network_requester: Default::default(),
|
||||
storage_paths: NetworkRequesterPathsV1_1_20 {
|
||||
common_paths: CommonClientPathsV1_1_20 {
|
||||
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,
|
||||
},
|
||||
allowed_list_location: value.network_requester.allowed_list_location,
|
||||
unknown_list_location: value.network_requester.unknown_list_location,
|
||||
},
|
||||
network_requester_debug: value.network_requester_debug.into(),
|
||||
logging: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MigrationNymConfig for ConfigV1_1_19 {
|
||||
fn default_root_directory() -> PathBuf {
|
||||
dirs::home_dir()
|
||||
.expect("Failed to evaluate $HOME value")
|
||||
.join(".nym")
|
||||
.join("service-providers")
|
||||
.join("network-requester")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct NetworkRequster {
|
||||
pub allowed_list_location: PathBuf,
|
||||
pub unknown_list_location: PathBuf,
|
||||
}
|
||||
|
||||
impl Default for NetworkRequster {
|
||||
fn default() -> Self {
|
||||
// same defaults as we had in <= v1.1.13
|
||||
NetworkRequster {
|
||||
allowed_list_location: <ConfigV1_1_19 as MigrationNymConfig>::default_root_directory()
|
||||
.join("allowed.list"),
|
||||
unknown_list_location: <ConfigV1_1_19 as MigrationNymConfig>::default_root_directory()
|
||||
.join("unknown.list"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct DebugV1_1_19 {
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub standard_list_update_interval: Duration,
|
||||
}
|
||||
|
||||
impl From<DebugV1_1_19> for DebugV1_1_20 {
|
||||
fn from(value: DebugV1_1_19) -> Self {
|
||||
DebugV1_1_20 {
|
||||
standard_list_update_interval: value.standard_list_update_interval,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DebugV1_1_19 {
|
||||
fn default() -> Self {
|
||||
DebugV1_1_19 {
|
||||
standard_list_update_interval: DEFAULT_STANDARD_LIST_UPDATE_INTERVAL,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,15 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::persistence::NetworkRequesterPaths;
|
||||
use crate::config::{BaseClientConfig, Config, Debug};
|
||||
use crate::config::old_config_v1_1_20_2::{
|
||||
ConfigV1_1_20_2, DebugV1_1_20_2, NetworkRequesterPathsV1_1_20_2,
|
||||
};
|
||||
use nym_client_core::config::disk_persistence::keys_paths::ClientKeysPaths;
|
||||
use nym_client_core::config::disk_persistence::CommonClientPaths;
|
||||
use nym_client_core::config::disk_persistence::old_v1_1_20_2::CommonClientPathsV1_1_20_2;
|
||||
use nym_client_core::config::old_config_v1_1_20::ConfigV1_1_20 as BaseConfigV1_1_20;
|
||||
use nym_client_core::config::Client;
|
||||
use nym_client_core::config::old_config_v1_1_20_2::{
|
||||
ClientV1_1_20_2, ConfigV1_1_20_2 as BaseClientConfigV1_1_20_2,
|
||||
};
|
||||
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
@@ -27,11 +30,11 @@ pub struct ConfigV1_1_20 {
|
||||
pub network_requester_debug: DebugV1_1_20,
|
||||
}
|
||||
|
||||
impl From<ConfigV1_1_20> for Config {
|
||||
impl From<ConfigV1_1_20> for ConfigV1_1_20_2 {
|
||||
fn from(value: ConfigV1_1_20) -> Self {
|
||||
Config {
|
||||
base: BaseClientConfig {
|
||||
client: Client {
|
||||
ConfigV1_1_20_2 {
|
||||
base: BaseClientConfigV1_1_20_2 {
|
||||
client: ClientV1_1_20_2 {
|
||||
version: value.base.client.version,
|
||||
id: value.base.client.id,
|
||||
disabled_credentials_mode: value.base.client.disabled_credentials_mode,
|
||||
@@ -42,8 +45,8 @@ impl From<ConfigV1_1_20> for Config {
|
||||
debug: Default::default(),
|
||||
},
|
||||
network_requester: Default::default(),
|
||||
storage_paths: NetworkRequesterPaths {
|
||||
common_paths: CommonClientPaths {
|
||||
storage_paths: NetworkRequesterPathsV1_1_20_2 {
|
||||
common_paths: CommonClientPathsV1_1_20_2 {
|
||||
keys: ClientKeysPaths {
|
||||
private_identity_key_file: value.base.client.private_identity_key_file,
|
||||
public_identity_key_file: value.base.client.public_identity_key_file,
|
||||
@@ -100,9 +103,9 @@ pub struct DebugV1_1_20 {
|
||||
pub standard_list_update_interval: Duration,
|
||||
}
|
||||
|
||||
impl From<DebugV1_1_20> for Debug {
|
||||
impl From<DebugV1_1_20> for DebugV1_1_20_2 {
|
||||
fn from(value: DebugV1_1_20) -> Self {
|
||||
Debug {
|
||||
DebugV1_1_20_2 {
|
||||
standard_list_update_interval: value.standard_list_update_interval,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::persistence::NetworkRequesterPaths;
|
||||
use crate::config::{default_config_filepath, Config, Debug, NetworkRequester};
|
||||
use nym_bin_common::logging::LoggingSettings;
|
||||
use nym_client_core::config::disk_persistence::old_v1_1_20_2::CommonClientPathsV1_1_20_2;
|
||||
use nym_client_core::config::old_config_v1_1_20_2::ConfigV1_1_20_2 as BaseClientConfigV1_1_20_2;
|
||||
use nym_client_core::config::GatewayEndpointConfig;
|
||||
use nym_config::read_config_from_toml_file;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Duration;
|
||||
|
||||
pub const DEFAULT_STANDARD_LIST_UPDATE_INTERVAL: Duration = Duration::from_secs(30 * 60);
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone)]
|
||||
pub struct NetworkRequesterPathsV1_1_20_2 {
|
||||
#[serde(flatten)]
|
||||
pub common_paths: CommonClientPathsV1_1_20_2,
|
||||
|
||||
/// Location of the file containing our allow.list
|
||||
pub allowed_list_location: PathBuf,
|
||||
|
||||
/// Location of the file containing our unknown.list
|
||||
pub unknown_list_location: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ConfigV1_1_20_2 {
|
||||
#[serde(flatten)]
|
||||
pub base: BaseClientConfigV1_1_20_2,
|
||||
|
||||
#[serde(default)]
|
||||
pub network_requester: NetworkRequesterV1_1_20_2,
|
||||
|
||||
pub storage_paths: NetworkRequesterPathsV1_1_20_2,
|
||||
|
||||
#[serde(default)]
|
||||
pub network_requester_debug: DebugV1_1_20_2,
|
||||
|
||||
pub logging: LoggingSettings,
|
||||
}
|
||||
|
||||
impl ConfigV1_1_20_2 {
|
||||
pub fn read_from_toml_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
|
||||
read_config_from_toml_file(path)
|
||||
}
|
||||
|
||||
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
|
||||
Self::read_from_toml_file(default_config_filepath(id))
|
||||
}
|
||||
|
||||
// in this upgrade, gateway endpoint configuration was moved out of the config file,
|
||||
// so its returned to be stored elsewhere.
|
||||
pub fn upgrade(self) -> (Config, GatewayEndpointConfig) {
|
||||
let gateway_details = self.base.client.gateway_endpoint.clone().into();
|
||||
let config = Config {
|
||||
base: self.base.into(),
|
||||
storage_paths: NetworkRequesterPaths {
|
||||
common_paths: self.storage_paths.common_paths.upgrade_default(),
|
||||
allowed_list_location: self.storage_paths.allowed_list_location,
|
||||
unknown_list_location: self.storage_paths.unknown_list_location,
|
||||
},
|
||||
network_requester_debug: self.network_requester_debug.into(),
|
||||
logging: self.logging,
|
||||
network_requester: self.network_requester.into(),
|
||||
};
|
||||
|
||||
(config, gateway_details)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct NetworkRequesterV1_1_20_2 {}
|
||||
|
||||
impl From<NetworkRequesterV1_1_20_2> for NetworkRequester {
|
||||
fn from(_value: NetworkRequesterV1_1_20_2) -> Self {
|
||||
NetworkRequester {}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct DebugV1_1_20_2 {
|
||||
/// Defines how often the standard allow list should get updated
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub standard_list_update_interval: Duration,
|
||||
}
|
||||
|
||||
impl From<DebugV1_1_20_2> for Debug {
|
||||
fn from(value: DebugV1_1_20_2) -> Self {
|
||||
Debug {
|
||||
standard_list_update_interval: value.standard_list_update_interval,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DebugV1_1_20_2 {
|
||||
fn default() -> Self {
|
||||
DebugV1_1_20_2 {
|
||||
standard_list_update_interval: DEFAULT_STANDARD_LIST_UPDATE_INTERVAL,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,6 +62,10 @@ keys.ack_key_file = '{{ storage_paths.keys.ack_key_file }}'
|
||||
# Path to the database containing bandwidth credentials
|
||||
credentials_database = '{{ storage_paths.credentials_database }}'
|
||||
|
||||
# Path to the file containing information about gateway used by this client,
|
||||
# i.e. details such as its public key, owner address or the network information.
|
||||
gateway_details = '{{ storage_paths.gateway_details }}'
|
||||
|
||||
# Path to the persistent store for received reply surbs, unused encryption keys and used sender tags.
|
||||
reply_surb_database = '{{ storage_paths.reply_surb_database }}'
|
||||
|
||||
@@ -72,17 +76,6 @@ allowed_list_location = '{{ storage_paths.allowed_list_location }}'
|
||||
unknown_list_location = '{{ storage_paths.unknown_list_location }}'
|
||||
|
||||
|
||||
# DEPRECATED
|
||||
[client.gateway_endpoint]
|
||||
# ID of the gateway from which the client should be fetching messages.
|
||||
gateway_id = '{{ client.gateway_endpoint.gateway_id }}'
|
||||
|
||||
# Address of the gateway owner to which the client should send messages.
|
||||
gateway_owner = '{{ client.gateway_endpoint.gateway_owner }}'
|
||||
|
||||
# Address of the gateway listener to which all client requests should be sent.
|
||||
gateway_listener = '{{ client.gateway_endpoint.gateway_listener }}'
|
||||
|
||||
##### logging configuration options #####
|
||||
|
||||
[logging]
|
||||
|
||||
@@ -513,8 +513,7 @@ async fn create_mixnet_client(
|
||||
.await
|
||||
.map_err(|err| NetworkRequesterError::FailedToSetupMixnetClient { source: err })?
|
||||
.network_details(NymNetworkDetails::new_from_env())
|
||||
.debug_config(debug_config)
|
||||
.registered_gateway(config.get_gateway_endpoint_config().clone());
|
||||
.debug_config(debug_config);
|
||||
if !config.get_disabled_credentials_mode() {
|
||||
client_builder = client_builder.enable_credentials_mode();
|
||||
}
|
||||
|
||||
@@ -15,9 +15,6 @@ pub enum NetworkRequesterError {
|
||||
source: Socks5RequestError,
|
||||
},
|
||||
|
||||
#[error("failed to setup gateway: {source}")]
|
||||
FailedToSetupGateway { source: ClientCoreError },
|
||||
|
||||
#[error("failed to load configuration file: {0}")]
|
||||
FailedToLoadConfig(String),
|
||||
|
||||
|
||||
Reference in New Issue
Block a user