diff --git a/Cargo.lock b/Cargo.lock index 98af870502..445aa4a9dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5429,19 +5429,12 @@ dependencies = [ "async-trait", "bip39", "bs58", - "clap 4.5.20", - "colored", "dashmap", "defguard_wireguard_rs", - "dirs", - "dotenvy", "futures", - "humantime-serde", "ipnetwork 0.20.0", "nym-api-requests", "nym-authenticator", - "nym-bin-common", - "nym-config", "nym-credential-verification", "nym-credentials", "nym-credentials-interface", @@ -5455,7 +5448,6 @@ dependencies = [ "nym-network-defaults", "nym-network-requester", "nym-node-http-api", - "nym-pemstore", "nym-sdk", "nym-sphinx", "nym-statistics-common", @@ -5465,14 +5457,9 @@ dependencies = [ "nym-validator-client", "nym-wireguard", "nym-wireguard-types", - "once_cell", "rand", - "serde", - "serde_json", "sha2 0.10.8", - "si-scale", "sqlx", - "subtle-encoding", "thiserror", "time", "tokio", @@ -5792,19 +5779,8 @@ dependencies = [ name = "nym-mixnode" version = "1.1.37" dependencies = [ - "anyhow", - "axum 0.7.7", - "bs58", - "clap 4.5.20", "colored", - "cupid", - "dirs", "futures", - "humantime-serde", - "lazy_static", - "log", - "nym-bin-common", - "nym-config", "nym-contracts-common", "nym-crypto", "nym-http-api-common", @@ -5813,7 +5789,6 @@ dependencies = [ "nym-mixnode-common", "nym-node-http-api", "nym-nonexhaustive-delayqueue", - "nym-pemstore", "nym-sphinx", "nym-sphinx-params", "nym-sphinx-types", @@ -5821,15 +5796,11 @@ dependencies = [ "nym-topology", "nym-types", "nym-validator-client", - "rand", - "serde", - "serde_json", - "sysinfo", "thiserror", "time", "tokio", "tokio-util", - "toml 0.8.14", + "tracing", "url", ] diff --git a/gateway/Cargo.toml b/gateway/Cargo.toml index 0798ebedb5..f760a6ba23 100644 --- a/gateway/Cargo.toml +++ b/gateway/Cargo.toml @@ -18,31 +18,16 @@ rust-version = "1.76" [lib] path = "src/lib.rs" -[[bin]] -name = "nym-gateway" -required-features = ["bin-deps"] -path = "src/main.rs" - [dependencies] anyhow = { workspace = true } async-trait = { workspace = true } bip39 = { workspace = true } bs58 = { workspace = true } -clap = { workspace = true, features = ["cargo", "derive"], optional = true } -colored = { workspace = true } dashmap = { workspace = true } -dirs = { workspace = true } -dotenvy = { workspace = true } futures = { workspace = true } -humantime-serde = { workspace = true } ipnetwork = { workspace = true } -once_cell = { workspace = true } rand = { workspace = true } -serde = { workspace = true, features = ["derive"] } -serde_json = { workspace = true } sha2 = { workspace = true } -si-scale = { workspace = true } -subtle-encoding = { workspace = true, features = ["bech32-preview"] } thiserror = { workspace = true } time = { workspace = true } tokio = { workspace = true, features = [ @@ -60,11 +45,8 @@ url = { workspace = true, features = ["serde"] } zeroize = { workspace = true } # internal - nym-authenticator = { path = "../service-providers/authenticator" } nym-api-requests = { path = "../nym-api/nym-api-requests" } -nym-bin-common = { path = "../common/bin-common" } -nym-config = { path = "../common/config" } nym-credentials = { path = "../common/credentials" } nym-credentials-interface = { path = "../common/credentials-interface" } nym-credential-verification = { path = "../common/credential-verification" } @@ -77,7 +59,6 @@ nym-mixnode-common = { path = "../common/mixnode-common" } nym-network-defaults = { path = "../common/network-defaults" } nym-network-requester = { path = "../service-providers/network-requester" } nym-node-http-api = { path = "../nym-node/nym-node-http-api" } -nym-pemstore = { path = "../common/pemstore" } nym-sdk = { path = "../sdk/rust/nym-sdk" } nym-sphinx = { path = "../common/nymsphinx" } nym-statistics-common = { path = "../common/statistics" } @@ -101,12 +82,3 @@ sqlx = { workspace = true, features = [ "macros", "migrate", ] } - -[features] -bin-deps = ["clap", 'nym-bin-common/output_format'] - -[package.metadata.deb] -name = "nym-gateway" -maintainer-scripts = "debian" -depends = "curl" -systemd-units = { enable = false } diff --git a/gateway/debian/postinst b/gateway/debian/postinst deleted file mode 100755 index a6b058a689..0000000000 --- a/gateway/debian/postinst +++ /dev/null @@ -1,63 +0,0 @@ -#!/bin/sh - -default_location="/usr/bin/nym-gateway" -default_user="nym" -default_group="nym" - -if [ -f "/tmp/nym_gateway_preinst_marker" ]; then - backup_path=$(cat /tmp/nym_gateway_preinst_marker) - echo "Upgrade detected. Previous version backed up at $backup_path" - - existing_location=$(dirname "$backup_path" | sed 's/\.backup\..*//') - echo "Existing location: ${existing_location}" - - if [ "$existing_location" != "$default_location" ]; then - echo "Custom installation location detected: $existing_location" - - mv "$default_location" "$existing_location/nym-gateway" - - original_user=$(stat -c "%U" "$backup_path") - original_group=$(stat -c "%G" "$backup_path") - original_perms=$(stat -c "%a" "$backup_path") - - chown "$original_user:$original_group" "$existing_location/nym-gateway" - chmod "$original_perms" "$existing_location/nym-gateway" - fi - - rm -f /tmp/nym_gateway_preinst_marker -else - echo "Fresh installation detected." - - if [ -f "$default_location" ]; then - # Leave the binary as the user to perform the apt install - # It's down to the user to specify the correct ownership and permissions - chmod 755 "$default_location" - - echo "Installation complete. Please configure and start the nym-gateway process manually." - echo "Refer to https://nymtech.net/operators/nodes/gateway-setup.html" - echo "Example for setting up the nym-gateway service:" - echo - cat </dev/null) - -if [ -n "$existing_binaries" ]; then - echo "Existing installation(s) detected. Preparing for upgrade." - - for binary_path in $existing_binaries; do - backup_path="$backup_dir/$(basename $binary_path).backup.$(date +%Y-%m-%dT%H:%M:%S)" - cp "$binary_path" "$backup_path" - - echo "Backed up existing binary from $binary_path to $backup_path" - done - - oldest_binary=$(echo "$existing_binaries" | head -n 1) - echo "$oldest_binary" > /tmp/nym_gateway_preinst_marker -else - echo "No existing nym-gateway installation detected. Proceeding with fresh installation." -fi - -exit 0 - -#DEBHELPER# diff --git a/gateway/src/commands/build_info.rs b/gateway/src/commands/build_info.rs deleted file mode 100644 index 3b763adf3f..0000000000 --- a/gateway/src/commands/build_info.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2023 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use clap::Args; -use nym_bin_common::bin_info_owned; -use nym_bin_common::output_format::OutputFormat; - -#[derive(Args)] -pub(crate) struct BuildInfo { - #[clap(short, long, default_value_t = OutputFormat::default())] - output: OutputFormat, -} - -pub(crate) fn execute(args: BuildInfo) { - println!("{}", args.output.format(&bin_info_owned!())) -} diff --git a/gateway/src/commands/helpers.rs b/gateway/src/commands/helpers.rs deleted file mode 100644 index 9711ab1583..0000000000 --- a/gateway/src/commands/helpers.rs +++ /dev/null @@ -1,265 +0,0 @@ -// Copyright 2023 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use crate::commands::upgrade_helpers; -use log::{error, info}; -use nym_config::{save_formatted_config_to_file, OptionalSet}; -use nym_crypto::asymmetric::identity; -use nym_gateway::config::default_config_filepath; -use nym_gateway::config::persistence::paths::{ - default_ip_packet_router_data_dir, default_network_requester_data_dir, -}; -use nym_gateway::config::Config; -use nym_gateway::error::GatewayError; -use nym_gateway::helpers::{ - override_ip_packet_router_config, override_network_requester_config, - OverrideIpPacketRouterConfig, OverrideNetworkRequesterConfig, -}; -use nym_network_defaults::mainnet; -use nym_network_defaults::var_names::NYXD; -use nym_network_defaults::var_names::{BECH32_PREFIX, NYM_API}; -use tracing::{error, info}; - -use nym_network_requester::{ - generate_new_client_keys, set_active_gateway, setup_fs_gateways_storage, setup_gateway, - GatewaySetup, OnDiskKeys, -}; -use nym_types::gateway::{GatewayIpPacketRouterDetails, GatewayNetworkRequesterDetails}; -use nym_validator_client::nyxd::AccountId; -use rand::rngs::OsRng; -use std::net::IpAddr; -use std::path::PathBuf; - -// Configuration that can be overridden. -#[derive(Default)] -pub(crate) struct OverrideConfig { - pub(crate) listening_address: Option, - pub(crate) public_ips: Option>, - pub(crate) hostname: Option, - - pub(crate) mix_port: Option, - pub(crate) clients_port: Option, - pub(crate) datastore: Option, - pub(crate) nym_apis: Option>, - pub(crate) mnemonic: Option, - pub(crate) nyxd_urls: Option>, - pub(crate) only_coconut_credentials: Option, - pub(crate) with_network_requester: Option, - pub(crate) with_ip_packet_router: Option, -} - -impl OverrideConfig { - pub(crate) fn do_override(self, mut config: Config) -> Result { - config = config - .with_optional(Config::with_hostname, self.hostname) - .with_optional(Config::with_public_ips, self.public_ips) - .with_optional(Config::with_listening_address, self.listening_address) - .with_optional(Config::with_mix_port, self.mix_port) - .with_optional(Config::with_clients_port, self.clients_port) - .with_optional_custom_env( - Config::with_custom_nym_apis, - self.nym_apis, - NYM_API, - nym_config::parse_urls, - ) - .with_optional(Config::with_custom_persistent_store, self.datastore) - .with_optional(Config::with_cosmos_mnemonic, self.mnemonic) - .with_optional_custom_env( - Config::with_custom_validator_nyxd, - self.nyxd_urls, - NYXD, - nym_config::parse_urls, - ) - .with_optional( - Config::with_only_coconut_credentials, - self.only_coconut_credentials, - ) - .with_optional( - Config::with_enabled_network_requester, - self.with_network_requester, - ) - .with_optional( - Config::with_enabled_ip_packet_router, - self.with_ip_packet_router, - ); - - if config.network_requester.enabled - && config.storage_paths.network_requester_config.is_none() - { - config = config.with_default_network_requester_config_path(); - } - - if config.ip_packet_router.enabled && config.storage_paths.ip_packet_router_config.is_none() - { - config = config.with_default_ip_packet_router_config_path(); - } - - Ok(config) - } -} - -pub(crate) fn try_override_config>( - config: Config, - override_args: O, -) -> Result { - override_args.into().do_override(config) -} - -/// Ensures that a given bech32 address is valid -pub(crate) fn ensure_correct_bech32_prefix(address: &AccountId) -> Result<(), GatewayError> { - let expected_prefix = - std::env::var(BECH32_PREFIX).unwrap_or(mainnet::BECH32_PREFIX.to_string()); - let actual_prefix = address.prefix(); - - if expected_prefix != actual_prefix { - return Err(GatewayError::InvalidBech32AccountPrefix { - account: address.to_owned(), - expected_prefix, - actual_prefix: actual_prefix.to_owned(), - }); - } - - Ok(()) -} - -pub(crate) fn try_load_current_config(id: &str) -> Result { - Config::read_from_default_path(id).map_err(|err| { - error!( - "Failed to load config for {id}. Are you sure you have run `init` before? (Error was: {err})", - ); - GatewayError::ConfigLoadFailure { - path: default_config_filepath(id), - id: id.to_string(), - source: err, - } - }) -} - -fn make_nr_id(gateway_id: &str) -> String { - format!("{gateway_id}-network-requester") -} - -fn make_ip_id(gateway_id: &str) -> String { - format!("{gateway_id}-ip-packet-router") -} - -pub(crate) async fn initialise_local_network_requester( - gateway_config: &Config, - opts: OverrideNetworkRequesterConfig, - identity: identity::PublicKey, -) -> Result { - info!("initialising network requester..."); - let Some(nr_cfg_path) = gateway_config.storage_paths.network_requester_config() else { - return Err(GatewayError::UnspecifiedNetworkRequesterConfig); - }; - - let id = &gateway_config.gateway.id; - let nr_id = make_nr_id(id); - let nr_data_dir = default_network_requester_data_dir(id); - let mut nr_cfg = nym_network_requester::Config::new(&nr_id).with_data_directory(nr_data_dir); - nr_cfg = override_network_requester_config(nr_cfg, Some(opts)); - - let key_store = OnDiskKeys::new(nr_cfg.storage_paths.common_paths.keys.clone()); - let details_store = - setup_fs_gateways_storage(&nr_cfg.storage_paths.common_paths.gateway_registrations).await?; - - // if this is a first time client with this particular id is initialised, generated long-term keys - if !nr_cfg_path.exists() { - let mut rng = OsRng; - generate_new_client_keys(&mut rng, &key_store).await?; - } - - // gateway setup here is way simpler as we're 'connecting' to ourselves - let init_res = setup_gateway( - GatewaySetup::new_inbuilt(identity), - &key_store, - &details_store, - ) - .await?; - set_active_gateway(&details_store, &init_res.gateway_id().to_base58_string()).await?; - - let address = init_res.client_address(); - - if let Err(err) = save_formatted_config_to_file(&nr_cfg, nr_cfg_path) { - log::error!("Failed to save the network requester config file: {err}"); - return Err(GatewayError::ConfigSaveFailure { - id: nr_id, - path: nr_cfg_path.to_path_buf(), - source: err, - }); - } else { - eprintln!( - "Saved network requester configuration file to {}", - nr_cfg_path.display() - ) - } - - Ok(GatewayNetworkRequesterDetails { - enabled: gateway_config.network_requester.enabled, - identity_key: address.identity().to_string(), - encryption_key: address.encryption_key().to_string(), - open_proxy: nr_cfg.network_requester.open_proxy, - address: address.to_string(), - config_path: nr_cfg_path.display().to_string(), - }) -} - -pub(crate) async fn initialise_local_ip_packet_router( - gateway_config: &Config, - opts: OverrideIpPacketRouterConfig, - identity: identity::PublicKey, -) -> Result { - info!("initialising ip packet router..."); - let Some(ip_cfg_path) = gateway_config.storage_paths.ip_packet_router_config() else { - return Err(GatewayError::UnspecifiedIpPacketRouterConfig); - }; - - let id = &gateway_config.gateway.id; - let ip_id = make_ip_id(id); - let ip_data_dir = default_ip_packet_router_data_dir(id); - let mut ip_cfg = nym_ip_packet_router::Config::new(&ip_id).with_data_directory(ip_data_dir); - ip_cfg = override_ip_packet_router_config(ip_cfg, Some(opts)); - - let key_store = OnDiskKeys::new(ip_cfg.storage_paths.common_paths.keys.clone()); - let details_store = - setup_fs_gateways_storage(&ip_cfg.storage_paths.common_paths.gateway_registrations).await?; - - // if this is a first time client with this particular id is initialised, generated long-term keys - if !ip_cfg_path.exists() { - let mut rng = OsRng; - generate_new_client_keys(&mut rng, &key_store).await?; - } - - // gateway setup here is way simpler as we're 'connecting' to ourselves - let init_res = setup_gateway( - GatewaySetup::new_inbuilt(identity), - &key_store, - &details_store, - ) - .await?; - set_active_gateway(&details_store, &init_res.gateway_id().to_base58_string()).await?; - - let address = init_res.client_address(); - - if let Err(err) = save_formatted_config_to_file(&ip_cfg, ip_cfg_path) { - log::error!("Failed to save the ip packet router config file: {err}"); - return Err(GatewayError::ConfigSaveFailure { - id: ip_id, - path: ip_cfg_path.to_path_buf(), - source: err, - }); - } else { - eprintln!( - "Saved ip packet router configuration file to {}", - ip_cfg_path.display() - ) - } - - Ok(GatewayIpPacketRouterDetails { - enabled: gateway_config.ip_packet_router.enabled, - identity_key: address.identity().to_string(), - encryption_key: address.encryption_key().to_string(), - address: address.to_string(), - config_path: ip_cfg_path.display().to_string(), - }) -} diff --git a/gateway/src/commands/init.rs b/gateway/src/commands/init.rs deleted file mode 100644 index 4c43236b7b..0000000000 --- a/gateway/src/commands/init.rs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2020-2024 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use anyhow::bail; -use clap::Args; -use colored::Colorize; - -#[allow(dead_code)] -#[derive(Args, Clone, Debug)] -pub struct Init { - /// Id of the gateway we want to create config for - #[clap(long)] - id: Option, -} - -pub async fn execute(_args: Init) -> anyhow::Result<()> { - bail!( - "standalone mixnode initialisation has been removed - please initialise a `nym-node` instead" - .red() - .bold() - ) -} diff --git a/gateway/src/commands/mod.rs b/gateway/src/commands/mod.rs deleted file mode 100644 index 1a08e249a0..0000000000 --- a/gateway/src/commands/mod.rs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2020-2023 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use crate::Cli; -use anyhow::bail; -use clap::CommandFactory; -use clap::Subcommand; -use nym_bin_common::completions::{fig_generate, ArgShell}; -use std::io::IsTerminal; -use std::time::Duration; -use tracing::{error, warn}; - -pub(crate) mod build_info; -pub(crate) mod helpers; -pub(crate) mod init; -pub(crate) mod node_details; -pub(crate) mod run; -pub(crate) mod setup_ip_packet_router; -pub(crate) mod setup_network_requester; -pub(crate) mod sign; - -#[derive(Subcommand)] -pub(crate) enum Commands { - /// Initialise the gateway - Init(init::Init), - - /// Show details of this gateway - NodeDetails(node_details::NodeDetails), - - /// Starts the gateway - Run(Box), - - /// Add network requester support to this gateway - // essentially an option to include NR without having to setup fresh gateway - SetupNetworkRequester(setup_network_requester::CmdArgs), - - /// Add ip packet router support to this gateway - // essentially an option to include ip packet router without having to setup fresh gateway - #[command(hide = true)] - SetupIpPacketRouter(setup_ip_packet_router::CmdArgs), - - /// Sign text to prove ownership of this mixnode - Sign(sign::Sign), - - /// Show build information of this binary - BuildInfo(build_info::BuildInfo), - - /// Generate shell completions - Completions(ArgShell), - - /// Generate Fig specification - GenerateFigSpec, -} - -pub(crate) async fn execute(args: Cli) -> anyhow::Result<()> { - let bin_name = "nym-gateway"; - - if !args.force_run { - let msg = "standalone gateways have been deprecated - please migrate to a `nym-node` via `nym-node migrate gateways` command"; - error!("{msg}"); - bail!("{msg}") - } - - warn!("standalone gateways have been deprecated - please consider migrating it to a `nym-node` via `nym-node migrate gateway` command"); - if std::io::stdout().is_terminal() { - // if user is running it in terminal session, - // introduce the delay, so they'd notice the message - tokio::time::sleep(Duration::from_secs(1)).await - } - - match args.command { - Commands::Init(m) => init::execute(m).await?, - Commands::NodeDetails(m) => node_details::execute(m).await?, - Commands::Run(m) => run::execute(*m).await?, - Commands::SetupNetworkRequester(m) => setup_network_requester::execute(m).await?, - Commands::SetupIpPacketRouter(m) => setup_ip_packet_router::execute(m).await?, - Commands::Sign(m) => sign::execute(m)?, - Commands::BuildInfo(m) => build_info::execute(m), - Commands::Completions(s) => s.generate(&mut crate::Cli::command(), bin_name), - Commands::GenerateFigSpec => fig_generate(&mut crate::Cli::command(), bin_name), - } - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn verify_cli() { - Cli::command().debug_assert(); - } -} diff --git a/gateway/src/commands/node_details.rs b/gateway/src/commands/node_details.rs deleted file mode 100644 index 6bf2f6f330..0000000000 --- a/gateway/src/commands/node_details.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2021-2023 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use crate::commands::helpers::try_load_current_config; -use clap::Args; -use nym_bin_common::output_format::OutputFormat; -use nym_gateway::helpers::node_details; - -#[derive(Args, Clone)] -pub struct NodeDetails { - /// The id of the gateway you want to show details for - #[clap(long)] - id: String, - - #[clap(short, long, default_value_t = OutputFormat::default())] - output: OutputFormat, -} - -pub async fn execute(args: NodeDetails) -> anyhow::Result<()> { - let config = try_load_current_config(&args.id)?; - args.output.to_stdout(&node_details(&config).await?); - - Ok(()) -} diff --git a/gateway/src/commands/run.rs b/gateway/src/commands/run.rs deleted file mode 100644 index e9b4fb8d1b..0000000000 --- a/gateway/src/commands/run.rs +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright 2020-2023 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use crate::commands::helpers::{try_load_current_config, try_override_config, OverrideConfig}; -use anyhow::bail; -use clap::Args; -use nym_bin_common::output_format::OutputFormat; -use nym_config::helpers::SPECIAL_ADDRESSES; -use nym_gateway::helpers::{OverrideIpPacketRouterConfig, OverrideNetworkRequesterConfig}; -use nym_gateway::GatewayError; -use std::net::IpAddr; -use std::path::PathBuf; -use tracing::warn; - -#[derive(Args, Clone)] -pub struct Run { - /// Id of the gateway we want to run - #[arg(long)] - id: String, - - /// The custom listening address on which the gateway will be running for receiving sphinx packets - #[arg(long, alias = "host")] - listening_address: Option, - - /// Comma separated list of public ip addresses that will be announced to the nym-api and subsequently to the clients. - /// In nearly all circumstances, it's going to be identical to the address you're going to use for bonding. - #[arg(long, value_delimiter = ',')] - public_ips: Option>, - - /// Optional hostname associated with this gateway that will be announced to the nym-api and subsequently to the clients - #[arg(long)] - hostname: Option, - - /// The port on which the gateway will be listening for sphinx packets - #[arg(long)] - mix_port: Option, - - /// The port on which the gateway will be listening for clients gateway-requests - #[arg(long)] - clients_port: Option, - - /// Path to sqlite database containing all gateway persistent data - #[arg(long)] - datastore: Option, - - /// Comma separated list of endpoints of nym APIs - #[arg( - long, - alias = "validator_apis", - value_delimiter = ',', - group = "network" - )] - // the alias here is included for backwards compatibility (1.1.4 and before) - nym_apis: Option>, - - /// Comma separated list of endpoints of the validator - #[arg( - long, - alias = "validators", - alias = "nyxd_validators", - value_delimiter = ',', - hide = true - )] - // the alias here is included for backwards compatibility (1.1.4 and before) - nyxd_urls: Option>, - - /// Cosmos wallet mnemonic - #[arg(long)] - mnemonic: Option, - - /// Set this gateway to work only with coconut credentials; that would disallow clients to - /// bypass bandwidth credential requirement - #[arg(long, hide = true)] - only_coconut_credentials: Option, - - /// Allows this gateway to run an embedded network requester for minimal network overhead - #[arg(long)] - with_network_requester: Option, - - /// Allows this gateway to run an embedded network requester for minimal network overhead - #[arg(long, hide = true)] - with_ip_packet_router: Option, - - // ##### NETWORK REQUESTER FLAGS ##### - /// Specifies whether this network requester should run in 'open-proxy' mode - #[arg(long)] - open_proxy: Option, - - /// Mostly debug-related option to increase default traffic rate so that you would not need to - /// modify config post init - #[arg(long, hide = true, conflicts_with = "medium_toggle")] - fastmode: bool, - - /// Disable loop cover traffic and the Poisson rate limiter (for debugging only) - #[arg(long, hide = true, conflicts_with = "medium_toggle")] - no_cover: bool, - - /// Enable medium mixnet traffic, for experiments only. - /// This includes things like disabling cover traffic, no per hop delays, etc. - #[arg( - long, - hide = true, - conflicts_with = "no_cover", - conflicts_with = "fastmode" - )] - medium_toggle: bool, - - /// Path to .json file containing custom network specification. - /// Only usable when local network requester is enabled. - #[arg(long, group = "network", hide = true)] - custom_mixnet: Option, - - #[arg(short, long, default_value_t = OutputFormat::default())] - output: OutputFormat, - - /// Flag specifying this node will be running in a local setting. - #[arg(long)] - local: bool, -} - -impl From for OverrideConfig { - fn from(run_config: Run) -> Self { - OverrideConfig { - listening_address: run_config.listening_address, - public_ips: run_config.public_ips, - hostname: run_config.hostname, - mix_port: run_config.mix_port, - clients_port: run_config.clients_port, - datastore: run_config.datastore, - nym_apis: run_config.nym_apis, - mnemonic: run_config.mnemonic, - - nyxd_urls: run_config.nyxd_urls, - only_coconut_credentials: run_config.only_coconut_credentials, - with_network_requester: run_config.with_network_requester, - with_ip_packet_router: run_config.with_ip_packet_router, - } - } -} - -impl<'a> From<&'a Run> for OverrideNetworkRequesterConfig { - fn from(value: &'a Run) -> Self { - OverrideNetworkRequesterConfig { - fastmode: value.fastmode, - no_cover: value.no_cover, - medium_toggle: value.medium_toggle, - open_proxy: value.open_proxy, - } - } -} - -impl From<&Run> for OverrideIpPacketRouterConfig { - fn from(_value: &Run) -> Self { - OverrideIpPacketRouterConfig {} - } -} - -fn show_binding_warning(address: IpAddr) { - eprintln!("\n##### NOTE #####"); - eprintln!( - "\nYou are trying to bind to {address} - you might not be accessible to other nodes\n\ - You can ignore this warning if you're running setup on a local network \n\ - or have used different host when bonding your node" - ); - eprintln!("\n\n"); -} - -fn check_public_ips(ips: &[IpAddr], local: bool) -> anyhow::Result<()> { - let mut suspicious_ip = Vec::new(); - for ip in ips { - if SPECIAL_ADDRESSES.contains(ip) { - if !local { - return Err(GatewayError::InvalidPublicIp { address: *ip }.into()); - } - suspicious_ip.push(ip); - } - } - - if !suspicious_ip.is_empty() { - warn!("\n##### WARNING #####"); - for ip in suspicious_ip { - warn!("The 'public' IP address you're trying to announce: {ip} may not be accessible to other clients.\ - Please make sure this is what you intended to announce.\ - You can ignore this warning if you're running setup on a local network ") - } - warn!("\n##### WARNING #####\n"); - } - Ok(()) -} - -pub async fn execute(args: Run) -> anyhow::Result<()> { - let id = args.id.clone(); - let local = args.local; - - eprintln!("Starting gateway {id}..."); - - let output = args.output; - let custom_mixnet = args.custom_mixnet.clone(); - let nr_opts = (&args).into(); - let ip_opts = (&args).into(); - - let mut config = try_load_current_config(&args.id)?; - config = try_override_config(config, args)?; - - let public_ips = &config.host.public_ips; - if public_ips.is_empty() { - return Err(GatewayError::NoPublicIps.into()); - } - check_public_ips(public_ips, local)?; - if config.gateway.clients_wss_port.is_some() && config.host.hostname.is_none() { - bail!("attempted to announce 'wss' port without a valid hostname") - } - - if SPECIAL_ADDRESSES.contains(&config.gateway.listening_address) { - show_binding_warning(config.gateway.listening_address); - } - - let gateway = - nym_gateway::create_gateway(config, Some(nr_opts), Some(ip_opts), custom_mixnet).await?; - let node_details = gateway.node_details().await?; - eprintln!( - "\nTo bond your gateway you will need to install the Nym wallet, go to https://nymtech.net/get-involved and select the Download button.\n\ - Select the correct version and install it to your machine. You will need to provide some of the following: \n "); - output.to_stdout(&node_details); - - gateway.run().await?; - Ok(()) -} diff --git a/gateway/src/commands/setup_ip_packet_router.rs b/gateway/src/commands/setup_ip_packet_router.rs deleted file mode 100644 index 5f22e794e4..0000000000 --- a/gateway/src/commands/setup_ip_packet_router.rs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2023 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use crate::commands::helpers::{initialise_local_ip_packet_router, try_load_current_config}; -use clap::Args; -use nym_bin_common::output_format::OutputFormat; -use nym_gateway::helpers::{load_public_key, OverrideIpPacketRouterConfig}; -use std::path::PathBuf; - -#[derive(Args, Clone)] -pub struct CmdArgs { - /// The id of the gateway you want to initialise local ip packet router for. - #[arg(long)] - id: String, - - /// Path to custom location for ip packet routers' config. - #[arg(long)] - custom_config_path: Option, - - /// Specify whether the ip packet router should be enabled. - // (you might want to create all the configs, generate keys, etc. but not actually run the NR just yet) - #[arg(long)] - enabled: Option, - - #[arg(short, long, default_value_t = OutputFormat::default())] - output: OutputFormat, -} - -impl From<&CmdArgs> for OverrideIpPacketRouterConfig { - fn from(_value: &CmdArgs) -> Self { - OverrideIpPacketRouterConfig {} - } -} - -pub async fn execute(args: CmdArgs) -> anyhow::Result<()> { - let mut config = try_load_current_config(&args.id)?; - let opts = (&args).into(); - - // if somebody provided config file of a custom NR, that's fine - // but in 90% cases, I'd assume, it won't work due to invalid gateway configuration - // but it might be nice to be able to move files around. - if let Some(custom_config_path) = args.custom_config_path { - // if you specified anything as the argument, overwrite whatever was already in the config file - config.storage_paths.ip_packet_router_config = Some(custom_config_path); - } - - if let Some(override_enabled) = args.enabled { - config.ip_packet_router.enabled = override_enabled; - } - - if config.storage_paths.ip_packet_router_config.is_none() { - config = config.with_default_ip_packet_router_config_path() - } - - let identity_public_key = load_public_key( - &config.storage_paths.keys.public_identity_key_file, - "gateway identity", - )?; - let details = initialise_local_ip_packet_router(&config, opts, identity_public_key).await?; - config.try_save()?; - - args.output.to_stdout(&details); - - Ok(()) -} diff --git a/gateway/src/commands/setup_network_requester.rs b/gateway/src/commands/setup_network_requester.rs deleted file mode 100644 index 3611392a56..0000000000 --- a/gateway/src/commands/setup_network_requester.rs +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2023 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use crate::commands::helpers::{initialise_local_network_requester, try_load_current_config}; -use clap::Args; -use nym_bin_common::output_format::OutputFormat; -use nym_gateway::helpers::{load_public_key, OverrideNetworkRequesterConfig}; -use std::io::IsTerminal; -use std::path::PathBuf; -use std::time::Duration; -use tracing::warn; - -#[derive(Args, Clone)] -pub struct CmdArgs { - /// The id of the gateway you want to initialise local network requester for. - #[clap(long)] - id: String, - - /// Path to custom location for network requester's config. - #[clap(long)] - custom_config_path: Option, - - /// Specify whether the network requester should be enabled. - // (you might want to create all the configs, generate keys, etc. but not actually run the NR just yet) - #[clap(long)] - enabled: Option, - - // note: those flags are set as bools as we want to explicitly override any settings values - // so say `open_proxy` was set to true in the config.toml. youd have to explicitly state `open-proxy=false` - // as an argument here to override it as opposed to not providing the value at all. - /// Specifies whether this network requester should run in 'open-proxy' mode - #[clap(long)] - open_proxy: Option, - - /// Mostly debug-related option to increase default traffic rate so that you would not need to - /// modify config post init - #[clap(long, hide = true, conflicts_with = "medium_toggle")] - fastmode: bool, - - /// Disable loop cover traffic and the Poisson rate limiter (for debugging only) - #[clap(long, hide = true, conflicts_with = "medium_toggle")] - no_cover: bool, - - /// Enable medium mixnet traffic, for experiments only. - /// This includes things like disabling cover traffic, no per hop delays, etc. - #[clap( - long, - hide = true, - conflicts_with = "no_cover", - conflicts_with = "fastmode" - )] - medium_toggle: bool, - - #[clap(short, long, default_value_t = OutputFormat::default())] - output: OutputFormat, -} - -impl<'a> From<&'a CmdArgs> for OverrideNetworkRequesterConfig { - fn from(value: &'a CmdArgs) -> Self { - OverrideNetworkRequesterConfig { - fastmode: value.fastmode, - no_cover: value.no_cover, - medium_toggle: value.medium_toggle, - open_proxy: value.open_proxy, - } - } -} - -pub async fn execute(args: CmdArgs) -> anyhow::Result<()> { - warn!("standalone gateways have been deprecated - please consider migrating it to a `nym-node` via `nym-node migrate gateway` command"); - if std::io::stdout().is_terminal() { - // if user is running it in terminal session, - // introduce the delay, so they'd notice the message - tokio::time::sleep(Duration::from_secs(1)).await - } - - let mut config = try_load_current_config(&args.id)?; - let opts = (&args).into(); - - // if somebody provided config file of a custom NR, that's fine - // but in 90% cases, I'd assume, it won't work due to invalid gateway configuration - // but it might be nice to be able to move files around. - if let Some(custom_config_path) = args.custom_config_path { - // if you specified anything as the argument, overwrite whatever was already in the config file - config.storage_paths.network_requester_config = Some(custom_config_path); - } - - if let Some(override_enabled) = args.enabled { - config.network_requester.enabled = override_enabled; - } - - if config.storage_paths.network_requester_config.is_none() { - config = config.with_default_network_requester_config_path() - } - - let identity_public_key = load_public_key( - &config.storage_paths.keys.public_identity_key_file, - "gateway identity", - )?; - let details = initialise_local_network_requester(&config, opts, identity_public_key).await?; - config.try_save()?; - - args.output.to_stdout(&details); - - Ok(()) -} diff --git a/gateway/src/commands/sign.rs b/gateway/src/commands/sign.rs deleted file mode 100644 index 4aa5e16d1f..0000000000 --- a/gateway/src/commands/sign.rs +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2020-2023 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use crate::commands::helpers::{ - ensure_correct_bech32_prefix, try_load_current_config, try_override_config, OverrideConfig, -}; -use anyhow::{bail, Result}; -use clap::{ArgGroup, Args}; -use nym_bin_common::output_format::OutputFormat; -use nym_crypto::asymmetric::identity; -use nym_gateway::error::GatewayError; -use nym_gateway::helpers::load_identity_keys; -use nym_types::helpers::ConsoleSigningOutput; -use nym_validator_client::nyxd; - -#[derive(Args, Clone)] -#[clap(group(ArgGroup::new("sign").required(true).args(&["wallet_address", "text", "contract_msg"])))] -pub struct Sign { - /// The id of the mixnode you want to sign with - #[clap(long)] - id: String, - - /// Signs your blockchain address with your identity key - // the alias here is included for backwards compatibility (1.1.4 and before) - #[clap(long, alias = "address")] - wallet_address: Option, - - /// Signs an arbitrary piece of text with your identity key - #[clap(long)] - text: Option, - - /// Signs a transaction-specific payload, that is going to be sent to the smart contract, with your identity key - #[clap(long)] - contract_msg: Option, - - #[clap(short, long, default_value_t = OutputFormat::default())] - output: OutputFormat, -} - -enum SignedTarget { - Text(String), - Address(nyxd::AccountId), - ContractMsg(String), -} - -impl TryFrom for SignedTarget { - type Error = anyhow::Error; - - fn try_from(args: Sign) -> Result { - if let Some(text) = args.text { - Ok(SignedTarget::Text(text)) - } else if let Some(address) = args.wallet_address { - Ok(SignedTarget::Address(address)) - } else if let Some(msg) = args.contract_msg { - Ok(SignedTarget::ContractMsg(msg)) - } else { - // This is unreachable, and hopefully clap will support it explicitly by outputting an - // enum from the ArgGroup in the future. - // See: https://github.com/clap-rs/clap/issues/2621 - bail!("Error: missing signed target flag") - } - } -} - -fn print_signed_address( - private_key: &identity::PrivateKey, - wallet_address: nyxd::AccountId, - output: OutputFormat, -) -> Result<(), GatewayError> { - // perform extra validation to ensure we have correct prefix - ensure_correct_bech32_prefix(&wallet_address)?; - - print_signed_text(private_key, wallet_address.as_ref(), output) -} - -fn print_signed_text( - private_key: &identity::PrivateKey, - text: &str, - output: OutputFormat, -) -> Result<(), GatewayError> { - eprintln!("Signing the text {text:?} using your mixnode's Ed25519 identity key...",); - - let signature = private_key.sign_text(text); - let sign_output = ConsoleSigningOutput::new(text, signature); - output.to_stdout(&sign_output); - - Ok(()) -} - -fn print_signed_contract_msg( - private_key: &identity::PrivateKey, - raw_msg: &str, - output: OutputFormat, -) { - let trimmed = raw_msg.trim(); - eprintln!(">>> attempting to sign {trimmed}"); - - let Ok(decoded) = bs58::decode(trimmed).into_vec() else { - println!("it seems you have incorrectly copied the message to sign. Make sure you didn't accidentally skip any characters"); - return; - }; - - eprintln!(">>> decoding the message..."); - - // we don't really care about what particular information is embedded inside of it, - // we just want to know if user correctly copied the string, i.e. whether it's a valid bs58 encoded json - if serde_json::from_slice::(&decoded).is_err() { - println!("it seems you have incorrectly copied the message to sign. Make sure you didn't accidentally skip any characters"); - return; - }; - - // if this is a valid json, it MUST be a valid string - #[allow(clippy::unwrap_used)] - let decoded_string = String::from_utf8(decoded.clone()).unwrap(); - let signature = private_key.sign(&decoded).to_base58_string(); - - let sign_output = ConsoleSigningOutput::new(decoded_string, signature); - println!("{}", output.format(&sign_output)); -} - -pub fn execute(args: Sign) -> anyhow::Result<()> { - let mut config = try_load_current_config(&args.id)?; - config = try_override_config(config, OverrideConfig::default())?; - - let output = args.output; - let signed_target = SignedTarget::try_from(args)?; - - let identity_keypair = load_identity_keys(&config)?; - - match signed_target { - SignedTarget::Text(text) => { - print_signed_text(identity_keypair.private_key(), &text, output)? - } - SignedTarget::Address(addr) => { - print_signed_address(identity_keypair.private_key(), addr, output)? - } - SignedTarget::ContractMsg(raw_msg) => { - print_signed_contract_msg(identity_keypair.private_key(), &raw_msg, output) - } - } - - Ok(()) -} diff --git a/gateway/src/config/mod.rs b/gateway/src/config.rs similarity index 54% rename from gateway/src/config/mod.rs rename to gateway/src/config.rs index 7605987a79..0c2d7568d0 100644 --- a/gateway/src/config/mod.rs +++ b/gateway/src/config.rs @@ -1,32 +1,13 @@ -// Copyright 2020-2023 - Nym Technologies SA +// Copyright 2024 - Nym Technologies SA // SPDX-License-Identifier: GPL-3.0-only -use crate::config::template::CONFIG_TEMPLATE; -use nym_bin_common::logging::LoggingSettings; -use nym_config::defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT}; -use nym_config::helpers::inaddr_any; -use nym_config::serde_helpers::{de_maybe_port, de_maybe_stringified}; -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 nym_network_defaults::{mainnet, DEFAULT_NYM_NODE_HTTP_PORT, TICKETBOOK_VALIDITY_DAYS}; -use serde::{Deserialize, Serialize}; -use std::io; +use nym_network_defaults::{DEFAULT_NYM_NODE_HTTP_PORT, TICKETBOOK_VALIDITY_DAYS}; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::time::Duration; -use tracing::{debug, warn}; use url::Url; use zeroize::{Zeroize, ZeroizeOnDrop}; -pub use crate::config::persistence::paths::GatewayPaths; - -pub mod persistence; -mod template; - -const DEFAULT_GATEWAYS_DIR: &str = "gateways"; - // 'DEBUG' // where applicable, the below are defined in milliseconds const DEFAULT_PRESENCE_SENDING_DELAY: Duration = Duration::from_millis(10_000); @@ -41,224 +22,42 @@ const DEFAULT_MESSAGE_RETRIEVAL_LIMIT: i64 = 100; const DEFAULT_CLIENT_BANDWIDTH_MAX_FLUSHING_RATE: Duration = Duration::from_millis(5); const DEFAULT_CLIENT_BANDWIDTH_MAX_DELTA_FLUSHING_AMOUNT: i64 = 512 * 1024; // 512kB -/// Derive default path to gateway's config directory. -/// It should get resolved to `$HOME/.nym/gateways//config` -pub fn default_config_directory>(id: P) -> PathBuf { - must_get_home() - .join(NYM_DIR) - .join(DEFAULT_GATEWAYS_DIR) - .join(id) - .join(DEFAULT_CONFIG_DIR) -} - -/// Derive default path to gateways's config file. -/// It should get resolved to `$HOME/.nym/gateways//config/config.toml` -pub fn default_config_filepath>(id: P) -> PathBuf { - default_config_directory(id).join(DEFAULT_CONFIG_FILENAME) -} - -/// Derive default path to gateways's data directory where files, such as keys, are stored. -/// It should get resolved to `$HOME/.nym/gateways//data` -pub fn default_data_directory>(id: P) -> PathBuf { - must_get_home() - .join(NYM_DIR) - .join(DEFAULT_GATEWAYS_DIR) - .join(id) - .join(DEFAULT_DATA_DIR) -} - -#[derive(Debug, Deserialize, Serialize)] -#[serde(deny_unknown_fields)] +#[derive(Debug)] pub struct Config { - // additional metadata holding on-disk location of this config file - #[serde(skip)] - pub(crate) save_path: Option, - pub host: Host, - #[serde(default)] pub http: Http, pub gateway: Gateway, - pub storage_paths: GatewayPaths, - + // pub storage_paths: GatewayPaths, pub network_requester: NetworkRequester, - #[serde(default)] pub ip_packet_router: IpPacketRouter, - #[serde(default)] - pub logging: LoggingSettings, - - #[serde(default)] pub debug: Debug, } -impl NymConfigTemplate for Config { - fn template(&self) -> &'static str { - CONFIG_TEMPLATE - } -} - impl Config { - pub fn new>(id: S) -> Self { - let default_gateway = Gateway::new_default(id.as_ref()); - Config { - save_path: None, - host: Host { - // this is a very bad default! - public_ips: vec![default_gateway.listening_address], - hostname: None, - }, - http: Default::default(), - gateway: default_gateway, - storage_paths: GatewayPaths::new_default(id.as_ref()), - network_requester: Default::default(), - ip_packet_router: Default::default(), - logging: Default::default(), - debug: Default::default(), - } - } - #[allow(clippy::too_many_arguments)] pub fn externally_loaded( host: impl Into, http: impl Into, gateway: impl Into, - storage_paths: impl Into, network_requester: impl Into, ip_packet_router: impl Into, - logging: impl Into, debug: impl Into, ) -> Self { Config { - save_path: None, host: host.into(), http: http.into(), gateway: gateway.into(), - storage_paths: storage_paths.into(), network_requester: network_requester.into(), ip_packet_router: ip_packet_router.into(), - logging: logging.into(), debug: debug.into(), } } - // simple wrapper that reads config file and assigns path location - fn read_from_path>(path: P) -> io::Result { - let path = path.as_ref(); - let mut loaded: Config = read_config_from_toml_file(path)?; - loaded.save_path = Some(path.to_path_buf()); - debug!("loaded config file from {}", path.display()); - Ok(loaded) - } - - pub fn read_from_toml_file>(path: P) -> io::Result { - Self::read_from_path(path) - } - - pub fn read_from_default_path>(id: P) -> io::Result { - Self::read_from_path(default_config_filepath(id)) - } - - pub fn default_location(&self) -> PathBuf { - default_config_filepath(&self.gateway.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) - } - - pub fn try_save(&self) -> io::Result<()> { - if let Some(save_location) = &self.save_path { - save_formatted_config_to_file(self, save_location) - } else { - warn!("config file save location is unknown. falling back to the default"); - self.save_to_default_location() - } - } - - #[must_use] - pub fn with_hostname(mut self, hostname: String) -> Self { - self.host.hostname = Some(hostname); - self - } - - #[must_use] - pub fn with_public_ips(mut self, public_ips: Vec) -> Self { - self.host.public_ips = public_ips; - self - } - - pub fn with_enabled_network_requester(mut self, enabled_network_requester: bool) -> Self { - self.network_requester.enabled = enabled_network_requester; - self - } - - pub fn with_default_network_requester_config_path(mut self) -> Self { - self.storage_paths = self - .storage_paths - .with_default_network_requester_config(&self.gateway.id); - self - } - - pub fn with_enabled_ip_packet_router(mut self, enabled_ip_packet_router: bool) -> Self { - self.ip_packet_router.enabled = enabled_ip_packet_router; - self - } - - pub fn with_default_ip_packet_router_config_path(mut self) -> Self { - self.storage_paths = self - .storage_paths - .with_default_ip_packet_router_config(&self.gateway.id); - self - } - - pub fn with_only_coconut_credentials(mut self, only_coconut_credentials: bool) -> Self { - self.gateway.only_coconut_credentials = only_coconut_credentials; - self - } - pub fn with_custom_nym_apis(mut self, nym_api_urls: Vec) -> Self { - self.gateway.nym_api_urls = nym_api_urls; - self - } - - pub fn with_custom_validator_nyxd(mut self, validator_nyxd_urls: Vec) -> Self { - self.gateway.nyxd_urls = validator_nyxd_urls; - self - } - - pub fn with_cosmos_mnemonic(mut self, cosmos_mnemonic: bip39::Mnemonic) -> Self { - self.gateway.cosmos_mnemonic = cosmos_mnemonic; - self - } - - pub fn with_listening_address(mut self, listening_address: IpAddr) -> Self { - self.gateway.listening_address = listening_address; - - let http_port = self.http.bind_address.port(); - self.http.bind_address = SocketAddr::new(listening_address, http_port); - - self - } - - pub fn with_mix_port(mut self, port: u16) -> Self { - self.gateway.mix_port = port; - self - } - - pub fn with_clients_port(mut self, port: u16) -> Self { - self.gateway.clients_port = port; - self - } - - pub fn with_custom_persistent_store(mut self, store_dir: PathBuf) -> Self { - self.storage_paths.clients_storage = store_dir; - self - } - pub fn get_nym_api_endpoints(&self) -> Vec { self.gateway.nym_api_urls.clone() } @@ -273,15 +72,13 @@ impl Config { } // TODO: this is very much a WIP. we need proper ssl certificate support here -#[derive(Debug, Deserialize, PartialEq, Serialize)] -#[serde(deny_unknown_fields)] +#[derive(Debug, PartialEq)] pub struct Host { /// Ip address(es) of this host, such as 1.1.1.1 that external clients will use for connections. pub public_ips: Vec, /// Optional hostname of this node, for example nymtech.net. // TODO: this is temporary. to be replaced by pulling the data directly from the certs. - #[serde(deserialize_with = "de_maybe_stringified")] pub hostname: Option, } @@ -295,15 +92,13 @@ impl Host { } } -#[derive(Debug, Deserialize, PartialEq, Serialize)] -#[serde(deny_unknown_fields)] +#[derive(Debug, PartialEq)] pub struct Http { /// Socket address this node will use for binding its http API. /// default: `0.0.0.0:8000` pub bind_address: SocketAddr, /// Path to assets directory of custom landing page of this node. - #[serde(deserialize_with = "de_maybe_stringified")] pub landing_page_assets_path: Option, } @@ -320,7 +115,7 @@ impl Default for Http { } // we only really care about the mnemonic being zeroized -#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Zeroize, ZeroizeOnDrop)] +#[derive(Debug, PartialEq, Eq, Zeroize, ZeroizeOnDrop)] pub struct Gateway { /// Version of the gateway for which this configuration was created. pub version: String, @@ -330,7 +125,6 @@ pub struct Gateway { /// Indicates whether this gateway is accepting only coconut credentials for accessing the /// the mixnet, or if it also accepts non-paying clients - #[serde(default)] pub only_coconut_credentials: bool, /// Address to which this mixnode will bind to and will be listening for packets. @@ -347,16 +141,13 @@ pub struct Gateway { /// If applicable, announced port for listening for secure websocket client traffic. /// (default: None) - #[serde(deserialize_with = "de_maybe_port")] pub clients_wss_port: Option, /// Addresses to APIs from which the node gets the view of the network. - #[serde(alias = "validator_api_urls")] #[zeroize(skip)] pub nym_api_urls: Vec, /// Addresses to validators which the node uses to check for double spending of ERC20 tokens. - #[serde(alias = "validator_nymd_urls")] #[zeroize(skip)] pub nyxd_urls: Vec, @@ -366,28 +157,7 @@ pub struct Gateway { pub cosmos_mnemonic: bip39::Mnemonic, } -impl Gateway { - pub fn new_default>(id: S) -> Self { - // allow usage of `expect` here as our default mainnet values should have been well-formed. - #[allow(clippy::expect_used)] - Gateway { - version: env!("CARGO_PKG_VERSION").to_string(), - id: id.into(), - only_coconut_credentials: false, - listening_address: inaddr_any(), - mix_port: DEFAULT_MIX_LISTENING_PORT, - clients_port: DEFAULT_CLIENT_LISTENING_PORT, - clients_wss_port: None, - nym_api_urls: vec![mainnet::NYM_API.parse().expect("Invalid default API URL")], - nyxd_urls: vec![mainnet::NYXD_URL.parse().expect("Invalid default nyxd URL")], - cosmos_mnemonic: bip39::Mnemonic::generate(24) - .expect("failed to generate fresh mnemonic"), - } - } -} - -#[derive(Debug, Deserialize, PartialEq, Serialize)] -#[serde(default)] +#[derive(Debug, PartialEq)] pub struct NetworkRequester { /// Specifies whether network requester service is enabled in this process. pub enabled: bool, @@ -400,8 +170,7 @@ impl Default for NetworkRequester { } } -#[derive(Debug, Deserialize, PartialEq, Serialize)] -#[serde(default)] +#[derive(Debug, PartialEq)] pub struct IpPacketRouter { /// Specifies whether ip packet router service is enabled in this process. pub enabled: bool, @@ -414,28 +183,23 @@ impl Default for IpPacketRouter { } } -#[derive(Debug, Deserialize, Serialize)] -#[serde(default)] +#[derive(Debug)] pub struct Debug { /// Initial value of an exponential backoff to reconnect to dropped TCP connection when /// forwarding sphinx packets. - #[serde(with = "humantime_serde")] 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")] pub packet_forwarding_maximum_backoff: Duration, /// Timeout for establishing initial connection when trying to forward a sphinx packet. - #[serde(with = "humantime_serde")] pub initial_connection_timeout: Duration, /// Maximum number of packets that can be stored waiting to get sent to a particular connection. pub maximum_connection_buffer_size: usize, /// Delay between each subsequent presence data being sent. - #[serde(with = "humantime_serde")] // DEAD FIELD pub presence_sending_delay: Duration, @@ -447,7 +211,6 @@ pub struct Debug { pub message_retrieval_limit: i64, /// Defines maximum delay between client bandwidth information being flushed to the persistent storage. - #[serde(with = "humantime_serde")] pub client_bandwidth_max_flushing_rate: Duration, /// Defines a maximum change in client bandwidth before it gets flushed to the persistent storage. @@ -459,7 +222,6 @@ pub struct Debug { // It shall be disabled in the subsequent releases. pub use_legacy_framed_packet_version: bool, - #[serde(default)] pub zk_nym_tickets: ZkNymTicketHandlerDebug, } @@ -482,7 +244,7 @@ impl Default for Debug { } } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone)] pub struct ZkNymTicketHandlerDebug { /// Specifies the multiplier for revoking a malformed/double-spent ticket /// (if it has to go all the way to the nym-api for verification) @@ -492,7 +254,6 @@ pub struct ZkNymTicketHandlerDebug { /// Specifies the interval for attempting to resolve any failed, pending operations, /// such as ticket verification or redemption. - #[serde(with = "humantime_serde")] pub pending_poller: Duration, pub minimum_api_quorum: f32, @@ -502,7 +263,6 @@ pub struct ZkNymTicketHandlerDebug { /// Specifies the maximum time between two subsequent tickets redemptions. /// That's required as nym-apis will purge all ticket information for tickets older than maximum validity. - #[serde(with = "humantime_serde")] pub maximum_time_between_redemption: Duration, } diff --git a/gateway/src/config/persistence/mod.rs b/gateway/src/config/persistence/mod.rs deleted file mode 100644 index d2b55c30f5..0000000000 --- a/gateway/src/config/persistence/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright 2020 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -pub mod paths; diff --git a/gateway/src/config/persistence/paths.rs b/gateway/src/config/persistence/paths.rs deleted file mode 100644 index 4062d41bd1..0000000000 --- a/gateway/src/config/persistence/paths.rs +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright 2020-2023 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use crate::config::{default_config_directory, default_data_directory}; -use nym_config::serde_helpers::de_maybe_stringified; -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_CLIENTS_STORAGE_FILENAME: &str = "db.sqlite"; -pub const DEFAULT_STATS_STORAGE_FILENAME: &str = "stats.sqlite"; - -pub const DEFAULT_NETWORK_REQUESTER_CONFIG_FILENAME: &str = "network_requester_config.toml"; -pub const DEFAULT_NETWORK_REQUESTER_DATA_DIR: &str = "network-requester-data"; - -pub const DEFAULT_IP_PACKET_ROUTER_CONFIG_FILENAME: &str = "ip_packet_router_config.toml"; -pub const DEFAULT_IP_PACKET_ROUTER_DATA_DIR: &str = "ip-packet-router-data"; - -// pub const DEFAULT_DESCRIPTION_FILENAME: &str = "description.toml"; - -pub fn default_network_requester_data_dir>(id: P) -> PathBuf { - default_data_directory(id).join(DEFAULT_NETWORK_REQUESTER_DATA_DIR) -} - -pub fn default_ip_packet_router_data_dir>(id: P) -> PathBuf { - default_data_directory(id).join(DEFAULT_IP_PACKET_ROUTER_DATA_DIR) -} - -#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)] -#[serde(deny_unknown_fields)] -pub struct GatewayPaths { - pub keys: KeysPaths, - - /// Path to sqlite database containing all persistent data: messages for offline clients, - /// derived shared keys and available client bandwidths. - #[serde(alias = "persistent_storage")] - pub clients_storage: PathBuf, - - /// Path to sqlite database containing all persistent stats data. - pub stats_storage: PathBuf, - - /// Path to the configuration of the embedded network requester. - #[serde(deserialize_with = "de_maybe_stringified")] - pub network_requester_config: Option, - // pub node_description: PathBuf, - - // pub cosmos_bip39_mnemonic: PathBuf, - /// Path to the configuration of the embedded ip packet router. - #[serde(deserialize_with = "de_maybe_stringified")] - pub ip_packet_router_config: Option, -} - -impl GatewayPaths { - pub fn new_default>(id: P) -> Self { - GatewayPaths { - keys: KeysPaths::new_default(id.as_ref()), - clients_storage: default_data_directory(id.as_ref()) - .join(DEFAULT_CLIENTS_STORAGE_FILENAME), - stats_storage: default_data_directory(id).join(DEFAULT_STATS_STORAGE_FILENAME), - // node_description: default_config_filepath(id).join(DEFAULT_DESCRIPTION_FILENAME), - network_requester_config: None, - ip_packet_router_config: None, - } - } - - pub fn new_empty() -> Self { - GatewayPaths { - keys: KeysPaths { - private_identity_key_file: Default::default(), - public_identity_key_file: Default::default(), - private_sphinx_key_file: Default::default(), - public_sphinx_key_file: Default::default(), - }, - clients_storage: Default::default(), - stats_storage: Default::default(), - network_requester_config: None, - ip_packet_router_config: None, - } - } - - #[must_use] - pub fn with_network_requester_config>(mut self, path: P) -> Self { - self.network_requester_config = Some(path.as_ref().into()); - self - } - - #[must_use] - pub fn with_default_network_requester_config>(self, id: P) -> Self { - self.with_network_requester_config( - default_config_directory(id).join(DEFAULT_NETWORK_REQUESTER_CONFIG_FILENAME), - ) - } - - #[must_use] - pub fn with_ip_packet_router_config>(mut self, path: P) -> Self { - self.ip_packet_router_config = Some(path.as_ref().into()); - self - } - - #[must_use] - pub fn with_default_ip_packet_router_config>(self, id: P) -> Self { - self.with_ip_packet_router_config( - default_config_directory(id).join(DEFAULT_IP_PACKET_ROUTER_CONFIG_FILENAME), - ) - } - - pub fn network_requester_config(&self) -> &Option { - &self.network_requester_config - } - - pub fn ip_packet_router_config(&self) -> &Option { - &self.ip_packet_router_config - } - - 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>(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 - } -} diff --git a/gateway/src/config/template.rs b/gateway/src/config/template.rs deleted file mode 100644 index 2f1a34d822..0000000000 --- a/gateway/src/config/template.rs +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2020-2023 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -// 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 - -##### main base gateway config options ##### - -[host] -# Ip address(es) of this host, such as 1.1.1.1 that external clients will use for connections. -public_ips = [ - {{#each host.public_ips }} - '{{this}}', - {{/each}} -] - -# (temporary) Optional hostname of this node, for example nymtech.net. -hostname = '{{ host.hostname }}' - -[gateway] -# Version of the gateway for which this configuration was created. -version = '{{ gateway.version }}' - -# Human readable ID of this particular gateway. -id = '{{ gateway.id }}' - -# Indicates whether this gateway is accepting only coconut credentials for accessing the -# the mixnet, or if it also accepts non-paying clients -only_coconut_credentials = {{ gateway.only_coconut_credentials }} - -# Socket address to which this gateway will bind to and will be listening for packets. -listening_address = '{{ gateway.listening_address }}' - -# Port used for listening for all mixnet traffic. -# (default: 1789) -mix_port = {{ gateway.mix_port }} - -# Port used for listening for all client websocket traffic. -# (default: 9000) -clients_port = {{ gateway.clients_port }} - -# If applicable, announced port for listening for secure websocket client traffic. -# (default: 0 - disabled) -clients_wss_port ={{#if gateway.clients_wss_port }} {{ gateway.clients_wss_port }} {{else}} 0 {{/if}} - -# Addresses to APIs running on validator from which the node gets the view of the network. -nym_api_urls = [ - {{#each gateway.nym_api_urls }} - '{{this}}', - {{/each}} -] - -# Addresses to validators which the node uses to check for double spending of nym tokens. -nyxd_urls = [ - {{#each gateway.nyxd_urls }} - '{{this}}', - {{/each}} -] - -cosmos_mnemonic = '{{ gateway.cosmos_mnemonic }}' - -[http] -# Socket address this node will use for binding its http API. -# default: `0.0.0.0:8080` -bind_address = '{{ http.bind_address }}' - -# Path to assets directory of custom landing page of this node -landing_page_assets_path = '{{ http.landing_page_assets_path }}' - -[network_requester] -# Specifies whether network requester service is enabled in this process. -enabled = {{ network_requester.enabled }} - -[ip_packet_router] -# Specifies whether ip packet router service is enabled in this process. -enabled = {{ ip_packet_router.enabled }} - -[storage_paths] - -# Path to file containing private identity key. -keys.private_identity_key_file = '{{ storage_paths.keys.private_identity_key_file }}' - -# 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 sqlite database containing all persistent data: messages for offline clients, -# derived shared keys and available client bandwidths. -clients_storage = '{{ storage_paths.clients_storage }}' - -# Path to the configuration of the embedded network requester. -network_requester_config = '{{ storage_paths.network_requester_config }}' - -# Path to the configuration of the embedded ip packet router. -ip_packet_router_config = '{{ storage_paths.ip_packet_router_config }}' - -##### logging configuration options ##### - -[logging] - -# TODO - -"#; diff --git a/gateway/src/error.rs b/gateway/src/error.rs index ca2c9fd7bd..fbd85c4654 100644 --- a/gateway/src/error.rs +++ b/gateway/src/error.rs @@ -9,88 +9,13 @@ use nym_network_requester::error::{ClientCoreError, NetworkRequesterError}; use nym_validator_client::nyxd::error::NyxdError; use nym_validator_client::nyxd::{AccountId, Coin}; use nym_validator_client::ValidatorClientError; -use std::io; use std::net::IpAddr; -use std::path::PathBuf; use thiserror::Error; pub use crate::node::client_handling::websocket::connection_handler::authenticated::RequestHandlingError; #[derive(Debug, Error)] pub enum GatewayError { - #[error("failed to load {keys} keys from '{}' (private key) and '{}' (public key): {err}", .paths.private_key_path.display(), .paths.public_key_path.display())] - KeyPairLoadFailure { - keys: String, - paths: nym_pemstore::KeyPairPath, - #[source] - err: io::Error, - }, - - #[error("failed to load {key} public key from '{}': {err}", .path.display())] - PublicKeyLoadFailure { - key: String, - path: PathBuf, - #[source] - err: io::Error, - }, - - #[error( - "failed to load config file for id {id} using path '{}'. detailed message: {source}", path.display() - )] - ConfigLoadFailure { - id: String, - path: PathBuf, - source: io::Error, - }, - - #[error( - "failed to load config file for network requester (gateway-id: '{id}') using path '{}'. detailed message: {source}", path.display() - )] - NetworkRequesterConfigLoadFailure { - id: String, - path: PathBuf, - source: io::Error, - }, - - #[error( - "failed to load config file for ip packet router (gateway-id: '{id}') using path '{}'. detailed message: {source}", - path.display() - )] - IpPacketRouterConfigLoadFailure { - id: String, - path: PathBuf, - source: io::Error, - }, - - #[error( - "failed to load config file for authenticator (gateway-id: '{id}') using path '{}'. detailed message: {source}", - path.display() - )] - AuthenticatorConfigLoadFailure { - id: String, - path: PathBuf, - source: io::Error, - }, - - #[error( - "failed to load config file for wireguard (gateway-id: '{id}') using path '{}'. detailed message: {source}", - path.display() - )] - WireguardConfigLoadFailure { - id: String, - path: PathBuf, - source: io::Error, - }, - - #[error( - "failed to save config file for id {id} using path '{}'. detailed message: {source}", path.display() - )] - ConfigSaveFailure { - id: String, - path: PathBuf, - source: io::Error, - }, - #[error("the configured version of the gateway ({config_version}) is incompatible with the binary version ({binary_version})")] LocalVersionCheckFailure { binary_version: String, @@ -187,9 +112,6 @@ pub enum GatewayError { #[error("this node attempted to announce an invalid public address: {address}. Please modify [host.public_ips] section of your config. Alternatively, if you wanted to use it in the local setting, run the node with the '--local' flag.")] InvalidPublicIp { address: IpAddr }, - #[error(transparent)] - NymNodeHttpError(#[from] nym_node_http_api::NymNodeHttpError), - #[error("there was an issue with wireguard IP network: {source}")] IpNetworkError { #[from] diff --git a/gateway/src/helpers.rs b/gateway/src/helpers.rs deleted file mode 100644 index 7b17da7170..0000000000 --- a/gateway/src/helpers.rs +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright 2024 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use crate::config::Config; -use crate::node::helpers::load_keypair; -use crate::GatewayError; -use nym_config::OptionalSet; -use nym_crypto::asymmetric::{encryption, identity}; -use nym_pemstore::traits::PemStorableKey; -use nym_pemstore::KeyPairPath; -use nym_sphinx::addressing::clients::Recipient; -use nym_types::gateway::{ - GatewayIpPacketRouterDetails, GatewayNetworkRequesterDetails, GatewayNodeDetailsResponse, -}; -use std::path::Path; -use tracing::info; - -pub use crate::node::helpers::{load_ip_packet_router_config, load_network_requester_config}; - -fn display_maybe_path>(path: Option

) -> String { - path.as_ref() - .map(|p| p.as_ref().display().to_string()) - .unwrap_or_default() -} - -fn display_path>(path: P) -> String { - path.as_ref().display().to_string() -} - -#[derive(Default)] -pub struct OverrideNetworkRequesterConfig { - pub fastmode: bool, - pub no_cover: bool, - pub medium_toggle: bool, - - pub open_proxy: Option, -} - -#[derive(Default)] -pub struct OverrideIpPacketRouterConfig { - // TODO -} - -pub fn override_network_requester_config( - mut cfg: nym_network_requester::Config, - opts: Option, -) -> nym_network_requester::Config { - let Some(opts) = opts else { return cfg }; - - // in the old code we had calls to `assert` thus panicking - #[allow(clippy::expect_used)] - cfg.base - .try_apply_traffic_modes( - cfg.network_requester.disable_poisson_rate, - opts.medium_toggle, - opts.fastmode, - opts.no_cover, - ) - .expect("failed to apply traffic modes"); - - cfg.with_optional( - nym_network_requester::Config::with_open_proxy, - opts.open_proxy, - ) -} - -// NOTE: make sure this is in sync with service-providers/ip-packet-router/src/cli/mod.rs::override_config -pub fn override_ip_packet_router_config( - mut cfg: nym_ip_packet_router::Config, - opts: Option, -) -> nym_ip_packet_router::Config { - let Some(_opts) = opts else { return cfg }; - - // disable poisson rate in the BASE client if the IPR option is enabled - if cfg.ip_packet_router.disable_poisson_rate { - info!("Disabling poisson rate for ip packet router"); - cfg.set_no_poisson_process(); - } - - cfg -} - -pub fn load_public_key(path: P, name: S) -> Result -where - T: PemStorableKey, - P: AsRef, - S: Into, -{ - nym_pemstore::load_key(path.as_ref()).map_err(|err| GatewayError::PublicKeyLoadFailure { - key: name.into(), - path: path.as_ref().to_path_buf(), - err, - }) -} - -/// Loads identity keys stored on disk -pub fn load_identity_keys(config: &Config) -> Result { - let identity_paths = KeyPairPath::new( - config.storage_paths.keys.private_identity_key(), - config.storage_paths.keys.public_identity_key(), - ); - load_keypair(identity_paths, "gateway identity") -} - -pub async fn node_details(config: &Config) -> Result { - let gateway_identity_public_key: identity::PublicKey = load_public_key( - &config.storage_paths.keys.public_identity_key_file, - "gateway identity", - )?; - - let gateway_sphinx_public_key: encryption::PublicKey = load_public_key( - &config.storage_paths.keys.public_sphinx_key_file, - "gateway sphinx", - )?; - - let network_requester = - if let Some(nr_cfg_path) = &config.storage_paths.network_requester_config { - let cfg = load_network_requester_config(&config.gateway.id, nr_cfg_path).await?; - - let nr_identity_public_key: identity::PublicKey = load_public_key( - &cfg.storage_paths.common_paths.keys.public_identity_key_file, - "network requester identity", - )?; - - let nr_encryption_key: encryption::PublicKey = load_public_key( - &cfg.storage_paths - .common_paths - .keys - .public_encryption_key_file, - "network requester encryption", - )?; - - let address = Recipient::new( - nr_identity_public_key, - nr_encryption_key, - gateway_identity_public_key, - ); - - Some(GatewayNetworkRequesterDetails { - enabled: config.network_requester.enabled, - identity_key: nr_identity_public_key.to_base58_string(), - encryption_key: nr_encryption_key.to_base58_string(), - open_proxy: cfg.network_requester.open_proxy, - address: address.to_string(), - config_path: display_path(nr_cfg_path), - }) - } else { - None - }; - - let ip_packet_router = if let Some(nr_cfg_path) = &config.storage_paths.ip_packet_router_config - { - let cfg = load_ip_packet_router_config(&config.gateway.id, nr_cfg_path).await?; - - let nr_identity_public_key: identity::PublicKey = load_public_key( - &cfg.storage_paths.common_paths.keys.public_identity_key_file, - "ip packet router identity", - )?; - - let nr_encryption_key: encryption::PublicKey = load_public_key( - &cfg.storage_paths - .common_paths - .keys - .public_encryption_key_file, - "ip packet router encryption", - )?; - - let address = Recipient::new( - nr_identity_public_key, - nr_encryption_key, - gateway_identity_public_key, - ); - - Some(GatewayIpPacketRouterDetails { - enabled: config.ip_packet_router.enabled, - identity_key: nr_identity_public_key.to_base58_string(), - encryption_key: nr_encryption_key.to_base58_string(), - address: address.to_string(), - config_path: display_path(nr_cfg_path), - }) - } else { - None - }; - - Ok(GatewayNodeDetailsResponse { - identity_key: gateway_identity_public_key.to_base58_string(), - sphinx_key: gateway_sphinx_public_key.to_base58_string(), - bind_address: config.gateway.listening_address.to_string(), - mix_port: config.gateway.mix_port, - clients_port: config.gateway.clients_port, - config_path: display_maybe_path(config.save_path.as_ref()), - data_store: display_path(&config.storage_paths.clients_storage), - network_requester, - ip_packet_router, - }) -} diff --git a/gateway/src/http/mod.rs b/gateway/src/http/mod.rs deleted file mode 100644 index a3a697d56f..0000000000 --- a/gateway/src/http/mod.rs +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright 2023-2024 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use crate::config::Config; -use crate::error::GatewayError; -use crate::helpers::load_public_key; -use nym_bin_common::bin_info_owned; -use nym_crypto::asymmetric::{encryption, identity}; -use nym_network_requester::RequestFilter; -use nym_node_http_api::api::api_requests; -use nym_node_http_api::api::api_requests::v1::network_requester::exit_policy::models::UsedExitPolicy; -use nym_node_http_api::api::api_requests::SignedHostInformation; -use nym_node_http_api::NymNodeHttpError; -use nym_sphinx::addressing::clients::Recipient; -use nym_task::TaskClient; -use std::sync::Arc; -use tracing::{debug, error, warn}; - -fn load_gateway_details( - config: &Config, -) -> Result { - Ok(api_requests::v1::gateway::models::Gateway { - enforces_zk_nyms: config.gateway.only_coconut_credentials, - client_interfaces: api_requests::v1::gateway::models::ClientInterfaces { - wireguard: None, - mixnet_websockets: Some(api_requests::v1::gateway::models::WebSockets { - ws_port: config.gateway.clients_port, - wss_port: config.gateway.clients_wss_port, - }), - }, - }) -} - -fn load_host_details( - config: &Config, - sphinx_key: &encryption::PublicKey, - identity_keypair: &identity::KeyPair, -) -> Result { - let host_info = api_requests::v1::node::models::HostInformation { - ip_address: config.host.public_ips.clone(), - hostname: config.host.hostname.clone(), - keys: api_requests::v1::node::models::HostKeys { - ed25519_identity: *identity_keypair.public_key(), - x25519_sphinx: *sphinx_key, - x25519_noise: None, - }, - }; - - let signed_info = SignedHostInformation::new(host_info, identity_keypair.private_key()) - .map_err(NymNodeHttpError::from)?; - Ok(signed_info) -} - -fn load_network_requester_details( - config: &Config, - network_requester_config: &nym_network_requester::Config, -) -> Result { - let identity_public_key: identity::PublicKey = load_public_key( - &network_requester_config - .storage_paths - .common_paths - .keys - .public_identity_key_file, - "network requester identity", - )?; - - let dh_public_key: encryption::PublicKey = load_public_key( - &network_requester_config - .storage_paths - .common_paths - .keys - .public_encryption_key_file, - "network requester diffie hellman", - )?; - - let gateway_identity_public_key: identity::PublicKey = load_public_key( - &config.storage_paths.keys.public_identity_key_file, - "gateway identity", - )?; - - Ok( - api_requests::v1::network_requester::models::NetworkRequester { - encoded_identity_key: identity_public_key.to_base58_string(), - encoded_x25519_key: dh_public_key.to_base58_string(), - address: Recipient::new( - identity_public_key, - dh_public_key, - gateway_identity_public_key, - ) - .to_string(), - }, - ) -} - -fn load_ip_packet_router_details( - config: &Config, - ip_packet_router_config: &nym_ip_packet_router::Config, -) -> Result { - let identity_public_key: identity::PublicKey = load_public_key( - &ip_packet_router_config - .storage_paths - .common_paths - .keys - .public_identity_key_file, - "ip packet router identity", - )?; - - let dh_public_key: encryption::PublicKey = load_public_key( - &ip_packet_router_config - .storage_paths - .common_paths - .keys - .public_encryption_key_file, - "ip packet router diffie hellman", - )?; - - let gateway_identity_public_key: identity::PublicKey = load_public_key( - &config.storage_paths.keys.public_identity_key_file, - "gateway identity", - )?; - - Ok(api_requests::v1::ip_packet_router::models::IpPacketRouter { - encoded_identity_key: identity_public_key.to_base58_string(), - encoded_x25519_key: dh_public_key.to_base58_string(), - address: Recipient::new( - identity_public_key, - dh_public_key, - gateway_identity_public_key, - ) - .to_string(), - }) -} - -pub(crate) struct HttpApiBuilder<'a> { - gateway_config: &'a Config, - network_requester_config: Option<&'a nym_network_requester::Config>, - exit_policy: Option, - ip_packet_router_config: Option<&'a nym_ip_packet_router::Config>, - - identity_keypair: &'a identity::KeyPair, - // TODO: this should be a wg specific key and not re-used sphinx - sphinx_keypair: Arc, -} - -impl<'a> HttpApiBuilder<'a> { - pub(crate) fn new( - gateway_config: &'a Config, - identity_keypair: &'a identity::KeyPair, - sphinx_keypair: Arc, - ) -> Self { - HttpApiBuilder { - gateway_config, - network_requester_config: None, - ip_packet_router_config: None, - exit_policy: None, - identity_keypair, - sphinx_keypair, - } - } - - #[must_use] - pub(crate) fn with_maybe_network_requester( - mut self, - network_requester_config: Option<&'a nym_network_requester::Config>, - ) -> Self { - self.network_requester_config = network_requester_config; - self - } - - #[must_use] - pub(crate) fn with_maybe_network_request_filter( - mut self, - request_filter: Option, - ) -> Self { - let Some(request_filter) = request_filter else { - warn!("no valid request filter has been passed. no changes will be made"); - return self; - }; - - // we can cheat here a bit since we're not refreshing the exit policy - // thus: - // - we can ignore the Arc pointer and clone the inner value - // - we can set the last refresh time to the current time - // - // once we start refreshing it, we'll have to change it, but at that point - // the allow list will be probably be completely removed and thus the pointer management - // will be much easier - - let upstream = request_filter.current_exit_policy_filter().upstream(); - - // if there's no upstream (i.e. open proxy), we couldn't have possibly updated it : ) - let last_updated = if upstream.is_some() { - #[allow(clippy::expect_used)] - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .expect("system clock is set to before the unix epoch") - .as_secs() - } else { - 0 - }; - - self.exit_policy = Some(UsedExitPolicy { - enabled: true, - upstream_source: upstream.map(|u| u.to_string()).unwrap_or_default(), - last_updated, - policy: Some(request_filter.current_exit_policy_filter().policy().clone()), - }); - - self - } - - #[must_use] - pub(crate) fn with_maybe_ip_packet_router( - mut self, - ip_packet_router_config: Option<&'a nym_ip_packet_router::Config>, - ) -> Self { - self.ip_packet_router_config = ip_packet_router_config; - self - } - - pub(crate) fn start(self, task_client: TaskClient) -> Result<(), GatewayError> { - debug!("starting http API"); - - // is it suboptimal to load all the keys, etc for the second time after they've already been - // retrieved during startup of the rest of the components? - // yes, a bit. - // but in the grand scheme of things performance penalty is negligible since it's only happening on startup - // and makes the code a bit nicer to manage. on top of it, all of it will refactored anyway at some point - // (famous last words, eh? - 22.09.23) - let mut config = nym_node_http_api::Config::new( - bin_info_owned!(), - load_host_details( - self.gateway_config, - self.sphinx_keypair.public_key(), - self.identity_keypair, - )?, - ) - .with_gateway(load_gateway_details(self.gateway_config)?) - .with_landing_page_assets(self.gateway_config.http.landing_page_assets_path.as_ref()); - - if let Some(nr_config) = self.network_requester_config { - config = config.with_network_requester(load_network_requester_details( - self.gateway_config, - nr_config, - )?); - - if let Some(exit_policy) = self.exit_policy { - config = config.with_used_exit_policy(exit_policy) - } - } - - if let Some(ipr_config) = self.ip_packet_router_config { - config = config.with_ip_packet_router(load_ip_packet_router_details( - self.gateway_config, - ipr_config, - )?); - } - - let bind_address = self.gateway_config.http.bind_address; - let router = nym_node_http_api::NymNodeRouter::new(config, None); - - tokio::spawn(async move { - let server = match router.build_server(&bind_address).await { - Ok(server) => server.with_task_client(task_client), - Err(err) => { - error!("failed to create http server: {err}"); - return; - } - }; - server.run().await - }); - Ok(()) - } -} diff --git a/gateway/src/lib.rs b/gateway/src/lib.rs index f6181ac62a..0f19d6f7c5 100644 --- a/gateway/src/lib.rs +++ b/gateway/src/lib.rs @@ -6,9 +6,7 @@ pub mod config; pub mod error; -pub mod helpers; -pub(crate) mod http; pub mod node; pub use error::GatewayError; -pub use node::{create_gateway, Gateway}; +pub use node::Gateway; diff --git a/gateway/src/main.rs b/gateway/src/main.rs deleted file mode 100644 index 6759180420..0000000000 --- a/gateway/src/main.rs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2020-2023 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -#![warn(clippy::expect_used)] -#![warn(clippy::unwrap_used)] - -use clap::{crate_name, crate_version, Parser}; -use colored::Colorize; -use nym_bin_common::bin_info; -use nym_bin_common::logging::{maybe_print_banner, setup_logging}; -use nym_network_defaults::setup_env; -use std::io::IsTerminal; -use std::sync::OnceLock; -use tracing::error; - -mod commands; - -fn pretty_build_info_static() -> &'static str { - static PRETTY_BUILD_INFORMATION: OnceLock = OnceLock::new(); - PRETTY_BUILD_INFORMATION.get_or_init(|| bin_info!().pretty_print()) -} - -#[derive(Parser)] -#[clap(author = "Nymtech", version, about, long_version = pretty_build_info_static())] -struct Cli { - /// Path pointing to an env file that configures the gateway. - #[clap(short, long)] - pub(crate) config_env_file: Option, - - /// Force run the binary bypassing the deprecation in favour of nym-node - #[clap(long, hide = true)] - pub(crate) force_run: bool, - - /// Flag used for disabling the printed banner in tty. - #[clap(long)] - pub(crate) no_banner: bool, - - #[clap(subcommand)] - command: commands::Commands, -} - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - setup_logging(); - - let args = Cli::parse(); - setup_env(args.config_env_file.as_ref()); - - if !args.no_banner { - maybe_print_banner(crate_name!(), crate_version!()); - } - - commands::execute(args).await.map_err(|err| { - if std::io::stdout().is_terminal() { - let error_message = format!("{err}").red(); - error!("{error_message}"); - error!("Exiting..."); - } - - err - }) -} - -#[cfg(test)] -mod tests { - use super::*; - use clap::CommandFactory; - - #[test] - fn verify_cli() { - Cli::command().debug_assert(); - } -} diff --git a/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs b/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs index 1b60fb6956..fd4f200177 100644 --- a/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs +++ b/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs @@ -553,7 +553,7 @@ where while !shutdown.is_shutdown() { tokio::select! { _ = shutdown.recv() => { - log::trace!("client_handling::AuthenticatedHandler: received shutdown"); + trace!("client_handling::AuthenticatedHandler: received shutdown"); }, // Received a request to ping the client to check if it's still active tx = self.is_active_request_receiver.next() => { diff --git a/gateway/src/node/client_handling/websocket/listener.rs b/gateway/src/node/client_handling/websocket/listener.rs index be3c6c51a2..425123d3e9 100644 --- a/gateway/src/node/client_handling/websocket/listener.rs +++ b/gateway/src/node/client_handling/websocket/listener.rs @@ -49,7 +49,7 @@ where tokio::select! { biased; _ = shutdown.recv() => { - log::trace!("client_handling::Listener: received shutdown"); + trace!("client_handling::Listener: received shutdown"); } connection = tcp_listener.accept() => { match connection { diff --git a/gateway/src/node/helpers.rs b/gateway/src/node/helpers.rs index 1c54e0a1b3..d0862f1688 100644 --- a/gateway/src/node/helpers.rs +++ b/gateway/src/node/helpers.rs @@ -1,113 +1,14 @@ // Copyright 2023 - Nym Technologies SA // SPDX-License-Identifier: GPL-3.0-only -use crate::config::Config; -use crate::error::GatewayError; use async_trait::async_trait; -use nym_crypto::asymmetric::encryption; -use nym_gateway_stats_storage::PersistentStatsStorage; -use nym_gateway_storage::PersistentStorage; -use nym_pemstore::traits::PemStorableKeyPair; -use nym_pemstore::KeyPairPath; use nym_sdk::{NymApiTopologyProvider, NymApiTopologyProviderConfig, UserAgent}; use nym_topology::{gateway, NymTopology, TopologyProvider}; -use std::path::Path; use std::sync::Arc; use tokio::sync::Mutex; use tracing::debug; use url::Url; -pub async fn load_network_requester_config>( - id: &str, - path: P, -) -> Result { - let path = path.as_ref(); - if let Ok(cfg) = read_network_requester_config(id, path) { - return Ok(cfg); - } - - nym_network_requester::config::helpers::try_upgrade_config(path).await?; - read_network_requester_config(id, path) -} - -pub async fn load_ip_packet_router_config>( - id: &str, - path: P, -) -> Result { - let path = path.as_ref(); - if let Ok(cfg) = read_ip_packet_router_config(id, path) { - return Ok(cfg); - } - - nym_ip_packet_router::config::helpers::try_upgrade_config(path).await?; - read_ip_packet_router_config(id, path) -} - -pub fn read_network_requester_config>( - id: &str, - path: P, -) -> Result { - let path = path.as_ref(); - nym_network_requester::Config::read_from_toml_file(path).map_err(|err| { - GatewayError::NetworkRequesterConfigLoadFailure { - id: id.to_string(), - path: path.to_path_buf(), - source: err, - } - }) -} - -pub fn read_ip_packet_router_config>( - id: &str, - path: P, -) -> Result { - let path = path.as_ref(); - nym_ip_packet_router::Config::read_from_toml_file(path).map_err(|err| { - GatewayError::IpPacketRouterConfigLoadFailure { - id: id.to_string(), - path: path.to_path_buf(), - source: err, - } - }) -} - -pub(crate) async fn initialise_main_storage( - config: &Config, -) -> Result { - let path = &config.storage_paths.clients_storage; - let retrieval_limit = config.debug.message_retrieval_limit; - - Ok(PersistentStorage::init(path, retrieval_limit).await?) -} - -pub(crate) async fn initialise_stats_storage( - config: &Config, -) -> Result { - let path = &config.storage_paths.stats_storage; - - Ok(PersistentStatsStorage::init(path).await?) -} - -pub fn load_keypair( - paths: KeyPairPath, - name: impl Into, -) -> Result { - nym_pemstore::load_keypair(&paths).map_err(|err| GatewayError::KeyPairLoadFailure { - keys: name.into(), - paths, - err, - }) -} - -/// Loads Sphinx keys stored on disk -pub(crate) fn load_sphinx_keys(config: &Config) -> Result { - let sphinx_paths = KeyPairPath::new( - config.storage_paths.keys.private_encryption_key(), - config.storage_paths.keys.public_encryption_key(), - ); - load_keypair(sphinx_paths, "gateway sphinx") -} - #[derive(Clone)] pub struct GatewayTopologyProvider { inner: Arc>, diff --git a/gateway/src/node/mixnet_handling/receiver/connection_handler.rs b/gateway/src/node/mixnet_handling/receiver/connection_handler.rs index 5eb0b0c136..4cb1a23c70 100644 --- a/gateway/src/node/mixnet_handling/receiver/connection_handler.rs +++ b/gateway/src/node/mixnet_handling/receiver/connection_handler.rs @@ -211,7 +211,7 @@ impl ConnectionHandler { tokio::select! { biased; _ = shutdown.recv() => { - log::trace!("ConnectionHandler: received shutdown"); + trace!("ConnectionHandler: received shutdown"); } framed_sphinx_packet = framed_conn.next() => { match framed_sphinx_packet { diff --git a/gateway/src/node/mixnet_handling/receiver/listener.rs b/gateway/src/node/mixnet_handling/receiver/listener.rs index 8f815f6068..86b99108ef 100644 --- a/gateway/src/node/mixnet_handling/receiver/listener.rs +++ b/gateway/src/node/mixnet_handling/receiver/listener.rs @@ -37,7 +37,7 @@ impl Listener { tokio::select! { biased; _ = self.shutdown.recv() => { - log::trace!("mixnet_handling::Listener: Received shutdown"); + trace!("mixnet_handling::Listener: Received shutdown"); } connection = tcp_listener.accept() => { match connection { diff --git a/gateway/src/node/mod.rs b/gateway/src/node/mod.rs index e01683d2ad..c399deefa9 100644 --- a/gateway/src/node/mod.rs +++ b/gateway/src/node/mod.rs @@ -1,44 +1,34 @@ // Copyright 2020-2023 - Nym Technologies SA // SPDX-License-Identifier: GPL-3.0-only -use self::helpers::load_ip_packet_router_config; use crate::config::Config; use crate::error::GatewayError; -use crate::helpers::{ - load_identity_keys, override_ip_packet_router_config, override_network_requester_config, - OverrideIpPacketRouterConfig, OverrideNetworkRequesterConfig, -}; -use crate::http::HttpApiBuilder; use crate::node::client_handling::active_clients::ActiveClientsStore; use crate::node::client_handling::embedded_clients::{LocalEmbeddedClientHandle, MessageRouter}; use crate::node::client_handling::websocket; -use crate::node::helpers::{ - initialise_main_storage, initialise_stats_storage, load_network_requester_config, - GatewayTopologyProvider, -}; +use crate::node::helpers::GatewayTopologyProvider; use crate::node::mixnet_handling::receiver::connection_handler::ConnectionHandler; use futures::channel::{mpsc, oneshot}; -use nym_bin_common::bin_info; use nym_credential_verification::ecash::{ credential_sender::CredentialHandlerConfig, EcashManager, }; use nym_crypto::asymmetric::{encryption, identity}; use nym_mixnet_client::forwarder::{MixForwardingSender, PacketForwarder}; use nym_network_defaults::NymNetworkDetails; -use nym_network_requester::{LocalGateway, NRServiceProviderBuilder, RequestFilter}; +use nym_network_requester::{LocalGateway, NRServiceProviderBuilder}; use nym_node_http_api::state::metrics::SharedSessionStats; use nym_statistics_common::gateways::{self, GatewayStatsReporter}; use nym_task::{TaskClient, TaskHandle, TaskManager}; use nym_topology::NetworkAddress; -use nym_types::gateway::GatewayNodeDetailsResponse; use nym_validator_client::client::NodeId; use nym_validator_client::nyxd::{Coin, CosmWasmClient}; -use nym_validator_client::{nyxd, DirectSigningHttpRpcNyxdClient}; +use nym_validator_client::{nyxd, DirectSigningHttpRpcNyxdClient, UserAgent}; use rand::seq::SliceRandom; use rand::thread_rng; use statistics::GatewayStatisticsCollector; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::path::PathBuf; +use std::process; use std::sync::Arc; use tracing::*; @@ -52,9 +42,6 @@ pub use nym_gateway_storage::{PersistentStorage, Storage}; // TODO: should this struct live here? struct StartedNetworkRequester { - /// Request filter, either an exit policy or the allow list, used by the network requester. - used_request_filter: RequestFilter, - /// Handle to interact with the local network requester handle: LocalEmbeddedClientHandle, } @@ -68,56 +55,6 @@ struct StartedAuthenticator { handle: LocalEmbeddedClientHandle, } -/// Wire up and create Gateway instance -pub async fn create_gateway( - config: Config, - nr_config_override: Option, - ip_config_override: Option, - custom_mixnet: Option, -) -> Result { - // don't attempt to read config if NR is disabled - let network_requester_config = if config.network_requester.enabled { - if let Some(path) = &config.storage_paths.network_requester_config { - let cfg = load_network_requester_config(&config.gateway.id, path).await?; - Some(override_network_requester_config(cfg, nr_config_override)) - } else { - // if NR is enabled, the config path must be specified - return Err(GatewayError::UnspecifiedNetworkRequesterConfig); - } - } else { - None - }; - - // don't attempt to read config if NR is disabled - let ip_packet_router_config = if config.ip_packet_router.enabled { - if let Some(path) = &config.storage_paths.ip_packet_router_config { - let cfg = load_ip_packet_router_config(&config.gateway.id, path).await?; - Some(override_ip_packet_router_config(cfg, ip_config_override)) - } else { - // if IPR is enabled, the config path must be specified - return Err(GatewayError::UnspecifiedIpPacketRouterConfig); - } - } else { - None - }; - - let storage = initialise_main_storage(&config).await?; - - let stats_storage = initialise_stats_storage(&config).await?; - - let nr_opts = network_requester_config.map(|config| LocalNetworkRequesterOpts { - config: config.clone(), - custom_mixnet_path: custom_mixnet.clone(), - }); - - let ip_opts = ip_packet_router_config.map(|config| LocalIpPacketRouterOpts { - config, - custom_mixnet_path: custom_mixnet.clone(), - }); - - Gateway::new(config, nr_opts, ip_opts, storage, stats_storage) -} - #[derive(Debug, Clone)] pub struct LocalNetworkRequesterOpts { pub config: nym_network_requester::Config, @@ -158,40 +95,18 @@ pub struct Gateway { client_storage: St, + user_agent: UserAgent, + stats_storage: PersistentStatsStorage, wireguard_data: Option, session_stats: Option, - run_http_server: bool, task_client: Option, } impl Gateway { - /// Construct from the given `Config` instance. - pub fn new( - config: Config, - network_requester_opts: Option, - ip_packet_router_opts: Option, - client_storage: St, - stats_storage: PersistentStatsStorage, - ) -> Result { - Ok(Gateway { - client_storage, - stats_storage, - identity_keypair: Arc::new(load_identity_keys(&config)?), - sphinx_keypair: Arc::new(helpers::load_sphinx_keys(&config)?), - config, - network_requester_opts, - ip_packet_router_opts, - authenticator_opts: None, - wireguard_data: None, - session_stats: None, - run_http_server: true, - task_client: None, - }) - } #[allow(clippy::too_many_arguments)] pub fn new_loaded( config: Config, @@ -201,6 +116,7 @@ impl Gateway { identity_keypair: Arc, sphinx_keypair: Arc, client_storage: St, + user_agent: UserAgent, stats_storage: PersistentStatsStorage, ) -> Self { Gateway { @@ -211,18 +127,14 @@ impl Gateway { identity_keypair, sphinx_keypair, client_storage, + user_agent, stats_storage, wireguard_data: None, session_stats: None, - run_http_server: true, task_client: None, } } - pub fn disable_http_server(&mut self) { - self.run_http_server = false - } - pub fn set_task_client(&mut self, task_client: TaskClient) { self.task_client = Some(task_client) } @@ -235,15 +147,10 @@ impl Gateway { self.wireguard_data = Some(wireguard_data) } - pub async fn node_details(&self) -> Result { - // TODO: this is doing redundant key loads, but I guess that's fine for now - crate::helpers::node_details(&self.config).await - } - fn gateway_topology_provider(&self) -> GatewayTopologyProvider { GatewayTopologyProvider::new( self.as_topology_node(), - bin_info!().into(), + self.user_agent.clone(), self.config.gateway.nym_api_urls.clone(), ) } @@ -362,7 +269,7 @@ impl Gateway { tokio::spawn(async move { if let Err(e) = authenticator_server.run_service_provider().await { - log::error!("Run authenticator server - {e}"); + error!("Run authenticator server - {e}"); } }); @@ -533,7 +440,6 @@ impl Gateway { info!("the local network requester is running on {address}",); Ok(StartedNetworkRequester { - used_request_filter: start_data.request_filter, handle: LocalEmbeddedClientHandle::new(address, nr_mix_sender), }) } @@ -602,15 +508,6 @@ impl Gateway { Ok(LocalEmbeddedClientHandle::new(address, ipr_mix_sender)) } - fn random_api_client(&self) -> Result { - let endpoints = self.config.get_nym_api_endpoints(); - let nym_api = endpoints - .choose(&mut thread_rng()) - .ok_or(GatewayError::NoNymApisAvailable)?; - - Ok(nym_validator_client::NymApiClient::new(nym_api.clone())) - } - fn random_nyxd_client(&self) -> Result { let endpoints = self.config.get_nyxd_urls(); let validator_nyxd = endpoints @@ -628,21 +525,27 @@ impl Gateway { .map_err(Into::into) } - async fn check_if_bonded(&self) -> Result { + 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()?; - let existing_nodes = match validator_client.get_all_basic_nodes(None).await { - Ok(nodes) => nodes, - Err(err) => { - error!("failed to grab initial network gateways - {err}\n Please try to startup again in few minutes"); - return Err(GatewayError::NetworkGatewaysQueryFailure { source: err }); + for api_url in self.config.get_nym_api_endpoints() { + let client = nym_validator_client::NymApiClient::new(api_url.clone()); + match client.get_all_basic_nodes(None).await { + Ok(nodes) => { + return nodes.iter().any(|node| { + &node.ed25519_identity_pubkey == self.identity_keypair.public_key() + }) + } + Err(err) => { + error!("failed to grab initial network gateways from {api_url}: {err}",); + } } - }; + } - Ok(existing_nodes - .iter() - .any(|node| &node.ed25519_identity_pubkey == self.identity_keypair.public_key())) + error!( + "failed to grab initial network gateways from any of the available apis. Please try to startup again in few minutes", + ); + process::exit(1); } pub async fn run(mut self) -> Result<(), GatewayError> @@ -651,7 +554,7 @@ impl Gateway { { info!("Starting nym gateway!"); - if self.check_if_bonded().await? { + if self.check_if_bonded().await { warn!("You seem to have bonded your gateway before starting it - that's highly unrecommended as in the future it might result in slashing"); } @@ -733,7 +636,7 @@ impl Gateway { stats_event_sender.clone(), ); - let nr_request_filter = if self.config.network_requester.enabled { + if self.config.network_requester.enabled { let embedded_nr = self .start_network_requester( mix_forwarding_channel.clone(), @@ -743,10 +646,8 @@ impl Gateway { .await?; // insert information about embedded NR to the active clients store active_clients_store.insert_embedded(embedded_nr.handle); - Some(embedded_nr.used_request_filter) } else { info!("embedded network requester is disabled"); - None }; if self.config.ip_packet_router.enabled { @@ -778,18 +679,6 @@ impl Gateway { None }; - if self.run_http_server { - HttpApiBuilder::new( - &self.config, - self.identity_keypair.as_ref(), - self.sphinx_keypair.clone(), - ) - .with_maybe_network_requester(self.network_requester_opts.as_ref().map(|o| &o.config)) - .with_maybe_network_request_filter(nr_request_filter) - .with_maybe_ip_packet_router(self.ip_packet_router_opts.as_ref().map(|o| &o.config)) - .start(shutdown.fork("http-api"))?; - } - info!("Finished nym gateway startup procedure - it should now be able to receive mix and client traffic!"); info!( diff --git a/mixnode/Cargo.toml b/mixnode/Cargo.toml index c2546c000c..9255e9e589 100644 --- a/mixnode/Cargo.toml +++ b/mixnode/Cargo.toml @@ -17,30 +17,16 @@ rust-version = "1.70" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -axum = { workspace = true } -anyhow = { workspace = true } -bs58 = { workspace = true } -clap = { workspace = true, features = ["cargo", "derive"] } colored = { workspace = true } -cupid = { workspace = true } -dirs = { workspace = true } futures = { workspace = true } -humantime-serde = { workspace = true } -lazy_static = { workspace = true } -log = { workspace = true } -rand = { workspace = true } -serde = { workspace = true, features = ["derive"] } -serde_json = { workspace = true } -sysinfo = { workspace = true } time.workspace = true tokio = { workspace = true, features = ["rt-multi-thread", "net", "signal"] } tokio-util = { workspace = true, features = ["codec"] } -toml = { workspace = true } url = { workspace = true, features = ["serde"] } +tracing = { workspace = true } thiserror = { workspace = true } # internal -nym-config = { path = "../common/config" } nym-crypto = { path = "../common/crypto" } nym-contracts-common = { path = "../common/cosmwasm-smart-contracts/contracts-common" } nym-http-api-common = { path = "../common/http-api-common" } @@ -51,12 +37,10 @@ nym-nonexhaustive-delayqueue = { path = "../common/nonexhaustive-delayqueue" } nym-node-http-api = { path = "../nym-node/nym-node-http-api" } nym-sphinx = { path = "../common/nymsphinx" } nym-sphinx-params = { path = "../common/nymsphinx/params" } -nym-pemstore = { path = "../common/pemstore", version = "0.3.0" } nym-task = { path = "../common/task" } nym-types = { path = "../common/types" } nym-topology = { path = "../common/topology" } nym-validator-client = { path = "../common/client-libs/validator-client" } -nym-bin-common = { path = "../common/bin-common", features = ["output_format", "clap"] } [dev-dependencies] tokio = { workspace = true, features = [ @@ -68,8 +52,3 @@ tokio = { workspace = true, features = [ nym-sphinx-types = { path = "../common/nymsphinx/types" } nym-sphinx-params = { path = "../common/nymsphinx/params" } - -[package.metadata.deb] -name = "nym-mixnode" -maintainer-scripts = "debian" -systemd-units = { enable = false } diff --git a/mixnode/debian/postinst b/mixnode/debian/postinst deleted file mode 100644 index 7c672de34b..0000000000 --- a/mixnode/debian/postinst +++ /dev/null @@ -1,64 +0,0 @@ -#!/bin/sh - -default_location="/usr/bin/nym-mixnode" -default_user="nym" -default_group="nym" - -if [ -f "/tmp/nym_mixnode_preinst_marker" ]; then - backup_path=$(cat /tmp/nym_mixnode_preinst_marker) - echo "Upgrade detected. Previous version backed up at $backup_path" - - existing_location=$(dirname "$backup_path" | sed 's/\.backup\..*//') - echo "Existing location: ${existing_location}" - - if [ "$existing_location" != "$default_location" ]; then - echo "Custom installation location detected: $existing_location" - - mv "$default_location" "$existing_location/nym-mixnode" - - original_user=$(stat -c "%U" "$backup_path") - original_group=$(stat -c "%G" "$backup_path") - original_perms=$(stat -c "%a" "$backup_path") - - chown "$original_user:$original_group" "$existing_location/nym-mixnode" - chmod "$original_perms" "$existing_location/nym-mixnode" - fi - - rm -f /tmp/nym_mixnode_preinst_marker -else - echo "Fresh installation detected." - - if [ -f "$default_location" ]; then - # Leave the binary as the user to perform the apt install - # It's down to the user to specify the correct ownership and permissions - chmod 755 "$default_location" - - echo "Installation complete. Please configure and start the nym-mixnode process manually." - echo "Refer to https://nymtech.net/operators/nodes/mixnode-setup.html" - echo "Example for setting up the nym-mixnode service:" - echo - - cat </dev/null) - -if [ -n "$existing_binaries" ]; then - echo "Existing installation(s) detected. Preparing for upgrade." - - for binary_path in $existing_binaries; do - backup_path="$backup_dir/$(basename $binary_path).backup.$(date +%Y-%m-%dT%H:%M:%S)" - cp "$binary_path" "$backup_path" - - echo "Backed up existing binary from $binary_path to $backup_path" - done - - oldest_binary=$(echo "$existing_binaries" | head -n 1) - echo "$oldest_binary" > /tmp/nym_mixnode_preinst_marker -else - echo "No existing nym-mixnode installation detected. Proceeding with fresh installation." -fi - -exit 0 - -#DEBHELPER# diff --git a/mixnode/src/commands/build_info.rs b/mixnode/src/commands/build_info.rs deleted file mode 100644 index 3b763adf3f..0000000000 --- a/mixnode/src/commands/build_info.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2023 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use clap::Args; -use nym_bin_common::bin_info_owned; -use nym_bin_common::output_format::OutputFormat; - -#[derive(Args)] -pub(crate) struct BuildInfo { - #[clap(short, long, default_value_t = OutputFormat::default())] - output: OutputFormat, -} - -pub(crate) fn execute(args: BuildInfo) { - println!("{}", args.output.format(&bin_info_owned!())) -} diff --git a/mixnode/src/commands/describe.rs b/mixnode/src/commands/describe.rs deleted file mode 100644 index d5b17682e2..0000000000 --- a/mixnode/src/commands/describe.rs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2021-2023 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use crate::commands::try_load_current_config; -use clap::Args; -use colored::Colorize; -use nym_mixnode::node::node_description::NodeDescription; -use std::io; -use std::io::Write; - -#[derive(Args)] -pub(crate) struct Describe { - /// The id of the mixnode you want to describe - #[clap(long)] - id: String, - - /// Human readable name of this node - #[clap(long)] - name: Option, - - /// Description of this node - #[clap(long)] - description: Option, - - /// Link associated with this node, for example `https://mixnode.yourdomain.com` - #[clap(long)] - link: Option, - - /// Physical location of this node, for example `City: London, Country: UK` - #[clap(long)] - location: Option, -} - -fn read_user_input() -> String { - io::stdout().flush().unwrap(); - let mut buf = String::new(); - io::stdin().read_line(&mut buf).unwrap(); - buf.trim().to_string() -} - -pub(crate) fn execute(args: Describe) -> anyhow::Result<()> { - // ensure that the mixnode has in fact been initialized - let config = try_load_current_config(&args.id)?; - - let example_url = "https://mixnode.yourdomain.com".bright_cyan(); - let example_location = "City: London, Country: UK"; - - // get input from the user if not provided via the arguments - let name = args.name.unwrap_or_else(|| { - print!("name: "); - read_user_input() - }); - - let description = args.description.unwrap_or_else(|| { - print!("description: "); - read_user_input() - }); - - let link = args.link.unwrap_or_else(|| { - print!("link, e.g. {example_url}: "); - read_user_input() - }); - - let location = args.location.unwrap_or_else(|| { - print!("location, e.g. {example_location}: "); - read_user_input() - }); - - let node_description = NodeDescription { - name, - description, - link, - location, - }; - - // save the struct - node_description.save_to_file(config.storage_paths.node_description)?; - Ok(()) -} diff --git a/mixnode/src/commands/init.rs b/mixnode/src/commands/init.rs deleted file mode 100644 index a60663768a..0000000000 --- a/mixnode/src/commands/init.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2020-2024 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use anyhow::bail; -use colored::Colorize; - -#[allow(dead_code)] -#[derive(clap::Args, Clone)] -pub(crate) struct Init { - /// Id of the mixnode we want to create config for - #[clap(long)] - id: Option, -} - -pub(crate) fn execute(_args: &Init) -> anyhow::Result<()> { - bail!( - "standalone mixnode initialisation has been removed - please initialise a `nym-node` instead" - .red() - .bold() - ) -} diff --git a/mixnode/src/commands/mod.rs b/mixnode/src/commands/mod.rs deleted file mode 100644 index a5cdfcc6c7..0000000000 --- a/mixnode/src/commands/mod.rs +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright 2020 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use crate::Cli; -use anyhow::bail; -use clap::CommandFactory; -use clap::Subcommand; -use colored::Colorize; -use log::{error, warn}; -use nym_bin_common::completions::{fig_generate, ArgShell}; -use nym_config::defaults::var_names::{BECH32_PREFIX, NYM_API}; -use nym_config::OptionalSet; -use nym_crypto::bech32_address_validation; -use nym_mixnode::config::{default_config_filepath, Config}; -use nym_mixnode::error::MixnodeError; -use std::io::IsTerminal; -use std::net::IpAddr; -use std::process; -use std::time::Duration; - -mod build_info; -mod describe; -mod init; -mod node_details; -mod run; -mod sign; - -#[derive(Subcommand)] -pub(crate) enum Commands { - /// Describe your mixnode and tell people why they should delegate state to you - Describe(describe::Describe), - - /// Initialise the mixnode - Init(init::Init), - - /// Starts the mixnode - Run(run::Run), - - /// Sign text to prove ownership of this mixnode - Sign(sign::Sign), - - /// Show details of this mixnode - NodeDetails(node_details::NodeDetails), - - /// Show build information of this binary - BuildInfo(build_info::BuildInfo), - - /// Generate shell completions - Completions(ArgShell), - - /// Generate Fig specification - GenerateFigSpec, -} - -// Configuration that can be overridden. -#[allow(dead_code)] -struct OverrideConfig { - id: String, - host: Option, - mix_port: Option, - verloc_port: Option, - http_api_port: Option, - nym_apis: Option>, - metrics_key: Option, -} - -pub(crate) async fn execute(args: Cli) -> anyhow::Result<()> { - let bin_name = "nym-mixnode"; - - if !args.force_run { - let msg = "standalone mixnodes have been deprecated - please migrate to a `nym-node` via `nym-node migrate mixnode` command"; - error!("{msg}"); - bail!("{msg}") - } - - warn!("standalone mixnodes have been deprecated - please consider migrating it to a `nym-node` via `nym-node migrate mixnode` command"); - if std::io::stdout().is_terminal() { - // if user is running it in terminal session, - // introduce the delay, so they'd notice the message - tokio::time::sleep(Duration::from_secs(1)).await - } - - match args.command { - 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::NodeDetails(m) => node_details::execute(&m)?, - Commands::BuildInfo(m) => build_info::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(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) - .with_optional(Config::with_metrics_key, args.metrics_key) - .with_optional_custom_env( - Config::with_custom_nym_apis, - args.nym_apis, - NYM_API, - nym_config::parse_urls, - ) -} - -/// Ensures that a given bech32 address is valid, or exits -pub(crate) fn validate_bech32_address_or_exit(address: &str) { - let prefix = std::env::var(BECH32_PREFIX).expect("bech32 prefix not set"); - if let Err(bech32_address_validation::Bech32Error::DecodeFailed(err)) = - bech32_address_validation::try_bech32_decode(address) - { - let error_message = format!("Error: wallet address decoding failed: {err}").red(); - error!("{}", error_message); - error!("Exiting..."); - process::exit(1); - } - - if let Err(bech32_address_validation::Bech32Error::WrongPrefix(err)) = - bech32_address_validation::validate_bech32_prefix(&prefix, address) - { - let error_message = format!("Error: wallet address type is wrong, {err}").red(); - error!("{}", error_message); - error!("Exiting..."); - process::exit(1); - } -} - -fn try_load_current_config(id: &str) -> Result { - Config::read_from_default_path(id).map_err(|err| { - error!( - "Failed to load config for {id}. Are you sure you have run `init` before? (Error was: {err})", - ); - MixnodeError::ConfigLoadFailure { - path: default_config_filepath(id), - id: id.to_string(), - source: err, - } - }) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn verify_cli() { - Cli::command().debug_assert(); - } -} diff --git a/mixnode/src/commands/node_details.rs b/mixnode/src/commands/node_details.rs deleted file mode 100644 index cddf41a04f..0000000000 --- a/mixnode/src/commands/node_details.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2021 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use crate::commands::try_load_current_config; -use clap::Args; -use nym_bin_common::output_format::OutputFormat; -use nym_mixnode::MixNode; - -#[derive(Args)] -pub(crate) struct NodeDetails { - /// The id of the mixnode you want to show details for - #[clap(long)] - id: String, - - #[clap(short, long, default_value_t = OutputFormat::default())] - output: OutputFormat, -} - -pub(crate) fn execute(args: &NodeDetails) -> anyhow::Result<()> { - let config = try_load_current_config(&args.id)?; - - MixNode::new(config)?.print_node_details(args.output); - Ok(()) -} diff --git a/mixnode/src/commands/run.rs b/mixnode/src/commands/run.rs deleted file mode 100644 index bef1ef3049..0000000000 --- a/mixnode/src/commands/run.rs +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2020 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use super::OverrideConfig; -use crate::commands::{override_config, try_load_current_config}; -use clap::Args; -use nym_bin_common::output_format::OutputFormat; -use nym_config::helpers::SPECIAL_ADDRESSES; -use nym_mixnode::MixNode; -use nym_validator_client::nyxd; -use std::net::IpAddr; - -#[derive(Args, Clone)] -pub(crate) struct Run { - /// Id of the nym-mixnode we want to run - #[clap(long)] - id: String, - - /// The custom host on which the mixnode will be running - #[clap(long)] - host: Option, - - /// The wallet address you will use to bond this mixnode, e.g. nymt1z9egw0knv47nmur0p8vk4rcx59h9gg4zuxrrr9 - #[clap(long)] - wallet_address: Option, - - /// The port on which the mixnode will be listening for mix packets - #[clap(long)] - mix_port: Option, - - /// The port on which the mixnode will be listening for verloc packets - #[clap(long)] - verloc_port: Option, - - /// The port on which the mixnode will be listening for http requests - #[clap(long)] - http_api_port: Option, - - /// 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 = ',')] - nym_apis: Option>, - - #[clap(short, long, default_value_t = OutputFormat::default())] - output: OutputFormat, - - #[clap(long)] - metrics_key: Option, -} - -impl From for OverrideConfig { - fn from(run_config: Run) -> Self { - OverrideConfig { - id: run_config.id, - host: run_config.host, - mix_port: run_config.mix_port, - verloc_port: run_config.verloc_port, - http_api_port: run_config.http_api_port, - nym_apis: run_config.nym_apis, - metrics_key: run_config.metrics_key, - } - } -} - -fn show_binding_warning(address: &str) { - eprintln!("\n##### NOTE #####"); - 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 used different host when bonding your node" - ); - eprintln!("\n\n"); -} - -pub(crate) async fn execute(args: &Run) -> anyhow::Result<()> { - eprintln!("Starting mixnode {}...", args.id); - - let mut config = try_load_current_config(&args.id)?; - config = override_config(config, OverrideConfig::from(args.clone())); - - if SPECIAL_ADDRESSES.contains(&config.mixnode.listening_address) { - show_binding_warning(&config.mixnode.listening_address.to_string()); - } - - let mut mixnode = MixNode::new(config)?; - - eprintln!( - "\nTo bond your mixnode you will need to install the Nym wallet, go to https://nymtech.net/get-involved and select the Download button.\n\ - 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?; - Ok(()) -} diff --git a/mixnode/src/commands/sign.rs b/mixnode/src/commands/sign.rs deleted file mode 100644 index 405f9845e2..0000000000 --- a/mixnode/src/commands/sign.rs +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2020-2023 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use crate::commands::{try_load_current_config, validate_bech32_address_or_exit}; -use anyhow::{bail, Result}; -use clap::{ArgGroup, Args}; -use log::error; -use nym_bin_common::output_format::OutputFormat; -use nym_crypto::asymmetric::identity; -use nym_mixnode::node::helpers::load_identity_keys; -use nym_types::helpers::ConsoleSigningOutput; -use nym_validator_client::nyxd; - -#[derive(Args, Clone)] -#[clap(group(ArgGroup::new("sign").required(true).args(&["wallet_address", "text", "contract_msg"])))] -pub(crate) struct Sign { - /// The id of the mixnode you want to sign with - #[clap(long)] - id: String, - - /// Signs your blockchain address with your identity key - // the alias here is included for backwards compatibility (1.1.4 and before) - #[clap(long, alias = "address")] - wallet_address: Option, - - /// Signs an arbitrary piece of text with your identity key - #[clap(long)] - text: Option, - - /// Signs a transaction-specific payload, that is going to be sent to the smart contract, with your identity key - #[clap(long)] - contract_msg: Option, - - #[clap(short, long, default_value_t = OutputFormat::default())] - output: OutputFormat, -} - -enum SignedTarget { - Text(String), - Address(nyxd::AccountId), - ContractMsg(String), -} - -impl TryFrom for SignedTarget { - type Error = anyhow::Error; - - fn try_from(args: Sign) -> Result { - if let Some(text) = args.text { - Ok(SignedTarget::Text(text)) - } else if let Some(address) = args.wallet_address { - Ok(SignedTarget::Address(address)) - } else if let Some(msg) = args.contract_msg { - Ok(SignedTarget::ContractMsg(msg)) - } else { - // This is unreachable, and hopefully clap will support it explicitly by outputting an - // enum from the ArgGroup in the future. - // See: https://github.com/clap-rs/clap/issues/2621 - bail!("Error: missing signed target flag") - } - } -} - -fn print_signed_address( - private_key: &identity::PrivateKey, - wallet_address: nyxd::AccountId, - output: OutputFormat, -) { - // perform extra validation to ensure we have correct prefix - validate_bech32_address_or_exit(wallet_address.as_ref()); - - print_signed_text(private_key, wallet_address.as_ref(), output) -} - -fn print_signed_text(private_key: &identity::PrivateKey, text: &str, output: OutputFormat) { - eprintln!("Signing the text {text:?} using your mixnode's Ed25519 identity key..."); - - let signature = private_key.sign_text(text); - let sign_output = ConsoleSigningOutput::new(text, signature); - println!("{}", output.format(&sign_output)); -} - -fn print_signed_contract_msg( - private_key: &identity::PrivateKey, - raw_msg: &str, - output: OutputFormat, -) { - let trimmed = raw_msg.trim(); - eprintln!(">>> attempting to sign {trimmed}"); - - let Ok(decoded) = bs58::decode(trimmed).into_vec() else { - println!("it seems you have incorrectly copied the message to sign. Make sure you didn't accidentally skip any characters"); - return; - }; - - eprintln!(">>> decoding the message..."); - - // we don't really care about what particular information is embedded inside of it, - // we just want to know if user correctly copied the string, i.e. whether it's a valid bs58 encoded json - if serde_json::from_slice::(&decoded).is_err() { - println!("it seems you have incorrectly copied the message to sign. Make sure you didn't accidentally skip any characters"); - return; - }; - - // if this is a valid json, it MUST be a valid string - let decoded_string = String::from_utf8(decoded.clone()).unwrap(); - let signature = private_key.sign(&decoded).to_base58_string(); - - let sign_output = ConsoleSigningOutput::new(decoded_string, signature); - println!("{}", output.format(&sign_output)); -} - -pub(crate) fn execute(args: &Sign) -> anyhow::Result<()> { - let config = try_load_current_config(&args.id)?; - - let signed_target = match SignedTarget::try_from(args.clone()) { - Ok(s) => s, - Err(err) => { - error!("{err}"); - bail!(err); - } - }; - let identity_keypair = load_identity_keys(&config)?; - - match signed_target { - SignedTarget::Text(text) => { - print_signed_text(identity_keypair.private_key(), &text, args.output) - } - SignedTarget::Address(addr) => { - print_signed_address(identity_keypair.private_key(), addr, args.output) - } - SignedTarget::ContractMsg(raw_msg) => { - print_signed_contract_msg(identity_keypair.private_key(), &raw_msg, args.output) - } - } - Ok(()) -} diff --git a/mixnode/src/config/mod.rs b/mixnode/src/config.rs similarity index 55% rename from mixnode/src/config/mod.rs rename to mixnode/src/config.rs index 2f96403544..d20f891ec5 100644 --- a/mixnode/src/config/mod.rs +++ b/mixnode/src/config.rs @@ -1,34 +1,11 @@ -// Copyright 2020-2023 - Nym Technologies SA +// Copyright 2024 - Nym Technologies SA // SPDX-License-Identifier: GPL-3.0-only -use crate::config::template::CONFIG_TEMPLATE; -use log::{debug, warn}; -use nym_bin_common::logging::LoggingSettings; -use nym_config::defaults::{ - mainnet, DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, - DEFAULT_VERLOC_LISTENING_PORT, -}; -use nym_config::helpers::inaddr_any; -use nym_config::{ - must_get_home, read_config_from_toml_file, save_formatted_config_to_file, - serde_helpers::de_maybe_stringified, NymConfigTemplate, DEFAULT_CONFIG_DIR, - DEFAULT_CONFIG_FILENAME, DEFAULT_DATA_DIR, NYM_DIR, -}; -use serde::{Deserialize, Serialize}; -use std::io; -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; -use std::path::{Path, PathBuf}; -use std::str::FromStr; +use std::net::{IpAddr, SocketAddr}; +use std::path::PathBuf; use std::time::Duration; use url::Url; -pub mod persistence; -mod template; - -pub use crate::config::persistence::paths::MixNodePaths; - -const DEFAULT_MIXNODES_DIR: &str = "mixnodes"; - // 'RTT MEASUREMENT' const DEFAULT_PACKETS_PER_NODE: usize = 100; const DEFAULT_CONNECTION_TIMEOUT: Duration = Duration::from_millis(5000); @@ -46,152 +23,36 @@ 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; -/// Derive default path to mixnodes's config directory. -/// It should get resolved to `$HOME/.nym/mixnodes//config` -pub fn default_config_directory>(id: P) -> PathBuf { - must_get_home() - .join(NYM_DIR) - .join(DEFAULT_MIXNODES_DIR) - .join(id) - .join(DEFAULT_CONFIG_DIR) -} - -/// Derive default path to mixnodes's config file. -/// It should get resolved to `$HOME/.nym/mixnodes//config/config.toml` -pub fn default_config_filepath>(id: P) -> PathBuf { - default_config_directory(id).join(DEFAULT_CONFIG_FILENAME) -} - -/// Derive default path to mixnodes's data directory where files, such as keys, are stored. -/// It should get resolved to `$HOME/.nym/mixnodes//data` -pub fn default_data_directory>(id: P) -> PathBuf { - must_get_home() - .join(NYM_DIR) - .join(DEFAULT_MIXNODES_DIR) - .join(id) - .join(DEFAULT_DATA_DIR) -} - -fn default_mixnode_http_config() -> Http { - Http { - bind_address: SocketAddr::new( - IpAddr::V4(Ipv4Addr::UNSPECIFIED), - DEFAULT_HTTP_API_LISTENING_PORT, - ), - landing_page_assets_path: None, - metrics_key: None, - } -} - -#[derive(Debug, Deserialize, PartialEq, Serialize)] -#[serde(deny_unknown_fields)] +#[derive(Debug, PartialEq)] pub struct Config { - // additional metadata holding on-disk location of this config file - #[serde(skip)] - pub(crate) save_path: Option, - pub host: Host, - #[serde(default = "default_mixnode_http_config")] pub http: Http, pub mixnode: MixNode, - pub storage_paths: MixNodePaths, - - #[serde(default)] pub verloc: Verloc, - #[serde(default)] - pub logging: LoggingSettings, - - #[serde(default)] pub debug: Debug, } -impl NymConfigTemplate for Config { - fn template(&self) -> &'static str { - CONFIG_TEMPLATE - } -} - impl Config { - pub fn new>(id: S) -> Self { - let default_mixnode = MixNode::new_default(id.as_ref()); - - Config { - save_path: None, - host: Host { - // this is a very bad default! - public_ips: vec![default_mixnode.listening_address], - hostname: None, - }, - http: default_mixnode_http_config(), - mixnode: default_mixnode, - storage_paths: MixNodePaths::new_default(id.as_ref()), - verloc: Default::default(), - logging: Default::default(), - debug: Default::default(), - } - } - pub fn externally_loaded( host: impl Into, http: impl Into, mixnode: impl Into, - storage_paths: impl Into, verloc: impl Into, - logging: impl Into, debug: impl Into, ) -> Self { Config { - save_path: None, host: host.into(), http: http.into(), mixnode: mixnode.into(), - storage_paths: storage_paths.into(), verloc: verloc.into(), - logging: logging.into(), debug: debug.into(), } } - // simple wrapper that reads config file and assigns path location - fn read_from_path>(path: P) -> io::Result { - let path = path.as_ref(); - let mut loaded: Config = read_config_from_toml_file(path)?; - loaded.save_path = Some(path.to_path_buf()); - debug!("loaded config file from {}", path.display()); - Ok(loaded) - } - - pub fn read_from_toml_file>(path: P) -> io::Result { - Self::read_from_path(path) - } - - pub fn read_from_default_path>(id: P) -> io::Result { - Self::read_from_path(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) - } - - #[allow(unused)] - pub fn try_save(&self) -> io::Result<()> { - if let Some(save_location) = &self.save_path { - save_formatted_config_to_file(self, save_location) - } else { - warn!("config file save location is unknown. falling back to the default"); - self.save_to_default_location() - } - } - // builder methods pub fn with_custom_nym_apis(mut self, nym_api_urls: Vec) -> Self { self.mixnode.nym_api_urls = nym_api_urls; @@ -238,15 +99,13 @@ impl Config { } // TODO: this is very much a WIP. we need proper ssl certificate support here -#[derive(Debug, Deserialize, PartialEq, Serialize)] -#[serde(deny_unknown_fields)] +#[derive(Debug, PartialEq)] pub struct Host { /// Ip address(es) of this host, such as 1.1.1.1 that external clients will use for connections. pub public_ips: Vec, /// Optional hostname of this node, for example nymtech.net. // TODO: this is temporary. to be replaced by pulling the data directly from the certs. - #[serde(deserialize_with = "de_maybe_stringified")] pub hostname: Option, } @@ -260,22 +119,19 @@ impl Host { } } -#[derive(Debug, Deserialize, PartialEq, Serialize)] -#[serde(deny_unknown_fields)] +#[derive(Debug, PartialEq)] pub struct Http { /// Socket address this node will use for binding its http API. /// default: `0.0.0.0:8000` pub bind_address: SocketAddr, /// Path to assets directory of custom landing page of this node. - #[serde(deserialize_with = "de_maybe_stringified")] pub landing_page_assets_path: Option, - #[serde(default)] pub metrics_key: Option, } -#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[derive(Debug, PartialEq)] pub struct MixNode { /// Version of the mixnode for which this configuration was created. pub version: String, @@ -298,47 +154,28 @@ pub struct MixNode { pub nym_api_urls: Vec, } -impl MixNode { - pub fn new_default>(id: S) -> Self { - MixNode { - version: env!("CARGO_PKG_VERSION").to_string(), - id: id.into(), - listening_address: inaddr_any(), - mix_port: DEFAULT_MIX_LISTENING_PORT, - verloc_port: DEFAULT_VERLOC_LISTENING_PORT, - nym_api_urls: vec![Url::from_str(mainnet::NYM_API).expect("Invalid default API URL")], - } - } -} - -#[derive(Debug, Deserialize, PartialEq, Serialize)] -#[serde(deny_unknown_fields)] +#[derive(Debug, PartialEq)] pub struct Verloc { /// Specifies number of echo packets sent to each node during a measurement run. pub packets_per_node: usize, /// Specifies maximum amount of time to wait for the connection to get established. - #[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. - #[serde(with = "humantime_serde")] pub packet_timeout: Duration, /// Specifies delay between subsequent test packets being sent (after receiving a reply). - #[serde(with = "humantime_serde")] pub delay_between_packets: Duration, /// Specifies number of nodes being tested at once. pub tested_nodes_batch_size: usize, /// Specifies delay between subsequent test runs. - #[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. - #[serde(with = "humantime_serde")] pub retry_timeout: Duration, } @@ -356,29 +193,23 @@ impl Default for Verloc { } } -#[derive(Debug, Deserialize, PartialEq, Serialize)] -#[serde(default)] +#[derive(Debug, PartialEq)] pub struct Debug { /// Delay between each subsequent node statistics being logged to the console - #[serde(with = "humantime_serde")] pub node_stats_logging_delay: Duration, /// Delay between each subsequent node statistics being updated - #[serde(with = "humantime_serde")] 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")] 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")] pub packet_forwarding_maximum_backoff: Duration, /// Timeout for establishing initial connection when trying to forward a sphinx packet. - #[serde(with = "humantime_serde")] pub initial_connection_timeout: Duration, /// Maximum number of packets that can be stored waiting to get sent to a particular connection. diff --git a/mixnode/src/config/persistence/mod.rs b/mixnode/src/config/persistence/mod.rs deleted file mode 100644 index d2b55c30f5..0000000000 --- a/mixnode/src/config/persistence/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright 2020 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -pub mod paths; diff --git a/mixnode/src/config/persistence/paths.rs b/mixnode/src/config/persistence/paths.rs deleted file mode 100644 index d8b81634c7..0000000000 --- a/mixnode/src/config/persistence/paths.rs +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2020-2023 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -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>(id: P) -> Self { - MixNodePaths { - keys: KeysPaths::new_default(id.as_ref()), - // TODO: next time there is a breaking change in the mixnode config, change this to - // `default_base_directory`. - // I'd rather not change this willy-nilly since it means a `mixnode init` will break - // the existing configurated description. - node_description: default_config_directory(id).join(DEFAULT_DESCRIPTION_FILENAME), - } - } - - pub fn new_empty() -> Self { - MixNodePaths { - keys: KeysPaths { - private_identity_key_file: Default::default(), - public_identity_key_file: Default::default(), - private_sphinx_key_file: Default::default(), - public_sphinx_key_file: Default::default(), - }, - node_description: Default::default(), - } - } - - 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>(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 - } -} diff --git a/mixnode/src/config/template.rs b/mixnode/src/config/template.rs deleted file mode 100644 index 0e6651cc87..0000000000 --- a/mixnode/src/config/template.rs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2020 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -// 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 - -##### main base mixnode config options ##### - -[host] -# Ip address(es) of this host, such as 1.1.1.1 that external clients will use for connections. -# currently not in active use for mixnodes -public_ips = [ - {{#each host.public_ips }} - '{{this}}', - {{/each}} -] - -# (temporary) Optional hostname of this node, for example nymtech.net. -# currently not in active use for mixnodes -hostname = '{{ host.hostname }}' - -[mixnode] -# Version of the mixnode for which this configuration was created. -version = '{{ mixnode.version }}' - -# Human readable ID of this particular mixnode. -id = '{{ mixnode.id }}' - -# Socket address to which this mixnode will bind to and will be listening for packets. -listening_address = '{{ mixnode.listening_address }}' - -# Port used for listening for all mixnet traffic. -# (default: 1789) -mix_port = {{ mixnode.mix_port }} - -# Port used for listening for verloc traffic. -# (default: 1790) -verloc_port = {{ mixnode.verloc_port }} - -# Addresses to APIs running on validator from which the node gets the view of the network. -nym_api_urls = [ - {{#each mixnode.nym_api_urls }} - '{{this}}', - {{/each}} -] - -[http] -# Socket address this node will use for binding its http API. -# default: `0.0.0.0:8000` -bind_address = '{{ http.bind_address }}' - -# Path to assets directory of custom landing page of this node -landing_page_assets_path = '{{ http.landing_page_assets_path }}' - -metrics_key = '{{ http.metrics_key }}' - -[storage_paths] - -# Path to file containing private identity key. -keys.private_identity_key_file = '{{ storage_paths.keys.private_identity_key_file }}' - -# 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 ##### - -[logging] - -# TODO - -"#; diff --git a/mixnode/src/error.rs b/mixnode/src/error.rs deleted file mode 100644 index a12979e86e..0000000000 --- a/mixnode/src/error.rs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2023 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use std::io; -use std::path::PathBuf; -use thiserror::Error; - -#[derive(Debug, Error)] -pub enum MixnodeError { - #[error("failed to load {keys} keys from '{}' (private key) and '{}' (public key): {err}", .paths.private_key_path.display(), .paths.public_key_path.display())] - KeyPairLoadFailure { - keys: String, - paths: nym_pemstore::KeyPairPath, - #[source] - err: io::Error, - }, - - #[allow(dead_code)] - #[error("failed to load {key} public key from '{}': {err}", .path.display())] - PublicKeyLoadFailure { - key: String, - path: PathBuf, - #[source] - err: io::Error, - }, - - #[error( - "failed to load config file for id {id} using path '{}'. detailed message: {source}", path.display() - )] - ConfigLoadFailure { - id: String, - path: PathBuf, - #[source] - source: io::Error, - }, - - #[error( - "failed to save config file for id {id} using path '{}'. detailed message: {source}", path.display() - )] - ConfigSaveFailure { - id: String, - path: PathBuf, - #[source] - source: io::Error, - }, - - #[error(transparent)] - NymNodeHttpError(#[from] nym_node_http_api::NymNodeHttpError), -} diff --git a/mixnode/src/lib.rs b/mixnode/src/lib.rs index 18d0a47309..8cc9effac6 100644 --- a/mixnode/src/lib.rs +++ b/mixnode/src/lib.rs @@ -2,8 +2,6 @@ // SPDX-License-Identifier: GPL-3.0-only pub mod config; -pub mod error; pub mod node; -pub use error::MixnodeError; pub use node::MixNode; diff --git a/mixnode/src/main.rs b/mixnode/src/main.rs deleted file mode 100644 index e7a577a80f..0000000000 --- a/mixnode/src/main.rs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2020-2023 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use ::nym_config::defaults::setup_env; -use clap::{crate_name, crate_version, Parser}; -use nym_bin_common::bin_info; -use std::sync::OnceLock; - -#[allow(unused_imports)] -use nym_bin_common::logging::{maybe_print_banner, setup_logging}; - -mod commands; - -fn pretty_build_info_static() -> &'static str { - static PRETTY_BUILD_INFORMATION: OnceLock = OnceLock::new(); - PRETTY_BUILD_INFORMATION.get_or_init(|| bin_info!().pretty_print()) -} - -#[derive(Parser)] -#[clap(author = "Nymtech", version, about, long_version = pretty_build_info_static())] -struct Cli { - /// Path pointing to an env file that configures the mixnode. - #[clap(short, long)] - pub(crate) config_env_file: Option, - - /// Force run the binary bypassing the deprecation in favour of nym-node - #[clap(long, hide = true)] - pub(crate) force_run: bool, - - /// Flag used for disabling the printed banner in tty. - #[clap(long)] - pub(crate) no_banner: bool, - - #[clap(subcommand)] - command: commands::Commands, -} - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - let args = Cli::parse(); - setup_env(args.config_env_file.as_ref()); - - if !args.no_banner { - maybe_print_banner(crate_name!(), crate_version!()); - } - - setup_logging(); - - commands::execute(args).await?; - - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::*; - use clap::CommandFactory; - - #[test] - fn verify_cli() { - Cli::command().debug_assert(); - } -} diff --git a/mixnode/src/node/helpers.rs b/mixnode/src/node/helpers.rs deleted file mode 100644 index 612e301dc2..0000000000 --- a/mixnode/src/node/helpers.rs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2023 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use crate::config::Config; -use crate::error::MixnodeError; -use nym_crypto::asymmetric::{encryption, identity}; -use nym_pemstore::traits::{PemStorableKey, PemStorableKeyPair}; -use nym_pemstore::KeyPairPath; -use std::path::Path; - -pub(crate) fn load_keypair( - paths: KeyPairPath, - name: impl Into, -) -> Result { - nym_pemstore::load_keypair(&paths).map_err(|err| MixnodeError::KeyPairLoadFailure { - keys: name.into(), - paths, - err, - }) -} - -#[allow(unused)] -pub(crate) fn load_public_key(path: P, name: S) -> Result -where - T: PemStorableKey, - P: AsRef, - S: Into, -{ - nym_pemstore::load_key(path.as_ref()).map_err(|err| MixnodeError::PublicKeyLoadFailure { - key: name.into(), - path: path.as_ref().to_path_buf(), - err, - }) -} - -/// Loads identity keys stored on disk -pub fn load_identity_keys(config: &Config) -> Result { - let identity_paths = KeyPairPath::new( - config.storage_paths.keys.private_identity_key(), - config.storage_paths.keys.public_identity_key(), - ); - load_keypair(identity_paths, "mixnode identity") -} - -/// Loads Sphinx keys stored on disk -pub fn load_sphinx_keys(config: &Config) -> Result { - let sphinx_paths = KeyPairPath::new( - config.storage_paths.keys.private_encryption_key(), - config.storage_paths.keys.public_encryption_key(), - ); - load_keypair(sphinx_paths, "mixnode sphinx") -} diff --git a/mixnode/src/node/http/legacy/description.rs b/mixnode/src/node/http/legacy/description.rs deleted file mode 100644 index 8921551339..0000000000 --- a/mixnode/src/node/http/legacy/description.rs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2023 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use crate::node::node_description::NodeDescription; -use axum::extract::Query; -use nym_http_api_common::{FormattedResponse, OutputParams}; - -/// Returns a description of the node and why someone might want to delegate stake to it. -pub(crate) async fn description( - description: NodeDescription, - Query(output): Query, -) -> MixnodeDescriptionResponse { - let output = output.output.unwrap_or_default(); - output.to_response(description) -} - -pub type MixnodeDescriptionResponse = FormattedResponse; diff --git a/mixnode/src/node/http/legacy/hardware.rs b/mixnode/src/node/http/legacy/hardware.rs deleted file mode 100644 index b1ce61b6ff..0000000000 --- a/mixnode/src/node/http/legacy/hardware.rs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2023 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use axum::extract::Query; -use cupid::TopologyType; -use nym_http_api_common::{FormattedResponse, OutputParams}; -use serde::Serialize; -use sysinfo::System; - -#[derive(Serialize, Debug)] -pub struct Hardware { - ram: String, - num_cores: usize, - crypto_hardware: Option, -} - -#[allow(clippy::struct_excessive_bools)] -#[derive(Serialize, Debug)] -pub(crate) struct CryptoHardware { - aesni: bool, - avx2: bool, - brand_string: String, - smt_logical_processor_count: Vec, - osxsave: bool, - sgx: bool, - xsave: bool, -} - -/// Provides hardware information which Nym can use to optimize mixnet speed over time (memory, crypto hardware, CPU, cores, etc). -pub(crate) async fn hardware(Query(output): Query) -> MixnodeHardwareResponse { - let output = output.output.unwrap_or_default(); - output.to_response(hardware_info()) -} - -pub type MixnodeHardwareResponse = FormattedResponse>; - -/// Gives back a summary report of whatever system hardware info we can get for this platform. -fn hardware_info() -> Option { - let crypto_hardware = hardware_info_from_cupid(); - hardware_from_sysinfo(crypto_hardware) -} - -/// Sysinfo gives back basic stuff like number of CPU cores and available memory. If available, this includes the hardware encryption -/// extensions report -fn hardware_from_sysinfo(crypto_hardware: Option) -> Option { - if sysinfo::IS_SUPPORTED_SYSTEM { - let mut system = System::new_all(); - system.refresh_all(); - let ram = format!("{}KB", system.total_memory()); - let cores = system.cpus(); - let num_cores = cores.len(); - Some(Hardware { - ram, - num_cores, - crypto_hardware, - }) - } else { - None - } -} - -/// The `cupid` crate gives back a report on available hardware encryption extensions which may be useful for future mixnet optimizations. -/// -/// Note: this information is generally only available on x86 platforms for Linux. -fn hardware_info_from_cupid() -> Option { - cupid::master().map(|info| -> CryptoHardware { - let smt_logical_processor_count = - if let Some(extended_topology) = info.extended_topology_enumeration() { - extended_topology - .clone() - .filter_map(|entry| { - if entry.level_type() == TopologyType::SMT { - Some(entry.logical_processor_count()) - } else { - None - } - }) - .collect() - } else { - Vec::new() - }; - - CryptoHardware { - aesni: info.aesni(), - avx2: info.avx2(), - brand_string: info.brand_string().map(String::from).unwrap_or_default(), - smt_logical_processor_count, - osxsave: info.osxsave(), - sgx: info.sgx(), - xsave: info.xsave(), - } - }) -} diff --git a/mixnode/src/node/http/legacy/mod.rs b/mixnode/src/node/http/legacy/mod.rs deleted file mode 100644 index 97d844bd13..0000000000 --- a/mixnode/src/node/http/legacy/mod.rs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2023 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -#![allow(unused)] - -use crate::node::http::legacy::description::description; -use crate::node::http::legacy::hardware::hardware; -use crate::node::http::legacy::state::MixnodeAppState; -use crate::node::http::legacy::stats::metrics; -use crate::node::http::legacy::stats::stats; -use crate::node::http::legacy::verloc::verloc; -use crate::node::node_description::NodeDescription; -use axum::http::{StatusCode, Uri}; -use axum::response::IntoResponse; -use axum::routing::get; -use axum::Router; - -pub(crate) mod description; -pub(crate) mod hardware; -pub(crate) mod state; -pub(crate) mod stats; -pub(crate) mod verloc; - -pub(crate) async fn not_found(uri: Uri) -> impl IntoResponse { - ( - StatusCode::NOT_FOUND, - format!("I couldn't find '{uri}'. Try something else?"), - ) -} - -pub(crate) mod api_routes { - pub(crate) const VERLOC: &str = "/verloc"; - pub(crate) const DESCRIPTION: &str = "/description"; - pub(crate) const STATS: &str = "/stats"; - pub(crate) const METRICS: &str = "/metrics"; - pub(crate) const HARDWARE: &str = "/hardware"; -} - -pub(crate) fn routes( - state: MixnodeAppState, - descriptor: NodeDescription, -) -> Router { - Router::new() - .route(api_routes::VERLOC, get(verloc)) - .route( - api_routes::DESCRIPTION, - get(move |query| description(descriptor, query)), - ) - .route(api_routes::STATS, get(stats)) - .route(api_routes::HARDWARE, get(hardware)) - .route(api_routes::METRICS, get(metrics)) - .fallback(not_found) - .with_state(state) -} diff --git a/mixnode/src/node/http/legacy/state.rs b/mixnode/src/node/http/legacy/state.rs deleted file mode 100644 index d51d8741b2..0000000000 --- a/mixnode/src/node/http/legacy/state.rs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2023 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use axum::extract::FromRef; -use nym_node_http_api::state::metrics::{SharedMixingStats, SharedVerlocStats}; - -// this is a temporary thing for the transition period -#[derive(Clone, Default)] -pub(crate) struct MixnodeAppState { - pub(crate) verloc: SharedVerlocStats, - pub(crate) stats: SharedMixingStats, - pub(crate) metrics_key: Option, -} - -impl FromRef for SharedVerlocStats { - fn from_ref(app_state: &MixnodeAppState) -> Self { - app_state.verloc.clone() - } -} - -impl FromRef for SharedMixingStats { - fn from_ref(app_state: &MixnodeAppState) -> Self { - app_state.stats.clone() - } -} diff --git a/mixnode/src/node/http/legacy/stats.rs b/mixnode/src/node/http/legacy/stats.rs deleted file mode 100644 index 8898b8ab74..0000000000 --- a/mixnode/src/node/http/legacy/stats.rs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2023 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use axum::{ - extract::{Query, State}, - http::HeaderMap, -}; -use nym_http_api_common::{FormattedResponse, Output}; -use nym_metrics::metrics; -use nym_node_http_api::api::api_requests::v1::metrics::models::MixingStats; -use nym_node_http_api::state::metrics::SharedMixingStats; -use serde::{Deserialize, Serialize}; - -use super::state::MixnodeAppState; - -#[derive(Serialize)] -#[serde(untagged)] -pub enum NodeStatsResponse { - Full(String), - Simple(MixingStats), -} - -pub(crate) async fn metrics(State(state): State, headers: HeaderMap) -> String { - if let Some(metrics_key) = state.metrics_key { - if let Some(auth) = headers.get("Authorization") { - if auth.to_str().unwrap_or_default() == format!("Bearer {}", metrics_key) { - metrics!() - } else { - "Unauthorized".to_string() - } - } else { - "Unauthorized".to_string() - } - } else { - "Set metrics_key in config to enable Prometheus metrics".to_string() - } -} - -pub(crate) async fn stats( - Query(params): Query, - State(stats): State, -) -> MixnodeStatsResponse { - let output = params.output.unwrap_or_default(); - - // there's no point in returning the entire hashmap of sending destinations in regular mode - let response = generate_stats(params.debug, stats).await; - output.to_response(response) -} - -async fn generate_stats(full: bool, stats: SharedMixingStats) -> NodeStatsResponse { - if full { - NodeStatsResponse::Full(metrics!()) - } else { - NodeStatsResponse::Simple(stats.read().await.as_response()) - } -} - -pub type MixnodeStatsResponse = FormattedResponse; - -#[derive(Default, Debug, Serialize, Deserialize, Copy, Clone)] -// #[derive(Default, Debug, Serialize, Deserialize, Copy, Clone, IntoParams, ToSchema)] -#[serde(default)] -pub(crate) struct StatsQueryParams { - debug: bool, - pub output: Option, -} diff --git a/mixnode/src/node/http/legacy/verloc.rs b/mixnode/src/node/http/legacy/verloc.rs deleted file mode 100644 index 8376081b4e..0000000000 --- a/mixnode/src/node/http/legacy/verloc.rs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2023 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use axum::extract::{Query, State}; -use nym_http_api_common::{FormattedResponse, OutputParams}; -use nym_node_http_api::api::api_requests::v1::metrics::models::VerlocResultData; -use nym_node_http_api::state::metrics::SharedVerlocStats; - -/// Provides verifiable location (verloc) measurements for this mixnode - a list of the -/// round-trip times, in milliseconds, for all other mixnodes that this node knows about. -pub(crate) async fn verloc( - State(verloc): State, - Query(output): Query, -) -> MixnodeVerlocResponse { - let output = output.output.unwrap_or_default(); - output.to_response(verloc.read().await.current_run_data.clone()) -} - -pub type MixnodeVerlocResponse = FormattedResponse; diff --git a/mixnode/src/node/http/mod.rs b/mixnode/src/node/http/mod.rs deleted file mode 100644 index 658c2ce980..0000000000 --- a/mixnode/src/node/http/mod.rs +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright 2023-2024 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use crate::config::Config; -use crate::error::MixnodeError; -use crate::node::node_description::NodeDescription; -use log::{error, info}; -use nym_bin_common::bin_info_owned; -use nym_crypto::asymmetric::{encryption, identity}; -use nym_node_http_api::api::api_requests; -use nym_node_http_api::api::api_requests::SignedHostInformation; -use nym_node_http_api::state::metrics::{SharedMixingStats, SharedVerlocStats}; -use nym_node_http_api::NymNodeHttpError; -use nym_task::TaskClient; - -pub(crate) mod legacy; - -fn load_host_details( - config: &Config, - sphinx_key: &encryption::PublicKey, - identity_keypair: &identity::KeyPair, -) -> Result { - let host_info = api_requests::v1::node::models::HostInformation { - ip_address: config.host.public_ips.clone(), - hostname: config.host.hostname.clone(), - keys: api_requests::v1::node::models::HostKeys { - ed25519_identity: *identity_keypair.public_key(), - x25519_sphinx: *sphinx_key, - x25519_noise: None, - }, - }; - - let signed_info = SignedHostInformation::new(host_info, identity_keypair.private_key()) - .map_err(NymNodeHttpError::from)?; - Ok(signed_info) -} - -fn load_mixnode_details( - _config: &Config, -) -> Result { - Ok(api_requests::v1::mixnode::models::Mixnode {}) -} - -pub(crate) struct HttpApiBuilder<'a> { - mixnode_config: &'a Config, - identity_keypair: &'a identity::KeyPair, - sphinx_keypair: &'a encryption::KeyPair, - legacy_mixnode: legacy::state::MixnodeAppState, - legacy_descriptor: NodeDescription, -} - -impl<'a> HttpApiBuilder<'a> { - pub(crate) fn new( - mixnode_config: &'a Config, - identity_keypair: &'a identity::KeyPair, - sphinx_keypair: &'a encryption::KeyPair, - ) -> Self { - HttpApiBuilder { - mixnode_config, - identity_keypair, - sphinx_keypair, - legacy_mixnode: legacy::state::MixnodeAppState::default(), - legacy_descriptor: Default::default(), - } - } - - #[must_use] - pub(crate) fn with_metrics_key(mut self, metrics_key: Option<&String>) -> Self { - self.legacy_mixnode.metrics_key = metrics_key.map(|k| k.to_string()); - self - } - - #[must_use] - pub(crate) fn with_verloc(mut self, verloc: SharedVerlocStats) -> Self { - self.legacy_mixnode.verloc = verloc; - self - } - - #[must_use] - pub(crate) fn with_mixing_stats(mut self, stats: SharedMixingStats) -> Self { - self.legacy_mixnode.stats = stats; - self - } - - #[must_use] - pub(crate) fn with_descriptor(mut self, descriptor: NodeDescription) -> Self { - self.legacy_descriptor = descriptor; - self - } - - pub(crate) fn start(self, task_client: TaskClient) -> Result<(), MixnodeError> { - let bind_address = self.mixnode_config.http.bind_address; - info!("Starting HTTP API on http://{bind_address}",); - - let config = nym_node_http_api::Config::new( - bin_info_owned!(), - load_host_details( - self.mixnode_config, - self.sphinx_keypair.public_key(), - self.identity_keypair, - )?, - ) - .with_mixnode(load_mixnode_details(self.mixnode_config)?) - .with_landing_page_assets(self.mixnode_config.http.landing_page_assets_path.as_ref()); - - let router = nym_node_http_api::NymNodeRouter::new(config, None); - - tokio::spawn(async move { - let server = match router.build_server(&bind_address).await { - Ok(server) => server.with_task_client(task_client), - Err(err) => { - error!("failed to create http server: {err}"); - return; - } - }; - server.run().await - }); - Ok(()) - } -} diff --git a/mixnode/src/node/listener/connection_handler/mod.rs b/mixnode/src/node/listener/connection_handler/mod.rs index 0b76762667..8d04330e72 100644 --- a/mixnode/src/node/listener/connection_handler/mod.rs +++ b/mixnode/src/node/listener/connection_handler/mod.rs @@ -5,8 +5,6 @@ use crate::node::listener::connection_handler::packet_processing::PacketProcesso use crate::node::packet_delayforwarder::PacketDelayForwardSender; use crate::node::TaskClient; use futures::StreamExt; -use log::debug; -use log::{error, info, warn}; use nym_metrics::nanos; use nym_sphinx::forwarding::packet::MixPacket; use nym_sphinx::framing::codec::NymCodec; @@ -18,6 +16,7 @@ use std::net::SocketAddr; use tokio::net::TcpStream; use tokio::time::Instant; use tokio_util::codec::Framed; +use tracing::{debug, error, info, trace, warn}; pub(crate) mod packet_processing; @@ -94,7 +93,7 @@ impl ConnectionHandler { tokio::select! { biased; _ = shutdown.recv() => { - log::trace!("ConnectionHandler: received shutdown"); + trace!("ConnectionHandler: received shutdown"); } framed_sphinx_packet = framed_conn.next() => { match framed_sphinx_packet { @@ -125,6 +124,6 @@ impl ConnectionHandler { "Closing connection from {:?}", framed_conn.into_inner().peer_addr() ); - log::trace!("ConnectionHandler: Exiting"); + trace!("ConnectionHandler: Exiting"); } } diff --git a/mixnode/src/node/listener/mod.rs b/mixnode/src/node/listener/mod.rs index 995a6e7c73..a532ffc5d0 100644 --- a/mixnode/src/node/listener/mod.rs +++ b/mixnode/src/node/listener/mod.rs @@ -2,11 +2,11 @@ // SPDX-License-Identifier: GPL-3.0-only use crate::node::listener::connection_handler::ConnectionHandler; -use log::{error, info, warn}; use std::net::SocketAddr; use std::process; use tokio::net::TcpListener; use tokio::task::JoinHandle; +use tracing::{error, info, trace, warn}; use super::TaskClient; @@ -23,7 +23,7 @@ impl Listener { } async fn run(&mut self, connection_handler: ConnectionHandler) { - log::trace!("Starting Listener"); + trace!("Starting Listener"); let listener = match TcpListener::bind(self.address).await { Ok(listener) => listener, Err(err) => { @@ -36,7 +36,7 @@ impl Listener { tokio::select! { biased; _ = self.shutdown.recv() => { - log::trace!("Listener: Received shutdown"); + trace!("Listener: Received shutdown"); } connection = listener.accept() => { match connection { @@ -49,7 +49,7 @@ impl Listener { }, }; } - log::trace!("Listener: Exiting"); + trace!("Listener: Exiting"); } pub(crate) fn start(mut self, connection_handler: ConnectionHandler) -> JoinHandle<()> { diff --git a/mixnode/src/node/mod.rs b/mixnode/src/node/mod.rs index e630007454..9e6358de57 100644 --- a/mixnode/src/node/mod.rs +++ b/mixnode/src/node/mod.rs @@ -2,72 +2,44 @@ // SPDX-License-Identifier: GPL-3.0-only use crate::config::Config; -use crate::error::MixnodeError; -use crate::node::helpers::{load_identity_keys, load_sphinx_keys}; -use crate::node::http::HttpApiBuilder; use crate::node::listener::connection_handler::packet_processing::PacketProcessor; use crate::node::listener::connection_handler::ConnectionHandler; use crate::node::listener::Listener; -use crate::node::node_description::NodeDescription; use crate::node::packet_delayforwarder::{DelayForwarder, PacketDelayForwardSender}; -use log::{error, info, warn}; -use nym_bin_common::output_format::OutputFormat; use nym_crypto::asymmetric::{encryption, identity}; use nym_mixnode_common::verloc; use nym_mixnode_common::verloc::VerlocMeasurer; use nym_node_http_api::state::metrics::{SharedMixingStats, SharedVerlocStats}; use nym_task::{TaskClient, TaskHandle}; -use rand::seq::SliceRandom; -use rand::thread_rng; use std::net::SocketAddr; use std::process; use std::sync::Arc; +use tracing::{error, info, warn}; -pub mod helpers; -mod http; mod listener; -pub mod node_description; mod node_statistics; mod packet_delayforwarder; // the MixNode will live for whole duration of this program pub struct MixNode { config: Config, - descriptor: NodeDescription, identity_keypair: Arc, sphinx_keypair: Arc, - run_http_server: bool, task_client: Option, mixing_stats: Option, verloc_stats: Option, } impl MixNode { - pub fn new(config: Config) -> Result { - Ok(MixNode { - run_http_server: true, - descriptor: Self::load_node_description(&config), - identity_keypair: Arc::new(load_identity_keys(&config)?), - sphinx_keypair: Arc::new(load_sphinx_keys(&config)?), - config, - task_client: None, - mixing_stats: None, - verloc_stats: None, - }) - } - pub fn new_loaded( config: Config, - descriptor: NodeDescription, identity_keypair: Arc, sphinx_keypair: Arc, ) -> Self { MixNode { - run_http_server: true, task_client: None, config, - descriptor, identity_keypair, sphinx_keypair, mixing_stats: None, @@ -75,10 +47,6 @@ impl MixNode { } } - pub fn disable_http_server(&mut self) { - self.run_http_server = false - } - pub fn set_task_client(&mut self, task_client: TaskClient) { self.task_client = Some(task_client) } @@ -91,40 +59,6 @@ impl MixNode { self.verloc_stats = Some(verloc_stats) } - fn load_node_description(config: &Config) -> NodeDescription { - NodeDescription::load_from_file(&config.storage_paths.node_description).unwrap_or_default() - } - - /// Prints relevant node details to the console - pub fn print_node_details(&self, output: OutputFormat) { - 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(), - 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.http.bind_address.port(), - verloc_port: self.config.mixnode.verloc_port, - }; - - println!("{}", output.format(&node_details)); - } - - fn start_http_api( - &self, - atomic_verloc_result: SharedVerlocStats, - node_stats_pointer: SharedMixingStats, - metrics_key: Option<&String>, - task_client: TaskClient, - ) -> Result<(), MixnodeError> { - HttpApiBuilder::new(&self.config, &self.identity_keypair, &self.sphinx_keypair) - .with_verloc(atomic_verloc_result) - .with_mixing_stats(node_stats_pointer) - .with_metrics_key(metrics_key) - .with_descriptor(self.descriptor.clone()) - .start(task_client) - } - fn start_node_stats_controller( &mut self, shutdown: TaskClient, @@ -221,41 +155,35 @@ impl MixNode { verloc_state } - fn random_api_client(&self) -> nym_validator_client::NymApiClient { - let endpoints = self.config.get_nym_api_endpoints(); - let nym_api = endpoints - .choose(&mut thread_rng()) - .expect("The list of validator apis is empty"); - - nym_validator_client::NymApiClient::new(nym_api.clone()) - } - 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(); - let existing_nodes = match validator_client.get_all_basic_nodes(None).await { - Ok(nodes) => nodes, - Err(err) => { - error!( - "failed to grab initial network mixnodes - {err}\n \ - Please try to startup again in few minutes", - ); - process::exit(1); + for api_url in self.config.get_nym_api_endpoints() { + let client = nym_validator_client::NymApiClient::new(api_url.clone()); + match client.get_all_basic_nodes(None).await { + Ok(nodes) => { + return nodes.iter().any(|node| { + &node.ed25519_identity_pubkey == self.identity_keypair.public_key() + }) + } + Err(err) => { + error!("failed to grab initial network mixnodes from {api_url}: {err}",); + } } - }; + } - existing_nodes - .iter() - .any(|node| &node.ed25519_identity_pubkey == self.identity_keypair.public_key()) + error!( + "failed to grab initial network mixnodes from any of the available apis. Please try to startup again in few minutes", + ); + process::exit(1); } async fn wait_for_interrupt(&self, shutdown: TaskHandle) { let _res = shutdown.wait_for_shutdown().await; - log::info!("Stopping nym mixnode"); + info!("Stopping nym mixnode"); } - pub async fn run(&mut self) -> Result<(), MixnodeError> { + pub async fn run(&mut self) { info!("Starting nym mixnode"); if self.check_if_bonded().await { @@ -270,7 +198,7 @@ impl MixNode { .unwrap_or_default() .name_if_unnamed("mixnode"); - let (node_stats_pointer, node_stats_update_sender) = + let (_, node_stats_update_sender) = self.start_node_stats_controller(shutdown.fork("node_statistics::Controller")); let delay_forwarding_channel = self.start_packet_delay_forwarder( node_stats_update_sender.clone(), @@ -281,23 +209,9 @@ impl MixNode { delay_forwarding_channel, shutdown.fork("Listener"), ); - let atomic_verloc_results = self.start_verloc_measurements(shutdown.fork("VerlocMeasurer")); - - // Rocket handles shutdown on it's own, but its shutdown handling should be incorporated - // with that of the rest of the tasks. - // Currently it's runtime is forcefully terminated once the mixnode exits. - if self.run_http_server { - self.start_http_api( - atomic_verloc_results, - node_stats_pointer, - self.config.metrics_key(), - shutdown.fork("http-api"), - )?; - } + self.start_verloc_measurements(shutdown.fork("VerlocMeasurer")); info!("Finished nym mixnode startup procedure - it should now be able to receive mix traffic!"); self.wait_for_interrupt(shutdown).await; - - Ok(()) } } diff --git a/mixnode/src/node/node_description.rs b/mixnode/src/node/node_description.rs deleted file mode 100644 index c2d6bbacf4..0000000000 --- a/mixnode/src/node/node_description.rs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2023 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use serde::Deserialize; -use serde::Serialize; -use std::fs::create_dir_all; -use std::path::Path; -use std::{fs, io}; - -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] -pub struct NodeDescription { - #[serde(default)] - pub name: String, - - #[serde(default)] - pub description: String, - - #[serde(default)] - pub link: String, - - #[serde(default)] - pub location: String, -} - -impl Default for NodeDescription { - fn default() -> Self { - NodeDescription { - name: "This node has not yet set a name".to_string(), - description: "This node has not yet set a description".to_string(), - link: "https://nymtech.net".to_string(), - location: "This node has not yet set a location".to_string(), - } - } -} - -impl NodeDescription { - pub fn load_from_file>(path: P) -> io::Result { - // 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 fn save_to_file>(&self, path: P) -> io::Result<()> { - // let description_file_path: PathBuf = [config_path.to_str().unwrap(), DESCRIPTION_FILE] - // .iter() - // .collect(); - let description_toml = toml::to_string(self).expect("could not encode description to toml"); - if let Some(parent) = path.as_ref().parent() { - create_dir_all(parent)?; - } - fs::write(path, description_toml)?; - Ok(()) - } -} diff --git a/mixnode/src/node/node_statistics.rs b/mixnode/src/node/node_statistics.rs index 7b84f89529..3e3f84cd87 100644 --- a/mixnode/src/node/node_statistics.rs +++ b/mixnode/src/node/node_statistics.rs @@ -7,7 +7,6 @@ use super::TaskClient; use futures::channel::mpsc; use futures::lock::Mutex; use futures::StreamExt; -use log::{debug, info, trace}; use nym_node_http_api::state::metrics::SharedMixingStats; use std::collections::HashMap; use std::ops::DerefMut; @@ -15,6 +14,7 @@ use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::Arc; use std::time::Duration; use time::OffsetDateTime; +use tracing::{debug, info, trace}; // convenience aliases type PacketsMap = HashMap; @@ -136,7 +136,7 @@ impl UpdateHandler { } async fn run(&mut self) { - log::trace!("Starting UpdateHandler"); + trace!("Starting UpdateHandler"); while !self.shutdown.is_shutdown() { tokio::select! { Some(packet_data) = self.update_receiver.next() => { @@ -151,13 +151,13 @@ impl UpdateHandler { } } _ = self.shutdown.recv() => { - log::trace!("UpdateHandler: Received shutdown"); + trace!("UpdateHandler: Received shutdown"); break; } } } - log::trace!("UpdateHandler: Exiting"); + trace!("UpdateHandler: Exiting"); } } @@ -230,11 +230,11 @@ impl StatsUpdater { tokio::select! { _ = tokio::time::sleep(self.updating_delay) => self.update_stats().await, _ = self.shutdown.recv() => { - log::trace!("StatsUpdater: Received shutdown"); + trace!("StatsUpdater: Received shutdown"); } } } - log::trace!("StatsUpdater: Exiting"); + trace!("StatsUpdater: Exiting"); } } @@ -319,16 +319,16 @@ impl PacketStatsConsoleLogger { } async fn run(&mut self) { - log::trace!("Starting PacketStatsConsoleLogger"); + trace!("Starting PacketStatsConsoleLogger"); while !self.shutdown.is_shutdown() { tokio::select! { _ = tokio::time::sleep(self.logging_delay) => self.log_running_stats().await, _ = self.shutdown.recv() => { - log::trace!("PacketStatsConsoleLogger: Received shutdown"); + trace!("PacketStatsConsoleLogger: Received shutdown"); } }; } - log::trace!("PacketStatsConsoleLogger: Exiting"); + trace!("PacketStatsConsoleLogger: Exiting"); } } diff --git a/mixnode/src/node/packet_delayforwarder.rs b/mixnode/src/node/packet_delayforwarder.rs index 2aa85d714c..99a61820ac 100644 --- a/mixnode/src/node/packet_delayforwarder.rs +++ b/mixnode/src/node/packet_delayforwarder.rs @@ -1,6 +1,7 @@ // Copyright 2020 - Nym Technologies SA // SPDX-License-Identifier: GPL-3.0-only +use super::TaskClient; use crate::node::node_statistics::UpdateSender; use futures::channel::mpsc; use futures::StreamExt; @@ -8,8 +9,7 @@ use nym_nonexhaustive_delayqueue::{Expired, NonExhaustiveDelayQueue}; use nym_sphinx::forwarding::packet::MixPacket; use std::io; use tokio::time::Instant; - -use super::TaskClient; +use tracing::trace; // Delay + MixPacket vs Instant + MixPacket @@ -105,7 +105,7 @@ where } pub(crate) async fn run(&mut self) { - log::trace!("Starting DelayForwarder"); + trace!("Starting DelayForwarder"); loop { tokio::select! { delayed = self.delay_queue.next() => { @@ -117,12 +117,12 @@ where self.handle_new_packet(new_packet.unwrap()) } _ = self.shutdown.recv() => { - log::trace!("DelayForwarder: Received shutdown"); + trace!("DelayForwarder: Received shutdown"); break; } } } - log::trace!("DelayForwarder: Exiting"); + trace!("DelayForwarder: Exiting"); } } diff --git a/nym-node/src/cli/commands/migrate.rs b/nym-node/src/cli/commands/migrate.rs index ec8cdaeeea..efb721245d 100644 --- a/nym-node/src/cli/commands/migrate.rs +++ b/nym-node/src/cli/commands/migrate.rs @@ -1,649 +1,17 @@ // Copyright 2024 - Nym Technologies SA // SPDX-License-Identifier: GPL-3.0-only -use crate::cli::helpers::{ - EntryGatewayArgs, ExitGatewayArgs, HostArgs, HttpArgs, MixnetArgs, MixnodeArgs, WireguardArgs, -}; -use crate::node::description::save_node_description; -use crate::node::helpers::{ - bonding_version, load_ed25519_identity_public_key, store_x25519_noise_keypair, -}; -use clap::ValueEnum; use colored::Color::TrueColor; use colored::Colorize; -use nym_crypto::asymmetric::x25519; -use nym_gateway::helpers::{load_ip_packet_router_config, load_network_requester_config}; -use nym_gateway::GatewayError; -use nym_mixnode::MixnodeError; -use nym_network_requester::{CustomGatewayDetails, GatewayDetails}; -use nym_node::config; -use nym_node::config::entry_gateway::ZkNymTicketHandlerDebug; -use nym_node::config::mixnode::DEFAULT_VERLOC_PORT; -use nym_node::config::Config; -use nym_node::config::{default_config_filepath, ConfigBuilder, NodeMode}; -use nym_node::error::{EntryGatewayError, ExitGatewayError, NymNodeError}; -use nym_node_http_api::api::api_requests::v1::node::models::NodeDescription; -use rand::rngs::OsRng; -use std::fmt::{Display, Formatter}; -use std::fs::create_dir_all; -use std::net::SocketAddr; -use std::path::{Path, PathBuf}; -use std::{fs, io}; -use tracing::{info, trace, warn}; -use zeroize::Zeroizing; - -#[derive(Debug, Clone, Copy, ValueEnum)] -enum NodeType { - Mixnode, - Gateway, -} - -impl Display for NodeType { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - NodeType::Mixnode => write!(f, "mixnode"), - NodeType::Gateway => write!(f, "gateway"), - } - } -} +use nym_node::error::NymNodeError; #[derive(clap::Args, Debug)] -#[clap(group = clap::ArgGroup::new("old-config").required(true))] pub(crate) struct Args { - /// Type of node (mixnode or gateway) to migrate into a nym-node. - node_type: NodeType, - - /// Id of the node that's going to get migrated - #[clap(long, group = "old-config")] - id: Option, - - /// Path to a configuration file of the node that's going to get migrated. - #[clap(long, group = "old-config")] - config_file: Option, - - /// Specify whether to preserve id of the imported node. - #[clap(long)] - preserve_id: bool, - - // totally optional arguments for overriding any defaults: - #[clap(flatten)] - host: HostArgs, - - #[clap(flatten)] - http: HttpArgs, - - #[clap(flatten)] - mixnet: MixnetArgs, - - #[clap(flatten)] - wireguard: WireguardArgs, - - #[clap(flatten)] - mixnode: MixnodeArgs, - - #[clap(flatten)] - entry_gateway: EntryGatewayArgs, - - #[clap(flatten)] - exit_gateway: ExitGatewayArgs, + #[arg(trailing_var_arg = true, allow_hyphen_values = true, hide = true)] + _args: Vec, } -impl Args { - fn take_mnemonic(&mut self) -> Option> { - self.entry_gateway.mnemonic.take().map(Zeroizing::new) - } - - fn config_path(&self) -> PathBuf { - // SAFETY: - // if `config_file` hasn't been specified, `id` MUST be available due to clap's ArgGroup - #[allow(clippy::unwrap_used)] - self.config_file.clone().unwrap_or_else(|| { - let id = self.id.as_ref().unwrap(); - match self.node_type { - NodeType::Mixnode => nym_mixnode::config::default_config_filepath(id), - NodeType::Gateway => nym_gateway::config::default_config_filepath(id), - } - }) - } -} - -fn nym_node_id( - typ: NodeType, - original_id: &str, - preserve_id: bool, -) -> Result { - if preserve_id { - let path = default_config_filepath(original_id); - if path.exists() { - return Err(NymNodeError::MigrationFailure { - node_type: typ.to_string(), - message: format!("nym-node with id '{original_id}' already exists"), - }); - } - } - - let mut candidate = original_id.to_string(); - let mut counter = 0; - loop { - let path = default_config_filepath(&candidate); - if path.exists() { - warn!("nym-node with id '{candidate}' already exists") - } else { - return Ok(candidate); - } - - candidate = format!("{original_id}-{counter}"); - counter += 1; - } -} - -fn copy_old_data, Q: AsRef>( - node_type: NodeType, - from: P, - to: Q, -) -> Result<(), NymNodeError> { - fn copy_inner, Q: AsRef>(from: P, to: Q) -> io::Result<()> { - if let Some(parent) = to.as_ref().parent() { - create_dir_all(parent)?; - } - fs::copy(from, to)?; - Ok(()) - } - - if let Err(err) = copy_inner(from.as_ref(), to.as_ref()) { - return Err(NymNodeError::MigrationFailure { - node_type: node_type.to_string(), - message: format!( - "failed to move '{}' to '{}': {err}", - from.as_ref().display(), - to.as_ref().display() - ), - }); - } - Ok(()) -} - -async fn migrate_mixnode(mut args: Args) -> Result<(), NymNodeError> { - let maybe_custom_mnemonic = args.take_mnemonic(); - let config_file = args.config_path(); - let preserve_id = args.preserve_id; - - info!( - "attempting to migrate mixnode from '{}'", - config_file.display() - ); - let cfg = nym_mixnode::config::Config::read_from_toml_file(&config_file).map_err(|source| { - MixnodeError::ConfigLoadFailure { - id: "???".to_string(), - path: config_file, - source, - } - })?; - - let old_description = if cfg.storage_paths.node_description.exists() { - Some( - nym_mixnode::node::node_description::NodeDescription::load_from_file( - &cfg.storage_paths.node_description, - ) - .map_err(|source| { - nym_node::error::MixnodeError::DescriptionLoadFailure { - path: cfg.storage_paths.node_description, - source, - } - })?, - ) - } else { - None - }; - - let nymnode_id = nym_node_id(NodeType::Mixnode, &cfg.mixnode.id, preserve_id)?; - let nym_node_config_path = default_config_filepath(&nymnode_id); - let data_dir = Config::default_data_directory(&nym_node_config_path)?; - - let ip = cfg.mixnode.listening_address; - - let location = old_description - .as_ref() - .and_then(|d| d.location.parse().ok()); - - // generate nym-node config - let config = ConfigBuilder::new(nymnode_id, nym_node_config_path, data_dir.clone()) - .with_mode(NodeMode::Mixnode) - .with_host(args.host.override_config_section(config::Host { - public_ips: cfg.host.public_ips, - hostname: cfg.host.hostname, - location, - })) - .with_http(args.http.override_config_section(config::Http { - bind_address: cfg.http.bind_address, - landing_page_assets_path: cfg.http.landing_page_assets_path, - access_token: cfg.http.metrics_key, - ..Default::default() - })) - .with_mixnet(args.mixnet.override_config_section(config::Mixnet { - bind_address: SocketAddr::new(ip, cfg.mixnode.mix_port), - nym_api_urls: cfg.mixnode.nym_api_urls, - debug: config::MixnetDebug { - packet_forwarding_initial_backoff: cfg.debug.packet_forwarding_initial_backoff, - packet_forwarding_maximum_backoff: cfg.debug.packet_forwarding_maximum_backoff, - initial_connection_timeout: cfg.debug.initial_connection_timeout, - maximum_connection_buffer_size: cfg.debug.maximum_connection_buffer_size, - ..Default::default() - }, - ..Default::default() - })) - .with_mixnode(args.mixnode.override_config_section(config::MixnodeConfig { - verloc: config::mixnode::Verloc { - bind_address: SocketAddr::new(ip, cfg.mixnode.verloc_port), - announce_port: None, - debug: config::mixnode::VerlocDebug { - packets_per_node: cfg.verloc.packets_per_node, - connection_timeout: cfg.verloc.connection_timeout, - packet_timeout: cfg.verloc.packet_timeout, - delay_between_packets: cfg.verloc.delay_between_packets, - tested_nodes_batch_size: cfg.verloc.tested_nodes_batch_size, - testing_interval: cfg.verloc.testing_interval, - retry_timeout: cfg.verloc.retry_timeout, - }, - }, - debug: config::mixnode::Debug { - node_stats_logging_delay: cfg.debug.node_stats_logging_delay, - node_stats_updating_delay: cfg.debug.node_stats_updating_delay, - }, - ..config::MixnodeConfig::new_default() - })) - .with_wireguard(args.wireguard.build_config_section(&data_dir)) - .with_entry_gateway(args.entry_gateway.build_config_section(&data_dir)) - .with_exit_gateway(args.exit_gateway.build_config_section(&data_dir)) - .build(); - - let d_ref = old_description.as_ref(); - - // update description - let node_description = NodeDescription { - moniker: d_ref.map(|d| &d.name).cloned().unwrap_or_default(), - website: d_ref.map(|d| &d.link).cloned().unwrap_or_default(), - security_contact: "".to_string(), - details: d_ref.map(|d| &d.description).cloned().unwrap_or_default(), - }; - save_node_description(&config.storage_paths.description, &node_description)?; - - // create noise keypair - let mut rng = OsRng; - let x25519_noise_keys = x25519::KeyPair::new(&mut rng); - trace!("attempting to store x25519 noise keypair"); - store_x25519_noise_keypair( - &x25519_noise_keys, - config.storage_paths.keys.x25519_noise_storage_paths(), - )?; - - // move existing keys and generate missing data - info!("attempting to copy mixnode keys to their new locations"); - copy_old_data( - NodeType::Mixnode, - cfg.storage_paths.keys.public_identity_key_file, - &config.storage_paths.keys.public_ed25519_identity_key_file, - )?; - copy_old_data( - NodeType::Mixnode, - cfg.storage_paths.keys.private_identity_key_file, - &config.storage_paths.keys.private_ed25519_identity_key_file, - )?; - copy_old_data( - NodeType::Mixnode, - cfg.storage_paths.keys.public_sphinx_key_file, - &config.storage_paths.keys.public_x25519_sphinx_key_file, - )?; - copy_old_data( - NodeType::Mixnode, - cfg.storage_paths.keys.private_sphinx_key_file, - &config.storage_paths.keys.private_x25519_sphinx_key_file, - )?; - - let ed25519_public_key = load_ed25519_identity_public_key( - &config.storage_paths.keys.public_ed25519_identity_key_file, - )?; - - // entry gateway initialisation - crate::node::EntryGatewayData::initialise(&config.entry_gateway, maybe_custom_mnemonic)?; - - // exit gateway initialisation - crate::node::ExitGatewayData::initialise(&config.exit_gateway, ed25519_public_key).await?; - - crate::node::WireguardData::initialise(&config.wireguard)?; - - config.save()?; - - info!( - "mixnode {} has been migrated into a nym-node! all of its data can now be deleted", - cfg.mixnode.id - ); - - Ok(()) -} - -async fn migrate_gateway(mut args: Args) -> Result<(), NymNodeError> { - let config_file = args.config_path(); - let preserve_id = args.preserve_id; - - info!( - "attempting to migrate gateway from '{}'", - config_file.display() - ); - let cfg = nym_gateway::config::Config::read_from_toml_file(&config_file) - .map_err(|source| GatewayError::ConfigLoadFailure { - id: "???".to_string(), - path: config_file, - source, - }) - .map_err(EntryGatewayError::from)?; - - let nr_cfg = match &cfg.storage_paths.network_requester_config { - None => None, - Some(nr_cfg) => Some( - load_network_requester_config("???", nr_cfg) - .await - .map_err(ExitGatewayError::from)?, - ), - }; - - let ipr_cfg = match &cfg.storage_paths.ip_packet_router_config { - None => None, - Some(ipr_cfg) => Some( - load_ip_packet_router_config("???", ipr_cfg) - .await - .map_err(ExitGatewayError::from)?, - ), - }; - - let nymnode_id = nym_node_id(NodeType::Gateway, &cfg.gateway.id, preserve_id)?; - let nym_node_config_path = default_config_filepath(&nymnode_id); - let data_dir = Config::default_data_directory(&nym_node_config_path)?; - - let mode = if cfg.network_requester.enabled && cfg.ip_packet_router.enabled { - NodeMode::ExitGateway - } else { - NodeMode::EntryGateway - }; - - let ip = cfg.gateway.listening_address; - - // prefer new mnemonic explicitly passed with cli; otherwise use the one already present - let mnemonic = args - .take_mnemonic() - .unwrap_or(Zeroizing::new(cfg.gateway.cosmos_mnemonic.clone())); - - let config = ConfigBuilder::new(nymnode_id, nym_node_config_path, data_dir.clone()) - .with_mode(mode) - .with_host(args.host.override_config_section(config::Host { - public_ips: cfg.host.public_ips, - hostname: cfg.host.hostname, - ..Default::default() - })) - .with_http(args.http.override_config_section(config::Http { - bind_address: cfg.http.bind_address, - landing_page_assets_path: cfg.http.landing_page_assets_path, - ..Default::default() - })) - .with_mixnet(args.mixnet.override_config_section(config::Mixnet { - bind_address: SocketAddr::new(ip, cfg.gateway.mix_port), - announce_port: None, - nym_api_urls: cfg.gateway.nym_api_urls.clone(), - nyxd_urls: cfg.gateway.nyxd_urls.clone(), - debug: config::MixnetDebug { - packet_forwarding_initial_backoff: cfg.debug.packet_forwarding_initial_backoff, - packet_forwarding_maximum_backoff: cfg.debug.packet_forwarding_maximum_backoff, - initial_connection_timeout: cfg.debug.initial_connection_timeout, - maximum_connection_buffer_size: cfg.debug.maximum_connection_buffer_size, - ..Default::default() - }, - })) - .with_mixnode(args.mixnode.override_config_section(config::MixnodeConfig { - verloc: config::mixnode::Verloc { - bind_address: SocketAddr::new(ip, DEFAULT_VERLOC_PORT), - ..Default::default() - }, - ..config::MixnodeConfig::new_default() - })) - .with_entry_gateway(args.entry_gateway.override_config_section( - config::EntryGatewayConfig { - storage_paths: config::persistence::EntryGatewayPaths::new(&data_dir), - enforce_zk_nyms: cfg.gateway.only_coconut_credentials, - bind_address: SocketAddr::new(ip, cfg.gateway.clients_port), - announce_ws_port: None, - announce_wss_port: cfg.gateway.clients_wss_port, - debug: config::entry_gateway::Debug { - message_retrieval_limit: cfg.debug.message_retrieval_limit, - zk_nym_tickets: ZkNymTicketHandlerDebug { - revocation_bandwidth_penalty: - cfg.debug.zk_nym_tickets.revocation_bandwidth_penalty, - pending_poller: cfg.debug.zk_nym_tickets.pending_poller, - minimum_api_quorum: cfg.debug.zk_nym_tickets.minimum_api_quorum, - minimum_redemption_tickets: - cfg.debug.zk_nym_tickets.minimum_redemption_tickets, - maximum_time_between_redemption: - cfg.debug.zk_nym_tickets.maximum_time_between_redemption, - }, - }, - }, - )) - .with_exit_gateway( - args.exit_gateway - .override_config_section(config::ExitGatewayConfig { - storage_paths: config::persistence::ExitGatewayPaths::new(&data_dir), - open_proxy: false, - upstream_exit_policy_url: nr_cfg - .as_ref() - .and_then(|c| c.network_requester.upstream_exit_policy_url.clone()) - .unwrap_or( - config::ExitGatewayConfig::new_default(".").upstream_exit_policy_url, - ), - network_requester: config::exit_gateway::NetworkRequester { - debug: config::exit_gateway::NetworkRequesterDebug { - enabled: cfg.network_requester.enabled, - disable_poisson_rate: nr_cfg - .as_ref() - .map(|c| c.network_requester.disable_poisson_rate) - .unwrap_or( - config::exit_gateway::NetworkRequesterDebug::default() - .disable_poisson_rate, - ), - client_debug: nr_cfg.as_ref().map(|c| c.base.debug).unwrap_or_default(), - }, - }, - ip_packet_router: config::exit_gateway::IpPacketRouter { - debug: config::exit_gateway::IpPacketRouterDebug { - enabled: cfg.ip_packet_router.enabled, - disable_poisson_rate: ipr_cfg - .as_ref() - .map(|c| c.ip_packet_router.disable_poisson_rate) - .unwrap_or( - config::exit_gateway::IpPacketRouterDebug::default() - .disable_poisson_rate, - ), - client_debug: ipr_cfg - .as_ref() - .map(|c| c.base.debug) - .unwrap_or_default(), - }, - }, - debug: Default::default(), - }), - ) - .build(); - - // create noise keypair - let mut rng = OsRng; - let x25519_noise_keys = x25519::KeyPair::new(&mut rng); - trace!("attempting to store x25519 noise keypair"); - store_x25519_noise_keypair( - &x25519_noise_keys, - config.storage_paths.keys.x25519_noise_storage_paths(), - )?; - - // move existing keys and generate missing data - info!("attempting to copy gateway keys to their new locations"); - - copy_old_data( - NodeType::Gateway, - cfg.storage_paths.keys.public_identity_key_file, - &config.storage_paths.keys.public_ed25519_identity_key_file, - )?; - copy_old_data( - NodeType::Gateway, - cfg.storage_paths.keys.private_identity_key_file, - &config.storage_paths.keys.private_ed25519_identity_key_file, - )?; - copy_old_data( - NodeType::Gateway, - cfg.storage_paths.keys.public_sphinx_key_file, - &config.storage_paths.keys.public_x25519_sphinx_key_file, - )?; - copy_old_data( - NodeType::Gateway, - cfg.storage_paths.keys.private_sphinx_key_file, - &config.storage_paths.keys.private_x25519_sphinx_key_file, - )?; - - let ed25519_public_key = load_ed25519_identity_public_key( - &config.storage_paths.keys.public_ed25519_identity_key_file, - )?; - - // mixnode data initialisation - crate::node::MixnodeData::initialise(&config.mixnode)?; - - // selectively initialise exit gateway - let gateway_details = - GatewayDetails::Custom(CustomGatewayDetails::new(ed25519_public_key)).into(); - let mut rng = OsRng; - - if let Some(nr_cfg) = nr_cfg { - let nr_paths = nr_cfg.storage_paths.common_paths; - let new_nr_paths = &config.exit_gateway.storage_paths.network_requester; - - copy_old_data( - NodeType::Gateway, - nr_paths.keys.public_identity_key_file, - &new_nr_paths.public_ed25519_identity_key_file, - )?; - copy_old_data( - NodeType::Gateway, - nr_paths.keys.private_identity_key_file, - &new_nr_paths.private_ed25519_identity_key_file, - )?; - copy_old_data( - NodeType::Gateway, - nr_paths.keys.public_encryption_key_file, - &new_nr_paths.public_x25519_diffie_hellman_key_file, - )?; - copy_old_data( - NodeType::Gateway, - nr_paths.keys.private_encryption_key_file, - &new_nr_paths.private_x25519_diffie_hellman_key_file, - )?; - copy_old_data( - NodeType::Gateway, - nr_paths.keys.ack_key_file, - &new_nr_paths.ack_key_file, - )?; - copy_old_data( - NodeType::Gateway, - nr_paths.gateway_registrations, - &new_nr_paths.gateway_registrations, - )?; - copy_old_data( - NodeType::Gateway, - nr_paths.reply_surb_database, - &new_nr_paths.reply_surb_database, - )?; - } else { - crate::node::ExitGatewayData::initialise_network_requester( - &mut rng, - &config.exit_gateway, - &gateway_details, - ) - .await?; - } - - if let Some(ipr_cfg) = ipr_cfg { - let ipr_paths = ipr_cfg.storage_paths.common_paths; - let new_ipr_paths = &config.exit_gateway.storage_paths.ip_packet_router; - - copy_old_data( - NodeType::Gateway, - ipr_paths.keys.public_identity_key_file, - &new_ipr_paths.public_ed25519_identity_key_file, - )?; - copy_old_data( - NodeType::Gateway, - ipr_paths.keys.private_identity_key_file, - &new_ipr_paths.private_ed25519_identity_key_file, - )?; - copy_old_data( - NodeType::Gateway, - ipr_paths.keys.public_encryption_key_file, - &new_ipr_paths.public_x25519_diffie_hellman_key_file, - )?; - copy_old_data( - NodeType::Gateway, - ipr_paths.keys.private_encryption_key_file, - &new_ipr_paths.private_x25519_diffie_hellman_key_file, - )?; - copy_old_data( - NodeType::Gateway, - ipr_paths.keys.ack_key_file, - &new_ipr_paths.ack_key_file, - )?; - copy_old_data( - NodeType::Gateway, - ipr_paths.gateway_registrations, - &new_ipr_paths.gateway_registrations, - )?; - copy_old_data( - NodeType::Gateway, - ipr_paths.reply_surb_database, - &new_ipr_paths.reply_surb_database, - )?; - } else { - crate::node::ExitGatewayData::initialise_ip_packet_router_requester( - &mut rng, - &config.exit_gateway, - &gateway_details, - ) - .await?; - } - - crate::node::WireguardData::initialise(&config.wireguard)?; - - save_node_description( - &config.storage_paths.description, - &NodeDescription::default(), - )?; - - // finally move the mnemonic - config - .entry_gateway - .storage_paths - .save_mnemonic_to_file(&mnemonic)?; - - config.save()?; - - info!( - "gateway {} has been migrated into a nym-node! all of its data can now be deleted", - cfg.gateway.id - ); - - Ok(()) -} - -pub(crate) async fn execute(args: Args) -> Result<(), NymNodeError> { - trace!("args: {args:#?}"); - - match args.node_type { - NodeType::Mixnode => migrate_mixnode(args).await?, - NodeType::Gateway => migrate_gateway(args).await?, - } - +pub(crate) async fn execute(_args: Args) -> Result<(), NymNodeError> { let orange = TrueColor { r: 251, g: 110, @@ -651,17 +19,12 @@ pub(crate) async fn execute(args: Args) -> Result<(), NymNodeError> { }; println!("{}", "** Attention **".color(orange).bold()); - print!("Please consider updating the '"); - print!("{}", "version".color(orange)); - print!("' field of your "); - print!("{}", "existing".bold().underline()); - println!(" node to:"); + print!("This binary "); + print!("{}", "DOES NOT".color(orange).bold()); + print!("' support migrating from older mixnode/gateway binaries anymore "); println!(); - println!("{}", bonding_version().bold().color(orange)); - println!(); - print!("in the settings section of the "); - println!("{}", "Nym Wallet".bold().color(orange)); + println!("please use an older version instead before attempting to use this binary again."); println!(); - Ok(()) + Err(NymNodeError::UnsupportedMigration) } diff --git a/nym-node/src/config/exit_gateway.rs b/nym-node/src/config/exit_gateway.rs index 9cc640e701..20aa884c08 100644 --- a/nym-node/src/config/exit_gateway.rs +++ b/nym-node/src/config/exit_gateway.rs @@ -266,11 +266,6 @@ pub fn ephemeral_exit_gateway_config( auth_opts.config.base.set_no_poisson_process(); } - let pub_id_path = config - .storage_paths - .keys - .public_ed25519_identity_key_file - .clone(); let ipr_enabled = config.exit_gateway.ip_packet_router.debug.enabled; let nr_enabled = config.exit_gateway.network_requester.debug.enabled; @@ -292,11 +287,6 @@ pub fn ephemeral_exit_gateway_config( gateway.ip_packet_router.enabled = ipr_enabled; gateway.network_requester.enabled = nr_enabled; - // this is temporary until http api is fully managed by nymnode itself - // (because currently gateway is loading its public key for the second time when starting the API to determine addresses of its clients. - // Obviously this doesn't work properly without the valid paths) - gateway.storage_paths.keys.public_identity_key_file = pub_id_path; - Ok(EphemeralConfig { nr_opts: Some(nr_opts), ipr_opts: Some(ipr_opts), diff --git a/nym-node/src/config/helpers.rs b/nym-node/src/config/helpers.rs index 1f91e290e1..9b7c9d3af9 100644 --- a/nym-node/src/config/helpers.rs +++ b/nym-node/src/config/helpers.rs @@ -64,10 +64,8 @@ pub fn ephemeral_gateway_config( host, http, gateway, - nym_gateway::config::GatewayPaths::new_empty(), nym_gateway::config::NetworkRequester { enabled: false }, nym_gateway::config::IpPacketRouter { enabled: false }, - config.logging, nym_gateway::config::Debug { packet_forwarding_initial_backoff: config .mixnet diff --git a/nym-node/src/config/mixnode.rs b/nym-node/src/config/mixnode.rs index c938610255..92f9279c2f 100644 --- a/nym-node/src/config/mixnode.rs +++ b/nym-node/src/config/mixnode.rs @@ -201,9 +201,7 @@ pub fn ephemeral_mixnode_config( host, http, mixnode, - nym_mixnode::config::MixNodePaths::new_empty(), config.mixnode.verloc.debug, - config.logging, nym_mixnode::config::Debug { node_stats_logging_delay: config.mixnode.debug.node_stats_logging_delay, node_stats_updating_delay: config.mixnode.debug.node_stats_updating_delay, diff --git a/nym-node/src/config/persistence.rs b/nym-node/src/config/persistence.rs index 4106156aff..e62ced0c55 100644 --- a/nym-node/src/config/persistence.rs +++ b/nym-node/src/config/persistence.rs @@ -3,7 +3,6 @@ use crate::error::EntryGatewayError; use nym_client_core_config_types::disk_persistence::{ClientKeysPaths, CommonClientPaths}; -use nym_mixnode::config::persistence::paths::DEFAULT_DESCRIPTION_FILENAME; use serde::{Deserialize, Serialize}; use std::fs::create_dir_all; use std::path::{Path, PathBuf}; @@ -20,6 +19,8 @@ pub const DEFAULT_X25519_PRIVATE_NOISE_KEY_FILENAME: &str = "x25519_noise"; pub const DEFAULT_X25519_PUBLIC_NOISE_KEY_FILENAME: &str = "x25519_noise.pub"; pub const DEFAULT_NYMNODE_DESCRIPTION_FILENAME: &str = "description.toml"; +pub const DEFAULT_DESCRIPTION_FILENAME: &str = "description.toml"; + // Mixnode: // Entry Gateway: diff --git a/nym-node/src/error.rs b/nym-node/src/error.rs index 06f1b6c3fa..afbed84a0e 100644 --- a/nym-node/src/error.rs +++ b/nym-node/src/error.rs @@ -47,6 +47,9 @@ pub enum KeyIOFailure { #[derive(Debug, Error)] pub enum NymNodeError { + #[error("this binary version no longer supports migration from legacy mixnodes and gateways")] + UnsupportedMigration, + #[error("could not find an existing config file at '{}' and fresh node initialisation has been disabled", config_path.display())] ForbiddenInitialisation { config_path: PathBuf }, @@ -152,29 +155,13 @@ pub enum NymNodeError { FailedUpgrade, } -impl From for NymNodeError { - fn from(value: nym_mixnode::error::MixnodeError) -> Self { - MixnodeError::from(value).into() - } -} - #[derive(Debug, Error)] pub enum MixnodeError { - #[error("failed to load mixnode description from {}: {source}", path.display())] - DescriptionLoadFailure { - path: PathBuf, - #[source] - source: io::Error, - }, - #[error("currently it's not supported to have different ip addresses for verloc and mixnet ({verloc_bind_ip} and {mix_bind_ip} were used)")] UnsupportedAddresses { verloc_bind_ip: IpAddr, mix_bind_ip: IpAddr, }, - - #[error("mixnode failure: {0}")] - External(#[from] nym_mixnode::error::MixnodeError), } #[derive(Debug, Error)] diff --git a/nym-node/src/node/mod.rs b/nym-node/src/node/mod.rs index 4b455cf6a5..965190de0a 100644 --- a/nym-node/src/node/mod.rs +++ b/nym-node/src/node/mod.rs @@ -8,7 +8,7 @@ use crate::node::helpers::{ store_x25519_sphinx_keypair, DisplayDetails, }; use crate::node::http::{sign_host_details, system_info::get_system_info}; -use nym_bin_common::bin_info_owned; +use nym_bin_common::{bin_info, bin_info_owned}; use nym_crypto::asymmetric::{ed25519, x25519}; use nym_gateway::Gateway; use nym_mixnode::MixNode; @@ -563,20 +563,14 @@ impl NymNode { let config = ephemeral_mixnode_config(self.config.clone())?; let mut mixnode = MixNode::new_loaded( config, - Default::default(), self.ed25519_identity_keys.clone(), self.x25519_sphinx_keys.clone(), ); - mixnode.disable_http_server(); mixnode.set_task_client(task_client); mixnode.set_mixing_stats(self.mixnode.mixing_stats.clone()); mixnode.set_verloc_stats(self.verloc_stats.clone()); - tokio::spawn(async move { - if let Err(err) = mixnode.run().await { - error!("the mixnode subtask has failed with the following message: {err}") - } - }); + tokio::spawn(async move { mixnode.run().await }); Ok(()) } @@ -593,9 +587,9 @@ impl NymNode { self.ed25519_identity_keys.clone(), self.x25519_sphinx_keys.clone(), self.entry_gateway.client_storage.clone(), + bin_info!().into(), self.entry_gateway.stats_storage.clone(), ); - entry_gateway.disable_http_server(); entry_gateway.set_task_client(task_client); entry_gateway.set_session_stats(self.entry_gateway.sessions_stats.clone()); if self.config.wireguard.enabled { @@ -624,9 +618,9 @@ impl NymNode { self.ed25519_identity_keys.clone(), self.x25519_sphinx_keys.clone(), self.exit_gateway.client_storage.clone(), + bin_info!().into(), self.exit_gateway.stats_storage.clone(), ); - exit_gateway.disable_http_server(); exit_gateway.set_task_client(task_client); if self.config.wireguard.enabled { exit_gateway.set_wireguard_data(self.wireguard.into());