running the api as a proper task in gateway 'run'

This commit is contained in:
Jędrzej Stuczyński
2023-09-22 16:56:19 +01:00
parent 0489e5aa65
commit c2893e5ca0
16 changed files with 577 additions and 82 deletions
+5 -1
View File
@@ -51,7 +51,11 @@ where
if T::pem_type() != key_pem.tag {
return Err(io::Error::new(
io::ErrorKind::Other,
"unexpected key pem tag",
format!(
"unexpected key pem tag. Got '{}', expected: '{}'",
key_pem.tag,
T::pem_type()
),
));
}
+29 -1
View File
@@ -3,6 +3,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::{default_config_filepath, Config};
use crate::error::GatewayError;
use log::info;
@@ -20,7 +21,8 @@ fn try_upgrade_v1_1_20_config(id: &str) -> Result<bool, GatewayError> {
info!("It is going to get updated to the current specification.");
let updated_step1: ConfigV1_1_28 = old_config.into();
let updated: Config = updated_step1.into();
let updated_step2: ConfigV1_1_29 = updated_step1.into();
let updated: Config = updated_step2.into();
updated
.save_to_default_location()
.map_err(|err| GatewayError::ConfigSaveFailure {
@@ -42,6 +44,29 @@ fn try_upgrade_v1_1_28_config(id: &str) -> Result<bool, GatewayError> {
info!("It seems the gateway is using <= v1.1.28 config template.");
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();
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_29_config(id: &str) -> Result<bool, GatewayError> {
// explicitly load it as v1.1.29 (which is incompatible with the current, i.e. 1.1.30+)
let Ok(old_config) = ConfigV1_1_29::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.29 config template.");
info!("It is going to get updated to the current specification.");
let updated: Config = old_config.into();
updated
.save_to_default_location()
@@ -61,6 +86,9 @@ pub(crate) fn try_upgrade_config(id: &str) -> Result<(), GatewayError> {
if try_upgrade_v1_1_28_config(id)? {
return Ok(());
}
if try_upgrade_v1_1_29_config(id)? {
return Ok(());
}
Ok(())
}
+22 -1
View File
@@ -14,7 +14,7 @@ use nym_config::{
use nym_network_defaults::mainnet;
use serde::{Deserialize, Serialize};
use std::io;
use std::net::IpAddr;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::path::{Path, PathBuf};
use std::time::Duration;
use url::Url;
@@ -22,6 +22,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 mod persistence;
mod template;
@@ -71,6 +72,9 @@ pub struct Config {
#[serde(skip)]
pub(crate) save_path: Option<PathBuf>,
#[serde(default)]
pub http: Http,
pub gateway: Gateway,
pub storage_paths: GatewayPaths,
@@ -94,6 +98,7 @@ impl Config {
pub fn new<S: AsRef<str>>(id: S) -> Self {
Config {
save_path: None,
http: Default::default(),
gateway: Gateway::new_default(id.as_ref()),
storage_paths: GatewayPaths::new_default(id.as_ref()),
network_requester: Default::default(),
@@ -219,6 +224,22 @@ impl Config {
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(default)]
pub struct Http {
/// Socket address this node will use for binding its http API.
/// default: `0.0.0.0:80`
pub bind_address: SocketAddr,
}
impl Default for Http {
fn default() -> Self {
Http {
bind_address: SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 80),
}
}
}
// we only really care about the mnemonic being zeroized
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Zeroize, ZeroizeOnDrop)]
pub struct Gateway {
+19 -10
View File
@@ -1,9 +1,10 @@
// 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};
use nym_bin_common::logging::LoggingSettings;
use crate::config::old_config_v1_1_29::{
ConfigV1_1_29, DebugV1_1_29, GatewayPathsV1_1_29, GatewayV1_1_29, KeysPathsV1_1_29,
LoggingSettingsV1_1_29,
};
use nym_config::{
must_get_home, read_config_from_toml_file, DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, NYM_DIR,
};
@@ -84,11 +85,13 @@ impl ConfigV1_1_28 {
}
}
impl From<ConfigV1_1_28> for Config {
impl From<ConfigV1_1_28> for ConfigV1_1_29 {
fn from(value: ConfigV1_1_28) -> Self {
Config {
ConfigV1_1_29 {
// \/ ADDED
save_path: None,
gateway: Gateway {
// /\ ADDED
gateway: GatewayV1_1_29 {
version: value.gateway.version,
id: value.gateway.id,
only_coconut_credentials: value.gateway.only_coconut_credentials,
@@ -101,19 +104,25 @@ impl From<ConfigV1_1_28> for Config {
statistics_service_url: value.gateway.statistics_service_url,
cosmos_mnemonic: value.gateway.cosmos_mnemonic,
},
storage_paths: GatewayPaths {
keys: KeysPaths {
storage_paths: GatewayPathsV1_1_29 {
keys: KeysPathsV1_1_29 {
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,
// \/ ADDED
network_requester_config: None,
// /\ ADDED
},
// \/ ADDED
network_requester: Default::default(),
logging: LoggingSettings {},
debug: Debug {
// /\ ADDED
logging: LoggingSettingsV1_1_29 {},
debug: DebugV1_1_29 {
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,
+264
View File
@@ -0,0 +1,264 @@
// 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,
};
use serde::{Deserialize, Deserializer, Serialize};
use std::io;
use std::net::IpAddr;
use std::path::{Path, PathBuf};
use std::time::Duration;
use url::Url;
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;
/// 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, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct KeysPathsV1_1_29 {
pub private_identity_key_file: PathBuf,
pub public_identity_key_file: PathBuf,
pub private_sphinx_key_file: PathBuf,
pub public_sphinx_key_file: PathBuf,
}
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(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct GatewayPathsV1_1_29 {
pub keys: KeysPathsV1_1_29,
#[serde(alias = "persistent_storage")]
pub clients_storage: PathBuf,
#[serde(deserialize_with = "de_maybe_path")]
pub network_requester_config: Option<PathBuf>,
}
#[derive(Debug, Default, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct LoggingSettingsV1_1_29 {}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_29 {
#[serde(skip)]
pub save_path: Option<PathBuf>,
pub gateway: GatewayV1_1_29,
pub storage_paths: GatewayPathsV1_1_29,
pub network_requester: NetworkRequesterV1_1_29,
#[serde(default)]
pub logging: LoggingSettingsV1_1_29,
#[serde(default)]
pub debug: DebugV1_1_29,
}
impl ConfigV1_1_29 {
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_29> for Config {
fn from(value: ConfigV1_1_29) -> Self {
Config {
save_path: value.save_path,
// \/ ADDED
http: Default::default(),
// /\ ADDED
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,
enabled_statistics: value.gateway.enabled_statistics,
nym_api_urls: value.gateway.nym_api_urls,
nyxd_urls: value.gateway.nyxd_urls,
statistics_service_url: value.gateway.statistics_service_url,
cosmos_mnemonic: value.gateway.cosmos_mnemonic,
},
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,
},
network_requester: NetworkRequester {
enabled: value.network_requester.enabled,
},
logging: LoggingSettings {},
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_29 {
/// 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,
/// 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)]
pub struct NetworkRequesterV1_1_29 {
pub enabled: bool,
}
#[allow(clippy::derivable_impls)]
impl Default for NetworkRequesterV1_1_29 {
fn default() -> Self {
NetworkRequesterV1_1_29 { enabled: false }
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(default)]
pub struct DebugV1_1_29 {
/// 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_29 {
fn default() -> Self {
DebugV1_1_29 {
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,
}
}
}
+5
View File
@@ -55,6 +55,11 @@ nyxd_urls = [
cosmos_mnemonic = '{{ gateway.cosmos_mnemonic }}'
[http]
# Socket address this node will use for binding its http API.
# default: `0.0.0.0:80`
bind_address = '{{ http.bind_address }}'
[network_requester]
# Specifies whether network requester service is enabled in this process.
enabled = {{ network_requester.enabled }}
+5
View File
@@ -106,6 +106,11 @@ pub(crate) enum GatewayError {
#[from]
source: NyxdError,
},
// TODO: in the future this should work the other way, i.e. NymNode depending on Gateway errors
#[error(transparent)]
NymNodeError(#[from] nym_node::error::NymNodeError),
#[error("Error verifying hmac digest")]
HmacDigestError {
#[from]
+96
View File
@@ -1,2 +1,98 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::Config;
use crate::error::GatewayError;
use crate::node::helpers::load_public_key;
use nym_bin_common::bin_info_owned;
use nym_crypto::asymmetric::{encryption, identity};
use nym_node::http;
use nym_sphinx::addressing::clients::Recipient;
use nym_task::TaskClient;
fn load_gateway_details(
config: &Config,
) -> Result<http::api::v1::gateway::types::Gateway, GatewayError> {
let identity_public_key: identity::PublicKey = load_public_key(
&config.storage_paths.keys.public_identity_key_file,
"gateway identity",
)?;
let sphinx_public_key: encryption::PublicKey = load_public_key(
&config.storage_paths.keys.public_sphinx_key_file,
"gateway sphinx",
)?;
Ok(http::api::v1::gateway::types::Gateway {
encoded_identity_key: identity_public_key.to_base58_string(),
encoded_sphinx_key: sphinx_public_key.to_base58_string(),
})
}
fn load_network_requester_details(
config: &Config,
network_requester_config: &nym_network_requester::Config,
) -> Result<http::api::v1::network_requester::types::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(http::api::v1::network_requester::types::NetworkRequester {
encoded_identity_key: identity_public_key.to_base58_string(),
encoded_sphinx_key: dh_public_key.to_base58_string(),
address: Recipient::new(
identity_public_key,
dh_public_key,
gateway_identity_public_key,
)
.to_string(),
})
}
pub(crate) fn start_http_api(
gateway_config: &Config,
network_requester_config: Option<&nym_network_requester::Config>,
task_client: TaskClient,
) -> Result<(), GatewayError> {
// 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::Config::new(bin_info_owned!())
.with_gateway(load_gateway_details(gateway_config)?);
if let Some(nr_config) = network_requester_config {
config = config
.with_network_requester(load_network_requester_details(gateway_config, nr_config)?)
}
let router = nym_node::http::NymNodeRouter::new(config);
let server = router
.build_server(&gateway_config.http.bind_address)?
.with_task_client(task_client);
tokio::spawn(async move { server.run().await });
Ok(())
}
+14 -49
View File
@@ -8,13 +8,11 @@ use clap::{crate_name, crate_version, Parser};
use colored::Colorize;
use lazy_static::lazy_static;
use log::error;
use nym_bin_common::bin_info;
use nym_bin_common::logging::{maybe_print_banner, setup_logging};
use nym_bin_common::output_format::OutputFormat;
use nym_bin_common::{bin_info, bin_info_owned};
use nym_network_defaults::setup_env;
use nym_node::http::api::v1::roles::NodeRoles;
use std::error::Error;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
mod commands;
mod config;
@@ -51,54 +49,21 @@ struct Cli {
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
setup_logging();
let config = nym_node::http::Config {
landing: Default::default(),
policy: Default::default(),
api: nym_node::http::api::Config {
v1_config: nym_node::http::api::v1::Config {
build_information: bin_info_owned!(),
roles: NodeRoles {
mixnode_enabled: false,
gateway_enabled: false,
network_requester_enabled: false,
},
gateway: nym_node::http::api::v1::gateway::Config {
details: Some(nym_node::http::api::v1::gateway::types::Gateway {
encoded_identity_key: "aaa".to_string(),
encoded_sphinx_key: "bbb".to_string(),
}),
},
mixnode: Default::default(),
network_requester: Default::default(),
},
},
};
let mut router = nym_node::http::NymNodeRouter::new(config);
let args = Cli::parse();
setup_env(args.config_env_file.as_ref());
let address = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 12345);
let server = router.build_server(&address)?;
server.await?;
if !args.no_banner {
maybe_print_banner(crate_name!(), crate_version!());
}
Ok(())
//
//
// 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.map_err(|err| {
// if atty::is(atty::Stream::Stdout) {
// let error_message = format!("{err}").red();
// error!("{error_message}");
// error!("Exiting...");
// }
// err
// })
commands::execute(args).await.map_err(|err| {
if atty::is(atty::Stream::Stdout) {
let error_message = format!("{err}").red();
error!("{error_message}");
error!("Exiting...");
}
err
})
}
#[cfg(test)]
+7
View File
@@ -6,6 +6,7 @@ use self::storage::PersistentStorage;
use crate::commands::helpers::{override_network_requester_config, OverrideNetworkRequesterConfig};
use crate::config::Config;
use crate::error::GatewayError;
use crate::http::start_http_api;
use crate::node::client_handling::active_clients::ActiveClientsStore;
use crate::node::client_handling::embedded_network_requester::{
LocalNetworkRequesterHandle, MessageRouter,
@@ -333,6 +334,12 @@ impl<St> Gateway<St> {
CoconutVerifier::new(nyxd_client)
};
start_http_api(
&self.config,
self.network_requester_opts.as_ref().map(|o| &o.config),
shutdown.subscribe().named("http-api"),
)?;
let mix_forwarding_channel =
self.start_packet_forwarder(shutdown.subscribe().named("PacketForwarder"));
+2
View File
@@ -19,6 +19,7 @@ serde_yaml = "0.9.25"
serde_json = { workspace = true }
thiserror = { workspace = true }
tracing = { workspace = true }
tokio = { workspace = true, features = ["macros"] }
# HTTP API:
axum = { workspace = true }
@@ -38,3 +39,4 @@ utoipa-swagger-ui = { version = "3.1.5", features = ["axum"] }
nym-bin-common = { path = "../common/bin-common", features = ["openapi"] }
nym-task = { path = "../common/task" }
+51 -11
View File
@@ -1,19 +1,59 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use axum::routing::IntoMakeService;
use axum::Router;
use hyper::server::conn::AddrIncoming;
use hyper::Server;
use nym_task::TaskClient;
use tracing::{debug, error, info};
pub mod middleware;
pub mod router;
pub mod state;
pub use router::{Config, NymNodeRouter, api, landing_page, policy};
pub use router::{api, landing_page, policy, Config, NymNodeRouter};
// pub struct Config {
// router_config: router::Config,
// bind_address: SocketAddr,
// }
//
// async fn run(config: Config) -> Result<(), NymNodeError> {
// NymNodeRouter::new(config.router_config)
// .start_server(&config.bind_address)
// .await
// }
pub struct NymNodeHTTPServer {
task_client: Option<TaskClient>,
inner: Server<AddrIncoming, IntoMakeService<Router>>,
}
impl NymNodeHTTPServer {
pub(crate) fn new(inner: Server<AddrIncoming, IntoMakeService<Router>>) -> Self {
NymNodeHTTPServer {
task_client: None,
inner,
}
}
#[must_use]
pub fn with_task_client(mut self, task_client: TaskClient) -> Self {
self.task_client = Some(task_client);
self
}
async fn run_server_forever(server: Server<AddrIncoming, IntoMakeService<Router>>) {
if let Err(err) = server.await {
error!("the HTTP server has terminated with the error: {err}");
} else {
error!("the HTTP server has terminated with producing any errors");
}
}
pub async fn run(self) {
info!("Started NymNodeHTTPServer on {}", self.inner.local_addr());
if let Some(mut task_client) = self.task_client {
tokio::select! {
_ = task_client.recv_with_delay() => {
debug!("NymNodeHTTPServer: Received shutdown");
}
_ = Self::run_server_forever(self.inner) => { }
}
} else {
Self::run_server_forever(self.inner).await
}
debug!("NymNodeHTTPServer: Exiting");
}
}
+6
View File
@@ -26,6 +26,12 @@ pub(super) fn routes(config: Config) -> Router<AppState> {
// .nest(routes::SWAGGER, openapi::route())
}
// #[derive(Debug, Clone, ToSchema)]
// pub struct SignedResponse<T>{
// encoded_signature: String,
// data: T,
// }
#[derive(Debug, Clone, ToSchema)]
pub enum FormattedResponse<T> {
Json(Json<T>),
@@ -11,4 +11,7 @@ pub struct NetworkRequester {
/// Base58-encoded x25519 public key used for performing key exchange with remote clients.
pub encoded_sphinx_key: String,
/// Nym address of this network requester.
pub address: String,
}
+1 -1
View File
@@ -6,7 +6,7 @@ use axum::extract::Query;
use serde::Serialize;
use utoipa::ToSchema;
#[derive(Clone, Debug, Copy, ToSchema, Serialize)]
#[derive(Clone, Default, Debug, Copy, ToSchema, Serialize)]
pub struct NodeRoles {
pub mixnode_enabled: bool,
pub gateway_enabled: bool,
+48 -8
View File
@@ -2,12 +2,14 @@
// SPDX-License-Identifier: Apache-2.0
use crate::error::NymNodeError;
use crate::http::api::v1::gateway::types::Gateway;
use crate::http::api::v1::mixnode::types::Mixnode;
use crate::http::api::v1::network_requester::types::NetworkRequester;
use crate::http::middleware::logging;
use crate::http::state::AppState;
use axum::routing::IntoMakeService;
use crate::http::NymNodeHTTPServer;
use axum::Router;
use hyper::server::conn::AddrIncoming;
use hyper::Server;
use nym_bin_common::build_information::BinaryBuildInformationOwned;
use std::net::SocketAddr;
pub mod api;
@@ -21,9 +23,6 @@ pub(crate) mod routes {
pub(crate) const API: &str = "/api";
}
// TODO: can it be made nicer?
pub type NymNodeHTTPServer = Server<AddrIncoming, IntoMakeService<Router>>;
#[derive(Debug, Clone)]
pub struct Config {
pub landing: landing_page::Config,
@@ -31,6 +30,45 @@ pub struct Config {
pub api: api::Config,
}
impl Config {
pub fn new(binary_info: BinaryBuildInformationOwned) -> Self {
Config {
landing: Default::default(),
policy: Default::default(),
api: api::Config {
v1_config: api::v1::Config {
build_information: binary_info,
roles: Default::default(),
gateway: Default::default(),
mixnode: Default::default(),
network_requester: Default::default(),
},
},
}
}
#[must_use]
pub fn with_gateway(mut self, gateway: Gateway) -> Self {
self.api.v1_config.roles.gateway_enabled = true;
self.api.v1_config.gateway.details = Some(gateway);
self
}
#[must_use]
pub fn with_mixnode(mut self, mixnode: Mixnode) -> Self {
self.api.v1_config.roles.mixnode_enabled = true;
self.api.v1_config.mixnode.details = Some(mixnode);
self
}
#[must_use]
pub fn with_network_requester(mut self, network_requester: NetworkRequester) -> Self {
self.api.v1_config.roles.network_requester_enabled = true;
self.api.v1_config.network_requester.details = Some(network_requester);
self
}
}
pub struct NymNodeRouter {
inner: Router,
}
@@ -60,11 +98,13 @@ impl NymNodeRouter {
self,
bind_address: &SocketAddr,
) -> Result<NymNodeHTTPServer, NymNodeError> {
Ok(axum::Server::try_bind(bind_address)
let axum_server = axum::Server::try_bind(bind_address)
.map_err(|source| NymNodeError::HttpBindFailure {
bind_address: *bind_address,
source,
})?
.serve(self.inner.into_make_service()))
.serve(self.inner.into_make_service());
Ok(NymNodeHTTPServer::new(axum_server))
}
}