Feature/config refactor (#3498)
* revamping mixnode connfig * wip * native client config revamping * wip * building socks5 * using const for mixnnode config template * compiling updated gateway * nym-api * nym-sdk * everything compiling once more but definitely not compatible with CI and older versions (yet) * creating full directory structure on init * renamed paths to storage_paths and fixed mixnode template * mixnode config migration * gateway config migration * nym-api config migration * native client config migration * socks5 client config migration * NR config migration * removed deprecations (that will be resolved in the following PRs) + fixed clippy * nym-connect clippy * nym-connect config updates * outfox fixes * defined socks5 lib config * clippy * fixed wasm client build * removed explicit packet_type argument when starting base client it's known implicitly from the previously passed config struct * Empty commit * fixed re-using gateway information when client configs are re-initialised * fixed borrowing id value in nym-connect * post-rebase fixes * updated 'old_config' versions --------- Co-authored-by: Tommy Verrall <tommy@nymtech.net>
This commit is contained in:
committed by
GitHub
parent
bc5198768e
commit
9a68702d4d
@@ -1,11 +1,10 @@
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::commands::try_load_current_config;
|
||||
use crate::node::node_description::NodeDescription;
|
||||
use clap::Args;
|
||||
use colored::Colorize;
|
||||
use nym_config::NymConfig;
|
||||
use std::io;
|
||||
use std::io::Write;
|
||||
|
||||
@@ -39,15 +38,9 @@ fn read_user_input() -> String {
|
||||
buf.trim().to_string()
|
||||
}
|
||||
|
||||
pub(crate) fn execute(args: Describe) {
|
||||
pub(crate) fn execute(args: Describe) -> anyhow::Result<()> {
|
||||
// ensure that the mixnode has in fact been initialized
|
||||
match Config::load_from_file(&args.id) {
|
||||
Ok(cfg) => cfg,
|
||||
Err(err) => {
|
||||
error!("Failed to load config for {}. Are you sure you have run `init` before? (Error was: {err})", &args.id);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let config = try_load_current_config(&args.id)?;
|
||||
|
||||
let example_url = "https://mixnode.yourdomain.com".bright_cyan();
|
||||
let example_location = "City: London, Country: UK";
|
||||
@@ -81,9 +74,6 @@ pub(crate) fn execute(args: Describe) {
|
||||
};
|
||||
|
||||
// save the struct
|
||||
NodeDescription::save_to_file(
|
||||
&node_description,
|
||||
Config::default_config_directory(&args.id),
|
||||
)
|
||||
.unwrap()
|
||||
NodeDescription::save_to_file(&node_description, config.storage_paths.node_description)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -2,15 +2,16 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::OverrideConfig;
|
||||
use crate::config::Config;
|
||||
use crate::commands::override_config;
|
||||
use crate::config::{
|
||||
default_config_directory, default_config_filepath, default_data_directory, Config,
|
||||
};
|
||||
use crate::node::MixNode;
|
||||
use crate::{commands::override_config, config::persistence::pathfinder::MixNodePathfinder};
|
||||
use clap::Args;
|
||||
use nym_bin_common::output_format::OutputFormat;
|
||||
use nym_config::NymConfig;
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
use nym_validator_client::nyxd;
|
||||
use std::net::IpAddr;
|
||||
use std::{fs, io};
|
||||
|
||||
#[derive(Args, Clone)]
|
||||
pub(crate) struct Init {
|
||||
@@ -22,10 +23,6 @@ pub(crate) struct Init {
|
||||
#[clap(long)]
|
||||
host: IpAddr,
|
||||
|
||||
/// The wallet address you will use to bond this mixnode, e.g. nymt1z9egw0knv47nmur0p8vk4rcx59h9gg4zuxrrr9
|
||||
#[clap(long)]
|
||||
wallet_address: nyxd::AccountId,
|
||||
|
||||
/// The port on which the mixnode will be listening for mix packets
|
||||
#[clap(long)]
|
||||
mix_port: Option<u16>,
|
||||
@@ -38,10 +35,6 @@ pub(crate) struct Init {
|
||||
#[clap(long)]
|
||||
http_api_port: Option<u16>,
|
||||
|
||||
/// The custom host that will be reported to the directory server
|
||||
#[clap(long)]
|
||||
announce_host: Option<String>,
|
||||
|
||||
/// Comma separated list of nym-api endpoints of the validators
|
||||
// the alias here is included for backwards compatibility (1.1.4 and before)
|
||||
#[clap(long, alias = "validators", value_delimiter = ',')]
|
||||
@@ -56,29 +49,33 @@ impl From<Init> for OverrideConfig {
|
||||
OverrideConfig {
|
||||
id: init_config.id,
|
||||
host: Some(init_config.host),
|
||||
wallet_address: Some(init_config.wallet_address),
|
||||
mix_port: init_config.mix_port,
|
||||
verloc_port: init_config.verloc_port,
|
||||
http_api_port: init_config.http_api_port,
|
||||
announce_host: init_config.announce_host,
|
||||
nym_apis: init_config.nym_apis,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn init_paths(id: &str) -> io::Result<()> {
|
||||
fs::create_dir_all(default_data_directory(id))?;
|
||||
fs::create_dir_all(default_config_directory(id))
|
||||
}
|
||||
|
||||
pub(crate) fn execute(args: &Init) {
|
||||
let override_config_fields = OverrideConfig::from(args.clone());
|
||||
let id = &override_config_fields.id;
|
||||
let id = override_config_fields.id.clone();
|
||||
eprintln!("Initialising mixnode {id}...");
|
||||
|
||||
let already_init = if Config::default_config_file_path(id).exists() {
|
||||
let already_init = if default_config_filepath(&id).exists() {
|
||||
eprintln!("Mixnode \"{id}\" was already initialised before! Config information will be overwritten (but keys will be kept)!");
|
||||
true
|
||||
} else {
|
||||
init_paths(&id).expect("failed to initialise storage paths");
|
||||
false
|
||||
};
|
||||
|
||||
let mut config = Config::new(id);
|
||||
let mut config = Config::new(&id);
|
||||
config = override_config(config, override_config_fields);
|
||||
|
||||
// if node was already initialised, don't generate new keys
|
||||
@@ -87,12 +84,12 @@ pub(crate) fn execute(args: &Init) {
|
||||
|
||||
let identity_keys = identity::KeyPair::new(&mut rng);
|
||||
let sphinx_keys = encryption::KeyPair::new(&mut rng);
|
||||
let pathfinder = MixNodePathfinder::new_from_config(&config);
|
||||
|
||||
nym_pemstore::store_keypair(
|
||||
&identity_keys,
|
||||
&nym_pemstore::KeyPairPath::new(
|
||||
pathfinder.private_identity_key().to_owned(),
|
||||
pathfinder.public_identity_key().to_owned(),
|
||||
config.storage_paths.private_identity_key(),
|
||||
config.storage_paths.public_identity_key(),
|
||||
),
|
||||
)
|
||||
.expect("Failed to save identity keys");
|
||||
@@ -100,19 +97,25 @@ pub(crate) fn execute(args: &Init) {
|
||||
nym_pemstore::store_keypair(
|
||||
&sphinx_keys,
|
||||
&nym_pemstore::KeyPairPath::new(
|
||||
pathfinder.private_encryption_key().to_owned(),
|
||||
pathfinder.public_encryption_key().to_owned(),
|
||||
config.storage_paths.private_encryption_key(),
|
||||
config.storage_paths.public_encryption_key(),
|
||||
),
|
||||
)
|
||||
.expect("Failed to save sphinx keys");
|
||||
eprintln!("Saved mixnet identity and sphinx keypairs");
|
||||
}
|
||||
|
||||
let config_save_location = config.get_config_file_save_location();
|
||||
config
|
||||
.save_to_file(None)
|
||||
.expect("Failed to save the config file");
|
||||
eprintln!("Saved configuration file to {config_save_location:?}");
|
||||
let config_save_location = config.default_location();
|
||||
config.save_to_default_location().unwrap_or_else(|_| {
|
||||
panic!(
|
||||
"Failed to save the config file to {}",
|
||||
config_save_location.display()
|
||||
)
|
||||
});
|
||||
eprintln!(
|
||||
"Saved configuration file to {}",
|
||||
config_save_location.display()
|
||||
);
|
||||
eprintln!("Mixnode configuration completed.\n\n\n");
|
||||
|
||||
MixNode::new(config).print_node_details(args.output)
|
||||
|
||||
+52
-32
@@ -1,7 +1,9 @@
|
||||
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::old_config_v1_1_21::ConfigV1_1_21;
|
||||
use crate::{config::Config, Cli};
|
||||
use anyhow::anyhow;
|
||||
use clap::CommandFactory;
|
||||
use clap::Subcommand;
|
||||
use colored::Colorize;
|
||||
@@ -10,7 +12,6 @@ use nym_bin_common::version_checker;
|
||||
use nym_config::defaults::var_names::{BECH32_PREFIX, NYM_API};
|
||||
use nym_config::OptionalSet;
|
||||
use nym_crypto::bech32_address_validation;
|
||||
use nym_validator_client::nyxd;
|
||||
use std::net::IpAddr;
|
||||
use std::process;
|
||||
|
||||
@@ -52,45 +53,31 @@ pub(crate) enum Commands {
|
||||
struct OverrideConfig {
|
||||
id: String,
|
||||
host: Option<IpAddr>,
|
||||
wallet_address: Option<nyxd::AccountId>,
|
||||
mix_port: Option<u16>,
|
||||
verloc_port: Option<u16>,
|
||||
http_api_port: Option<u16>,
|
||||
announce_host: Option<String>,
|
||||
nym_apis: Option<Vec<url::Url>>,
|
||||
}
|
||||
|
||||
pub(crate) async fn execute(args: Cli) {
|
||||
pub(crate) async fn execute(args: Cli) -> anyhow::Result<()> {
|
||||
let bin_name = "nym-mixnode";
|
||||
|
||||
match args.command {
|
||||
Commands::Describe(m) => describe::execute(m),
|
||||
Commands::Describe(m) => describe::execute(m)?,
|
||||
Commands::Init(m) => init::execute(&m),
|
||||
Commands::Run(m) => run::execute(&m).await,
|
||||
Commands::Sign(m) => sign::execute(&m),
|
||||
Commands::Upgrade(m) => upgrade::execute(&m),
|
||||
Commands::NodeDetails(m) => node_details::execute(&m),
|
||||
Commands::Run(m) => run::execute(&m).await?,
|
||||
Commands::Sign(m) => sign::execute(&m)?,
|
||||
Commands::Upgrade(m) => upgrade::execute(&m)?,
|
||||
Commands::NodeDetails(m) => node_details::execute(&m)?,
|
||||
Commands::Completions(s) => s.generate(&mut crate::Cli::command(), bin_name),
|
||||
Commands::GenerateFigSpec => fig_generate(&mut crate::Cli::command(), bin_name),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn override_config(mut config: Config, args: OverrideConfig) -> Config {
|
||||
// special case that I'm not sure could be easily handled with the trait
|
||||
let mut was_host_overridden = false;
|
||||
if let Some(host) = args.host {
|
||||
config = config.with_listening_address(host);
|
||||
was_host_overridden = true;
|
||||
}
|
||||
|
||||
if let Some(announce_host) = args.announce_host {
|
||||
config = config.with_announce_address(announce_host);
|
||||
} else if was_host_overridden {
|
||||
// make sure our 'announce-host' always defaults to 'host'
|
||||
config = config.announce_address_from_listening_address()
|
||||
}
|
||||
|
||||
fn override_config(config: Config, args: OverrideConfig) -> Config {
|
||||
config
|
||||
.with_optional(Config::with_listening_address, args.host)
|
||||
.with_optional(Config::with_mix_port, args.mix_port)
|
||||
.with_optional(Config::with_verloc_port, args.verloc_port)
|
||||
.with_optional(Config::with_http_api_port, args.http_api_port)
|
||||
@@ -100,13 +87,6 @@ fn override_config(mut config: Config, args: OverrideConfig) -> Config {
|
||||
NYM_API,
|
||||
nym_config::parse_urls,
|
||||
)
|
||||
.with_optional(
|
||||
|cfg, wallet_address| {
|
||||
validate_bech32_address_or_exit(wallet_address.as_ref());
|
||||
cfg.with_wallet_address(wallet_address)
|
||||
},
|
||||
args.wallet_address,
|
||||
)
|
||||
}
|
||||
|
||||
/// Ensures that a given bech32 address is valid, or exits
|
||||
@@ -135,7 +115,7 @@ pub(crate) fn validate_bech32_address_or_exit(address: &str) {
|
||||
// network version. It might do so in the future.
|
||||
pub(crate) fn version_check(cfg: &Config) -> bool {
|
||||
let binary_version = env!("CARGO_PKG_VERSION");
|
||||
let config_version = cfg.get_version();
|
||||
let config_version = &cfg.mixnode.version;
|
||||
if binary_version == config_version {
|
||||
true
|
||||
} else {
|
||||
@@ -149,3 +129,43 @@ pub(crate) fn version_check(cfg: &Config) -> bool {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_upgrade_v1_1_21_config(id: &str) -> std::io::Result<()> {
|
||||
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
|
||||
|
||||
// explicitly load it as v1.1.21 (which is incompatible with the current, i.e. 1.1.22+)
|
||||
let Ok(old_config) = ConfigV1_1_21::load_from_file(id) else {
|
||||
// if we failed to load it, there might have been nothing to upgrade
|
||||
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
|
||||
return Ok(());
|
||||
};
|
||||
info!("It seems the mixnode is using <= v1.1.21 config template.");
|
||||
info!("It is going to get updated to the current specification.");
|
||||
|
||||
let updated: Config = old_config.into();
|
||||
updated.save_to_default_location()
|
||||
}
|
||||
|
||||
fn try_load_current_config(id: &str) -> anyhow::Result<Config> {
|
||||
try_upgrade_v1_1_21_config(id)?;
|
||||
|
||||
Config::read_from_default_path(id).map_err(|err| {
|
||||
let error_msg =
|
||||
format!(
|
||||
"Failed to load config for {id}. Are you sure you have run `init` before? (Error was: {err})",
|
||||
);
|
||||
error!("{error_msg}");
|
||||
anyhow!(error_msg)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use clap::CommandFactory;
|
||||
|
||||
#[test]
|
||||
fn verify_cli() {
|
||||
Cli::command().debug_assert();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::commands::try_load_current_config;
|
||||
use crate::node::MixNode;
|
||||
use clap::Args;
|
||||
use nym_bin_common::output_format::OutputFormat;
|
||||
use nym_config::NymConfig;
|
||||
|
||||
#[derive(Args)]
|
||||
pub(crate) struct NodeDetails {
|
||||
@@ -17,18 +16,9 @@ pub(crate) struct NodeDetails {
|
||||
output: OutputFormat,
|
||||
}
|
||||
|
||||
pub(crate) fn execute(args: &NodeDetails) {
|
||||
let config = match Config::load_from_file(&args.id) {
|
||||
Ok(cfg) => cfg,
|
||||
Err(err) => {
|
||||
error!(
|
||||
"Failed to load config for {}. Are you sure you have run `init` before? (Error was: {})",
|
||||
args.id,
|
||||
err,
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
pub(crate) fn execute(args: &NodeDetails) -> anyhow::Result<()> {
|
||||
let config = try_load_current_config(&args.id)?;
|
||||
|
||||
MixNode::new(config).print_node_details(args.output)
|
||||
MixNode::new(config).print_node_details(args.output);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+11
-31
@@ -2,12 +2,12 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::OverrideConfig;
|
||||
use crate::commands::{override_config, version_check};
|
||||
use crate::config::Config;
|
||||
use crate::commands::{override_config, try_load_current_config, version_check};
|
||||
use crate::node::MixNode;
|
||||
use anyhow::bail;
|
||||
use clap::Args;
|
||||
use nym_bin_common::output_format::OutputFormat;
|
||||
use nym_config::NymConfig;
|
||||
use nym_config::helpers::SPECIAL_ADDRESSES;
|
||||
use nym_validator_client::nyxd;
|
||||
use std::net::IpAddr;
|
||||
|
||||
@@ -37,10 +37,6 @@ pub(crate) struct Run {
|
||||
#[clap(long)]
|
||||
http_api_port: Option<u16>,
|
||||
|
||||
/// The host that will be reported to the directory server
|
||||
#[clap(long)]
|
||||
announce_host: Option<String>,
|
||||
|
||||
/// Comma separated list of nym-api endpoints of the validators
|
||||
// the alias here is included for backwards compatibility (1.1.4 and before)
|
||||
#[clap(long, alias = "validators", value_delimiter = ',')]
|
||||
@@ -55,11 +51,9 @@ impl From<Run> for OverrideConfig {
|
||||
OverrideConfig {
|
||||
id: run_config.id,
|
||||
host: run_config.host,
|
||||
wallet_address: run_config.wallet_address,
|
||||
mix_port: run_config.mix_port,
|
||||
verloc_port: run_config.verloc_port,
|
||||
http_api_port: run_config.http_api_port,
|
||||
announce_host: run_config.announce_host,
|
||||
nym_apis: run_config.nym_apis,
|
||||
}
|
||||
}
|
||||
@@ -70,39 +64,24 @@ fn show_binding_warning(address: &str) {
|
||||
eprintln!(
|
||||
"\nYou are trying to bind to {address} - you might not be accessible to other nodes\n\
|
||||
You can ignore this note if you're running setup on a local network \n\
|
||||
or have set a custom 'announce-host'"
|
||||
or have used different host when bonding your node"
|
||||
);
|
||||
eprintln!("\n\n");
|
||||
}
|
||||
|
||||
fn special_addresses() -> Vec<&'static str> {
|
||||
vec!["localhost", "127.0.0.1", "0.0.0.0", "::1", "[::1]"]
|
||||
}
|
||||
|
||||
pub(crate) async fn execute(args: &Run) {
|
||||
pub(crate) async fn execute(args: &Run) -> anyhow::Result<()> {
|
||||
eprintln!("Starting mixnode {}...", args.id);
|
||||
|
||||
let mut config = match Config::load_from_file(&args.id) {
|
||||
Ok(cfg) => cfg,
|
||||
Err(err) => {
|
||||
error!(
|
||||
"Failed to load config for {}. Are you sure you have run `init` before? (Error was: {})",
|
||||
args.id,
|
||||
err
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let mut config = try_load_current_config(&args.id)?;
|
||||
config = override_config(config, OverrideConfig::from(args.clone()));
|
||||
|
||||
if !version_check(&config) {
|
||||
error!("failed the local version check");
|
||||
return;
|
||||
bail!("failed the local version check")
|
||||
}
|
||||
|
||||
if special_addresses().contains(&&*config.get_listening_address().to_string()) {
|
||||
show_binding_warning(&config.get_listening_address().to_string());
|
||||
if SPECIAL_ADDRESSES.contains(&config.mixnode.listening_address) {
|
||||
show_binding_warning(&config.mixnode.listening_address.to_string());
|
||||
}
|
||||
|
||||
let mut mixnode = MixNode::new(config);
|
||||
@@ -112,5 +91,6 @@ pub(crate) async fn execute(args: &Run) {
|
||||
Select the correct version and install it to your machine. You will need to provide the following: \n ");
|
||||
mixnode.print_node_details(args.output);
|
||||
|
||||
mixnode.run().await
|
||||
mixnode.run().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use crate::commands::validate_bech32_address_or_exit;
|
||||
use crate::config::{persistence::pathfinder::MixNodePathfinder, Config};
|
||||
use crate::commands::{try_load_current_config, validate_bech32_address_or_exit};
|
||||
use crate::node::MixNode;
|
||||
use anyhow::{bail, Result};
|
||||
use clap::{ArgGroup, Args};
|
||||
use nym_bin_common::output_format::OutputFormat;
|
||||
use nym_config::NymConfig;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_types::helpers::ConsoleSigningOutput;
|
||||
use nym_validator_client::nyxd;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
#[cfg(feature = "cpucycles")]
|
||||
use tracing::error;
|
||||
|
||||
@@ -116,32 +114,22 @@ fn print_signed_contract_msg(
|
||||
println!("{}", output.format(&sign_output));
|
||||
}
|
||||
|
||||
pub(crate) fn execute(args: &Sign) {
|
||||
let config = match Config::load_from_file(&args.id) {
|
||||
Ok(cfg) => cfg,
|
||||
Err(err) => {
|
||||
error!(
|
||||
"Failed to load config for {}. Are you sure you have run `init` before? (Error was: {err})",
|
||||
args.id,
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
pub(crate) fn execute(args: &Sign) -> anyhow::Result<()> {
|
||||
let config = try_load_current_config(&args.id)?;
|
||||
|
||||
if !version_check(&config) {
|
||||
error!("Failed the local version check");
|
||||
return;
|
||||
error!("failed the local version check");
|
||||
bail!("failed the local version check")
|
||||
}
|
||||
|
||||
let signed_target = match SignedTarget::try_from(args.clone()) {
|
||||
Ok(s) => s,
|
||||
Err(err) => {
|
||||
error!("{err}");
|
||||
return;
|
||||
bail!(err);
|
||||
}
|
||||
};
|
||||
let pathfinder = MixNodePathfinder::new_from_config(&config);
|
||||
let identity_keypair = MixNode::load_identity_keys(&pathfinder);
|
||||
let identity_keypair = MixNode::load_identity_keys(&config);
|
||||
|
||||
match signed_target {
|
||||
SignedTarget::Text(text) => {
|
||||
@@ -154,4 +142,5 @@ pub(crate) fn execute(args: &Sign) {
|
||||
print_signed_contract_msg(identity_keypair.private_key(), &raw_msg, args.output)
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::{missing_string_value, Config};
|
||||
use crate::commands::try_upgrade_v1_1_21_config;
|
||||
use crate::config::Config;
|
||||
use clap::Args;
|
||||
use nym_bin_common::version_checker::Version;
|
||||
use nym_config::NymConfig;
|
||||
use std::fmt::Display;
|
||||
use std::process;
|
||||
|
||||
@@ -46,7 +46,7 @@ fn unsupported_upgrade(config_version: &Version, package_version: &Version) -> !
|
||||
}
|
||||
|
||||
fn parse_config_version(config: &Config) -> Version {
|
||||
let version = Version::parse(config.get_version()).unwrap_or_else(|err| {
|
||||
let version = Version::parse(&config.mixnode.version).unwrap_or_else(|err| {
|
||||
eprintln!("failed to parse client version! - {err}");
|
||||
process::exit(1)
|
||||
});
|
||||
@@ -89,13 +89,15 @@ fn minor_0_12_upgrade(
|
||||
|
||||
print_start_upgrade(config_version, &to_version);
|
||||
|
||||
let upgraded_config = config.with_custom_version(to_version.to_string().as_ref());
|
||||
let upgraded_config = config.with_custom_version(to_version.to_string());
|
||||
|
||||
upgraded_config.save_to_file(None).unwrap_or_else(|err| {
|
||||
eprintln!("failed to overwrite config file! - {err}");
|
||||
print_failed_upgrade(config_version, &to_version);
|
||||
process::exit(1);
|
||||
});
|
||||
upgraded_config
|
||||
.save_to_default_location()
|
||||
.unwrap_or_else(|err| {
|
||||
eprintln!("failed to overwrite config file! - {err}");
|
||||
print_failed_upgrade(config_version, &to_version);
|
||||
process::exit(1);
|
||||
});
|
||||
|
||||
print_successful_upgrade(config_version, to_version);
|
||||
|
||||
@@ -122,18 +124,21 @@ fn do_upgrade(mut config: Config, args: &Upgrade, package_version: Version) {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn execute(args: &Upgrade) {
|
||||
pub(crate) fn execute(args: &Upgrade) -> anyhow::Result<()> {
|
||||
try_upgrade_v1_1_21_config(&args.id)?;
|
||||
|
||||
let package_version = parse_package_version();
|
||||
|
||||
let existing_config = Config::load_from_file(&args.id).unwrap_or_else(|err| {
|
||||
let existing_config = Config::read_from_default_path(&args.id).unwrap_or_else(|err| {
|
||||
eprintln!("failed to load existing config file! - {err}");
|
||||
process::exit(1)
|
||||
});
|
||||
|
||||
if existing_config.get_version() == missing_string_value::<String>() {
|
||||
if existing_config.mixnode.version.is_empty() {
|
||||
eprintln!("the existing configuration file does not seem to contain version number.");
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
do_upgrade(existing_config, args, package_version)
|
||||
do_upgrade(existing_config, args, package_version);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+108
-322
@@ -1,24 +1,31 @@
|
||||
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::template::config_template;
|
||||
use nym_config::defaults::mainnet::NYM_API;
|
||||
use crate::config::persistence::paths::MixNodePaths;
|
||||
use crate::config::template::CONFIG_TEMPLATE;
|
||||
use nym_bin_common::logging::LoggingSettings;
|
||||
use nym_config::defaults::{
|
||||
DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT,
|
||||
mainnet, DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT,
|
||||
DEFAULT_VERLOC_LISTENING_PORT,
|
||||
};
|
||||
use nym_config::NymConfig;
|
||||
use nym_validator_client::nyxd;
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::path::PathBuf;
|
||||
use nym_config::helpers::inaddr_any;
|
||||
use nym_config::{
|
||||
must_get_home, read_config_from_toml_file, save_formatted_config_to_file, NymConfigTemplate,
|
||||
DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, DEFAULT_DATA_DIR, NYM_DIR,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::io;
|
||||
use std::net::IpAddr;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
use url::Url;
|
||||
|
||||
pub(crate) mod old_config_v1_1_21;
|
||||
pub mod persistence;
|
||||
mod template;
|
||||
|
||||
pub(crate) const MISSING_VALUE: &str = "MISSING VALUE";
|
||||
const DEFAULT_MIXNODES_DIR: &str = "mixnodes";
|
||||
|
||||
// 'RTT MEASUREMENT'
|
||||
const DEFAULT_PACKETS_PER_NODE: usize = 100;
|
||||
@@ -37,125 +44,84 @@ const DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF: Duration = Duration::from_milli
|
||||
const DEFAULT_INITIAL_CONNECTION_TIMEOUT: Duration = Duration::from_millis(1_500);
|
||||
const DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE: usize = 2000;
|
||||
|
||||
pub fn missing_string_value<T: From<String>>() -> T {
|
||||
MISSING_VALUE.to_string().into()
|
||||
/// Derive default path to mixnodes's config directory.
|
||||
/// It should get resolved to `$HOME/.nym/mixnodes/<id>/config`
|
||||
pub fn default_config_directory<P: AsRef<Path>>(id: P) -> PathBuf {
|
||||
must_get_home()
|
||||
.join(NYM_DIR)
|
||||
.join(DEFAULT_MIXNODES_DIR)
|
||||
.join(id)
|
||||
.join(DEFAULT_CONFIG_DIR)
|
||||
}
|
||||
|
||||
fn bind_all_address() -> IpAddr {
|
||||
"0.0.0.0".parse().unwrap()
|
||||
/// Derive default path to mixnodes's config file.
|
||||
/// It should get resolved to `$HOME/.nym/mixnodes/<id>/config/config.toml`
|
||||
pub fn default_config_filepath<P: AsRef<Path>>(id: P) -> PathBuf {
|
||||
default_config_directory(id).join(DEFAULT_CONFIG_FILENAME)
|
||||
}
|
||||
|
||||
fn default_mix_port() -> u16 {
|
||||
DEFAULT_MIX_LISTENING_PORT
|
||||
/// Derive default path to mixnodes's data directory where files, such as keys, are stored.
|
||||
/// It should get resolved to `$HOME/.nym/mixnodes/<id>/data`
|
||||
pub fn default_data_directory<P: AsRef<Path>>(id: P) -> PathBuf {
|
||||
must_get_home()
|
||||
.join(NYM_DIR)
|
||||
.join(DEFAULT_MIXNODES_DIR)
|
||||
.join(id)
|
||||
.join(DEFAULT_DATA_DIR)
|
||||
}
|
||||
|
||||
fn default_verloc_port() -> u16 {
|
||||
DEFAULT_VERLOC_LISTENING_PORT
|
||||
}
|
||||
|
||||
fn default_http_api_port() -> u16 {
|
||||
DEFAULT_HTTP_API_LISTENING_PORT
|
||||
}
|
||||
|
||||
// basically a migration helper that deserialises string representation of a maybe socket addr (like "1.1.1.1:1234")
|
||||
// into just the ipaddr (like "1.1.1.1")
|
||||
pub(super) fn de_ipaddr_from_maybe_str_socks_addr<'de, D>(
|
||||
deserializer: D,
|
||||
) -> Result<IpAddr, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
if let Ok(socket_addr) = SocketAddr::from_str(&s) {
|
||||
Ok(socket_addr.ip())
|
||||
} else {
|
||||
IpAddr::from_str(&s).map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Config {
|
||||
mixnode: MixNode,
|
||||
pub mixnode: MixNode,
|
||||
|
||||
pub storage_paths: MixNodePaths,
|
||||
|
||||
#[serde(default)]
|
||||
verloc: Verloc,
|
||||
pub verloc: Verloc,
|
||||
|
||||
#[serde(default)]
|
||||
logging: Logging,
|
||||
pub logging: LoggingSettings,
|
||||
|
||||
#[serde(default)]
|
||||
debug: Debug,
|
||||
pub debug: Debug,
|
||||
}
|
||||
|
||||
impl NymConfig for Config {
|
||||
impl NymConfigTemplate for Config {
|
||||
fn template() -> &'static str {
|
||||
config_template()
|
||||
}
|
||||
|
||||
fn default_root_directory() -> PathBuf {
|
||||
dirs::home_dir()
|
||||
.expect("Failed to evaluate $HOME value")
|
||||
.join(".nym")
|
||||
.join("mixnodes")
|
||||
}
|
||||
|
||||
fn try_default_root_directory() -> Option<PathBuf> {
|
||||
dirs::home_dir().map(|path| path.join(".nym").join("mixnodes"))
|
||||
}
|
||||
|
||||
fn root_directory(&self) -> PathBuf {
|
||||
self.mixnode.nym_root_directory.clone()
|
||||
}
|
||||
|
||||
fn config_directory(&self) -> PathBuf {
|
||||
self.mixnode
|
||||
.nym_root_directory
|
||||
.join(&self.mixnode.id)
|
||||
.join("config")
|
||||
}
|
||||
|
||||
fn data_directory(&self) -> PathBuf {
|
||||
self.mixnode
|
||||
.nym_root_directory
|
||||
.join(&self.mixnode.id)
|
||||
.join("data")
|
||||
CONFIG_TEMPLATE
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn new<S: Into<String>>(id: S) -> Self {
|
||||
Config::default().with_id(id)
|
||||
pub fn new<S: AsRef<str>>(id: S) -> Self {
|
||||
Config {
|
||||
mixnode: MixNode::new_default(id.as_ref()),
|
||||
storage_paths: MixNodePaths::new_default(id.as_ref()),
|
||||
verloc: Default::default(),
|
||||
logging: Default::default(),
|
||||
debug: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_from_toml_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
|
||||
read_config_from_toml_file(path)
|
||||
}
|
||||
|
||||
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
|
||||
Self::read_from_toml_file(default_config_filepath(id))
|
||||
}
|
||||
|
||||
pub fn default_location(&self) -> PathBuf {
|
||||
default_config_filepath(&self.mixnode.id)
|
||||
}
|
||||
|
||||
pub fn save_to_default_location(&self) -> io::Result<()> {
|
||||
let config_save_location: PathBuf = self.default_location();
|
||||
save_formatted_config_to_file(self, config_save_location)
|
||||
}
|
||||
|
||||
// builder methods
|
||||
pub fn with_id<S: Into<String>>(mut self, id: S) -> Self {
|
||||
let id = id.into();
|
||||
if self
|
||||
.mixnode
|
||||
.private_identity_key_file
|
||||
.as_os_str()
|
||||
.is_empty()
|
||||
{
|
||||
self.mixnode.private_identity_key_file =
|
||||
self::MixNode::default_private_identity_key_file(&id);
|
||||
}
|
||||
if self.mixnode.public_identity_key_file.as_os_str().is_empty() {
|
||||
self.mixnode.public_identity_key_file =
|
||||
self::MixNode::default_public_identity_key_file(&id);
|
||||
}
|
||||
|
||||
if self.mixnode.private_sphinx_key_file.as_os_str().is_empty() {
|
||||
self.mixnode.private_sphinx_key_file =
|
||||
self::MixNode::default_private_sphinx_key_file(&id);
|
||||
}
|
||||
if self.mixnode.public_sphinx_key_file.as_os_str().is_empty() {
|
||||
self.mixnode.public_sphinx_key_file =
|
||||
self::MixNode::default_public_sphinx_key_file(&id);
|
||||
}
|
||||
|
||||
self.mixnode.id = id;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_custom_nym_apis(mut self, nym_api_urls: Vec<Url>) -> Self {
|
||||
self.mixnode.nym_api_urls = nym_api_urls;
|
||||
self
|
||||
@@ -166,11 +132,6 @@ impl Config {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_announce_address<S: Into<String>>(mut self, announce_address: S) -> Self {
|
||||
self.mixnode.announce_address = announce_address.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_mix_port(mut self, port: u16) -> Self {
|
||||
self.mixnode.mix_port = port;
|
||||
self
|
||||
@@ -186,261 +147,86 @@ impl Config {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn announce_address_from_listening_address(mut self) -> Self {
|
||||
self.mixnode.announce_address = self.mixnode.listening_address.to_string();
|
||||
pub fn with_custom_version<S: Into<String>>(mut self, version: S) -> Self {
|
||||
self.mixnode.version = version.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_custom_version(mut self, version: &str) -> Self {
|
||||
self.mixnode.version = version.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_wallet_address(mut self, wallet_address: nyxd::AccountId) -> Self {
|
||||
self.mixnode.wallet_address = Some(wallet_address);
|
||||
self
|
||||
}
|
||||
|
||||
// getters
|
||||
pub fn get_id(&self) -> String {
|
||||
self.mixnode.id.clone()
|
||||
}
|
||||
|
||||
pub fn get_config_file_save_location(&self) -> PathBuf {
|
||||
self.config_directory().join(Self::config_file_name())
|
||||
}
|
||||
|
||||
pub fn get_private_identity_key_file(&self) -> PathBuf {
|
||||
self.mixnode.private_identity_key_file.clone()
|
||||
}
|
||||
|
||||
pub fn get_public_identity_key_file(&self) -> PathBuf {
|
||||
self.mixnode.public_identity_key_file.clone()
|
||||
}
|
||||
|
||||
pub fn get_private_sphinx_key_file(&self) -> PathBuf {
|
||||
self.mixnode.private_sphinx_key_file.clone()
|
||||
}
|
||||
|
||||
pub fn get_public_sphinx_key_file(&self) -> PathBuf {
|
||||
self.mixnode.public_sphinx_key_file.clone()
|
||||
}
|
||||
|
||||
pub fn get_nym_api_endpoints(&self) -> Vec<Url> {
|
||||
self.mixnode.nym_api_urls.clone()
|
||||
}
|
||||
|
||||
pub fn get_node_stats_logging_delay(&self) -> Duration {
|
||||
self.debug.node_stats_logging_delay
|
||||
}
|
||||
|
||||
pub fn get_node_stats_updating_delay(&self) -> Duration {
|
||||
self.debug.node_stats_updating_delay
|
||||
}
|
||||
|
||||
pub fn get_listening_address(&self) -> IpAddr {
|
||||
self.mixnode.listening_address
|
||||
}
|
||||
|
||||
pub fn get_announce_address(&self) -> String {
|
||||
self.mixnode.announce_address.clone()
|
||||
}
|
||||
|
||||
pub fn get_mix_port(&self) -> u16 {
|
||||
self.mixnode.mix_port
|
||||
}
|
||||
|
||||
pub fn get_verloc_port(&self) -> u16 {
|
||||
self.mixnode.verloc_port
|
||||
}
|
||||
|
||||
pub fn get_http_api_port(&self) -> u16 {
|
||||
self.mixnode.http_api_port
|
||||
}
|
||||
|
||||
pub fn get_packet_forwarding_initial_backoff(&self) -> Duration {
|
||||
self.debug.packet_forwarding_initial_backoff
|
||||
}
|
||||
|
||||
pub fn get_packet_forwarding_maximum_backoff(&self) -> Duration {
|
||||
self.debug.packet_forwarding_maximum_backoff
|
||||
}
|
||||
|
||||
pub fn get_initial_connection_timeout(&self) -> Duration {
|
||||
self.debug.initial_connection_timeout
|
||||
}
|
||||
|
||||
pub fn get_maximum_connection_buffer_size(&self) -> usize {
|
||||
self.debug.maximum_connection_buffer_size
|
||||
}
|
||||
|
||||
pub fn get_use_legacy_sphinx_framing(&self) -> bool {
|
||||
self.debug.use_legacy_framed_packet_version
|
||||
}
|
||||
|
||||
pub fn get_version(&self) -> &str {
|
||||
&self.mixnode.version
|
||||
}
|
||||
|
||||
pub fn get_measurement_packets_per_node(&self) -> usize {
|
||||
self.verloc.packets_per_node
|
||||
}
|
||||
|
||||
pub fn get_measurement_packet_timeout(&self) -> Duration {
|
||||
self.verloc.packet_timeout
|
||||
}
|
||||
|
||||
pub fn get_measurement_connection_timeout(&self) -> Duration {
|
||||
self.verloc.connection_timeout
|
||||
}
|
||||
|
||||
pub fn get_measurement_delay_between_packets(&self) -> Duration {
|
||||
self.verloc.delay_between_packets
|
||||
}
|
||||
|
||||
pub fn get_measurement_tested_nodes_batch_size(&self) -> usize {
|
||||
self.verloc.tested_nodes_batch_size
|
||||
}
|
||||
|
||||
pub fn get_measurement_testing_interval(&self) -> Duration {
|
||||
self.verloc.testing_interval
|
||||
}
|
||||
|
||||
pub fn get_measurement_retry_timeout(&self) -> Duration {
|
||||
self.verloc.retry_timeout
|
||||
}
|
||||
|
||||
pub fn get_wallet_address(&self) -> Option<nyxd::AccountId> {
|
||||
self.mixnode.wallet_address.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
struct MixNode {
|
||||
pub struct MixNode {
|
||||
/// Version of the mixnode for which this configuration was created.
|
||||
#[serde(default = "missing_string_value")]
|
||||
version: String,
|
||||
pub version: String,
|
||||
|
||||
/// ID specifies the human readable ID of this particular mixnode.
|
||||
id: String,
|
||||
pub id: String,
|
||||
|
||||
/// Address to which this mixnode will bind to and will be listening for packets.
|
||||
#[serde(deserialize_with = "de_ipaddr_from_maybe_str_socks_addr")]
|
||||
listening_address: IpAddr,
|
||||
|
||||
/// Optional address announced to the validator for the clients to connect to.
|
||||
/// It is useful, say, in NAT scenarios or wanting to more easily update actual IP address
|
||||
/// later on by using name resolvable with a DNS query, such as `nymtech.net`.
|
||||
announce_address: String,
|
||||
pub listening_address: IpAddr,
|
||||
|
||||
/// Port used for listening for all mixnet traffic.
|
||||
/// (default: 1789)
|
||||
#[serde(default = "default_mix_port")]
|
||||
mix_port: u16,
|
||||
pub mix_port: u16,
|
||||
|
||||
/// Port used for listening for verloc traffic.
|
||||
/// (default: 1790)
|
||||
#[serde(default = "default_verloc_port")]
|
||||
verloc_port: u16,
|
||||
pub verloc_port: u16,
|
||||
|
||||
/// Port used for listening for http requests.
|
||||
/// (default: 8000)
|
||||
#[serde(default = "default_http_api_port")]
|
||||
http_api_port: u16,
|
||||
|
||||
/// Path to file containing private identity key.
|
||||
#[serde(default = "missing_string_value")]
|
||||
private_identity_key_file: PathBuf,
|
||||
|
||||
/// Path to file containing public identity key.
|
||||
#[serde(default = "missing_string_value")]
|
||||
public_identity_key_file: PathBuf,
|
||||
|
||||
/// Path to file containing private sphinx key.
|
||||
private_sphinx_key_file: PathBuf,
|
||||
|
||||
/// Path to file containing public sphinx key.
|
||||
public_sphinx_key_file: PathBuf,
|
||||
pub http_api_port: u16,
|
||||
|
||||
/// Addresses to nym APIs from which the node gets the view of the network.
|
||||
nym_api_urls: Vec<Url>,
|
||||
|
||||
/// nym_home_directory specifies absolute path to the home nym MixNodes directory.
|
||||
/// It is expected to use default value and hence .toml file should not redefine this field.
|
||||
nym_root_directory: PathBuf,
|
||||
|
||||
/// The Cosmos wallet address that will control this mixnode
|
||||
// the only reason this is an Option is because of the lack of existence of a sane default value
|
||||
wallet_address: Option<nyxd::AccountId>,
|
||||
pub nym_api_urls: Vec<Url>,
|
||||
}
|
||||
|
||||
impl MixNode {
|
||||
fn default_private_identity_key_file(id: &str) -> PathBuf {
|
||||
Config::default_data_directory(id).join("private_identity.pem")
|
||||
}
|
||||
|
||||
fn default_public_identity_key_file(id: &str) -> PathBuf {
|
||||
Config::default_data_directory(id).join("public_identity.pem")
|
||||
}
|
||||
|
||||
fn default_private_sphinx_key_file(id: &str) -> PathBuf {
|
||||
Config::default_data_directory(id).join("private_sphinx.pem")
|
||||
}
|
||||
|
||||
fn default_public_sphinx_key_file(id: &str) -> PathBuf {
|
||||
Config::default_data_directory(id).join("public_sphinx.pem")
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MixNode {
|
||||
fn default() -> Self {
|
||||
pub fn new_default<S: Into<String>>(id: S) -> Self {
|
||||
MixNode {
|
||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||
id: "".to_string(),
|
||||
listening_address: bind_all_address(),
|
||||
announce_address: "127.0.0.1".to_string(),
|
||||
id: id.into(),
|
||||
listening_address: inaddr_any(),
|
||||
mix_port: DEFAULT_MIX_LISTENING_PORT,
|
||||
verloc_port: DEFAULT_VERLOC_LISTENING_PORT,
|
||||
http_api_port: DEFAULT_HTTP_API_LISTENING_PORT,
|
||||
private_identity_key_file: Default::default(),
|
||||
public_identity_key_file: Default::default(),
|
||||
private_sphinx_key_file: Default::default(),
|
||||
public_sphinx_key_file: Default::default(),
|
||||
nym_api_urls: vec![Url::from_str(NYM_API).expect("Invalid default API URL")],
|
||||
nym_root_directory: Config::default_root_directory(),
|
||||
wallet_address: None,
|
||||
nym_api_urls: vec![Url::from_str(mainnet::NYM_API).expect("Invalid default API URL")],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct Logging {}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct Verloc {
|
||||
pub struct Verloc {
|
||||
/// Specifies number of echo packets sent to each node during a measurement run.
|
||||
packets_per_node: usize,
|
||||
pub packets_per_node: usize,
|
||||
|
||||
/// Specifies maximum amount of time to wait for the connection to get established.
|
||||
connection_timeout: Duration,
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub connection_timeout: Duration,
|
||||
|
||||
/// Specifies maximum amount of time to wait for the reply packet to arrive before abandoning the test.
|
||||
packet_timeout: Duration,
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub packet_timeout: Duration,
|
||||
|
||||
/// Specifies delay between subsequent test packets being sent (after receiving a reply).
|
||||
delay_between_packets: Duration,
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub delay_between_packets: Duration,
|
||||
|
||||
/// Specifies number of nodes being tested at once.
|
||||
tested_nodes_batch_size: usize,
|
||||
pub tested_nodes_batch_size: usize,
|
||||
|
||||
/// Specifies delay between subsequent test runs.
|
||||
testing_interval: Duration,
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub testing_interval: Duration,
|
||||
|
||||
/// Specifies delay between attempting to run the measurement again if the previous run failed
|
||||
/// due to being unable to get the list of nodes.
|
||||
retry_timeout: Duration,
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub retry_timeout: Duration,
|
||||
}
|
||||
|
||||
impl Default for Verloc {
|
||||
@@ -459,37 +245,37 @@ impl Default for Verloc {
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default)]
|
||||
struct Debug {
|
||||
pub struct Debug {
|
||||
/// Delay between each subsequent node statistics being logged to the console
|
||||
#[serde(with = "humantime_serde")]
|
||||
node_stats_logging_delay: Duration,
|
||||
pub node_stats_logging_delay: Duration,
|
||||
|
||||
/// Delay between each subsequent node statistics being updated
|
||||
#[serde(with = "humantime_serde")]
|
||||
node_stats_updating_delay: Duration,
|
||||
pub node_stats_updating_delay: Duration,
|
||||
|
||||
/// Initial value of an exponential backoff to reconnect to dropped TCP connection when
|
||||
/// forwarding sphinx packets.
|
||||
#[serde(with = "humantime_serde")]
|
||||
packet_forwarding_initial_backoff: Duration,
|
||||
pub packet_forwarding_initial_backoff: Duration,
|
||||
|
||||
/// Maximum value of an exponential backoff to reconnect to dropped TCP connection when
|
||||
/// forwarding sphinx packets.
|
||||
#[serde(with = "humantime_serde")]
|
||||
packet_forwarding_maximum_backoff: Duration,
|
||||
pub packet_forwarding_maximum_backoff: Duration,
|
||||
|
||||
/// Timeout for establishing initial connection when trying to forward a sphinx packet.
|
||||
#[serde(with = "humantime_serde")]
|
||||
initial_connection_timeout: Duration,
|
||||
pub initial_connection_timeout: Duration,
|
||||
|
||||
/// Maximum number of packets that can be stored waiting to get sent to a particular connection.
|
||||
maximum_connection_buffer_size: usize,
|
||||
pub maximum_connection_buffer_size: usize,
|
||||
|
||||
/// Specifies whether the mixnode should be using the legacy framing for the sphinx packets.
|
||||
// it's set to true by default. The reason for that decision is to preserve compatibility with the
|
||||
// existing nodes whilst everyone else is upgrading and getting the code for handling the new field.
|
||||
// It shall be disabled in the subsequent releases.
|
||||
use_legacy_framed_packet_version: bool,
|
||||
pub use_legacy_framed_packet_version: bool,
|
||||
}
|
||||
|
||||
impl Default for Debug {
|
||||
|
||||
@@ -0,0 +1,249 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::persistence::paths::{KeysPaths, MixNodePaths};
|
||||
use crate::config::{Config, Debug, MixNode, Verloc};
|
||||
use nym_bin_common::logging::LoggingSettings;
|
||||
use nym_config::legacy_helpers::nym_config::MigrationNymConfig;
|
||||
use nym_validator_client::nyxd;
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
use url::Url;
|
||||
|
||||
const DEFAULT_MIX_LISTENING_PORT: u16 = 1789;
|
||||
const DEFAULT_VERLOC_LISTENING_PORT: u16 = 1790;
|
||||
const DEFAULT_HTTP_API_LISTENING_PORT: u16 = 8000;
|
||||
const NYM_API: &str = "https://validator.nymtech.net/api/";
|
||||
const DESCRIPTION_FILE: &str = "description.toml";
|
||||
|
||||
// 'RTT MEASUREMENT'
|
||||
const DEFAULT_PACKETS_PER_NODE: usize = 100;
|
||||
const DEFAULT_CONNECTION_TIMEOUT: Duration = Duration::from_millis(5000);
|
||||
const DEFAULT_PACKET_TIMEOUT: Duration = Duration::from_millis(1500);
|
||||
const DEFAULT_DELAY_BETWEEN_PACKETS: Duration = Duration::from_millis(50);
|
||||
const DEFAULT_BATCH_SIZE: usize = 50;
|
||||
const DEFAULT_TESTING_INTERVAL: Duration = Duration::from_secs(60 * 60 * 12);
|
||||
const DEFAULT_RETRY_TIMEOUT: Duration = Duration::from_secs(60 * 30);
|
||||
|
||||
// 'DEBUG'
|
||||
const DEFAULT_NODE_STATS_LOGGING_DELAY: Duration = Duration::from_millis(60_000);
|
||||
const DEFAULT_NODE_STATS_UPDATING_DELAY: Duration = Duration::from_millis(30_000);
|
||||
const DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF: Duration = Duration::from_millis(10_000);
|
||||
const DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF: Duration = Duration::from_millis(300_000);
|
||||
const DEFAULT_INITIAL_CONNECTION_TIMEOUT: Duration = Duration::from_millis(1_500);
|
||||
const DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE: usize = 2000;
|
||||
|
||||
pub(super) fn de_ipaddr_from_maybe_str_socks_addr<'de, D>(
|
||||
deserializer: D,
|
||||
) -> Result<IpAddr, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
if let Ok(socket_addr) = SocketAddr::from_str(&s) {
|
||||
Ok(socket_addr.ip())
|
||||
} else {
|
||||
IpAddr::from_str(&s).map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
fn bind_all_address() -> IpAddr {
|
||||
"0.0.0.0".parse().unwrap()
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ConfigV1_1_21 {
|
||||
mixnode: MixNodeV1_1_21,
|
||||
|
||||
#[serde(default)]
|
||||
verloc: VerlocV1_1_21,
|
||||
#[serde(default)]
|
||||
logging: LoggingV1_1_21,
|
||||
#[serde(default)]
|
||||
debug: DebugV1_1_21,
|
||||
}
|
||||
|
||||
impl From<ConfigV1_1_21> for Config {
|
||||
fn from(value: ConfigV1_1_21) -> Self {
|
||||
let node_description =
|
||||
ConfigV1_1_21::default_config_directory(&value.mixnode.id).join(DESCRIPTION_FILE);
|
||||
|
||||
Config {
|
||||
mixnode: MixNode {
|
||||
version: value.mixnode.version,
|
||||
id: value.mixnode.id,
|
||||
listening_address: value.mixnode.listening_address,
|
||||
mix_port: value.mixnode.mix_port,
|
||||
verloc_port: value.mixnode.verloc_port,
|
||||
http_api_port: value.mixnode.http_api_port,
|
||||
nym_api_urls: value.mixnode.nym_api_urls,
|
||||
},
|
||||
storage_paths: MixNodePaths {
|
||||
keys: KeysPaths {
|
||||
private_identity_key_file: value.mixnode.private_identity_key_file,
|
||||
public_identity_key_file: value.mixnode.public_identity_key_file,
|
||||
private_sphinx_key_file: value.mixnode.private_sphinx_key_file,
|
||||
public_sphinx_key_file: value.mixnode.public_sphinx_key_file,
|
||||
},
|
||||
node_description,
|
||||
},
|
||||
verloc: value.verloc.into(),
|
||||
logging: value.logging.into(),
|
||||
debug: value.debug.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MigrationNymConfig for ConfigV1_1_21 {
|
||||
fn default_root_directory() -> PathBuf {
|
||||
dirs::home_dir()
|
||||
.expect("Failed to evaluate $HOME value")
|
||||
.join(".nym")
|
||||
.join("mixnodes")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
struct MixNodeV1_1_21 {
|
||||
version: String,
|
||||
id: String,
|
||||
#[serde(deserialize_with = "de_ipaddr_from_maybe_str_socks_addr")]
|
||||
listening_address: IpAddr,
|
||||
announce_address: String,
|
||||
mix_port: u16,
|
||||
verloc_port: u16,
|
||||
http_api_port: u16,
|
||||
private_identity_key_file: PathBuf,
|
||||
public_identity_key_file: PathBuf,
|
||||
private_sphinx_key_file: PathBuf,
|
||||
public_sphinx_key_file: PathBuf,
|
||||
nym_api_urls: Vec<Url>,
|
||||
nym_root_directory: PathBuf,
|
||||
wallet_address: Option<nyxd::AccountId>,
|
||||
}
|
||||
|
||||
impl Default for MixNodeV1_1_21 {
|
||||
fn default() -> Self {
|
||||
MixNodeV1_1_21 {
|
||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||
id: "".to_string(),
|
||||
listening_address: bind_all_address(),
|
||||
announce_address: "127.0.0.1".to_string(),
|
||||
mix_port: DEFAULT_MIX_LISTENING_PORT,
|
||||
verloc_port: DEFAULT_VERLOC_LISTENING_PORT,
|
||||
http_api_port: DEFAULT_HTTP_API_LISTENING_PORT,
|
||||
private_identity_key_file: Default::default(),
|
||||
public_identity_key_file: Default::default(),
|
||||
private_sphinx_key_file: Default::default(),
|
||||
public_sphinx_key_file: Default::default(),
|
||||
nym_api_urls: vec![Url::from_str(NYM_API).expect("Invalid default API URL")],
|
||||
nym_root_directory: ConfigV1_1_21::default_root_directory(),
|
||||
wallet_address: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct LoggingV1_1_21 {}
|
||||
|
||||
impl From<LoggingV1_1_21> for LoggingSettings {
|
||||
fn from(_value: LoggingV1_1_21) -> Self {
|
||||
LoggingSettings {}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct VerlocV1_1_21 {
|
||||
packets_per_node: usize,
|
||||
connection_timeout: Duration,
|
||||
packet_timeout: Duration,
|
||||
delay_between_packets: Duration,
|
||||
tested_nodes_batch_size: usize,
|
||||
testing_interval: Duration,
|
||||
retry_timeout: Duration,
|
||||
}
|
||||
|
||||
impl From<VerlocV1_1_21> for Verloc {
|
||||
fn from(value: VerlocV1_1_21) -> Self {
|
||||
Verloc {
|
||||
packets_per_node: value.packets_per_node,
|
||||
connection_timeout: value.connection_timeout,
|
||||
packet_timeout: value.packet_timeout,
|
||||
delay_between_packets: value.delay_between_packets,
|
||||
tested_nodes_batch_size: value.tested_nodes_batch_size,
|
||||
testing_interval: value.testing_interval,
|
||||
retry_timeout: value.retry_timeout,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for VerlocV1_1_21 {
|
||||
fn default() -> Self {
|
||||
VerlocV1_1_21 {
|
||||
packets_per_node: DEFAULT_PACKETS_PER_NODE,
|
||||
connection_timeout: DEFAULT_CONNECTION_TIMEOUT,
|
||||
packet_timeout: DEFAULT_PACKET_TIMEOUT,
|
||||
delay_between_packets: DEFAULT_DELAY_BETWEEN_PACKETS,
|
||||
tested_nodes_batch_size: DEFAULT_BATCH_SIZE,
|
||||
testing_interval: DEFAULT_TESTING_INTERVAL,
|
||||
retry_timeout: DEFAULT_RETRY_TIMEOUT,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default)]
|
||||
struct DebugV1_1_21 {
|
||||
#[serde(with = "humantime_serde")]
|
||||
node_stats_logging_delay: Duration,
|
||||
|
||||
#[serde(with = "humantime_serde")]
|
||||
node_stats_updating_delay: Duration,
|
||||
|
||||
#[serde(with = "humantime_serde")]
|
||||
packet_forwarding_initial_backoff: Duration,
|
||||
|
||||
#[serde(with = "humantime_serde")]
|
||||
packet_forwarding_maximum_backoff: Duration,
|
||||
|
||||
#[serde(with = "humantime_serde")]
|
||||
initial_connection_timeout: Duration,
|
||||
|
||||
maximum_connection_buffer_size: usize,
|
||||
|
||||
use_legacy_framed_packet_version: bool,
|
||||
}
|
||||
|
||||
impl From<DebugV1_1_21> for Debug {
|
||||
fn from(value: DebugV1_1_21) -> Self {
|
||||
Debug {
|
||||
node_stats_logging_delay: value.node_stats_logging_delay,
|
||||
node_stats_updating_delay: value.node_stats_updating_delay,
|
||||
packet_forwarding_initial_backoff: value.packet_forwarding_initial_backoff,
|
||||
packet_forwarding_maximum_backoff: value.packet_forwarding_maximum_backoff,
|
||||
initial_connection_timeout: value.initial_connection_timeout,
|
||||
maximum_connection_buffer_size: value.maximum_connection_buffer_size,
|
||||
use_legacy_framed_packet_version: value.use_legacy_framed_packet_version,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DebugV1_1_21 {
|
||||
fn default() -> Self {
|
||||
DebugV1_1_21 {
|
||||
node_stats_logging_delay: DEFAULT_NODE_STATS_LOGGING_DELAY,
|
||||
node_stats_updating_delay: DEFAULT_NODE_STATS_UPDATING_DELAY,
|
||||
packet_forwarding_initial_backoff: DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF,
|
||||
packet_forwarding_maximum_backoff: DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF,
|
||||
initial_connection_timeout: DEFAULT_INITIAL_CONNECTION_TIMEOUT,
|
||||
maximum_connection_buffer_size: DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE,
|
||||
use_legacy_framed_packet_version: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod pathfinder;
|
||||
pub mod paths;
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::Config;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MixNodePathfinder {
|
||||
identity_private_key: PathBuf,
|
||||
identity_public_key: PathBuf,
|
||||
private_sphinx_key: PathBuf,
|
||||
public_sphinx_key: PathBuf,
|
||||
}
|
||||
|
||||
impl MixNodePathfinder {
|
||||
pub fn new_from_config(config: &Config) -> Self {
|
||||
MixNodePathfinder {
|
||||
identity_private_key: config.get_private_identity_key_file(),
|
||||
identity_public_key: config.get_public_identity_key_file(),
|
||||
private_sphinx_key: config.get_private_sphinx_key_file(),
|
||||
public_sphinx_key: config.get_public_sphinx_key_file(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn private_identity_key(&self) -> &Path {
|
||||
&self.identity_private_key
|
||||
}
|
||||
|
||||
pub fn public_identity_key(&self) -> &Path {
|
||||
&self.identity_public_key
|
||||
}
|
||||
|
||||
pub fn private_encryption_key(&self) -> &Path {
|
||||
&self.private_sphinx_key
|
||||
}
|
||||
|
||||
pub fn public_encryption_key(&self) -> &Path {
|
||||
&self.public_sphinx_key
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::{default_config_directory, default_data_directory};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
pub const DEFAULT_PRIVATE_IDENTITY_KEY_FILENAME: &str = "private_identity.pem";
|
||||
pub const DEFAULT_PUBLIC_IDENTITY_KEY_FILENAME: &str = "public_identity.pem";
|
||||
pub const DEFAULT_PRIVATE_SPHINX_KEY_FILENAME: &str = "private_sphinx.pem";
|
||||
pub const DEFAULT_PUBLIC_SPHINX_KEY_FILENAME: &str = "public_sphinx.pem";
|
||||
|
||||
pub const DEFAULT_DESCRIPTION_FILENAME: &str = "description.toml";
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct MixNodePaths {
|
||||
pub keys: KeysPaths,
|
||||
|
||||
pub node_description: PathBuf,
|
||||
}
|
||||
|
||||
impl MixNodePaths {
|
||||
pub fn new_default<P: AsRef<Path>>(id: P) -> Self {
|
||||
MixNodePaths {
|
||||
keys: KeysPaths::new_default(id.as_ref()),
|
||||
node_description: default_config_directory(id).join(DEFAULT_DESCRIPTION_FILENAME),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn private_identity_key(&self) -> &Path {
|
||||
self.keys.private_identity_key()
|
||||
}
|
||||
|
||||
pub fn public_identity_key(&self) -> &Path {
|
||||
self.keys.public_identity_key()
|
||||
}
|
||||
|
||||
pub fn private_encryption_key(&self) -> &Path {
|
||||
self.keys.private_encryption_key()
|
||||
}
|
||||
|
||||
pub fn public_encryption_key(&self) -> &Path {
|
||||
self.keys.public_encryption_key()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
|
||||
pub struct KeysPaths {
|
||||
/// Path to file containing private identity key.
|
||||
pub private_identity_key_file: PathBuf,
|
||||
|
||||
/// Path to file containing public identity key.
|
||||
pub public_identity_key_file: PathBuf,
|
||||
|
||||
/// Path to file containing private sphinx key.
|
||||
pub private_sphinx_key_file: PathBuf,
|
||||
|
||||
/// Path to file containing public sphinx key.
|
||||
pub public_sphinx_key_file: PathBuf,
|
||||
}
|
||||
|
||||
impl KeysPaths {
|
||||
pub fn new_default<P: AsRef<Path>>(id: P) -> Self {
|
||||
let data_dir = default_data_directory(id);
|
||||
|
||||
KeysPaths {
|
||||
private_identity_key_file: data_dir.join(DEFAULT_PRIVATE_IDENTITY_KEY_FILENAME),
|
||||
public_identity_key_file: data_dir.join(DEFAULT_PUBLIC_IDENTITY_KEY_FILENAME),
|
||||
private_sphinx_key_file: data_dir.join(DEFAULT_PRIVATE_SPHINX_KEY_FILENAME),
|
||||
public_sphinx_key_file: data_dir.join(DEFAULT_PUBLIC_SPHINX_KEY_FILENAME),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn private_identity_key(&self) -> &Path {
|
||||
&self.private_identity_key_file
|
||||
}
|
||||
|
||||
pub fn public_identity_key(&self) -> &Path {
|
||||
&self.public_identity_key_file
|
||||
}
|
||||
|
||||
pub fn private_encryption_key(&self) -> &Path {
|
||||
&self.private_sphinx_key_file
|
||||
}
|
||||
|
||||
pub fn public_encryption_key(&self) -> &Path {
|
||||
&self.public_sphinx_key_file
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,11 @@
|
||||
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub(crate) fn config_template() -> &'static str {
|
||||
// While using normal toml marshalling would have been way simpler with less overhead,
|
||||
// I think it's useful to have comments attached to the saved config file to explain behaviour of
|
||||
// particular fields.
|
||||
// Note: any changes to the template must be reflected in the appropriate structs in verloc.
|
||||
r#"
|
||||
// While using normal toml marshalling would have been way simpler with less overhead,
|
||||
// I think it's useful to have comments attached to the saved config file to explain behaviour of
|
||||
// particular fields.
|
||||
// Note: any changes to the template must be reflected in the appropriate structs.
|
||||
pub(crate) const CONFIG_TEMPLATE: &str = r#"
|
||||
# This is a TOML config file.
|
||||
# For more information, see https://github.com/toml-lang/toml
|
||||
|
||||
@@ -22,25 +21,6 @@ id = '{{ mixnode.id }}'
|
||||
# Socket address to which this mixnode will bind to and will be listening for packets.
|
||||
listening_address = '{{ mixnode.listening_address }}'
|
||||
|
||||
# Path to file containing private identity key.
|
||||
private_identity_key_file = '{{ mixnode.private_identity_key_file }}'
|
||||
|
||||
# Path to file containing public identity key.
|
||||
public_identity_key_file = '{{ mixnode.public_identity_key_file }}'
|
||||
|
||||
# Path to file containing private identity key.
|
||||
private_sphinx_key_file = '{{ mixnode.private_sphinx_key_file }}'
|
||||
|
||||
# Path to file containing public sphinx key.
|
||||
public_sphinx_key_file = '{{ mixnode.public_sphinx_key_file }}'
|
||||
|
||||
##### additional mixnode config options #####
|
||||
|
||||
# Optional address announced to the directory server for the clients to connect to.
|
||||
# It is useful, say, in NAT scenarios or wanting to more easily update actual IP address
|
||||
# later on by using name resolvable with a DNS query, such as `nymtech.net`.
|
||||
announce_address = '{{ mixnode.announce_address }}'
|
||||
|
||||
# Port used for listening for all mixnet traffic.
|
||||
# (default: 1789)
|
||||
mix_port = {{ mixnode.mix_port }}
|
||||
@@ -60,14 +40,22 @@ nym_api_urls = [
|
||||
{{/each}}
|
||||
]
|
||||
|
||||
# Nym wallet address on the blockchain that should control this mixnode
|
||||
wallet_address = '{{ mixnode.wallet_address }}'
|
||||
[storage_paths]
|
||||
|
||||
##### advanced configuration options #####
|
||||
# Path to file containing private identity key.
|
||||
keys.private_identity_key_file = '{{ storage_paths.keys.private_identity_key_file }}'
|
||||
|
||||
# Absolute path to the home Nym Clients directory.
|
||||
nym_root_directory = '{{ mixnode.nym_root_directory }}'
|
||||
# Path to file containing public identity key.
|
||||
keys.public_identity_key_file = '{{ storage_paths.keys.public_identity_key_file }}'
|
||||
|
||||
# Path to file containing private identity key.
|
||||
keys.private_sphinx_key_file = '{{ storage_paths.keys.private_sphinx_key_file }}'
|
||||
|
||||
# Path to file containing public sphinx key.
|
||||
keys.public_sphinx_key_file = '{{ storage_paths.keys.public_sphinx_key_file }}'
|
||||
|
||||
# Path to file containing description of this node.
|
||||
node_description = '{{ storage_paths.node_description }}'
|
||||
|
||||
##### logging configuration options #####
|
||||
|
||||
@@ -75,5 +63,4 @@ nym_root_directory = '{{ mixnode.nym_root_directory }}'
|
||||
|
||||
# TODO
|
||||
|
||||
"#
|
||||
}
|
||||
"#;
|
||||
|
||||
+4
-2
@@ -48,7 +48,7 @@ fn test_function() {
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "cpucycles")] {
|
||||
setup_tracing!("mixnode");
|
||||
@@ -63,12 +63,14 @@ async fn main() {
|
||||
|
||||
let args = Cli::parse();
|
||||
setup_env(args.config_env_file.as_ref());
|
||||
commands::execute(args).await;
|
||||
commands::execute(args).await?;
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "cpucycles")] {
|
||||
opentelemetry::global::shutdown_tracer_provider();
|
||||
}}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
+51
-65
@@ -1,14 +1,13 @@
|
||||
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::persistence::pathfinder::MixNodePathfinder;
|
||||
use crate::config::Config;
|
||||
use crate::node::http::{
|
||||
description::description,
|
||||
hardware::hardware,
|
||||
not_found,
|
||||
stats::stats,
|
||||
verloc::{verloc as verlocRoute, VerlocState},
|
||||
verloc::{verloc as verloc_route, VerlocState},
|
||||
};
|
||||
use crate::node::listener::connection_handler::packet_processing::PacketProcessor;
|
||||
use crate::node::listener::connection_handler::ConnectionHandler;
|
||||
@@ -18,7 +17,6 @@ use crate::node::node_statistics::SharedNodeStats;
|
||||
use crate::node::packet_delayforwarder::{DelayForwarder, PacketDelayForwardSender};
|
||||
use nym_bin_common::output_format::OutputFormat;
|
||||
use nym_bin_common::version_checker::parse_version;
|
||||
use nym_config::NymConfig;
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
use nym_mixnode_common::verloc::{self, AtomicVerlocResult, VerlocMeasurer};
|
||||
use nym_task::{TaskClient, TaskManager};
|
||||
@@ -27,6 +25,7 @@ use rand::thread_rng;
|
||||
use std::net::SocketAddr;
|
||||
use std::process;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(feature = "cpucycles")]
|
||||
use tracing::{error, info, warn};
|
||||
|
||||
@@ -46,38 +45,35 @@ pub struct MixNode {
|
||||
|
||||
impl MixNode {
|
||||
pub fn new(config: Config) -> Self {
|
||||
let pathfinder = MixNodePathfinder::new_from_config(&config);
|
||||
|
||||
MixNode {
|
||||
descriptor: Self::load_node_description(&config),
|
||||
identity_keypair: Arc::new(Self::load_identity_keys(&pathfinder)),
|
||||
sphinx_keypair: Arc::new(Self::load_sphinx_keys(&pathfinder)),
|
||||
identity_keypair: Arc::new(Self::load_identity_keys(&config)),
|
||||
sphinx_keypair: Arc::new(Self::load_sphinx_keys(&config)),
|
||||
config,
|
||||
}
|
||||
}
|
||||
|
||||
fn load_node_description(config: &Config) -> NodeDescription {
|
||||
NodeDescription::load_from_file(Config::default_config_directory(&config.get_id()))
|
||||
.unwrap_or_default()
|
||||
NodeDescription::load_from_file(&config.storage_paths.node_description).unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Loads identity keys stored on disk
|
||||
pub(crate) fn load_identity_keys(pathfinder: &MixNodePathfinder) -> identity::KeyPair {
|
||||
pub(crate) fn load_identity_keys(config: &Config) -> identity::KeyPair {
|
||||
let identity_keypair: identity::KeyPair =
|
||||
nym_pemstore::load_keypair(&nym_pemstore::KeyPairPath::new(
|
||||
pathfinder.private_identity_key().to_owned(),
|
||||
pathfinder.public_identity_key().to_owned(),
|
||||
config.storage_paths.keys.private_identity_key(),
|
||||
config.storage_paths.keys.public_identity_key(),
|
||||
))
|
||||
.expect("Failed to read stored identity key files");
|
||||
identity_keypair
|
||||
}
|
||||
|
||||
/// Loads Sphinx keys stored on disk
|
||||
fn load_sphinx_keys(pathfinder: &MixNodePathfinder) -> encryption::KeyPair {
|
||||
fn load_sphinx_keys(config: &Config) -> encryption::KeyPair {
|
||||
let sphinx_keypair: encryption::KeyPair =
|
||||
nym_pemstore::load_keypair(&nym_pemstore::KeyPairPath::new(
|
||||
pathfinder.private_encryption_key().to_owned(),
|
||||
pathfinder.public_encryption_key().to_owned(),
|
||||
config.storage_paths.keys.private_encryption_key(),
|
||||
config.storage_paths.keys.public_encryption_key(),
|
||||
))
|
||||
.expect("Failed to read stored sphinx key files");
|
||||
sphinx_keypair
|
||||
@@ -88,13 +84,11 @@ impl MixNode {
|
||||
let node_details = nym_types::mixnode::MixnodeNodeDetailsResponse {
|
||||
identity_key: self.identity_keypair.public_key().to_base58_string(),
|
||||
sphinx_key: self.sphinx_keypair.public_key().to_base58_string(),
|
||||
announce_address: self.config.get_announce_address(),
|
||||
bind_address: self.config.get_listening_address().to_string(),
|
||||
version: self.config.get_version().to_string(),
|
||||
mix_port: self.config.get_mix_port(),
|
||||
http_api_port: self.config.get_http_api_port(),
|
||||
verloc_port: self.config.get_verloc_port(),
|
||||
wallet_address: self.config.get_wallet_address().map(|x| x.to_string()),
|
||||
bind_address: self.config.mixnode.listening_address,
|
||||
version: self.config.mixnode.version.clone(),
|
||||
mix_port: self.config.mixnode.mix_port,
|
||||
http_api_port: self.config.mixnode.http_api_port,
|
||||
verloc_port: self.config.mixnode.verloc_port,
|
||||
};
|
||||
|
||||
println!("{}", output.format(&node_details));
|
||||
@@ -105,13 +99,16 @@ impl MixNode {
|
||||
atomic_verloc_result: AtomicVerlocResult,
|
||||
node_stats_pointer: SharedNodeStats,
|
||||
) {
|
||||
info!("Starting HTTP API on http://localhost:8000");
|
||||
info!(
|
||||
"Starting HTTP API on http://{}:{}",
|
||||
self.config.mixnode.listening_address, self.config.mixnode.http_api_port
|
||||
);
|
||||
|
||||
let mut config = rocket::config::Config::release_default();
|
||||
|
||||
// bind to the same address as we are using for mixnodes
|
||||
config.address = self.config.get_listening_address();
|
||||
config.port = self.config.get_http_api_port();
|
||||
config.address = self.config.mixnode.listening_address;
|
||||
config.port = self.config.mixnode.http_api_port;
|
||||
|
||||
let verloc_state = VerlocState::new(atomic_verloc_result);
|
||||
let descriptor = self.descriptor.clone();
|
||||
@@ -119,7 +116,7 @@ impl MixNode {
|
||||
tokio::spawn(async move {
|
||||
rocket::build()
|
||||
.configure(config)
|
||||
.mount("/", routes![verlocRoute, description, stats, hardware])
|
||||
.mount("/", routes![verloc_route, description, stats, hardware])
|
||||
.register("/", catchers![not_found])
|
||||
.manage(verloc_state)
|
||||
.manage(descriptor)
|
||||
@@ -135,8 +132,8 @@ impl MixNode {
|
||||
) -> (SharedNodeStats, node_statistics::UpdateSender) {
|
||||
info!("Starting node stats controller...");
|
||||
let controller = node_statistics::Controller::new(
|
||||
self.config.get_node_stats_logging_delay(),
|
||||
self.config.get_node_stats_updating_delay(),
|
||||
self.config.debug.node_stats_logging_delay,
|
||||
self.config.debug.node_stats_updating_delay,
|
||||
shutdown,
|
||||
);
|
||||
let node_stats_pointer = controller.get_node_stats_data_pointer();
|
||||
@@ -159,8 +156,8 @@ impl MixNode {
|
||||
let connection_handler = ConnectionHandler::new(packet_processor, delay_forwarding_channel);
|
||||
|
||||
let listening_address = SocketAddr::new(
|
||||
self.config.get_listening_address(),
|
||||
self.config.get_mix_port(),
|
||||
self.config.mixnode.listening_address,
|
||||
self.config.mixnode.mix_port,
|
||||
);
|
||||
|
||||
Listener::new(listening_address, shutdown).start(connection_handler);
|
||||
@@ -174,11 +171,11 @@ impl MixNode {
|
||||
info!("Starting packet delay-forwarder...");
|
||||
|
||||
let client_config = nym_mixnet_client::Config::new(
|
||||
self.config.get_packet_forwarding_initial_backoff(),
|
||||
self.config.get_packet_forwarding_maximum_backoff(),
|
||||
self.config.get_initial_connection_timeout(),
|
||||
self.config.get_maximum_connection_buffer_size(),
|
||||
self.config.get_use_legacy_sphinx_framing(),
|
||||
self.config.debug.packet_forwarding_initial_backoff,
|
||||
self.config.debug.packet_forwarding_maximum_backoff,
|
||||
self.config.debug.initial_connection_timeout,
|
||||
self.config.debug.maximum_connection_buffer_size,
|
||||
self.config.debug.use_legacy_framed_packet_version,
|
||||
);
|
||||
|
||||
let mut packet_forwarder = DelayForwarder::new(
|
||||
@@ -199,8 +196,8 @@ impl MixNode {
|
||||
// this is a sanity check to make sure we didn't mess up with the minimum version at some point
|
||||
// and whether the user has run update if they're using old config
|
||||
// if this code exists in the node, it MUST BE compatible
|
||||
let config_version =
|
||||
parse_version(self.config.get_version()).expect("malformed version in the config file");
|
||||
let config_version = parse_version(&self.config.mixnode.version)
|
||||
.expect("malformed version in the config file");
|
||||
let minimum_version = parse_version(verloc::MINIMUM_NODE_VERSION).unwrap();
|
||||
if config_version < minimum_version {
|
||||
error!("You seem to have not updated your mixnode configuration file - please run `upgrade` before attempting again");
|
||||
@@ -210,19 +207,19 @@ impl MixNode {
|
||||
// use the same binding address with the HARDCODED port for time being (I don't like that approach personally)
|
||||
|
||||
let listening_address = SocketAddr::new(
|
||||
self.config.get_listening_address(),
|
||||
self.config.get_verloc_port(),
|
||||
self.config.mixnode.listening_address,
|
||||
self.config.mixnode.verloc_port,
|
||||
);
|
||||
|
||||
let config = verloc::ConfigBuilder::new()
|
||||
.listening_address(listening_address)
|
||||
.packets_per_node(self.config.get_measurement_packets_per_node())
|
||||
.connection_timeout(self.config.get_measurement_connection_timeout())
|
||||
.packet_timeout(self.config.get_measurement_packet_timeout())
|
||||
.delay_between_packets(self.config.get_measurement_delay_between_packets())
|
||||
.tested_nodes_batch_size(self.config.get_measurement_tested_nodes_batch_size())
|
||||
.testing_interval(self.config.get_measurement_testing_interval())
|
||||
.retry_timeout(self.config.get_measurement_retry_timeout())
|
||||
.packets_per_node(self.config.verloc.packets_per_node)
|
||||
.connection_timeout(self.config.verloc.connection_timeout)
|
||||
.packet_timeout(self.config.verloc.packet_timeout)
|
||||
.delay_between_packets(self.config.verloc.delay_between_packets)
|
||||
.tested_nodes_batch_size(self.config.verloc.tested_nodes_batch_size)
|
||||
.testing_interval(self.config.verloc.testing_interval)
|
||||
.retry_timeout(self.config.verloc.retry_timeout)
|
||||
.nym_api_urls(self.config.get_nym_api_endpoints())
|
||||
.build();
|
||||
|
||||
@@ -242,8 +239,7 @@ impl MixNode {
|
||||
nym_validator_client::NymApiClient::new(nym_api.clone())
|
||||
}
|
||||
|
||||
// TODO: ask DH whether this function still makes sense in ^0.10
|
||||
async fn check_if_same_ip_node_exists(&mut self) -> Option<String> {
|
||||
async fn check_if_bonded(&self) -> bool {
|
||||
// TODO: if anything, this should be getting data directly from the contract
|
||||
// as opposed to the validator API
|
||||
let validator_client = self.random_api_client();
|
||||
@@ -258,12 +254,10 @@ impl MixNode {
|
||||
}
|
||||
};
|
||||
|
||||
let our_host = self.config.get_announce_address();
|
||||
|
||||
existing_nodes
|
||||
.iter()
|
||||
.find(|node| node.bond_information.mix_node.host == our_host)
|
||||
.map(|node| node.bond_information.mix_node.identity_key.clone())
|
||||
existing_nodes.iter().any(|node| {
|
||||
node.bond_information.mix_node.identity_key
|
||||
== self.identity_keypair.public_key().to_base58_string()
|
||||
})
|
||||
}
|
||||
|
||||
async fn wait_for_interrupt(&self, shutdown: TaskManager) {
|
||||
@@ -274,16 +268,8 @@ impl MixNode {
|
||||
pub async fn run(&mut self) {
|
||||
info!("Starting nym mixnode");
|
||||
|
||||
if let Some(duplicate_node_key) = self.check_if_same_ip_node_exists().await {
|
||||
if duplicate_node_key == self.identity_keypair.public_key().to_base58_string() {
|
||||
warn!("You seem to have bonded your mixnode before starting it - that's highly unrecommended as in the future it might result in slashing");
|
||||
} else {
|
||||
log::error!(
|
||||
"Our announce-host is identical to an existing node's announce-host! (its key is {:?})",
|
||||
duplicate_node_key
|
||||
);
|
||||
return;
|
||||
}
|
||||
if self.check_if_bonded().await {
|
||||
warn!("You seem to have bonded your mixnode before starting it - that's highly unrecommended as in the future it might result in slashing");
|
||||
}
|
||||
|
||||
let shutdown = TaskManager::default();
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::path::PathBuf;
|
||||
use std::path::Path;
|
||||
use std::{fs, io};
|
||||
|
||||
pub(crate) const DESCRIPTION_FILE: &str = "description.toml";
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||
pub struct NodeDescription {
|
||||
pub(crate) name: String,
|
||||
@@ -25,24 +23,26 @@ impl Default for NodeDescription {
|
||||
}
|
||||
|
||||
impl NodeDescription {
|
||||
pub(crate) fn load_from_file(config_path: PathBuf) -> io::Result<NodeDescription> {
|
||||
let description_file_path: PathBuf = [config_path.to_str().unwrap(), DESCRIPTION_FILE]
|
||||
.iter()
|
||||
.collect();
|
||||
let toml = fs::read_to_string(description_file_path)?;
|
||||
pub(crate) fn load_from_file<P: AsRef<Path>>(path: P) -> io::Result<NodeDescription> {
|
||||
// let description_file_path: PathBuf = [config_path.to_str().unwrap(), DESCRIPTION_FILE]
|
||||
// .iter()
|
||||
// .collect();
|
||||
// let toml = fs::read_to_string(description_file_path)?;
|
||||
// toml::from_str(&toml).map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err))
|
||||
let toml = fs::read_to_string(path)?;
|
||||
toml::from_str(&toml).map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err))
|
||||
}
|
||||
|
||||
pub(crate) fn save_to_file(
|
||||
pub(crate) fn save_to_file<P: AsRef<Path>>(
|
||||
description: &NodeDescription,
|
||||
config_path: PathBuf,
|
||||
path: P,
|
||||
) -> io::Result<()> {
|
||||
let description_file_path: PathBuf = [config_path.to_str().unwrap(), DESCRIPTION_FILE]
|
||||
.iter()
|
||||
.collect();
|
||||
// let description_file_path: PathBuf = [config_path.to_str().unwrap(), DESCRIPTION_FILE]
|
||||
// .iter()
|
||||
// .collect();
|
||||
let description_toml =
|
||||
toml::to_string(description).expect("could not encode description to toml");
|
||||
fs::write(description_file_path, description_toml)?;
|
||||
fs::write(path, description_toml)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user