Merge pull request #4064 from nymtech/jon/embedded-ip-forwarder-in-gateway
Run nym-ip-packet-router in gateway
This commit is contained in:
Generated
+4
-1
@@ -6540,6 +6540,7 @@ dependencies = [
|
||||
"nym-credentials",
|
||||
"nym-crypto",
|
||||
"nym-gateway-requests",
|
||||
"nym-ip-packet-router",
|
||||
"nym-mixnet-client",
|
||||
"nym-mixnode-common",
|
||||
"nym-network-defaults",
|
||||
@@ -6644,14 +6645,16 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-ip-forwarder"
|
||||
name = "nym-ip-packet-router"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"log",
|
||||
"nym-bin-common",
|
||||
"nym-client-core",
|
||||
"nym-config",
|
||||
"nym-sdk",
|
||||
"nym-service-providers-common",
|
||||
"nym-sphinx",
|
||||
"nym-task",
|
||||
"serde",
|
||||
|
||||
+1
-1
@@ -90,7 +90,7 @@ members = [
|
||||
"sdk/lib/socks5-listener",
|
||||
"sdk/rust/nym-sdk",
|
||||
"service-providers/common",
|
||||
"service-providers/ip-forwarder",
|
||||
"service-providers/ip-packet-router",
|
||||
"service-providers/network-requester",
|
||||
"service-providers/network-statistics",
|
||||
"nym-api",
|
||||
|
||||
@@ -146,3 +146,28 @@ impl fmt::Display for GatewayNetworkRequesterDetails {
|
||||
writeln!(f, "\tunknown list path: {}", self.unknown_list_path)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct GatewayIpPacketRouterDetails {
|
||||
pub enabled: bool,
|
||||
|
||||
pub identity_key: String,
|
||||
pub encryption_key: String,
|
||||
|
||||
// just a convenience wrapper around all the keys
|
||||
pub address: String,
|
||||
|
||||
pub config_path: String,
|
||||
}
|
||||
|
||||
impl fmt::Display for GatewayIpPacketRouterDetails {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
writeln!(f, "IP packet router:")?;
|
||||
writeln!(f, "\tenabled: {}", self.enabled)?;
|
||||
writeln!(f, "\tconfig path: {}", self.config_path)?;
|
||||
|
||||
writeln!(f, "\tidentity key: {}", self.identity_key)?;
|
||||
writeln!(f, "\tencryption key: {}", self.encryption_key)?;
|
||||
writeln!(f, "\taddress: {}", self.address)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +76,7 @@ nym-task = { path = "../common/task" }
|
||||
nym-types = { path = "../common/types" }
|
||||
nym-validator-client = { path = "../common/client-libs/validator-client" }
|
||||
nym-wireguard = { path = "../common/wireguard", optional = true }
|
||||
nym-ip-packet-router = { path = "../service-providers/ip-packet-router" }
|
||||
|
||||
[dev-dependencies]
|
||||
tower = "0.4.13"
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
|
||||
use crate::commands::upgrade_helpers;
|
||||
use crate::config::default_config_filepath;
|
||||
use crate::config::persistence::paths::default_network_requester_data_dir;
|
||||
use crate::config::persistence::paths::{
|
||||
default_ip_packet_router_data_dir, default_network_requester_data_dir,
|
||||
};
|
||||
use crate::config::Config;
|
||||
use crate::error::GatewayError;
|
||||
use log::{error, info};
|
||||
@@ -17,7 +19,7 @@ use nym_network_requester::config::BaseClientConfig;
|
||||
use nym_network_requester::{
|
||||
setup_gateway, GatewaySelectionSpecification, GatewaySetup, OnDiskGatewayDetails, OnDiskKeys,
|
||||
};
|
||||
use nym_types::gateway::GatewayNetworkRequesterDetails;
|
||||
use nym_types::gateway::{GatewayIpPacketRouterDetails, GatewayNetworkRequesterDetails};
|
||||
use nym_validator_client::nyxd::AccountId;
|
||||
use std::net::IpAddr;
|
||||
use std::path::PathBuf;
|
||||
@@ -39,6 +41,7 @@ pub(crate) struct OverrideConfig {
|
||||
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 {
|
||||
@@ -76,12 +79,20 @@ impl OverrideConfig {
|
||||
.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()
|
||||
{
|
||||
Ok(config.with_default_network_requester_config_path())
|
||||
} else if config.ip_packet_router.enabled
|
||||
&& config.storage_paths.ip_packet_router_config.is_none()
|
||||
{
|
||||
Ok(config.with_default_ip_packet_router_config_path())
|
||||
} else {
|
||||
Ok(config)
|
||||
}
|
||||
@@ -101,6 +112,11 @@ pub(crate) struct OverrideNetworkRequesterConfig {
|
||||
pub(crate) statistics_recipient: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct OverrideIpPacketRouterConfig {
|
||||
// TODO
|
||||
}
|
||||
|
||||
/// Ensures that a given bech32 address is valid
|
||||
pub(crate) fn ensure_correct_bech32_prefix(address: &AccountId) -> Result<(), GatewayError> {
|
||||
let expected_prefix =
|
||||
@@ -161,6 +177,10 @@ 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")
|
||||
}
|
||||
|
||||
// NOTE: make sure this is in sync with service-providers/network-requester/src/cli/mod.rs::override_config
|
||||
pub(crate) fn override_network_requester_config(
|
||||
mut cfg: nym_network_requester::Config,
|
||||
@@ -220,6 +240,13 @@ pub(crate) fn override_network_requester_config(
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn override_ip_packet_router_config(
|
||||
cfg: nym_ip_packet_router::Config,
|
||||
_opts: Option<OverrideIpPacketRouterConfig>,
|
||||
) -> nym_ip_packet_router::Config {
|
||||
cfg
|
||||
}
|
||||
|
||||
pub(crate) async fn initialise_local_network_requester(
|
||||
gateway_config: &Config,
|
||||
opts: OverrideNetworkRequesterConfig,
|
||||
@@ -291,3 +318,63 @@ pub(crate) async fn initialise_local_network_requester(
|
||||
.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 =
|
||||
OnDiskGatewayDetails::new(&ip_cfg.storage_paths.common_paths.gateway_details);
|
||||
|
||||
// gateway setup here is way simpler as we're 'connecting' to ourselves
|
||||
let init_res = setup_gateway(
|
||||
GatewaySetup::New {
|
||||
specification: GatewaySelectionSpecification::Custom {
|
||||
gateway_identity: identity.to_base58_string(),
|
||||
additional_data: Default::default(),
|
||||
},
|
||||
available_gateways: vec![],
|
||||
overwrite_data: false,
|
||||
},
|
||||
&key_store,
|
||||
&details_store,
|
||||
)
|
||||
.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(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::commands::helpers::{
|
||||
initialise_local_network_requester, OverrideNetworkRequesterConfig,
|
||||
initialise_local_ip_packet_router, initialise_local_network_requester,
|
||||
OverrideNetworkRequesterConfig,
|
||||
};
|
||||
use crate::config::{default_config_directory, default_config_filepath, default_data_directory};
|
||||
use crate::node::helpers::node_details;
|
||||
@@ -14,6 +15,8 @@ use std::net::IpAddr;
|
||||
use std::path::PathBuf;
|
||||
use std::{fs, io};
|
||||
|
||||
use super::helpers::OverrideIpPacketRouterConfig;
|
||||
|
||||
#[derive(Args, Clone)]
|
||||
pub struct Init {
|
||||
/// Id of the gateway we want to create config for
|
||||
@@ -79,9 +82,13 @@ pub struct Init {
|
||||
statistics_service_url: Option<url::Url>,
|
||||
|
||||
/// Allows this gateway to run an embedded network requester for minimal network overhead
|
||||
#[clap(long)]
|
||||
#[clap(long, conflicts_with = "with_ip_packet_router")]
|
||||
with_network_requester: bool,
|
||||
|
||||
/// Allows this gateway to run an embedded network requester for minimal network overhead
|
||||
#[clap(long, hide = true, conflicts_with = "with_network_requester")]
|
||||
with_ip_packet_router: bool,
|
||||
|
||||
// ##### NETWORK REQUESTER FLAGS #####
|
||||
/// Specifies whether this network requester should run in 'open-proxy' mode
|
||||
#[clap(long, requires = "with_network_requester")]
|
||||
@@ -154,6 +161,7 @@ impl From<Init> for OverrideConfig {
|
||||
nyxd_urls: init_config.nyxd_urls,
|
||||
only_coconut_credentials: init_config.only_coconut_credentials,
|
||||
with_network_requester: Some(init_config.with_network_requester),
|
||||
with_ip_packet_router: Some(init_config.with_ip_packet_router),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -172,6 +180,12 @@ impl<'a> From<&'a Init> for OverrideNetworkRequesterConfig {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Init> for OverrideIpPacketRouterConfig {
|
||||
fn from(_value: &Init) -> Self {
|
||||
OverrideIpPacketRouterConfig {}
|
||||
}
|
||||
}
|
||||
|
||||
fn init_paths(id: &str) -> io::Result<()> {
|
||||
fs::create_dir_all(default_data_directory(id))?;
|
||||
fs::create_dir_all(default_config_directory(id))
|
||||
@@ -196,6 +210,7 @@ pub async fn execute(args: Init) -> anyhow::Result<()> {
|
||||
// Initialising the config structure is just overriding a default constructed one
|
||||
let fresh_config = Config::new(&args.id);
|
||||
let nr_opts = (&args).into();
|
||||
let ip_opts = (&args).into();
|
||||
let mut config = OverrideConfig::from(args).do_override(fresh_config)?;
|
||||
|
||||
// if gateway was already initialised, don't generate new keys, et al.
|
||||
@@ -228,6 +243,9 @@ pub async fn execute(args: Init) -> anyhow::Result<()> {
|
||||
if config.network_requester.enabled {
|
||||
initialise_local_network_requester(&config, nr_opts, *identity_keys.public_key())
|
||||
.await?;
|
||||
} else if config.ip_packet_router.enabled {
|
||||
initialise_local_ip_packet_router(&config, ip_opts, *identity_keys.public_key())
|
||||
.await?;
|
||||
}
|
||||
|
||||
eprintln!("Saved identity and mixnet sphinx keypairs");
|
||||
@@ -276,6 +294,7 @@ mod tests {
|
||||
only_coconut_credentials: None,
|
||||
output: Default::default(),
|
||||
with_network_requester: false,
|
||||
with_ip_packet_router: false,
|
||||
open_proxy: None,
|
||||
enable_statistics: None,
|
||||
statistics_recipient: None,
|
||||
@@ -303,6 +322,7 @@ mod tests {
|
||||
let _gateway = Gateway::new_from_keys_and_storage(
|
||||
config,
|
||||
None,
|
||||
None,
|
||||
identity_keys,
|
||||
sphinx_keys,
|
||||
InMemStorage,
|
||||
|
||||
@@ -12,6 +12,7 @@ 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;
|
||||
mod upgrade_helpers;
|
||||
@@ -31,6 +32,11 @@ pub(crate) enum Commands {
|
||||
// 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),
|
||||
|
||||
@@ -52,6 +58,7 @@ pub(crate) async fn execute(args: Cli) -> Result<(), Box<dyn Error + Send + Sync
|
||||
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),
|
||||
|
||||
+34
-19
@@ -15,39 +15,41 @@ use nym_node::error::NymNodeError;
|
||||
use std::net::IpAddr;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use super::helpers::OverrideIpPacketRouterConfig;
|
||||
|
||||
#[derive(Args, Clone)]
|
||||
pub struct Run {
|
||||
/// Id of the gateway we want to run
|
||||
#[clap(long)]
|
||||
#[arg(long)]
|
||||
id: String,
|
||||
|
||||
/// The custom listening address on which the gateway will be running for receiving sphinx packets
|
||||
#[clap(long, alias = "host")]
|
||||
#[arg(long, alias = "host")]
|
||||
listening_address: Option<IpAddr>,
|
||||
|
||||
/// Comma separated list of public ip addresses that will 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.
|
||||
#[clap(long, value_delimiter = ',')]
|
||||
#[arg(long, value_delimiter = ',')]
|
||||
public_ips: Option<Vec<IpAddr>>,
|
||||
|
||||
/// Optional hostname associated with this gateway that will announced to the nym-api and subsequently to the clients
|
||||
#[clap(long)]
|
||||
#[arg(long)]
|
||||
hostname: Option<String>,
|
||||
|
||||
/// The port on which the gateway will be listening for sphinx packets
|
||||
#[clap(long)]
|
||||
#[arg(long)]
|
||||
mix_port: Option<u16>,
|
||||
|
||||
/// The port on which the gateway will be listening for clients gateway-requests
|
||||
#[clap(long)]
|
||||
#[arg(long)]
|
||||
clients_port: Option<u16>,
|
||||
|
||||
/// Path to sqlite database containing all gateway persistent data
|
||||
#[clap(long)]
|
||||
#[arg(long)]
|
||||
datastore: Option<PathBuf>,
|
||||
|
||||
/// Comma separated list of endpoints of nym APIs
|
||||
#[clap(
|
||||
#[arg(
|
||||
long,
|
||||
alias = "validator_apis",
|
||||
value_delimiter = ',',
|
||||
@@ -57,7 +59,7 @@ pub struct Run {
|
||||
nym_apis: Option<Vec<url::Url>>,
|
||||
|
||||
/// Comma separated list of endpoints of the validator
|
||||
#[clap(
|
||||
#[arg(
|
||||
long,
|
||||
alias = "validators",
|
||||
alias = "nyxd_validators",
|
||||
@@ -68,26 +70,30 @@ pub struct Run {
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
|
||||
/// Cosmos wallet mnemonic
|
||||
#[clap(long)]
|
||||
#[arg(long)]
|
||||
mnemonic: Option<bip39::Mnemonic>,
|
||||
|
||||
/// Set this gateway to work only with coconut credentials; that would disallow clients to
|
||||
/// bypass bandwidth credential requirement
|
||||
#[clap(long, hide = true)]
|
||||
#[arg(long, hide = true)]
|
||||
only_coconut_credentials: Option<bool>,
|
||||
|
||||
/// Enable/disable gateway anonymized statistics that get sent to a statistics aggregator server
|
||||
#[clap(long)]
|
||||
#[arg(long)]
|
||||
enabled_statistics: Option<bool>,
|
||||
|
||||
/// URL where a statistics aggregator is running. The default value is a Nym aggregator server
|
||||
#[clap(long)]
|
||||
#[arg(long)]
|
||||
statistics_service_url: Option<url::Url>,
|
||||
|
||||
/// Allows this gateway to run an embedded network requester for minimal network overhead
|
||||
#[clap(long)]
|
||||
#[arg(long, conflicts_with = "with_ip_packet_router")]
|
||||
with_network_requester: Option<bool>,
|
||||
|
||||
/// Allows this gateway to run an embedded network requester for minimal network overhead
|
||||
#[arg(long, hide = true, conflicts_with = "with_network_requester")]
|
||||
with_ip_packet_router: Option<bool>,
|
||||
|
||||
// ##### NETWORK REQUESTER FLAGS #####
|
||||
/// Specifies whether this network requester should run in 'open-proxy' mode
|
||||
#[arg(long)]
|
||||
@@ -123,20 +129,20 @@ pub struct Run {
|
||||
|
||||
/// Path to .json file containing custom network specification.
|
||||
/// Only usable when local network requester is enabled.
|
||||
#[clap(long, group = "network", hide = true)]
|
||||
#[arg(long, group = "network", hide = true)]
|
||||
custom_mixnet: Option<PathBuf>,
|
||||
|
||||
/// Specifies whether this network requester will run using the default ExitPolicy
|
||||
/// as opposed to the allow list.
|
||||
/// Note: this setting will become the default in the future releases.
|
||||
#[clap(long)]
|
||||
#[arg(long)]
|
||||
with_exit_policy: Option<bool>,
|
||||
|
||||
#[clap(short, long, default_value_t = OutputFormat::default())]
|
||||
#[arg(short, long, default_value_t = OutputFormat::default())]
|
||||
output: OutputFormat,
|
||||
|
||||
/// Flag specifying this node will be running in a local setting.
|
||||
#[clap(long)]
|
||||
#[arg(long)]
|
||||
local: bool,
|
||||
}
|
||||
|
||||
@@ -157,6 +163,7 @@ impl From<Run> for OverrideConfig {
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -175,6 +182,12 @@ impl<'a> From<&'a Run> for OverrideNetworkRequesterConfig {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Run> for OverrideIpPacketRouterConfig {
|
||||
fn from(_value: &Run) -> Self {
|
||||
OverrideIpPacketRouterConfig {}
|
||||
}
|
||||
}
|
||||
|
||||
fn show_binding_warning(address: IpAddr) {
|
||||
eprintln!("\n##### NOTE #####");
|
||||
eprintln!(
|
||||
@@ -217,6 +230,7 @@ pub async fn execute(args: Run) -> anyhow::Result<()> {
|
||||
let output = args.output;
|
||||
let custom_mixnet = args.custom_mixnet.clone();
|
||||
let nr_opts = (&args).into();
|
||||
let ip_opts = (&args).into();
|
||||
|
||||
let config = build_config(id, args)?;
|
||||
ensure_config_version_compatibility(&config)?;
|
||||
@@ -235,7 +249,8 @@ pub async fn execute(args: Run) -> anyhow::Result<()> {
|
||||
}
|
||||
|
||||
let node_details = node_details(&config)?;
|
||||
let gateway = crate::node::create_gateway(config, Some(nr_opts), custom_mixnet).await?;
|
||||
let gateway =
|
||||
crate::node::create_gateway(config, Some(nr_opts), Some(ip_opts), custom_mixnet).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 ");
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::commands::helpers::{
|
||||
initialise_local_ip_packet_router, try_load_current_config, OverrideIpPacketRouterConfig,
|
||||
};
|
||||
use crate::node::helpers::load_public_key;
|
||||
use clap::Args;
|
||||
use nym_bin_common::output_format::OutputFormat;
|
||||
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(())
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
use crate::config::old_config_v1_1_20::ConfigV1_1_20;
|
||||
use crate::config::old_config_v1_1_28::ConfigV1_1_28;
|
||||
use crate::config::old_config_v1_1_29::ConfigV1_1_29;
|
||||
use crate::config::old_config_v1_1_31::ConfigV1_1_31;
|
||||
use crate::config::{default_config_filepath, Config};
|
||||
use crate::error::GatewayError;
|
||||
use log::info;
|
||||
@@ -22,7 +23,8 @@ fn try_upgrade_v1_1_20_config(id: &str) -> Result<bool, GatewayError> {
|
||||
|
||||
let updated_step1: ConfigV1_1_28 = old_config.into();
|
||||
let updated_step2: ConfigV1_1_29 = updated_step1.into();
|
||||
let updated: Config = updated_step2.into();
|
||||
let updated_step3: ConfigV1_1_31 = updated_step2.into();
|
||||
let updated: Config = updated_step3.into();
|
||||
updated
|
||||
.save_to_default_location()
|
||||
.map_err(|err| GatewayError::ConfigSaveFailure {
|
||||
@@ -45,7 +47,8 @@ fn try_upgrade_v1_1_28_config(id: &str) -> Result<bool, GatewayError> {
|
||||
info!("It is going to get updated to the current specification.");
|
||||
|
||||
let updated_step1: ConfigV1_1_29 = old_config.into();
|
||||
let updated: Config = updated_step1.into();
|
||||
let updated_step2: ConfigV1_1_31 = updated_step1.into();
|
||||
let updated: Config = updated_step2.into();
|
||||
updated
|
||||
.save_to_default_location()
|
||||
.map_err(|err| GatewayError::ConfigSaveFailure {
|
||||
@@ -67,6 +70,29 @@ fn try_upgrade_v1_1_29_config(id: &str) -> Result<bool, GatewayError> {
|
||||
info!("It seems the gateway is using <= v1.1.29 config template.");
|
||||
info!("It is going to get updated to the current specification.");
|
||||
|
||||
let updated_step1: ConfigV1_1_31 = old_config.into();
|
||||
let updated: Config = updated_step1.into();
|
||||
updated
|
||||
.save_to_default_location()
|
||||
.map_err(|err| GatewayError::ConfigSaveFailure {
|
||||
path: default_config_filepath(id),
|
||||
id: id.to_string(),
|
||||
source: err,
|
||||
})?;
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn try_upgrade_v1_1_31_config(id: &str) -> Result<bool, GatewayError> {
|
||||
// explicitly load it as v1.1.30 (which is incompatible with the current, i.e. 1.1.31+)
|
||||
let Ok(old_config) = ConfigV1_1_31::read_from_default_path(id) else {
|
||||
// if we failed to load it, there might have been nothing to upgrade
|
||||
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
|
||||
return Ok(false);
|
||||
};
|
||||
info!("It seems the gateway is using <= v1.1.30 config template.");
|
||||
info!("It is going to get updated to the current specification.");
|
||||
|
||||
let updated: Config = old_config.into();
|
||||
updated
|
||||
.save_to_default_location()
|
||||
@@ -89,6 +115,9 @@ pub(crate) fn try_upgrade_config(id: &str) -> Result<(), GatewayError> {
|
||||
if try_upgrade_v1_1_29_config(id)? {
|
||||
return Ok(());
|
||||
}
|
||||
if try_upgrade_v1_1_31_config(id)? {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
pub(crate) mod old_config_v1_1_20;
|
||||
pub(crate) mod old_config_v1_1_28;
|
||||
pub(crate) mod old_config_v1_1_29;
|
||||
pub(crate) mod old_config_v1_1_31;
|
||||
pub mod persistence;
|
||||
mod template;
|
||||
|
||||
@@ -100,6 +101,9 @@ pub struct Config {
|
||||
|
||||
pub network_requester: NetworkRequester,
|
||||
|
||||
#[serde(default)]
|
||||
pub ip_packet_router: IpPacketRouter,
|
||||
|
||||
#[serde(default)]
|
||||
pub logging: LoggingSettings,
|
||||
|
||||
@@ -128,6 +132,7 @@ impl Config {
|
||||
wireguard: Default::default(),
|
||||
storage_paths: GatewayPaths::new_default(id.as_ref()),
|
||||
network_requester: Default::default(),
|
||||
ip_packet_router: Default::default(),
|
||||
logging: Default::default(),
|
||||
debug: Default::default(),
|
||||
}
|
||||
@@ -195,6 +200,18 @@ impl Config {
|
||||
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
|
||||
@@ -360,6 +377,20 @@ impl Default for NetworkRequester {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct IpPacketRouter {
|
||||
/// Specifies whether ip packet router service is enabled in this process.
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
#[allow(clippy::derivable_impls)]
|
||||
impl Default for IpPacketRouter {
|
||||
fn default() -> Self {
|
||||
Self { enabled: false }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct Debug {
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::persistence::paths::{GatewayPaths, KeysPaths};
|
||||
use crate::config::{Config, Debug, Gateway, NetworkRequester};
|
||||
use nym_bin_common::logging::LoggingSettings;
|
||||
use nym_config::{
|
||||
must_get_home, read_config_from_toml_file, DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, NYM_DIR,
|
||||
};
|
||||
@@ -14,6 +11,11 @@ use std::path::{Path, PathBuf};
|
||||
use std::time::Duration;
|
||||
use url::Url;
|
||||
|
||||
use super::old_config_v1_1_31::{
|
||||
ConfigV1_1_31, DebugV1_1_31, GatewayPathsV1_1_31, GatewayV1_1_31, KeysPathsV1_1_31,
|
||||
LoggingSettingsV1_1_31, NetworkRequesterV1_1_31,
|
||||
};
|
||||
|
||||
const DEFAULT_GATEWAYS_DIR: &str = "gateways";
|
||||
|
||||
// 'DEBUG'
|
||||
@@ -105,9 +107,9 @@ impl ConfigV1_1_29 {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConfigV1_1_29> for Config {
|
||||
impl From<ConfigV1_1_29> for ConfigV1_1_31 {
|
||||
fn from(value: ConfigV1_1_29) -> Self {
|
||||
Config {
|
||||
ConfigV1_1_31 {
|
||||
save_path: value.save_path,
|
||||
|
||||
// \/ ADDED
|
||||
@@ -121,7 +123,7 @@ impl From<ConfigV1_1_29> for Config {
|
||||
// \/ ADDED
|
||||
http: Default::default(),
|
||||
// /\ ADDED
|
||||
gateway: Gateway {
|
||||
gateway: GatewayV1_1_31 {
|
||||
version: value.gateway.version,
|
||||
id: value.gateway.id,
|
||||
only_coconut_credentials: value.gateway.only_coconut_credentials,
|
||||
@@ -141,8 +143,8 @@ impl From<ConfigV1_1_29> for Config {
|
||||
// \/ ADDED
|
||||
wireguard: Default::default(),
|
||||
// /\ ADDED
|
||||
storage_paths: GatewayPaths {
|
||||
keys: KeysPaths {
|
||||
storage_paths: GatewayPathsV1_1_31 {
|
||||
keys: KeysPathsV1_1_31 {
|
||||
private_identity_key_file: value.storage_paths.keys.private_identity_key_file,
|
||||
public_identity_key_file: value.storage_paths.keys.public_identity_key_file,
|
||||
private_sphinx_key_file: value.storage_paths.keys.private_sphinx_key_file,
|
||||
@@ -151,11 +153,11 @@ impl From<ConfigV1_1_29> for Config {
|
||||
clients_storage: value.storage_paths.clients_storage,
|
||||
network_requester_config: value.storage_paths.network_requester_config,
|
||||
},
|
||||
network_requester: NetworkRequester {
|
||||
network_requester: NetworkRequesterV1_1_31 {
|
||||
enabled: value.network_requester.enabled,
|
||||
},
|
||||
logging: LoggingSettings {},
|
||||
debug: Debug {
|
||||
logging: LoggingSettingsV1_1_31 {},
|
||||
debug: DebugV1_1_31 {
|
||||
packet_forwarding_initial_backoff: value.debug.packet_forwarding_initial_backoff,
|
||||
packet_forwarding_maximum_backoff: value.debug.packet_forwarding_maximum_backoff,
|
||||
initial_connection_timeout: value.debug.initial_connection_timeout,
|
||||
|
||||
@@ -0,0 +1,362 @@
|
||||
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::persistence::paths::GatewayPaths;
|
||||
use nym_bin_common::logging::LoggingSettings;
|
||||
use nym_config::{
|
||||
must_get_home, read_config_from_toml_file, DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, NYM_DIR,
|
||||
};
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use std::io;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Duration;
|
||||
use url::Url;
|
||||
|
||||
use super::persistence::paths::KeysPaths;
|
||||
use super::{Config, Debug, Gateway, NetworkRequester};
|
||||
|
||||
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);
|
||||
const DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF: Duration = Duration::from_millis(10_000);
|
||||
const DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF: Duration = Duration::from_millis(300_000);
|
||||
const DEFAULT_INITIAL_CONNECTION_TIMEOUT: Duration = Duration::from_millis(1_500);
|
||||
const DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE: usize = 2000;
|
||||
|
||||
const DEFAULT_STORED_MESSAGE_FILENAME_LENGTH: u16 = 16;
|
||||
const DEFAULT_MESSAGE_RETRIEVAL_LIMIT: i64 = 100;
|
||||
|
||||
fn de_maybe_port<'de, D>(deserializer: D) -> Result<Option<u16>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let port = u16::deserialize(deserializer)?;
|
||||
if port == 0 {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(port))
|
||||
}
|
||||
}
|
||||
|
||||
fn de_maybe_path<'de, D>(deserializer: D) -> Result<Option<PathBuf>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let path = PathBuf::deserialize(deserializer)?;
|
||||
if path.as_os_str().is_empty() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(path))
|
||||
}
|
||||
}
|
||||
|
||||
/// 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(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ConfigV1_1_31 {
|
||||
// additional metadata holding on-disk location of this config file
|
||||
#[serde(skip)]
|
||||
pub(crate) save_path: Option<PathBuf>,
|
||||
|
||||
pub host: nym_node::config::Host,
|
||||
|
||||
#[serde(default)]
|
||||
pub http: nym_node::config::Http,
|
||||
|
||||
pub gateway: GatewayV1_1_31,
|
||||
|
||||
#[serde(default)]
|
||||
// currently not really used for anything useful
|
||||
pub wireguard: WireguardV1_1_31,
|
||||
|
||||
pub storage_paths: GatewayPathsV1_1_31,
|
||||
|
||||
pub network_requester: NetworkRequesterV1_1_31,
|
||||
|
||||
#[serde(default)]
|
||||
pub logging: LoggingSettingsV1_1_31,
|
||||
|
||||
#[serde(default)]
|
||||
pub debug: DebugV1_1_31,
|
||||
}
|
||||
|
||||
impl ConfigV1_1_31 {
|
||||
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
|
||||
read_config_from_toml_file(default_config_filepath(id))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConfigV1_1_31> for Config {
|
||||
fn from(value: ConfigV1_1_31) -> Self {
|
||||
Self {
|
||||
save_path: value.save_path,
|
||||
host: value.host,
|
||||
http: value.http,
|
||||
gateway: Gateway {
|
||||
version: value.gateway.version,
|
||||
id: value.gateway.id,
|
||||
only_coconut_credentials: value.gateway.only_coconut_credentials,
|
||||
listening_address: value.gateway.listening_address,
|
||||
mix_port: value.gateway.mix_port,
|
||||
clients_port: value.gateway.clients_port,
|
||||
clients_wss_port: value.gateway.clients_wss_port,
|
||||
enabled_statistics: value.gateway.enabled_statistics,
|
||||
statistics_service_url: value.gateway.statistics_service_url,
|
||||
nym_api_urls: value.gateway.nym_api_urls,
|
||||
nyxd_urls: value.gateway.nyxd_urls,
|
||||
cosmos_mnemonic: value.gateway.cosmos_mnemonic,
|
||||
},
|
||||
wireguard: nym_node::config::Wireguard {
|
||||
enabled: value.wireguard.enabled,
|
||||
bind_address: value.wireguard.bind_address,
|
||||
announced_port: value.wireguard.announced_port,
|
||||
storage_paths: nym_node::config::persistence::WireguardPaths {
|
||||
// no fields (yet)
|
||||
},
|
||||
},
|
||||
storage_paths: GatewayPaths {
|
||||
keys: KeysPaths {
|
||||
private_identity_key_file: value.storage_paths.keys.private_identity_key_file,
|
||||
public_identity_key_file: value.storage_paths.keys.public_identity_key_file,
|
||||
private_sphinx_key_file: value.storage_paths.keys.private_sphinx_key_file,
|
||||
public_sphinx_key_file: value.storage_paths.keys.public_sphinx_key_file,
|
||||
},
|
||||
clients_storage: value.storage_paths.clients_storage,
|
||||
network_requester_config: value.storage_paths.network_requester_config,
|
||||
// \/ ADDED
|
||||
ip_packet_router_config: Default::default(),
|
||||
// /\ ADDED
|
||||
},
|
||||
network_requester: NetworkRequester {
|
||||
enabled: value.network_requester.enabled,
|
||||
},
|
||||
// \/ ADDED
|
||||
ip_packet_router: Default::default(),
|
||||
// /\ ADDED
|
||||
logging: LoggingSettings {
|
||||
// no fields (yet)
|
||||
},
|
||||
debug: Debug {
|
||||
packet_forwarding_initial_backoff: value.debug.packet_forwarding_initial_backoff,
|
||||
packet_forwarding_maximum_backoff: value.debug.packet_forwarding_maximum_backoff,
|
||||
initial_connection_timeout: value.debug.initial_connection_timeout,
|
||||
maximum_connection_buffer_size: value.debug.maximum_connection_buffer_size,
|
||||
presence_sending_delay: value.debug.presence_sending_delay,
|
||||
stored_messages_filename_length: value.debug.stored_messages_filename_length,
|
||||
message_retrieval_limit: value.debug.message_retrieval_limit,
|
||||
use_legacy_framed_packet_version: value.debug.use_legacy_framed_packet_version,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||
pub struct GatewayV1_1_31 {
|
||||
/// Version of the gateway for which this configuration was created.
|
||||
pub version: String,
|
||||
|
||||
/// ID specifies the human readable ID of this particular gateway.
|
||||
pub id: String,
|
||||
|
||||
/// 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.
|
||||
pub listening_address: IpAddr,
|
||||
|
||||
/// Port used for listening for all mixnet traffic.
|
||||
/// (default: 1789)
|
||||
pub mix_port: u16,
|
||||
|
||||
/// Port used for listening for all client-related traffic.
|
||||
/// (default: 9000)
|
||||
pub clients_port: u16,
|
||||
|
||||
/// 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>,
|
||||
|
||||
/// Whether gateway collects and sends anonymized statistics
|
||||
pub enabled_statistics: bool,
|
||||
|
||||
/// Domain address of the statistics service
|
||||
pub statistics_service_url: Url,
|
||||
|
||||
/// Addresses to APIs from which the node gets the view of the network.
|
||||
#[serde(alias = "validator_api_urls")]
|
||||
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")]
|
||||
pub nyxd_urls: Vec<Url>,
|
||||
|
||||
/// Mnemonic of a cosmos wallet used in checking for double spending.
|
||||
// #[deprecated(note = "move to storage")]
|
||||
// TODO: I don't think this should be stored directly in the config...
|
||||
pub cosmos_mnemonic: bip39::Mnemonic,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct WireguardV1_1_31 {
|
||||
/// Specifies whether the wireguard service is enabled on this node.
|
||||
pub enabled: bool,
|
||||
|
||||
/// Socket address this node will use for binding its wireguard interface.
|
||||
/// default: `0.0.0.0:51820`
|
||||
pub bind_address: SocketAddr,
|
||||
|
||||
/// Port announced to external clients wishing to connect to the wireguard interface.
|
||||
/// Useful in the instances where the node is behind a proxy.
|
||||
pub announced_port: u16,
|
||||
|
||||
/// Paths for wireguard keys, client registries, etc.
|
||||
pub storage_paths: WireguardPathsV1_1_31,
|
||||
}
|
||||
|
||||
impl Default for WireguardV1_1_31 {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: false,
|
||||
bind_address: SocketAddr::new(
|
||||
IpAddr::V4(Ipv4Addr::UNSPECIFIED),
|
||||
nym_node::config::DEFAULT_WIREGUARD_PORT,
|
||||
),
|
||||
announced_port: nym_node::config::DEFAULT_WIREGUARD_PORT,
|
||||
storage_paths: WireguardPathsV1_1_31 {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct WireguardPathsV1_1_31 {
|
||||
// pub keys:
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct GatewayPathsV1_1_31 {
|
||||
pub keys: KeysPathsV1_1_31,
|
||||
|
||||
/// 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 the configuration of the embedded network requester.
|
||||
#[serde(deserialize_with = "de_maybe_path")]
|
||||
pub network_requester_config: Option<PathBuf>,
|
||||
// pub node_description: PathBuf,
|
||||
|
||||
// pub cosmos_bip39_mnemonic: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
|
||||
pub struct KeysPathsV1_1_31 {
|
||||
/// 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,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct NetworkRequesterV1_1_31 {
|
||||
/// Specifies whether network requester service is enabled in this process.
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
#[allow(clippy::derivable_impls)]
|
||||
impl Default for NetworkRequesterV1_1_31 {
|
||||
fn default() -> Self {
|
||||
Self { enabled: false }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Deserialize, PartialEq, Eq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct LoggingSettingsV1_1_31 {
|
||||
// well, we need to implement something here at some point...
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct DebugV1_1_31 {
|
||||
/// 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")]
|
||||
pub presence_sending_delay: Duration,
|
||||
|
||||
/// Length of filenames for new client messages.
|
||||
pub stored_messages_filename_length: u16,
|
||||
|
||||
/// Number of messages from offline client that can be pulled at once from the storage.
|
||||
pub message_retrieval_limit: i64,
|
||||
|
||||
/// Specifies whether the mixnode should be using the legacy framing for the sphinx packets.
|
||||
// it's set to true by default. The reason for that decision is to preserve compatibility with the
|
||||
// existing nodes whilst everyone else is upgrading and getting the code for handling the new field.
|
||||
// It shall be disabled in the subsequent releases.
|
||||
pub use_legacy_framed_packet_version: bool,
|
||||
}
|
||||
|
||||
impl Default for DebugV1_1_31 {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
packet_forwarding_initial_backoff: DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF,
|
||||
packet_forwarding_maximum_backoff: DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF,
|
||||
initial_connection_timeout: DEFAULT_INITIAL_CONNECTION_TIMEOUT,
|
||||
presence_sending_delay: DEFAULT_PRESENCE_SENDING_DELAY,
|
||||
maximum_connection_buffer_size: DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE,
|
||||
stored_messages_filename_length: DEFAULT_STORED_MESSAGE_FILENAME_LENGTH,
|
||||
message_retrieval_limit: DEFAULT_MESSAGE_RETRIEVAL_LIMIT,
|
||||
use_legacy_framed_packet_version: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,12 +15,19 @@ pub const DEFAULT_CLIENTS_STORAGE_FILENAME: &str = "db.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)
|
||||
}
|
||||
|
||||
/// makes sure that an empty path is converted into a `None` as opposed to `Some("")`
|
||||
fn de_maybe_path<'de, D>(deserializer: D) -> Result<Option<PathBuf>, D::Error>
|
||||
where
|
||||
@@ -50,6 +57,9 @@ pub struct GatewayPaths {
|
||||
// pub node_description: PathBuf,
|
||||
|
||||
// pub cosmos_bip39_mnemonic: PathBuf,
|
||||
/// Path to the configuration of the embedded ip packet router.
|
||||
#[serde(deserialize_with = "de_maybe_path")]
|
||||
pub ip_packet_router_config: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl GatewayPaths {
|
||||
@@ -59,6 +69,7 @@ impl GatewayPaths {
|
||||
clients_storage: default_data_directory(id).join(DEFAULT_CLIENTS_STORAGE_FILENAME),
|
||||
// node_description: default_config_filepath(id).join(DEFAULT_DESCRIPTION_FILENAME),
|
||||
network_requester_config: None,
|
||||
ip_packet_router_config: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,10 +86,27 @@ impl GatewayPaths {
|
||||
)
|
||||
}
|
||||
|
||||
#[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()
|
||||
}
|
||||
|
||||
@@ -82,6 +82,10 @@ landing_page_assets_path = '{{ http.landing_page_assets_path }}'
|
||||
# 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.
|
||||
@@ -103,6 +107,9 @@ 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]
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::node::storage::error::StorageError;
|
||||
use nym_ip_packet_router::error::IpForwarderError;
|
||||
use nym_network_requester::error::{ClientCoreError, NetworkRequesterError};
|
||||
use nym_validator_client::nyxd::error::NyxdError;
|
||||
use nym_validator_client::nyxd::AccountId;
|
||||
@@ -48,6 +49,17 @@ pub(crate) enum GatewayError {
|
||||
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]
|
||||
source: io::Error,
|
||||
},
|
||||
|
||||
#[error(
|
||||
"failed to save config file for id {id} using path '{}'. detailed message: {source}", path.display()
|
||||
)]
|
||||
@@ -86,15 +98,27 @@ pub(crate) enum GatewayError {
|
||||
#[error("Path to network requester configuration file hasn't been specified. Perhaps try to run `setup-network-requester`?")]
|
||||
UnspecifiedNetworkRequesterConfig,
|
||||
|
||||
#[error("Path to ip packet router configuration file hasn't been specified. Perhaps try to run `setup-ip-packet-router`?")]
|
||||
UnspecifiedIpPacketRouterConfig,
|
||||
|
||||
#[error("there was an issue with the local network requester: {source}")]
|
||||
NetworkRequesterFailure {
|
||||
#[from]
|
||||
source: NetworkRequesterError,
|
||||
},
|
||||
|
||||
#[error("there was an issue with the local ip packet router: {source}")]
|
||||
IpPacketRouterFailure {
|
||||
#[from]
|
||||
source: IpForwarderError,
|
||||
},
|
||||
|
||||
#[error("failed to startup local network requester")]
|
||||
NetworkRequesterStartupFailure,
|
||||
|
||||
#[error("failed to startup local ip packet router")]
|
||||
IpPacketRouterStartupFailure,
|
||||
|
||||
#[error("there are no nym API endpoints available")]
|
||||
NoNymApisAvailable,
|
||||
|
||||
|
||||
@@ -28,6 +28,17 @@ impl LocalNetworkRequesterHandle {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: generalize this whole thing to be general. And change the name(s).
|
||||
pub(crate) fn new_ip(
|
||||
start_data: nym_ip_packet_router::OnStartData,
|
||||
mix_message_sender: MixMessageSender,
|
||||
) -> Self {
|
||||
Self {
|
||||
address: start_data.address,
|
||||
mix_message_sender,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn client_destination(&self) -> DestinationAddressBytes {
|
||||
self.address.identity().derive_destination_address()
|
||||
}
|
||||
|
||||
@@ -96,6 +96,20 @@ pub(crate) fn load_network_requester_config<P: AsRef<Path>>(
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) 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();
|
||||
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> {
|
||||
|
||||
+120
-5
@@ -1,8 +1,12 @@
|
||||
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use self::helpers::load_ip_packet_router_config;
|
||||
use self::storage::PersistentStorage;
|
||||
use crate::commands::helpers::{override_network_requester_config, OverrideNetworkRequesterConfig};
|
||||
use crate::commands::helpers::{
|
||||
override_ip_packet_router_config, override_network_requester_config,
|
||||
OverrideIpPacketRouterConfig, OverrideNetworkRequesterConfig,
|
||||
};
|
||||
use crate::config::Config;
|
||||
use crate::error::GatewayError;
|
||||
use crate::http::HttpApiBuilder;
|
||||
@@ -54,7 +58,8 @@ struct StartedNetworkRequester {
|
||||
pub(crate) async fn create_gateway(
|
||||
config: Config,
|
||||
nr_config_override: Option<OverrideNetworkRequesterConfig>,
|
||||
custom_nr_mixnet: Option<PathBuf>,
|
||||
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 {
|
||||
@@ -69,14 +74,32 @@ pub(crate) async fn create_gateway(
|
||||
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)?;
|
||||
Some(override_ip_packet_router_config(cfg, ip_config_override))
|
||||
} else {
|
||||
// if NR is enabled, the config path must be specified
|
||||
return Err(GatewayError::UnspecifiedIpPacketRouterConfig);
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let storage = initialise_main_storage(&config).await?;
|
||||
|
||||
let nr_opts = network_requester_config.map(|config| LocalNetworkRequesterOpts {
|
||||
config,
|
||||
custom_mixnet_path: custom_nr_mixnet,
|
||||
config: config.clone(),
|
||||
custom_mixnet_path: custom_mixnet.clone(),
|
||||
});
|
||||
|
||||
Gateway::new(config, nr_opts, storage)
|
||||
let ip_opts = ip_packet_router_config.map(|config| LocalIpPacketRouterOpts {
|
||||
config,
|
||||
custom_mixnet_path: custom_mixnet,
|
||||
});
|
||||
|
||||
Gateway::new(config, nr_opts, ip_opts, storage)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -86,11 +109,20 @@ pub struct LocalNetworkRequesterOpts {
|
||||
custom_mixnet_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LocalIpPacketRouterOpts {
|
||||
config: nym_ip_packet_router::Config,
|
||||
|
||||
custom_mixnet_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
pub(crate) struct Gateway<St = PersistentStorage> {
|
||||
config: Config,
|
||||
|
||||
network_requester_opts: Option<LocalNetworkRequesterOpts>,
|
||||
|
||||
ip_packet_router_opts: Option<LocalIpPacketRouterOpts>,
|
||||
|
||||
/// ed25519 keypair used to assert one's identity.
|
||||
identity_keypair: Arc<identity::KeyPair>,
|
||||
|
||||
@@ -106,6 +138,7 @@ impl<St> Gateway<St> {
|
||||
pub fn new(
|
||||
config: Config,
|
||||
network_requester_opts: Option<LocalNetworkRequesterOpts>,
|
||||
ip_packet_router_opts: Option<LocalIpPacketRouterOpts>,
|
||||
storage: St,
|
||||
) -> Result<Self, GatewayError> {
|
||||
Ok(Gateway {
|
||||
@@ -114,6 +147,7 @@ impl<St> Gateway<St> {
|
||||
sphinx_keypair: Arc::new(helpers::load_sphinx_keys(&config)?),
|
||||
config,
|
||||
network_requester_opts,
|
||||
ip_packet_router_opts,
|
||||
client_registry: Arc::new(DashMap::new()),
|
||||
})
|
||||
}
|
||||
@@ -122,6 +156,7 @@ impl<St> Gateway<St> {
|
||||
pub async fn new_from_keys_and_storage(
|
||||
config: Config,
|
||||
network_requester_opts: Option<LocalNetworkRequesterOpts>,
|
||||
ip_packet_router_opts: Option<LocalIpPacketRouterOpts>,
|
||||
identity_keypair: identity::KeyPair,
|
||||
sphinx_keypair: encryption::KeyPair,
|
||||
storage: St,
|
||||
@@ -129,6 +164,7 @@ impl<St> Gateway<St> {
|
||||
Gateway {
|
||||
config,
|
||||
network_requester_opts,
|
||||
ip_packet_router_opts,
|
||||
identity_keypair: Arc::new(identity_keypair),
|
||||
sphinx_keypair: Arc::new(sphinx_keypair),
|
||||
storage,
|
||||
@@ -283,6 +319,73 @@ impl<St> Gateway<St> {
|
||||
})
|
||||
}
|
||||
|
||||
async fn start_ip_packet_router(
|
||||
&self,
|
||||
forwarding_channel: MixForwardingSender,
|
||||
shutdown: TaskClient,
|
||||
) -> Result<LocalNetworkRequesterHandle, GatewayError> {
|
||||
info!("Starting IP packet provider...");
|
||||
|
||||
// if network requester is enabled, configuration file must be provided!
|
||||
let Some(ip_opts) = &self.ip_packet_router_opts else {
|
||||
log::error!("IP packet router is enabled but no configuration file was provided!");
|
||||
return Err(GatewayError::UnspecifiedIpPacketRouterConfig);
|
||||
};
|
||||
|
||||
// this gateway, whenever it has anything to send to its local NR will use fake_client_tx
|
||||
let (nr_mix_sender, nr_mix_receiver) = mpsc::unbounded();
|
||||
let router_shutdown = shutdown.fork("message_router");
|
||||
|
||||
let (router_tx, mut router_rx) = oneshot::channel();
|
||||
|
||||
let transceiver = LocalGateway::new(
|
||||
*self.identity_keypair.public_key(),
|
||||
forwarding_channel,
|
||||
router_tx,
|
||||
);
|
||||
|
||||
// TODO: well, wire it up internally to gateway traffic, shutdowns, etc.
|
||||
let (on_start_tx, on_start_rx) = oneshot::channel();
|
||||
let mut ip_builder = nym_ip_packet_router::IpForwarderBuilder::new(ip_opts.config.clone())
|
||||
.with_shutdown(shutdown)
|
||||
.with_custom_gateway_transceiver(Box::new(transceiver))
|
||||
.with_wait_for_gateway(true)
|
||||
.with_on_start(on_start_tx);
|
||||
|
||||
if let Some(custom_mixnet) = &ip_opts.custom_mixnet_path {
|
||||
ip_builder = ip_builder.with_stored_topology(custom_mixnet)?
|
||||
}
|
||||
|
||||
tokio::spawn(async move {
|
||||
if let Err(err) = ip_builder.run_service_provider().await {
|
||||
// no need to panic as we have passed a task client to the ip packet router so
|
||||
// we're most likely already in the process of shutting down
|
||||
error!("ip packet router has failed: {err}")
|
||||
}
|
||||
});
|
||||
|
||||
let start_data = on_start_rx
|
||||
.await
|
||||
.map_err(|_| GatewayError::IpPacketRouterStartupFailure)?;
|
||||
|
||||
// this should be instantaneous since the data is sent on this channel before the on start is called;
|
||||
// the failure should be impossible
|
||||
let Ok(Some(packet_router)) = router_rx.try_recv() else {
|
||||
return Err(GatewayError::IpPacketRouterStartupFailure);
|
||||
};
|
||||
|
||||
MessageRouter::new(nr_mix_receiver, packet_router).start_with_shutdown(router_shutdown);
|
||||
info!(
|
||||
"the local ip packet router is running on {}",
|
||||
start_data.address
|
||||
);
|
||||
|
||||
Ok(LocalNetworkRequesterHandle::new_ip(
|
||||
start_data,
|
||||
nr_mix_sender,
|
||||
))
|
||||
}
|
||||
|
||||
async fn wait_for_interrupt(shutdown: TaskManager) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
let res = shutdown.catch_interrupt().await;
|
||||
log::info!("Stopping nym gateway");
|
||||
@@ -387,6 +490,18 @@ impl<St> Gateway<St> {
|
||||
None
|
||||
};
|
||||
|
||||
// NOTE: this is mutually exclusive with the network requester (for now). This is reflected
|
||||
// in the command line arguments as well.
|
||||
if self.config.ip_packet_router.enabled {
|
||||
let embedded_ip_sp = self
|
||||
.start_ip_packet_router(
|
||||
mix_forwarding_channel.clone(),
|
||||
shutdown.subscribe().named("ip_service_provider"),
|
||||
)
|
||||
.await?;
|
||||
active_clients_store.insert_embedded(embedded_ip_sp);
|
||||
}
|
||||
|
||||
HttpApiBuilder::new(
|
||||
&self.config,
|
||||
self.identity_keypair.as_ref(),
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
use std::{io, path::Path};
|
||||
|
||||
pub use nym_client_core::config::Config as BaseClientConfig;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::config::persistence::IpForwarderPaths;
|
||||
|
||||
mod persistence;
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Config {
|
||||
#[serde(flatten)]
|
||||
pub base: BaseClientConfig,
|
||||
|
||||
pub storage_paths: IpForwarderPaths,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn read_from_toml_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
|
||||
nym_config::read_config_from_toml_file(path)
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "nym-ip-forwarder"
|
||||
name = "nym-ip-packet-router"
|
||||
version = "0.1.0"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
@@ -11,9 +11,11 @@ license.workspace = true
|
||||
[dependencies]
|
||||
futures = { workspace = true }
|
||||
log = { workspace = true }
|
||||
nym-bin-common = { path = "../../common/bin-common" }
|
||||
nym-client-core = { path = "../../common/client-core" }
|
||||
nym-config = { path = "../../common/config" }
|
||||
nym-sdk = { path = "../../sdk/rust/nym-sdk" }
|
||||
nym-service-providers-common = { path = "../common" }
|
||||
nym-sphinx = { path = "../../common/nymsphinx" }
|
||||
nym-task = { path = "../../common/task" }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
@@ -0,0 +1,104 @@
|
||||
pub use nym_client_core::config::Config as BaseClientConfig;
|
||||
|
||||
use nym_bin_common::logging::LoggingSettings;
|
||||
use nym_config::{
|
||||
must_get_home, save_formatted_config_to_file, NymConfigTemplate, DEFAULT_CONFIG_DIR,
|
||||
DEFAULT_CONFIG_FILENAME, DEFAULT_DATA_DIR, NYM_DIR,
|
||||
};
|
||||
use nym_service_providers_common::DEFAULT_SERVICE_PROVIDERS_DIR;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
io,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use crate::config::persistence::IpForwarderPaths;
|
||||
|
||||
use self::template::CONFIG_TEMPLATE;
|
||||
|
||||
mod persistence;
|
||||
mod template;
|
||||
|
||||
const DEFAULT_IP_FORWARDERS_DIR: &str = "ip-forwarder";
|
||||
|
||||
/// Derive default path to ip forwarder's config directory.
|
||||
/// It should get resolved to `$HOME/.nym/service-providers/ip-forwareder/<id>/config`
|
||||
pub fn default_config_directory<P: AsRef<Path>>(id: P) -> PathBuf {
|
||||
must_get_home()
|
||||
.join(NYM_DIR)
|
||||
.join(DEFAULT_SERVICE_PROVIDERS_DIR)
|
||||
.join(DEFAULT_IP_FORWARDERS_DIR)
|
||||
.join(id)
|
||||
.join(DEFAULT_CONFIG_DIR)
|
||||
}
|
||||
|
||||
/// Derive default path to ip forwarder's config file.
|
||||
/// It should get resolved to `$HOME/.nym/service-providers/ip-forwarder/<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 network requester's data directory where files, such as keys, are stored.
|
||||
/// It should get resolved to `$HOME/.nym/service-providers/network-requester/<id>/data`
|
||||
pub fn default_data_directory<P: AsRef<Path>>(id: P) -> PathBuf {
|
||||
must_get_home()
|
||||
.join(NYM_DIR)
|
||||
.join(DEFAULT_SERVICE_PROVIDERS_DIR)
|
||||
.join(DEFAULT_IP_FORWARDERS_DIR)
|
||||
.join(id)
|
||||
.join(DEFAULT_DATA_DIR)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Config {
|
||||
#[serde(flatten)]
|
||||
pub base: BaseClientConfig,
|
||||
|
||||
pub storage_paths: IpForwarderPaths,
|
||||
|
||||
pub logging: LoggingSettings,
|
||||
}
|
||||
|
||||
impl NymConfigTemplate for Config {
|
||||
fn template(&self) -> &'static str {
|
||||
CONFIG_TEMPLATE
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn new<S: AsRef<str>>(id: S) -> Self {
|
||||
Config {
|
||||
base: BaseClientConfig::new(id.as_ref(), env!("CARGO_PKG_VERSION")),
|
||||
storage_paths: IpForwarderPaths::new_base(default_data_directory(id.as_ref())),
|
||||
logging: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_data_directory<P: AsRef<Path>>(mut self, data_directory: P) -> Self {
|
||||
self.storage_paths = IpForwarderPaths::new_base(data_directory);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn read_from_toml_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
|
||||
nym_config::read_config_from_toml_file(path)
|
||||
}
|
||||
|
||||
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
|
||||
Self::read_from_toml_file(default_config_filepath(id))
|
||||
}
|
||||
|
||||
pub fn default_location(&self) -> PathBuf {
|
||||
default_config_filepath(&self.base.client.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 validate(&self) -> bool {
|
||||
// no other sections have explicit requirements (yet)
|
||||
self.base.validate()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub(crate) const CONFIG_TEMPLATE: &str =
|
||||
// While using normal toml marshalling would have been way simpler with less overhead,
|
||||
// I think it's useful to have comments attached to the saved config file to explain behaviour of
|
||||
// particular fields.
|
||||
// Note: any changes to the template must be reflected in the appropriate structs.
|
||||
r#"
|
||||
# This is a TOML config file.
|
||||
# For more information, see https://github.com/toml-lang/toml
|
||||
|
||||
##### main base client config options #####
|
||||
|
||||
[client]
|
||||
# Version of the client for which this configuration was created.
|
||||
version = '{{ client.version }}'
|
||||
|
||||
# Human readable ID of this particular client.
|
||||
id = '{{ client.id }}'
|
||||
|
||||
# Indicates whether this client is running in a disabled credentials mode, thus attempting
|
||||
# to claim bandwidth without presenting bandwidth credentials.
|
||||
disabled_credentials_mode = {{ client.disabled_credentials_mode }}
|
||||
|
||||
# Addresses to nyxd validators via which the client can communicate with the chain.
|
||||
nyxd_urls = [
|
||||
{{#each client.nyxd_urls }}
|
||||
'{{this}}',
|
||||
{{/each}}
|
||||
]
|
||||
|
||||
# Addresses to APIs running on validator from which the client gets the view of the network.
|
||||
nym_api_urls = [
|
||||
{{#each client.nym_api_urls }}
|
||||
'{{this}}',
|
||||
{{/each}}
|
||||
]
|
||||
|
||||
[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 encryption key.
|
||||
keys.private_encryption_key_file = '{{ storage_paths.keys.private_encryption_key_file }}'
|
||||
|
||||
# Path to file containing public encryption key.
|
||||
keys.public_encryption_key_file = '{{ storage_paths.keys.public_encryption_key_file }}'
|
||||
|
||||
# A gateway specific, optional, base58 stringified shared key used for
|
||||
# communication with particular gateway.
|
||||
keys.gateway_shared_key_file = '{{ storage_paths.keys.gateway_shared_key_file }}'
|
||||
|
||||
# Path to file containing key used for encrypting and decrypting the content of an
|
||||
# acknowledgement so that nobody besides the client knows which packet it refers to.
|
||||
keys.ack_key_file = '{{ storage_paths.keys.ack_key_file }}'
|
||||
|
||||
# Path to the database containing bandwidth credentials
|
||||
credentials_database = '{{ storage_paths.credentials_database }}'
|
||||
|
||||
# Path to the file containing information about gateway used by this client,
|
||||
# i.e. details such as its public key, owner address or the network information.
|
||||
gateway_details = '{{ storage_paths.gateway_details }}'
|
||||
|
||||
# Path to the persistent store for received reply surbs, unused encryption keys and used sender tags.
|
||||
reply_surb_database = '{{ storage_paths.reply_surb_database }}'
|
||||
|
||||
# Location of the file containing our allow.list
|
||||
allowed_list_location = '{{ storage_paths.allowed_list_location }}'
|
||||
|
||||
# Location of the file containing our unknown.list
|
||||
unknown_list_location = '{{ storage_paths.unknown_list_location }}'
|
||||
|
||||
# Path to file containing description of this network-requester.
|
||||
ip_forwarder_description = '{{ storage_paths.ip_forwarder_description }}'
|
||||
|
||||
|
||||
##### logging configuration options #####
|
||||
|
||||
[logging]
|
||||
|
||||
# TODO
|
||||
|
||||
|
||||
##### debug configuration options #####
|
||||
# The following options should not be modified unless you know EXACTLY what you are doing
|
||||
# as if set incorrectly, they may impact your anonymity.
|
||||
|
||||
[debug]
|
||||
|
||||
[debug.traffic]
|
||||
average_packet_delay = '{{ debug.traffic.average_packet_delay }}'
|
||||
message_sending_average_delay = '{{ debug.traffic.message_sending_average_delay }}'
|
||||
|
||||
[debug.acknowledgements]
|
||||
average_ack_delay = '{{ debug.acknowledgements.average_ack_delay }}'
|
||||
|
||||
[debug.cover_traffic]
|
||||
loop_cover_traffic_average_delay = '{{ debug.cover_traffic.loop_cover_traffic_average_delay }}'
|
||||
|
||||
"#;
|
||||
Reference in New Issue
Block a user