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:
committed by
GitHub
parent
475a01c089
commit
69718db6d2
Generated
+1
-30
@@ -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",
|
||||
]
|
||||
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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#
|
||||
@@ -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#
|
||||
@@ -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!()))
|
||||
}
|
||||
@@ -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(),
|
||||
})
|
||||
}
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
pub mod paths;
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
"#;
|
||||
@@ -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]
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
@@ -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 }
|
||||
|
||||
@@ -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#
|
||||
@@ -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#
|
||||
@@ -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!()))
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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.
|
||||
@@ -1,4 +0,0 @@
|
||||
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
pub mod paths;
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
"#;
|
||||
@@ -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,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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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>;
|
||||
@@ -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(),
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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>,
|
||||
}
|
||||
@@ -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>;
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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)]
|
||||
|
||||
@@ -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());
|
||||
|
||||
Reference in New Issue
Block a user