chore: remove standalone legacy mixnode/gateway binaries (#5135)

* remove standalone gateway overhead

* remove standalone mixnode overhead

* additional cleanup: removed unused dependencies et al.

* removed calls to 'log::'
This commit is contained in:
Jędrzej Stuczyński
2024-11-15 12:37:35 +00:00
committed by GitHub
parent 475a01c089
commit 69718db6d2
66 changed files with 117 additions and 4966 deletions
Generated
+1 -30
View File
@@ -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",
]
-28
View File
@@ -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 }
-63
View File
@@ -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 <<EOF
[Unit]
Description=Nym Gateway
After=network-online.target
[Service]
ExecStart=$default_location run --id nym-gateway
User=$default_user
[Install]
WantedBy=multi-user.target
EOF
else
echo "Error: the new binary $default_location does not exist."
fi
fi
echo
echo "Consider restarting the nym-gateway service if it is already enabled."
echo "systemctl restart nym-gateway.service"
exit 0
#DEBHELPER#
-26
View File
@@ -1,26 +0,0 @@
#!/bin/sh
backup_dir="/var/lib/nym-gateway-backup"
mkdir -p "$backup_dir"
existing_binaries=$(find / \( -path /proc -o -path /sys -o -path /dev -o -path /mnt -o -path /media \) -prune -o -type f -name "nym-gateway" -print 2>/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#
-16
View File
@@ -1,16 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// 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!()))
}
-265
View File
@@ -1,265 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// 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<IpAddr>,
pub(crate) public_ips: Option<Vec<IpAddr>>,
pub(crate) hostname: Option<String>,
pub(crate) mix_port: Option<u16>,
pub(crate) clients_port: Option<u16>,
pub(crate) datastore: Option<PathBuf>,
pub(crate) nym_apis: Option<Vec<url::Url>>,
pub(crate) mnemonic: Option<bip39::Mnemonic>,
pub(crate) nyxd_urls: Option<Vec<url::Url>>,
pub(crate) only_coconut_credentials: Option<bool>,
pub(crate) with_network_requester: Option<bool>,
pub(crate) with_ip_packet_router: Option<bool>,
}
impl OverrideConfig {
pub(crate) fn do_override(self, mut config: Config) -> Result<Config, GatewayError> {
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<O: Into<OverrideConfig>>(
config: Config,
override_args: O,
) -> Result<Config, GatewayError> {
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, GatewayError> {
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<GatewayNetworkRequesterDetails, GatewayError> {
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<GatewayIpPacketRouterDetails, GatewayError> {
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(),
})
}
-22
View File
@@ -1,22 +0,0 @@
// Copyright 2020-2024 - Nym Technologies SA <contact@nymtech.net>
// 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<String>,
}
pub async fn execute(_args: Init) -> anyhow::Result<()> {
bail!(
"standalone mixnode initialisation has been removed - please initialise a `nym-node` instead"
.red()
.bold()
)
}
-93
View File
@@ -1,93 +0,0 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// 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<run::Run>),
/// 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();
}
}
-24
View File
@@ -1,24 +0,0 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// 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(())
}
-228
View File
@@ -1,228 +0,0 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// 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<IpAddr>,
/// 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<Vec<IpAddr>>,
/// Optional hostname associated with this gateway that will be announced to the nym-api and subsequently to the clients
#[arg(long)]
hostname: Option<String>,
/// The port on which the gateway will be listening for sphinx packets
#[arg(long)]
mix_port: Option<u16>,
/// The port on which the gateway will be listening for clients gateway-requests
#[arg(long)]
clients_port: Option<u16>,
/// Path to sqlite database containing all gateway persistent data
#[arg(long)]
datastore: Option<PathBuf>,
/// 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<Vec<url::Url>>,
/// 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<Vec<url::Url>>,
/// Cosmos wallet mnemonic
#[arg(long)]
mnemonic: Option<bip39::Mnemonic>,
/// 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<bool>,
/// Allows this gateway to run an embedded network requester for minimal network overhead
#[arg(long)]
with_network_requester: Option<bool>,
/// Allows this gateway to run an embedded network requester for minimal network overhead
#[arg(long, hide = true)]
with_ip_packet_router: Option<bool>,
// ##### NETWORK REQUESTER FLAGS #####
/// Specifies whether this network requester should run in 'open-proxy' mode
#[arg(long)]
open_proxy: Option<bool>,
/// 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<PathBuf>,
#[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<Run> 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(())
}
@@ -1,65 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// 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<PathBuf>,
/// 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<bool>,
#[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(())
}
@@ -1,106 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// 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<PathBuf>,
/// 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<bool>,
// 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<bool>,
/// 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(())
}
-143
View File
@@ -1,143 +0,0 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// 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<nyxd::AccountId>,
/// Signs an arbitrary piece of text with your identity key
#[clap(long)]
text: Option<String>,
/// Signs a transaction-specific payload, that is going to be sent to the smart contract, with your identity key
#[clap(long)]
contract_msg: Option<String>,
#[clap(short, long, default_value_t = OutputFormat::default())]
output: OutputFormat,
}
enum SignedTarget {
Text(String),
Address(nyxd::AccountId),
ContractMsg(String),
}
impl TryFrom<Sign> for SignedTarget {
type Error = anyhow::Error;
fn try_from(args: Sign) -> Result<Self, Self::Error> {
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::<serde_json::Value>(&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(())
}
@@ -1,32 +1,13 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// 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/<id>/config`
pub fn default_config_directory<P: AsRef<Path>>(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/<id>/config/config.toml`
pub fn default_config_filepath<P: AsRef<Path>>(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/<id>/data`
pub fn default_data_directory<P: AsRef<Path>>(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<PathBuf>,
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<S: AsRef<str>>(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<Host>,
http: impl Into<Http>,
gateway: impl Into<Gateway>,
storage_paths: impl Into<GatewayPaths>,
network_requester: impl Into<NetworkRequester>,
ip_packet_router: impl Into<IpPacketRouter>,
logging: impl Into<LoggingSettings>,
debug: impl Into<Debug>,
) -> 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<P: AsRef<Path>>(path: P) -> io::Result<Self> {
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<P: AsRef<Path>>(path: P) -> io::Result<Self> {
Self::read_from_path(path)
}
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
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<IpAddr>) -> 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<Url>) -> Self {
self.gateway.nym_api_urls = nym_api_urls;
self
}
pub fn with_custom_validator_nyxd(mut self, validator_nyxd_urls: Vec<Url>) -> 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<Url> {
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<IpAddr>,
/// 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<String>,
}
@@ -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<PathBuf>,
}
@@ -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<u16>,
/// 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<Url>,
/// 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<Url>,
@@ -366,28 +157,7 @@ pub struct Gateway {
pub cosmos_mnemonic: bip39::Mnemonic,
}
impl Gateway {
pub fn new_default<S: Into<String>>(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,
}
-4
View File
@@ -1,4 +0,0 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
pub mod paths;
-178
View File
@@ -1,178 +0,0 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// 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<P: AsRef<Path>>(id: P) -> PathBuf {
default_data_directory(id).join(DEFAULT_NETWORK_REQUESTER_DATA_DIR)
}
pub fn default_ip_packet_router_data_dir<P: AsRef<Path>>(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<PathBuf>,
// 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<PathBuf>,
}
impl GatewayPaths {
pub fn new_default<P: AsRef<Path>>(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<P: AsRef<Path>>(mut self, path: P) -> Self {
self.network_requester_config = Some(path.as_ref().into());
self
}
#[must_use]
pub fn with_default_network_requester_config<P: AsRef<Path>>(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<P: AsRef<Path>>(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<P: AsRef<Path>>(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<PathBuf> {
&self.network_requester_config
}
pub fn ip_packet_router_config(&self) -> &Option<PathBuf> {
&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<P: AsRef<Path>>(id: P) -> Self {
let data_dir = default_data_directory(id);
KeysPaths {
private_identity_key_file: data_dir.join(DEFAULT_PRIVATE_IDENTITY_KEY_FILENAME),
public_identity_key_file: data_dir.join(DEFAULT_PUBLIC_IDENTITY_KEY_FILENAME),
private_sphinx_key_file: data_dir.join(DEFAULT_PRIVATE_SPHINX_KEY_FILENAME),
public_sphinx_key_file: data_dir.join(DEFAULT_PUBLIC_SPHINX_KEY_FILENAME),
}
}
pub fn private_identity_key(&self) -> &Path {
&self.private_identity_key_file
}
pub fn public_identity_key(&self) -> &Path {
&self.public_identity_key_file
}
pub fn private_encryption_key(&self) -> &Path {
&self.private_sphinx_key_file
}
pub fn public_encryption_key(&self) -> &Path {
&self.public_sphinx_key_file
}
}
-113
View File
@@ -1,113 +0,0 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// 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
"#;
-78
View File
@@ -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]
-196
View File
@@ -1,196 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// 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<P: AsRef<Path>>(path: Option<P>) -> String {
path.as_ref()
.map(|p| p.as_ref().display().to_string())
.unwrap_or_default()
}
fn display_path<P: AsRef<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<bool>,
}
#[derive(Default)]
pub struct OverrideIpPacketRouterConfig {
// TODO
}
pub fn override_network_requester_config(
mut cfg: nym_network_requester::Config,
opts: Option<OverrideNetworkRequesterConfig>,
) -> 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<OverrideIpPacketRouterConfig>,
) -> 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<T, P, S>(path: P, name: S) -> Result<T, GatewayError>
where
T: PemStorableKey,
P: AsRef<Path>,
S: Into<String>,
{
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<identity::KeyPair, GatewayError> {
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<GatewayNodeDetailsResponse, GatewayError> {
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,
})
}
-274
View File
@@ -1,274 +0,0 @@
// Copyright 2023-2024 - Nym Technologies SA <contact@nymtech.net>
// 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<api_requests::v1::gateway::models::Gateway, GatewayError> {
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<api_requests::v1::node::models::SignedHostInformation, GatewayError> {
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<api_requests::v1::network_requester::models::NetworkRequester, GatewayError> {
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<api_requests::v1::ip_packet_router::models::IpPacketRouter, GatewayError> {
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<UsedExitPolicy>,
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<encryption::KeyPair>,
}
impl<'a> HttpApiBuilder<'a> {
pub(crate) fn new(
gateway_config: &'a Config,
identity_keypair: &'a identity::KeyPair,
sphinx_keypair: Arc<encryption::KeyPair>,
) -> 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<RequestFilter>,
) -> 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(())
}
}
+1 -3
View File
@@ -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;
-73
View File
@@ -1,73 +0,0 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// 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<String> = 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<std::path::PathBuf>,
/// 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();
}
}
@@ -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() => {
@@ -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 {
-99
View File
@@ -1,113 +1,14 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// 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<P: AsRef<Path>>(
id: &str,
path: P,
) -> Result<nym_network_requester::Config, GatewayError> {
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<P: AsRef<Path>>(
id: &str,
path: P,
) -> Result<nym_ip_packet_router::Config, GatewayError> {
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<P: AsRef<Path>>(
id: &str,
path: P,
) -> Result<nym_network_requester::Config, GatewayError> {
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<P: AsRef<Path>>(
id: &str,
path: P,
) -> Result<nym_ip_packet_router::Config, GatewayError> {
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<PersistentStorage, GatewayError> {
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<PersistentStatsStorage, GatewayError> {
let path = &config.storage_paths.stats_storage;
Ok(PersistentStatsStorage::init(path).await?)
}
pub fn load_keypair<T: PemStorableKeyPair>(
paths: KeyPairPath,
name: impl Into<String>,
) -> Result<T, GatewayError> {
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<encryption::KeyPair, GatewayError> {
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<Mutex<GatewayTopologyProviderInner>>,
@@ -211,7 +211,7 @@ impl<St: Storage> ConnectionHandler<St> {
tokio::select! {
biased;
_ = shutdown.recv() => {
log::trace!("ConnectionHandler: received shutdown");
trace!("ConnectionHandler: received shutdown");
}
framed_sphinx_packet = framed_conn.next() => {
match framed_sphinx_packet {
@@ -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 {
+29 -140
View File
@@ -1,44 +1,34 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// 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<OverrideNetworkRequesterConfig>,
ip_config_override: Option<OverrideIpPacketRouterConfig>,
custom_mixnet: Option<PathBuf>,
) -> Result<Gateway, GatewayError> {
// 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<St = PersistentStorage> {
client_storage: St,
user_agent: UserAgent,
stats_storage: PersistentStatsStorage,
wireguard_data: Option<nym_wireguard::WireguardData>,
session_stats: Option<SharedSessionStats>,
run_http_server: bool,
task_client: Option<TaskClient>,
}
impl<St> Gateway<St> {
/// Construct from the given `Config` instance.
pub fn new(
config: Config,
network_requester_opts: Option<LocalNetworkRequesterOpts>,
ip_packet_router_opts: Option<LocalIpPacketRouterOpts>,
client_storage: St,
stats_storage: PersistentStatsStorage,
) -> Result<Self, GatewayError> {
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<St> Gateway<St> {
identity_keypair: Arc<identity::KeyPair>,
sphinx_keypair: Arc<encryption::KeyPair>,
client_storage: St,
user_agent: UserAgent,
stats_storage: PersistentStatsStorage,
) -> Self {
Gateway {
@@ -211,18 +127,14 @@ impl<St> Gateway<St> {
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<St> Gateway<St> {
self.wireguard_data = Some(wireguard_data)
}
pub async fn node_details(&self) -> Result<GatewayNodeDetailsResponse, GatewayError> {
// 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<St> Gateway<St> {
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<St> Gateway<St> {
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<St> Gateway<St> {
Ok(LocalEmbeddedClientHandle::new(address, ipr_mix_sender))
}
fn random_api_client(&self) -> Result<nym_validator_client::NymApiClient, GatewayError> {
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<DirectSigningHttpRpcNyxdClient, GatewayError> {
let endpoints = self.config.get_nyxd_urls();
let validator_nyxd = endpoints
@@ -628,21 +525,27 @@ impl<St> Gateway<St> {
.map_err(Into::into)
}
async fn check_if_bonded(&self) -> Result<bool, GatewayError> {
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<St> Gateway<St> {
{
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<St> Gateway<St> {
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<St> Gateway<St> {
.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<St> Gateway<St> {
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!(
+1 -22
View File
@@ -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 }
-64
View File
@@ -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 <<EOF
[Unit]
Description=Nym mixnode
After=network-online.target
[Service]
ExecStart=$default_location run --id nym-mixnode
User=$default_user
[Install]
WantedBy=multi-user.target
EOF
else
echo "Error: the new binary $default_location does not exist."
fi
fi
echo
echo "Consider restarting the nym-mixnode service if it is already enabled."
echo "systemctl restart nym-mixnode.service"
exit 0
#DEBHELPER#
-26
View File
@@ -1,26 +0,0 @@
#!/bin/sh
backup_dir="/var/lib/nym-mixnode-backup"
mkdir -p "$backup_dir"
existing_binaries=$(find / \( -path /proc -o -path /sys -o -path /dev -o -path /mnt -o -path /media \) -prune -o -type f -name "nym-mixnode" -print 2>/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#
-16
View File
@@ -1,16 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// 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!()))
}
-79
View File
@@ -1,79 +0,0 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// 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<String>,
/// Description of this node
#[clap(long)]
description: Option<String>,
/// Link associated with this node, for example `https://mixnode.yourdomain.com`
#[clap(long)]
link: Option<String>,
/// Physical location of this node, for example `City: London, Country: UK`
#[clap(long)]
location: Option<String>,
}
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(())
}
-21
View File
@@ -1,21 +0,0 @@
// Copyright 2020-2024 - Nym Technologies SA <contact@nymtech.net>
// 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<String>,
}
pub(crate) fn execute(_args: &Init) -> anyhow::Result<()> {
bail!(
"standalone mixnode initialisation has been removed - please initialise a `nym-node` instead"
.red()
.bold()
)
}
-154
View File
@@ -1,154 +0,0 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// 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<IpAddr>,
mix_port: Option<u16>,
verloc_port: Option<u16>,
http_api_port: Option<u16>,
nym_apis: Option<Vec<url::Url>>,
metrics_key: Option<String>,
}
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, MixnodeError> {
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();
}
}
-24
View File
@@ -1,24 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// 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(())
}
-94
View File
@@ -1,94 +0,0 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// 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<IpAddr>,
/// The wallet address you will use to bond this mixnode, e.g. nymt1z9egw0knv47nmur0p8vk4rcx59h9gg4zuxrrr9
#[clap(long)]
wallet_address: Option<nyxd::AccountId>,
/// The port on which the mixnode will be listening for mix packets
#[clap(long)]
mix_port: Option<u16>,
/// The port on which the mixnode will be listening for verloc packets
#[clap(long)]
verloc_port: Option<u16>,
/// The port on which the mixnode will be listening for http requests
#[clap(long)]
http_api_port: Option<u16>,
/// 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<Vec<url::Url>>,
#[clap(short, long, default_value_t = OutputFormat::default())]
output: OutputFormat,
#[clap(long)]
metrics_key: Option<String>,
}
impl From<Run> 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(())
}
-136
View File
@@ -1,136 +0,0 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// 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<nyxd::AccountId>,
/// Signs an arbitrary piece of text with your identity key
#[clap(long)]
text: Option<String>,
/// Signs a transaction-specific payload, that is going to be sent to the smart contract, with your identity key
#[clap(long)]
contract_msg: Option<String>,
#[clap(short, long, default_value_t = OutputFormat::default())]
output: OutputFormat,
}
enum SignedTarget {
Text(String),
Address(nyxd::AccountId),
ContractMsg(String),
}
impl TryFrom<Sign> for SignedTarget {
type Error = anyhow::Error;
fn try_from(args: Sign) -> Result<Self, Self::Error> {
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::<serde_json::Value>(&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(())
}
@@ -1,34 +1,11 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// 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/<id>/config`
pub fn default_config_directory<P: AsRef<Path>>(id: P) -> PathBuf {
must_get_home()
.join(NYM_DIR)
.join(DEFAULT_MIXNODES_DIR)
.join(id)
.join(DEFAULT_CONFIG_DIR)
}
/// Derive default path to mixnodes's config file.
/// It should get resolved to `$HOME/.nym/mixnodes/<id>/config/config.toml`
pub fn default_config_filepath<P: AsRef<Path>>(id: P) -> PathBuf {
default_config_directory(id).join(DEFAULT_CONFIG_FILENAME)
}
/// Derive default path to mixnodes's data directory where files, such as keys, are stored.
/// It should get resolved to `$HOME/.nym/mixnodes/<id>/data`
pub fn default_data_directory<P: AsRef<Path>>(id: P) -> PathBuf {
must_get_home()
.join(NYM_DIR)
.join(DEFAULT_MIXNODES_DIR)
.join(id)
.join(DEFAULT_DATA_DIR)
}
fn default_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<PathBuf>,
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<S: AsRef<str>>(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<Host>,
http: impl Into<Http>,
mixnode: impl Into<MixNode>,
storage_paths: impl Into<MixNodePaths>,
verloc: impl Into<Verloc>,
logging: impl Into<LoggingSettings>,
debug: impl Into<Debug>,
) -> 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<P: AsRef<Path>>(path: P) -> io::Result<Self> {
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<P: AsRef<Path>>(path: P) -> io::Result<Self> {
Self::read_from_path(path)
}
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
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<Url>) -> 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<IpAddr>,
/// 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<String>,
}
@@ -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<PathBuf>,
#[serde(default)]
pub metrics_key: Option<String>,
}
#[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<Url>,
}
impl MixNode {
pub fn new_default<S: Into<String>>(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.
-4
View File
@@ -1,4 +0,0 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
pub mod paths;
-106
View File
@@ -1,106 +0,0 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// 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<P: AsRef<Path>>(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<P: AsRef<Path>>(id: P) -> Self {
let data_dir = default_data_directory(id);
KeysPaths {
private_identity_key_file: data_dir.join(DEFAULT_PRIVATE_IDENTITY_KEY_FILENAME),
public_identity_key_file: data_dir.join(DEFAULT_PUBLIC_IDENTITY_KEY_FILENAME),
private_sphinx_key_file: data_dir.join(DEFAULT_PRIVATE_SPHINX_KEY_FILENAME),
public_sphinx_key_file: data_dir.join(DEFAULT_PUBLIC_SPHINX_KEY_FILENAME),
}
}
pub fn private_identity_key(&self) -> &Path {
&self.private_identity_key_file
}
pub fn public_identity_key(&self) -> &Path {
&self.public_identity_key_file
}
pub fn private_encryption_key(&self) -> &Path {
&self.private_sphinx_key_file
}
pub fn public_encryption_key(&self) -> &Path {
&self.public_sphinx_key_file
}
}
-85
View File
@@ -1,85 +0,0 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// 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
"#;
-49
View File
@@ -1,49 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// 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),
}
-2
View File
@@ -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;
-63
View File
@@ -1,63 +0,0 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// 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<String> = 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<std::path::PathBuf>,
/// 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();
}
}
-52
View File
@@ -1,52 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// 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<T: PemStorableKeyPair>(
paths: KeyPairPath,
name: impl Into<String>,
) -> Result<T, MixnodeError> {
nym_pemstore::load_keypair(&paths).map_err(|err| MixnodeError::KeyPairLoadFailure {
keys: name.into(),
paths,
err,
})
}
#[allow(unused)]
pub(crate) fn load_public_key<T, P, S>(path: P, name: S) -> Result<T, MixnodeError>
where
T: PemStorableKey,
P: AsRef<Path>,
S: Into<String>,
{
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<identity::KeyPair, MixnodeError> {
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<encryption::KeyPair, MixnodeError> {
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")
}
@@ -1,17 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// 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<OutputParams>,
) -> MixnodeDescriptionResponse {
let output = output.output.unwrap_or_default();
output.to_response(description)
}
pub type MixnodeDescriptionResponse = FormattedResponse<NodeDescription>;
-93
View File
@@ -1,93 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// 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<CryptoHardware>,
}
#[allow(clippy::struct_excessive_bools)]
#[derive(Serialize, Debug)]
pub(crate) struct CryptoHardware {
aesni: bool,
avx2: bool,
brand_string: String,
smt_logical_processor_count: Vec<u32>,
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<OutputParams>) -> MixnodeHardwareResponse {
let output = output.output.unwrap_or_default();
output.to_response(hardware_info())
}
pub type MixnodeHardwareResponse = FormattedResponse<Option<Hardware>>;
/// Gives back a summary report of whatever system hardware info we can get for this platform.
fn hardware_info() -> Option<Hardware> {
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<CryptoHardware>) -> Option<Hardware> {
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<CryptoHardware> {
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(),
}
})
}
-54
View File
@@ -1,54 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// 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<S: Send + Sync + 'static + Clone>(
state: MixnodeAppState,
descriptor: NodeDescription,
) -> Router<S> {
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)
}
-25
View File
@@ -1,25 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// 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<String>,
}
impl FromRef<MixnodeAppState> for SharedVerlocStats {
fn from_ref(app_state: &MixnodeAppState) -> Self {
app_state.verloc.clone()
}
}
impl FromRef<MixnodeAppState> for SharedMixingStats {
fn from_ref(app_state: &MixnodeAppState) -> Self {
app_state.stats.clone()
}
}
-66
View File
@@ -1,66 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// 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<MixnodeAppState>, 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<StatsQueryParams>,
State(stats): State<SharedMixingStats>,
) -> 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<NodeStatsResponse>;
#[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<Output>,
}
-19
View File
@@ -1,19 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// 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<SharedVerlocStats>,
Query(output): Query<OutputParams>,
) -> MixnodeVerlocResponse {
let output = output.output.unwrap_or_default();
output.to_response(verloc.read().await.current_run_data.clone())
}
pub type MixnodeVerlocResponse = FormattedResponse<VerlocResultData>;
-120
View File
@@ -1,120 +0,0 @@
// Copyright 2023-2024 - Nym Technologies SA <contact@nymtech.net>
// 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<api_requests::v1::node::models::SignedHostInformation, MixnodeError> {
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<api_requests::v1::mixnode::models::Mixnode, MixnodeError> {
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(())
}
}
@@ -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");
}
}
+4 -4
View File
@@ -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<()> {
+21 -107
View File
@@ -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<identity::KeyPair>,
sphinx_keypair: Arc<encryption::KeyPair>,
run_http_server: bool,
task_client: Option<TaskClient>,
mixing_stats: Option<SharedMixingStats>,
verloc_stats: Option<SharedVerlocStats>,
}
impl MixNode {
pub fn new(config: Config) -> Result<Self, MixnodeError> {
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<identity::KeyPair>,
sphinx_keypair: Arc<encryption::KeyPair>,
) -> 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(())
}
}
-58
View File
@@ -1,58 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// 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<P: AsRef<Path>>(path: P) -> io::Result<NodeDescription> {
// let description_file_path: PathBuf = [config_path.to_str().unwrap(), DESCRIPTION_FILE]
// .iter()
// .collect();
// let toml = fs::read_to_string(description_file_path)?;
// toml::from_str(&toml).map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err))
let toml = fs::read_to_string(path)?;
toml::from_str(&toml).map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err))
}
pub fn save_to_file<P: AsRef<Path>>(&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(())
}
}
+9 -9
View File
@@ -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<String, u64>;
@@ -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");
}
}
+5 -5
View File
@@ -1,6 +1,7 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// 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");
}
}
+9 -646
View File
@@ -1,649 +1,17 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// 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<String>,
/// Path to a configuration file of the node that's going to get migrated.
#[clap(long, group = "old-config")]
config_file: Option<PathBuf>,
/// 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<String>,
}
impl Args {
fn take_mnemonic(&mut self) -> Option<Zeroizing<bip39::Mnemonic>> {
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<String, NymNodeError> {
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<P: AsRef<Path>, Q: AsRef<Path>>(
node_type: NodeType,
from: P,
to: Q,
) -> Result<(), NymNodeError> {
fn copy_inner<P: AsRef<Path>, Q: AsRef<Path>>(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)
}
-10
View File
@@ -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),
-2
View File
@@ -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
-2
View File
@@ -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,
+2 -1
View File
@@ -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:
+3 -16
View File
@@ -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<nym_mixnode::error::MixnodeError> 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)]
+4 -10
View File
@@ -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());