Initial fields for the client config
This commit is contained in:
@@ -18,7 +18,6 @@ curve25519-dalek = "1.2.3"
|
||||
dirs = "2.0.2"
|
||||
dotenv = "0.15.0"
|
||||
futures = "0.3.1"
|
||||
handlebars = "3.0.1"
|
||||
log = "0.4"
|
||||
pem = "0.7.0"
|
||||
pretty_env_logger = "0.3"
|
||||
@@ -30,6 +29,7 @@ tungstenite = "0.9.2"
|
||||
|
||||
## internal
|
||||
addressing = {path = "../common/addressing" }
|
||||
config = {path = "../common/config"}
|
||||
crypto = {path = "../common/crypto"}
|
||||
directory-client = { path = "../common/clients/directory-client" }
|
||||
healthcheck = { path = "../common/healthcheck" }
|
||||
@@ -48,3 +48,7 @@ tokio-tungstenite = { git = "https://github.com/snapview/tokio-tungstenite", rev
|
||||
|
||||
[build-dependencies]
|
||||
built = "0.3.2"
|
||||
|
||||
[features]
|
||||
qa = []
|
||||
local = []
|
||||
@@ -8,6 +8,7 @@ use directory_client::presence::Topology;
|
||||
use futures::channel::mpsc;
|
||||
use futures::join;
|
||||
use log::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sfw_provider_requests::AuthToken;
|
||||
use sphinx::route::Destination;
|
||||
use std::marker::PhantomData;
|
||||
@@ -31,6 +32,8 @@ const FETCH_MESSAGES_DELAY: f64 = 1.0; // seconds;
|
||||
|
||||
const TOPOLOGY_REFRESH_RATE: f64 = 10.0; // seconds
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub enum SocketType {
|
||||
TCP,
|
||||
WebSocket,
|
||||
|
||||
+194
-33
@@ -1,82 +1,243 @@
|
||||
use crate::config::persistance::pathfinder::ClientPathfinder;
|
||||
use crate::client::SocketType;
|
||||
use crate::config::template::config_template;
|
||||
use config::NymConfig;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub mod persistance;
|
||||
mod template;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
// all of the below are defined in seconds
|
||||
const DEFAULT_LOOP_COVER_SENDING_AVERAGE_DELAY: f64 = 1.0;
|
||||
const DEFAULT_MESSAGE_SENDING_AVERAGE_DELAY: f64 = 0.5;
|
||||
const DEFAULT_AVERAGE_PACKET_DELAY: f64 = 0.2;
|
||||
const DEFAULT_FETCH_MESSAGES_DELAY: f64 = 1.0;
|
||||
const DEFAULT_TOPOLOGY_REFRESH_RATE: f64 = 10.0;
|
||||
|
||||
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Config {
|
||||
client: Client,
|
||||
socket: Socket,
|
||||
|
||||
#[serde(default)]
|
||||
logging: Logging,
|
||||
#[serde(default)]
|
||||
debug: Debug,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
unimplemented!()
|
||||
impl NymConfig for Config {
|
||||
fn template() -> &'static str {
|
||||
config_template()
|
||||
}
|
||||
|
||||
fn default_root_directory() -> PathBuf {
|
||||
dirs::home_dir()
|
||||
.expect("Failed to evaluate $HOME value")
|
||||
.join(".nym")
|
||||
.join("clients")
|
||||
}
|
||||
|
||||
fn root_directory(&self) -> PathBuf {
|
||||
self.client.nym_root_directory.clone()
|
||||
}
|
||||
|
||||
fn config_directory(&self) -> PathBuf {
|
||||
self.client
|
||||
.nym_root_directory
|
||||
.join(&self.client.id)
|
||||
.join("config")
|
||||
}
|
||||
|
||||
fn data_directory(&self) -> PathBuf {
|
||||
self.client
|
||||
.nym_root_directory
|
||||
.join(&self.client.id)
|
||||
.join("data")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Client {
|
||||
// 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_home_directory: String,
|
||||
impl Config {
|
||||
pub fn new(id: String) -> Self {
|
||||
Config::default().with_id(id)
|
||||
}
|
||||
|
||||
// ID specifies the human readable ID of this particular client.
|
||||
// If not provided a pseudorandom id will be generated instead.
|
||||
pub fn with_id(mut self, id: String) -> Self {
|
||||
if self.client.private_identity_key_file.as_os_str().is_empty() {
|
||||
self.client.private_identity_key_file =
|
||||
self::Client::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::default_public_identity_key_file(&id);
|
||||
}
|
||||
self.client.id = id;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_provider_id(mut self, id: String) -> Self {
|
||||
self.client.provider_id = id;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Client {
|
||||
/// ID specifies the human readable ID of this particular client.
|
||||
id: String,
|
||||
|
||||
// URL to the directory server.
|
||||
/// URL to the directory server.
|
||||
directory_server: String,
|
||||
|
||||
// Path to file containing private identity key.
|
||||
/// Path to file containing private identity key.
|
||||
private_identity_key_file: PathBuf,
|
||||
|
||||
// Path to file containing public identity key.
|
||||
/// Path to file containing public identity key.
|
||||
public_identity_key_file: PathBuf,
|
||||
|
||||
// mix_apps_directory specifies directory for mixapps, such as a chat client,
|
||||
// to store their app-specific data.
|
||||
mix_apps_directory: String,
|
||||
|
||||
// provider_id specifies ID of the provider to which the client should send messages.
|
||||
// If initially omitted, a random provider will be chosen from the available topology.
|
||||
/// provider_id specifies ID of the provider to which the client should send messages.
|
||||
/// If initially omitted, a random provider will be chosen from the available topology.
|
||||
provider_id: 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,
|
||||
}
|
||||
|
||||
impl Default for Client {
|
||||
fn default() -> Self {
|
||||
unimplemented!()
|
||||
// there must be explicit checks for whether id is not empty later
|
||||
Client {
|
||||
id: "".to_string(),
|
||||
directory_server: Self::default_directory_server(),
|
||||
private_identity_key_file: Default::default(),
|
||||
public_identity_key_file: Default::default(),
|
||||
provider_id: "".to_string(),
|
||||
nym_root_directory: Config::default_root_directory(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Client {
|
||||
fn new() -> Self {
|
||||
Default::default()
|
||||
fn default_directory_server() -> String {
|
||||
#[cfg(feature = "qa")]
|
||||
return "https://qa-directory.nymtech.net".to_string();
|
||||
#[cfg(feature = "local")]
|
||||
return "http://localhost:8080".to_string();
|
||||
|
||||
"https://directory.nymtech.net".to_string()
|
||||
}
|
||||
|
||||
fn with_id(mut self, id: String) -> Self {
|
||||
self.id = id;
|
||||
self
|
||||
fn default_private_identity_key_file(id: &str) -> PathBuf {
|
||||
Config::default_data_directory()
|
||||
.join(id)
|
||||
.join("private_identity.pem")
|
||||
}
|
||||
|
||||
fn with_pathfinder(mut self, pathfinder: ClientPathfinder) -> Self {
|
||||
// pub config_dir: PathBuf,
|
||||
// pub private_mix_key: PathBuf,
|
||||
// pub public_mix_key: PathBuf,
|
||||
self
|
||||
fn default_public_identity_key_file(id: &str) -> PathBuf {
|
||||
Config::default_data_directory()
|
||||
.join(id)
|
||||
.join("public_identity.pem")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Socket {
|
||||
socket_type: SocketType,
|
||||
listening_port: u64,
|
||||
}
|
||||
|
||||
impl Default for Socket {
|
||||
fn default() -> Self {
|
||||
Socket {
|
||||
socket_type: SocketType::None,
|
||||
listening_port: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Logging {}
|
||||
|
||||
impl Default for Logging {
|
||||
fn default() -> Self {
|
||||
unimplemented!()
|
||||
Logging {}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(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.
|
||||
/// The provided value is interpreted as seconds.
|
||||
average_packet_delay: f64,
|
||||
|
||||
/// The parameter of Poisson distribution determining how long, on average,
|
||||
/// it is going to take for another loop cover traffic message to be sent.
|
||||
/// If set to a negative value, the loop cover traffic stream will be disabled.
|
||||
/// The provided value is interpreted as seconds.
|
||||
loop_cover_traffic_average_delay: f64,
|
||||
|
||||
/// The uniform delay every which clients are querying the providers for received packets.
|
||||
/// If set to a negative value, client will never try to fetch their messages.
|
||||
/// The provided value is interpreted as seconds.
|
||||
fetch_message_delay: f64,
|
||||
|
||||
/// 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.
|
||||
/// If set to a negative value, client will never try to send real traffic data.
|
||||
/// The provided value is interpreted as seconds.
|
||||
message_sending_average_delay: f64,
|
||||
|
||||
/// Whether loop cover messages should be sent to respect message_sending_rate.
|
||||
/// In the case of it being disabled and not having enough real traffic
|
||||
/// waiting to be sent the actual sending rate is going be lower than the desired value
|
||||
/// thus decreasing the anonymity.
|
||||
rate_compliant_cover_messages_disabled: bool,
|
||||
|
||||
/// The uniform delay every which clients are querying the directory server
|
||||
/// to try to obtain a compatible network topology to send sphinx packets through.
|
||||
/// If set to a negative value, client will never try to refresh its topology,
|
||||
/// meaning it will always try to use whatever it obtained on startup.
|
||||
/// The provided value is interpreted as seconds.
|
||||
topology_refresh_rate: f64,
|
||||
}
|
||||
|
||||
impl Default for Debug {
|
||||
fn default() -> Self {
|
||||
Debug {
|
||||
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
|
||||
loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_SENDING_AVERAGE_DELAY,
|
||||
fetch_message_delay: DEFAULT_FETCH_MESSAGES_DELAY,
|
||||
message_sending_average_delay: DEFAULT_MESSAGE_SENDING_AVERAGE_DELAY,
|
||||
rate_compliant_cover_messages_disabled: false,
|
||||
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod client_config {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn after_saving_default_config_the_loaded_one_is_identical() {
|
||||
let temp_location = std::env::temp_dir().join("config.toml");
|
||||
let default_config = Config::default().with_id("foomp".to_string());
|
||||
default_config
|
||||
.save_to_file(Some(temp_location.clone()))
|
||||
.unwrap();
|
||||
|
||||
let loaded_config = Config::load_from_file(Some(temp_location)).unwrap();
|
||||
|
||||
assert_eq!(default_config, loaded_config);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
use super::Config;
|
||||
use handlebars::{Handlebars, TemplateRenderError};
|
||||
|
||||
fn config_template() -> &'static str {
|
||||
pub(crate) fn config_template() -> &'static str {
|
||||
// While using normal toml marshalling would have been way simpler with less overhead,
|
||||
// I think it's useful to have comments attached to the saved config file to explain behaviour of
|
||||
// particular fields.
|
||||
@@ -20,49 +17,85 @@ id = "{{ client.id }}"
|
||||
directory_server = "{{ client.directory_server }}"
|
||||
|
||||
# Path to file containing private identity key.
|
||||
private_identity_key_file = "{{ client.private_identity_key }}"
|
||||
private_identity_key_file = "{{ client.private_identity_key_file }}"
|
||||
|
||||
# Path to file containing public identity key.
|
||||
public_identity_key_file = "{{ client.public_identity_key }}"
|
||||
public_identity_key_file = "{{ client.public_identity_key_file }}"
|
||||
|
||||
##### additional client config options #####
|
||||
|
||||
# ID of the provider to which the client should send messages.
|
||||
# ID of the provider from which the client should be fetching messages.
|
||||
provider_id = "{{ client.provider_id }}"
|
||||
|
||||
# directory for mixapps, such as a chat client, to store their app-specific data.
|
||||
mixapps_directory = "{{ client.mix_apps_directory }}"
|
||||
|
||||
##### advanced configuration options #####
|
||||
|
||||
# Absolute path to the home Nym Clients directory.
|
||||
nym_home_directory = "{{ client.home_directory }}"
|
||||
nym_root_directory = "{{ client.nym_root_directory }}"
|
||||
|
||||
|
||||
##### socket config options #####
|
||||
|
||||
[socket]
|
||||
|
||||
# allowed values are 'TCP', 'WebSocket' or 'None'
|
||||
socket_type = "{{ socket.socket_type }}"
|
||||
|
||||
# if applicable (for the case of 'TCP' or 'WebSocket'), the port on which the client
|
||||
# will be listening for incoming requests
|
||||
listening_port = {{ socket.listening_port }}
|
||||
|
||||
|
||||
##### logging configuration options #####
|
||||
|
||||
[logging]
|
||||
|
||||
# TODO
|
||||
|
||||
|
||||
##### debug configuration options #####
|
||||
# The following options should not be modified unless you know EXACTLY what you are doing
|
||||
# as if set incorrectly, they may impact your anonymity.
|
||||
|
||||
[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.
|
||||
# The provided value is interpreted as seconds.
|
||||
average_packet_delay = {{ debug.average_packet_delay }}
|
||||
|
||||
# The parameter of Poisson distribution determining how long, on average,
|
||||
# it is going to take for another loop cover traffic message to be sent.
|
||||
# If set to a negative value, the loop cover traffic stream will be disabled.
|
||||
# The provided value is interpreted as seconds.
|
||||
loop_cover_traffic_average_delay = {{ debug.loop_cover_traffic_average_delay }}
|
||||
|
||||
# The uniform delay every which clients are querying the providers for received packets.
|
||||
# If set to a negative value, client will never try to fetch their messages.
|
||||
# The provided value is interpreted as seconds.
|
||||
fetch_message_delay = {{ debug.fetch_message_delay }}
|
||||
|
||||
# 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.
|
||||
# If set to a negative value, client will never try to send real traffic data.
|
||||
# The provided value is interpreted as seconds.
|
||||
message_sending_average_delay = {{ debug.message_sending_average_delay }}
|
||||
|
||||
# Whether loop cover messages should be sent to respect message_sending_rate.
|
||||
# In the case of it being disabled and not having enough real traffic
|
||||
# waiting to be sent the actual sending rate is going be lower than the desired value
|
||||
# thus decreasing the anonymity.
|
||||
rate_compliant_cover_messages_disabled = {{ debug.rate_compliant_cover_messages_disabled }}
|
||||
|
||||
# The uniform delay every which clients are querying the directory server
|
||||
# to try to obtain a compatible network topology to send sphinx packets through.
|
||||
# If set to a negative value, client will never try to refresh its topology,
|
||||
# meaning it will always try to use whatever it obtained on startup.
|
||||
# The provided value is interpreted as seconds.
|
||||
topology_refresh_rate = {{ debug.topology_refresh_rate }}
|
||||
|
||||
"#
|
||||
}
|
||||
|
||||
fn render_template(config: &Config) -> Result<String, TemplateRenderError> {
|
||||
let reg = Handlebars::new();
|
||||
reg.render_template(config_template(), &config)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod config_template {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_works_for_default_config() {
|
||||
render_template(&Default::default()).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_works_for_dummy_config() {
|
||||
let dummy_cfg = Config {
|
||||
client: crate::config::Client {
|
||||
id: "foomp".to_string(),
|
||||
},
|
||||
};
|
||||
let render = render_template(&dummy_cfg).unwrap();
|
||||
|
||||
println!("{}", render);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user