Files
nym/mixnode/src/config/mod.rs
T
Jon Häggblad ff1d92b576 Tidy nym-mixnode module visibility (#1075)
* mixnode: make command modules private

* mixnode: make some node modules private

* mixnode: make config structs private

* mixnode: restore accidentally moved function
2022-01-27 15:03:58 +01:00

496 lines
16 KiB
Rust

// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::template::config_template;
use config::defaults::*;
use config::NymConfig;
use serde::{Deserialize, Deserializer, Serialize};
use std::net::{IpAddr, SocketAddr};
use std::path::PathBuf;
use std::str::FromStr;
use std::time::Duration;
use url::Url;
pub mod persistence;
mod template;
pub(crate) const MISSING_VALUE: &str = "MISSING VALUE";
// 'RTT MEASUREMENT'
const DEFAULT_PACKETS_PER_NODE: usize = 100;
const DEFAULT_CONNECTION_TIMEOUT: Duration = Duration::from_millis(5000);
const DEFAULT_PACKET_TIMEOUT: Duration = Duration::from_millis(1500);
const DEFAULT_DELAY_BETWEEN_PACKETS: Duration = Duration::from_millis(50);
const DEFAULT_BATCH_SIZE: usize = 50;
const DEFAULT_TESTING_INTERVAL: Duration = Duration::from_secs(60 * 60 * 12);
const DEFAULT_RETRY_TIMEOUT: Duration = Duration::from_secs(60 * 30);
// 'DEBUG'
const DEFAULT_NODE_STATS_LOGGING_DELAY: Duration = Duration::from_millis(60_000);
const DEFAULT_NODE_STATS_UPDATING_DELAY: Duration = Duration::from_millis(30_000);
const DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF: Duration = Duration::from_millis(10_000);
const DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF: Duration = Duration::from_millis(300_000);
const DEFAULT_INITIAL_CONNECTION_TIMEOUT: Duration = Duration::from_millis(1_500);
const DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE: usize = 128;
pub fn missing_string_value<T: From<String>>() -> T {
MISSING_VALUE.to_string().into()
}
fn bind_all_address() -> IpAddr {
"0.0.0.0".parse().unwrap()
}
fn default_mix_port() -> u16 {
DEFAULT_MIX_LISTENING_PORT
}
fn default_verloc_port() -> u16 {
DEFAULT_VERLOC_LISTENING_PORT
}
fn default_http_api_port() -> u16 {
DEFAULT_HTTP_API_LISTENING_PORT
}
// basically a migration helper that deserialises string representation of a maybe socket addr (like "1.1.1.1:1234")
// into just the ipaddr (like "1.1.1.1")
pub(super) fn de_ipaddr_from_maybe_str_socks_addr<'de, D>(
deserializer: D,
) -> Result<IpAddr, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
if let Ok(socket_addr) = SocketAddr::from_str(&s) {
Ok(socket_addr.ip())
} else {
IpAddr::from_str(&s).map_err(serde::de::Error::custom)
}
}
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Config {
mixnode: MixNode,
#[serde(default)]
verloc: Verloc,
#[serde(default)]
logging: Logging,
#[serde(default)]
debug: Debug,
}
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("mixnodes")
}
fn root_directory(&self) -> PathBuf {
self.mixnode.nym_root_directory.clone()
}
fn config_directory(&self) -> PathBuf {
self.mixnode
.nym_root_directory
.join(&self.mixnode.id)
.join("config")
}
fn data_directory(&self) -> PathBuf {
self.mixnode
.nym_root_directory
.join(&self.mixnode.id)
.join("data")
}
}
impl Config {
pub fn new<S: Into<String>>(id: S) -> Self {
Config::default().with_id(id)
}
// builder methods
pub fn with_id<S: Into<String>>(mut self, id: S) -> Self {
let id = id.into();
if self
.mixnode
.private_identity_key_file
.as_os_str()
.is_empty()
{
self.mixnode.private_identity_key_file =
self::MixNode::default_private_identity_key_file(&id);
}
if self.mixnode.public_identity_key_file.as_os_str().is_empty() {
self.mixnode.public_identity_key_file =
self::MixNode::default_public_identity_key_file(&id);
}
if self.mixnode.private_sphinx_key_file.as_os_str().is_empty() {
self.mixnode.private_sphinx_key_file =
self::MixNode::default_private_sphinx_key_file(&id);
}
if self.mixnode.public_sphinx_key_file.as_os_str().is_empty() {
self.mixnode.public_sphinx_key_file =
self::MixNode::default_public_sphinx_key_file(&id);
}
self.mixnode.id = id;
self
}
pub fn with_custom_validator_apis(mut self, validator_api_urls: Vec<Url>) -> Self {
self.mixnode.validator_api_urls = validator_api_urls;
self
}
pub fn with_listening_address<S: Into<String>>(mut self, listening_address: S) -> Self {
let listening_address_string = listening_address.into();
if let Ok(ip_addr) = listening_address_string.parse() {
self.mixnode.listening_address = ip_addr
} else {
error!(
"failed to change listening address. the provided value ({}) was invalid",
listening_address_string
)
}
self
}
pub fn with_announce_address<S: Into<String>>(mut self, announce_address: S) -> Self {
self.mixnode.announce_address = announce_address.into();
self
}
pub fn with_mix_port(mut self, port: u16) -> Self {
self.mixnode.mix_port = port;
self
}
pub fn with_verloc_port(mut self, port: u16) -> Self {
self.mixnode.verloc_port = port;
self
}
pub fn with_http_api_port(mut self, port: u16) -> Self {
self.mixnode.http_api_port = port;
self
}
pub fn announce_address_from_listening_address(mut self) -> Self {
self.mixnode.announce_address = self.mixnode.listening_address.to_string();
self
}
pub fn with_custom_version(mut self, version: &str) -> Self {
self.mixnode.version = version.to_string();
self
}
pub fn with_wallet_address(mut self, wallet_address: &str) -> Self {
self.mixnode.wallet_address = wallet_address.to_string();
self
}
// getters
pub fn get_id(&self) -> String {
self.mixnode.id.clone()
}
pub fn get_config_file_save_location(&self) -> PathBuf {
self.config_directory().join(Self::config_file_name())
}
pub fn get_private_identity_key_file(&self) -> PathBuf {
self.mixnode.private_identity_key_file.clone()
}
pub fn get_public_identity_key_file(&self) -> PathBuf {
self.mixnode.public_identity_key_file.clone()
}
pub fn get_private_sphinx_key_file(&self) -> PathBuf {
self.mixnode.private_sphinx_key_file.clone()
}
pub fn get_public_sphinx_key_file(&self) -> PathBuf {
self.mixnode.public_sphinx_key_file.clone()
}
pub fn get_validator_api_endpoints(&self) -> Vec<Url> {
self.mixnode.validator_api_urls.clone()
}
pub fn get_node_stats_logging_delay(&self) -> Duration {
self.debug.node_stats_logging_delay
}
pub fn get_node_stats_updating_delay(&self) -> Duration {
self.debug.node_stats_updating_delay
}
pub fn get_listening_address(&self) -> IpAddr {
self.mixnode.listening_address
}
pub fn get_announce_address(&self) -> String {
self.mixnode.announce_address.clone()
}
pub fn get_mix_port(&self) -> u16 {
self.mixnode.mix_port
}
pub fn get_verloc_port(&self) -> u16 {
self.mixnode.verloc_port
}
pub fn get_http_api_port(&self) -> u16 {
self.mixnode.http_api_port
}
pub fn get_packet_forwarding_initial_backoff(&self) -> Duration {
self.debug.packet_forwarding_initial_backoff
}
pub fn get_packet_forwarding_maximum_backoff(&self) -> Duration {
self.debug.packet_forwarding_maximum_backoff
}
pub fn get_initial_connection_timeout(&self) -> Duration {
self.debug.initial_connection_timeout
}
pub fn get_maximum_connection_buffer_size(&self) -> usize {
self.debug.maximum_connection_buffer_size
}
pub fn get_version(&self) -> &str {
&self.mixnode.version
}
pub fn get_measurement_packets_per_node(&self) -> usize {
self.verloc.packets_per_node
}
pub fn get_measurement_packet_timeout(&self) -> Duration {
self.verloc.packet_timeout
}
pub fn get_measurement_connection_timeout(&self) -> Duration {
self.verloc.connection_timeout
}
pub fn get_measurement_delay_between_packets(&self) -> Duration {
self.verloc.delay_between_packets
}
pub fn get_measurement_tested_nodes_batch_size(&self) -> usize {
self.verloc.tested_nodes_batch_size
}
pub fn get_measurement_testing_interval(&self) -> Duration {
self.verloc.testing_interval
}
pub fn get_measurement_retry_timeout(&self) -> Duration {
self.verloc.retry_timeout
}
pub fn get_wallet_address(&self) -> &str {
&self.mixnode.wallet_address
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
struct MixNode {
/// Version of the mixnode for which this configuration was created.
#[serde(default = "missing_string_value")]
version: String,
/// ID specifies the human readable ID of this particular mixnode.
id: String,
/// Address to which this mixnode will bind to and will be listening for packets.
#[serde(deserialize_with = "de_ipaddr_from_maybe_str_socks_addr")]
listening_address: IpAddr,
/// Optional address announced to the validator for the clients to connect to.
/// It is useful, say, in NAT scenarios or wanting to more easily update actual IP address
/// later on by using name resolvable with a DNS query, such as `nymtech.net`.
announce_address: String,
/// Port used for listening for all mixnet traffic.
/// (default: 1789)
#[serde(default = "default_mix_port")]
mix_port: u16,
/// Port used for listening for verloc traffic.
/// (default: 1790)
#[serde(default = "default_verloc_port")]
verloc_port: u16,
/// Port used for listening for http requests.
/// (default: 8000)
#[serde(default = "default_http_api_port")]
http_api_port: u16,
/// Path to file containing private identity key.
#[serde(default = "missing_string_value")]
private_identity_key_file: PathBuf,
/// Path to file containing public identity key.
#[serde(default = "missing_string_value")]
public_identity_key_file: PathBuf,
/// Path to file containing private sphinx key.
private_sphinx_key_file: PathBuf,
/// Path to file containing public sphinx key.
public_sphinx_key_file: PathBuf,
/// Addresses to APIs running on validator from which the node gets the view of the network.
validator_api_urls: Vec<Url>,
/// nym_home_directory specifies absolute path to the home nym MixNodes directory.
/// It is expected to use default value and hence .toml file should not redefine this field.
nym_root_directory: PathBuf,
/// The Cosmos wallet address that will control this mixnode
wallet_address: String,
}
impl MixNode {
fn default_private_identity_key_file(id: &str) -> PathBuf {
Config::default_data_directory(Some(id)).join("private_identity.pem")
}
fn default_public_identity_key_file(id: &str) -> PathBuf {
Config::default_data_directory(Some(id)).join("public_identity.pem")
}
fn default_private_sphinx_key_file(id: &str) -> PathBuf {
Config::default_data_directory(Some(id)).join("private_sphinx.pem")
}
fn default_public_sphinx_key_file(id: &str) -> PathBuf {
Config::default_data_directory(Some(id)).join("public_sphinx.pem")
}
}
impl Default for MixNode {
fn default() -> Self {
MixNode {
version: env!("CARGO_PKG_VERSION").to_string(),
id: "".to_string(),
listening_address: bind_all_address(),
announce_address: "127.0.0.1".to_string(),
mix_port: DEFAULT_MIX_LISTENING_PORT,
verloc_port: DEFAULT_VERLOC_LISTENING_PORT,
http_api_port: DEFAULT_HTTP_API_LISTENING_PORT,
private_identity_key_file: Default::default(),
public_identity_key_file: Default::default(),
private_sphinx_key_file: Default::default(),
public_sphinx_key_file: Default::default(),
validator_api_urls: default_api_endpoints(),
nym_root_directory: Config::default_root_directory(),
wallet_address: "nymXXXXXXXX".to_string(),
}
}
}
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
struct Logging {}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
struct Verloc {
/// Specifies number of echo packets sent to each node during a measurement run.
packets_per_node: usize,
/// Specifies maximum amount of time to wait for the connection to get established.
connection_timeout: Duration,
/// Specifies maximum amount of time to wait for the reply packet to arrive before abandoning the test.
packet_timeout: Duration,
/// Specifies delay between subsequent test packets being sent (after receiving a reply).
delay_between_packets: Duration,
/// Specifies number of nodes being tested at once.
tested_nodes_batch_size: usize,
/// Specifies delay between subsequent test runs.
testing_interval: Duration,
/// Specifies delay between attempting to run the measurement again if the previous run failed
/// due to being unable to get the list of nodes.
retry_timeout: Duration,
}
impl Default for Verloc {
fn default() -> Self {
Verloc {
packets_per_node: DEFAULT_PACKETS_PER_NODE,
connection_timeout: DEFAULT_CONNECTION_TIMEOUT,
packet_timeout: DEFAULT_PACKET_TIMEOUT,
delay_between_packets: DEFAULT_DELAY_BETWEEN_PACKETS,
tested_nodes_batch_size: DEFAULT_BATCH_SIZE,
testing_interval: DEFAULT_TESTING_INTERVAL,
retry_timeout: DEFAULT_RETRY_TIMEOUT,
}
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(default)]
struct Debug {
/// Delay between each subsequent node statistics being logged to the console
#[serde(with = "humantime_serde")]
node_stats_logging_delay: Duration,
/// Delay between each subsequent node statistics being updated
#[serde(with = "humantime_serde")]
node_stats_updating_delay: Duration,
/// Initial value of an exponential backoff to reconnect to dropped TCP connection when
/// forwarding sphinx packets.
#[serde(with = "humantime_serde")]
packet_forwarding_initial_backoff: Duration,
/// Maximum value of an exponential backoff to reconnect to dropped TCP connection when
/// forwarding sphinx packets.
#[serde(with = "humantime_serde")]
packet_forwarding_maximum_backoff: Duration,
/// Timeout for establishing initial connection when trying to forward a sphinx packet.
#[serde(with = "humantime_serde")]
initial_connection_timeout: Duration,
/// Maximum number of packets that can be stored waiting to get sent to a particular connection.
maximum_connection_buffer_size: usize,
}
impl Default for Debug {
fn default() -> Self {
Debug {
node_stats_logging_delay: DEFAULT_NODE_STATS_LOGGING_DELAY,
node_stats_updating_delay: DEFAULT_NODE_STATS_UPDATING_DELAY,
packet_forwarding_initial_backoff: DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF,
packet_forwarding_maximum_backoff: DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF,
initial_connection_timeout: DEFAULT_INITIAL_CONNECTION_TIMEOUT,
maximum_connection_buffer_size: DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE,
}
}
}