Files
nym/clients/client-core/src/config/mod.rs
T
2022-05-03 14:36:29 +02:00

487 lines
17 KiB
Rust

// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use config::defaults::*;
use config::NymConfig;
use serde::{Deserialize, Serialize};
use std::marker::PhantomData;
use std::path::PathBuf;
use std::time::Duration;
use url::Url;
pub mod persistence;
pub const MISSING_VALUE: &str = "MISSING VALUE";
// '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);
pub fn missing_string_value() -> String {
MISSING_VALUE.to_string()
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Config<T> {
client: Client<T>,
#[serde(default)]
logging: Logging,
#[serde(default)]
debug: Debug,
}
impl<T: NymConfig> Config<T> {
pub fn new<S: Into<String>>(id: S) -> Self {
let mut cfg = Config::default();
cfg.with_id(id);
cfg
}
pub fn with_id<S: Into<String>>(&mut self, id: S) {
let id = id.into();
// identity key setting
if self.client.private_identity_key_file.as_os_str().is_empty() {
self.client.private_identity_key_file =
self::Client::<T>::default_private_identity_key_file(&id);
}
if self.client.public_identity_key_file.as_os_str().is_empty() {
self.client.public_identity_key_file =
self::Client::<T>::default_public_identity_key_file(&id);
}
// encryption key setting
if self
.client
.private_encryption_key_file
.as_os_str()
.is_empty()
{
self.client.private_encryption_key_file =
self::Client::<T>::default_private_encryption_key_file(&id);
}
if self
.client
.public_encryption_key_file
.as_os_str()
.is_empty()
{
self.client.public_encryption_key_file =
self::Client::<T>::default_public_encryption_key_file(&id);
}
// shared gateway key setting
if self.client.gateway_shared_key_file.as_os_str().is_empty() {
self.client.gateway_shared_key_file =
self::Client::<T>::default_gateway_shared_key_file(&id);
}
// ack key setting
if self.client.ack_key_file.as_os_str().is_empty() {
self.client.ack_key_file = self::Client::<T>::default_ack_key_file(&id);
}
if self
.client
.reply_encryption_key_store_path
.as_os_str()
.is_empty()
{
self.client.reply_encryption_key_store_path =
self::Client::<T>::default_reply_encryption_key_store_path(&id);
}
if self.client.database_path.as_os_str().is_empty() {
self.client.database_path = self::Client::<T>::default_database_path(&id);
}
self.client.id = id;
}
pub fn with_disabled_credentials(&mut self, disabled_credentials_mode: bool) {
self.client.disabled_credentials_mode = disabled_credentials_mode;
}
pub fn with_gateway_endpoint<S: Into<String>>(&mut self, id: S, owner: S, listener: S) {
self.client.gateway_endpoint = GatewayEndpoint {
gateway_id: id.into(),
gateway_owner: owner.into(),
gateway_listener: listener.into(),
};
}
pub fn with_gateway_id<S: Into<String>>(&mut self, id: S) {
self.client.gateway_endpoint.gateway_id = id.into();
}
#[cfg(not(feature = "coconut"))]
pub fn with_eth_private_key<S: Into<String>>(&mut self, eth_private_key: S) {
self.client.eth_private_key = eth_private_key.into();
}
#[cfg(not(feature = "coconut"))]
pub fn with_eth_endpoint<S: Into<String>>(&mut self, eth_endpoint: S) {
self.client.eth_endpoint = eth_endpoint.into();
}
pub fn set_custom_validator_apis(&mut self, validator_api_urls: Vec<Url>) {
self.client.validator_api_urls = validator_api_urls;
}
pub fn set_high_default_traffic_volume(&mut self) {
self.debug.average_packet_delay = Duration::from_millis(10);
self.debug.loop_cover_traffic_average_delay = Duration::from_millis(2000000); // basically don't really send cover messages
self.debug.message_sending_average_delay = Duration::from_millis(4); // 250 "real" messages / s
}
pub fn set_custom_version(&mut self, version: &str) {
self.client.version = version.to_string();
}
pub fn get_id(&self) -> String {
self.client.id.clone()
}
pub fn get_disabled_credentials_mode(&self) -> bool {
self.client.disabled_credentials_mode
}
pub fn get_nym_root_directory(&self) -> PathBuf {
self.client.nym_root_directory.clone()
}
pub fn get_private_identity_key_file(&self) -> PathBuf {
self.client.private_identity_key_file.clone()
}
pub fn get_public_identity_key_file(&self) -> PathBuf {
self.client.public_identity_key_file.clone()
}
pub fn get_private_encryption_key_file(&self) -> PathBuf {
self.client.private_encryption_key_file.clone()
}
pub fn get_public_encryption_key_file(&self) -> PathBuf {
self.client.public_encryption_key_file.clone()
}
pub fn get_gateway_shared_key_file(&self) -> PathBuf {
self.client.gateway_shared_key_file.clone()
}
pub fn get_reply_encryption_key_store_path(&self) -> PathBuf {
self.client.reply_encryption_key_store_path.clone()
}
pub fn get_ack_key_file(&self) -> PathBuf {
self.client.ack_key_file.clone()
}
pub fn get_validator_api_endpoints(&self) -> Vec<Url> {
self.client.validator_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_database_path(&self) -> PathBuf {
self.client.database_path.clone()
}
#[cfg(not(feature = "coconut"))]
pub fn get_eth_endpoint(&self) -> String {
self.client.eth_endpoint.clone()
}
#[cfg(not(feature = "coconut"))]
pub fn get_eth_private_key(&self) -> String {
self.client.eth_private_key.clone()
}
// Debug getters
pub fn get_average_packet_delay(&self) -> Duration {
self.debug.average_packet_delay
}
pub fn get_average_ack_delay(&self) -> Duration {
self.debug.average_ack_delay
}
pub fn get_ack_wait_multiplier(&self) -> f64 {
self.debug.ack_wait_multiplier
}
pub fn get_ack_wait_addition(&self) -> Duration {
self.debug.ack_wait_addition
}
pub fn get_loop_cover_traffic_average_delay(&self) -> Duration {
self.debug.loop_cover_traffic_average_delay
}
pub fn get_message_sending_average_delay(&self) -> Duration {
self.debug.message_sending_average_delay
}
pub fn get_gateway_response_timeout(&self) -> Duration {
self.debug.gateway_response_timeout
}
pub fn get_topology_refresh_rate(&self) -> Duration {
self.debug.topology_refresh_rate
}
pub fn get_topology_resolution_timeout(&self) -> Duration {
self.debug.topology_resolution_timeout
}
pub fn get_version(&self) -> &str {
&self.client.version
}
}
impl<T: NymConfig> Default for Config<T> {
fn default() -> Self {
Config {
client: Client::<T>::default(),
logging: Default::default(),
debug: Default::default(),
}
}
}
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
struct GatewayEndpoint {
/// 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.
gateway_id: String,
/// Address of the gateway owner to which the client should send messages.
gateway_owner: String,
/// Address of the gateway listener to which all client requests should be sent.
gateway_listener: String,
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
pub struct Client<T> {
/// Version of the client for which this configuration was created.
#[serde(default = "missing_string_value")]
version: String,
/// ID specifies the human readable ID of this particular client.
id: String,
/// Indicates whether this client is running in a disabled credentials mode, thus attempting
/// to claim bandwidth without presenting bandwidth credentials.
#[serde(default)]
disabled_credentials_mode: bool,
/// Addresses to APIs running on validator from which the client gets the view of the network.
validator_api_urls: Vec<Url>,
/// Path to file containing private identity key.
private_identity_key_file: PathBuf,
/// Path to file containing public identity key.
public_identity_key_file: PathBuf,
/// Path to file containing private encryption key.
private_encryption_key_file: PathBuf,
/// Path to file containing public encryption key.
public_encryption_key_file: PathBuf,
/// Path to file containing shared key derived with the specified gateway that is used
/// for all communication with it.
gateway_shared_key_file: PathBuf,
/// Path to file containing key used for encrypting and decrypting the content of an
/// acknowledgement so that nobody besides the client knows which packet it refers to.
ack_key_file: PathBuf,
/// Full path to file containing reply encryption keys of all reply-SURBs we have ever
/// sent but not received back.
reply_encryption_key_store_path: PathBuf,
/// Information regarding how the client should send data to gateway.
gateway_endpoint: GatewayEndpoint,
/// Path to the database containing bandwidth credentials of this client.
database_path: PathBuf,
/// Ethereum private key.
#[cfg(not(feature = "coconut"))]
eth_private_key: String,
/// Address to an Ethereum full node.
#[cfg(not(feature = "coconut"))]
eth_endpoint: String,
/// nym_home_directory specifies absolute path to the home nym Clients directory.
/// It is expected to use default value and hence .toml file should not redefine this field.
nym_root_directory: PathBuf,
#[serde(skip)]
super_struct: PhantomData<*const T>,
}
impl<T: NymConfig> Default for Client<T> {
fn default() -> Self {
// there must be explicit checks for whether id is not empty later
Client {
version: env!("CARGO_PKG_VERSION").to_string(),
id: "".to_string(),
disabled_credentials_mode: true,
validator_api_urls: default_api_endpoints(),
private_identity_key_file: Default::default(),
public_identity_key_file: Default::default(),
private_encryption_key_file: Default::default(),
public_encryption_key_file: Default::default(),
gateway_shared_key_file: Default::default(),
ack_key_file: Default::default(),
reply_encryption_key_store_path: Default::default(),
gateway_endpoint: Default::default(),
database_path: Default::default(),
#[cfg(not(feature = "coconut"))]
eth_private_key: "".to_string(),
#[cfg(not(feature = "coconut"))]
eth_endpoint: "".to_string(),
nym_root_directory: T::default_root_directory(),
super_struct: Default::default(),
}
}
}
impl<T: NymConfig> Client<T> {
fn default_private_identity_key_file(id: &str) -> PathBuf {
T::default_data_directory(Some(id)).join("private_identity.pem")
}
fn default_public_identity_key_file(id: &str) -> PathBuf {
T::default_data_directory(Some(id)).join("public_identity.pem")
}
fn default_private_encryption_key_file(id: &str) -> PathBuf {
T::default_data_directory(Some(id)).join("private_encryption.pem")
}
fn default_public_encryption_key_file(id: &str) -> PathBuf {
T::default_data_directory(Some(id)).join("public_encryption.pem")
}
fn default_gateway_shared_key_file(id: &str) -> PathBuf {
T::default_data_directory(Some(id)).join("gateway_shared.pem")
}
fn default_ack_key_file(id: &str) -> PathBuf {
T::default_data_directory(Some(id)).join("ack_key.pem")
}
fn default_reply_encryption_key_store_path(id: &str) -> PathBuf {
T::default_data_directory(Some(id)).join("reply_key_store")
}
fn default_database_path(id: &str) -> PathBuf {
T::default_data_directory(Some(id)).join("db.sqlite")
}
}
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Logging {}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct Debug {
/// The parameter of Poisson distribution determining how long, on average,
/// sent packet is going to be delayed at any given mix node.
/// So for a packet going through three mix nodes, on average, it will take three times this value
/// until the packet reaches its destination.
#[serde(with = "humantime_serde")]
average_packet_delay: Duration,
/// The parameter of Poisson distribution determining how long, on average,
/// sent acknowledgement is going to be delayed at any given mix node.
/// So for an ack going through three mix nodes, on average, it will take three times this value
/// until the packet reaches its destination.
#[serde(with = "humantime_serde")]
average_ack_delay: Duration,
/// Value multiplied with the expected round trip time of an acknowledgement packet before
/// it is assumed it was lost and retransmission of the data packet happens.
/// In an ideal network with 0 latency, this value would have been 1.
ack_wait_multiplier: f64,
/// Value added to the expected round trip time of an acknowledgement packet before
/// it is assumed it was lost and retransmission of the data packet happens.
/// In an ideal network with 0 latency, this value would have been 0.
#[serde(with = "humantime_serde")]
ack_wait_addition: Duration,
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take for another loop cover traffic message to be sent.
#[serde(with = "humantime_serde")]
loop_cover_traffic_average_delay: Duration,
/// The parameter of Poisson distribution determining how long, on average,
/// it is going to take another 'real traffic stream' message to be sent.
/// If no real packets are available and cover traffic is enabled,
/// a loop cover message is sent instead in order to preserve the rate.
#[serde(with = "humantime_serde")]
message_sending_average_delay: Duration,
/// How long we're willing to wait for a response to a message sent to the gateway,
/// before giving up on it.
#[serde(with = "humantime_serde")]
gateway_response_timeout: Duration,
/// The uniform delay every which clients are querying the directory server
/// to try to obtain a compatible network topology to send sphinx packets through.
#[serde(with = "humantime_serde")]
topology_refresh_rate: Duration,
/// During topology refresh, test packets are sent through every single possible network
/// path. This timeout determines waiting period until it is decided that the packet
/// did not reach its destination.
#[serde(with = "humantime_serde")]
topology_resolution_timeout: Duration,
}
impl Default for Debug {
fn default() -> Self {
Debug {
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
ack_wait_addition: DEFAULT_ACK_WAIT_ADDITION,
loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
}
}
}