chore: remove standalone legacy mixnode/gateway binaries (#5135)
* remove standalone gateway overhead * remove standalone mixnode overhead * additional cleanup: removed unused dependencies et al. * removed calls to 'log::'
This commit is contained in:
committed by
GitHub
parent
475a01c089
commit
69718db6d2
@@ -1,52 +0,0 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::error::MixnodeError;
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
use nym_pemstore::traits::{PemStorableKey, PemStorableKeyPair};
|
||||
use nym_pemstore::KeyPairPath;
|
||||
use std::path::Path;
|
||||
|
||||
pub(crate) fn load_keypair<T: PemStorableKeyPair>(
|
||||
paths: KeyPairPath,
|
||||
name: impl Into<String>,
|
||||
) -> Result<T, MixnodeError> {
|
||||
nym_pemstore::load_keypair(&paths).map_err(|err| MixnodeError::KeyPairLoadFailure {
|
||||
keys: name.into(),
|
||||
paths,
|
||||
err,
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn load_public_key<T, P, S>(path: P, name: S) -> Result<T, MixnodeError>
|
||||
where
|
||||
T: PemStorableKey,
|
||||
P: AsRef<Path>,
|
||||
S: Into<String>,
|
||||
{
|
||||
nym_pemstore::load_key(path.as_ref()).map_err(|err| MixnodeError::PublicKeyLoadFailure {
|
||||
key: name.into(),
|
||||
path: path.as_ref().to_path_buf(),
|
||||
err,
|
||||
})
|
||||
}
|
||||
|
||||
/// Loads identity keys stored on disk
|
||||
pub fn load_identity_keys(config: &Config) -> Result<identity::KeyPair, MixnodeError> {
|
||||
let identity_paths = KeyPairPath::new(
|
||||
config.storage_paths.keys.private_identity_key(),
|
||||
config.storage_paths.keys.public_identity_key(),
|
||||
);
|
||||
load_keypair(identity_paths, "mixnode identity")
|
||||
}
|
||||
|
||||
/// Loads Sphinx keys stored on disk
|
||||
pub fn load_sphinx_keys(config: &Config) -> Result<encryption::KeyPair, MixnodeError> {
|
||||
let sphinx_paths = KeyPairPath::new(
|
||||
config.storage_paths.keys.private_encryption_key(),
|
||||
config.storage_paths.keys.public_encryption_key(),
|
||||
);
|
||||
load_keypair(sphinx_paths, "mixnode sphinx")
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::node::node_description::NodeDescription;
|
||||
use axum::extract::Query;
|
||||
use nym_http_api_common::{FormattedResponse, OutputParams};
|
||||
|
||||
/// Returns a description of the node and why someone might want to delegate stake to it.
|
||||
pub(crate) async fn description(
|
||||
description: NodeDescription,
|
||||
Query(output): Query<OutputParams>,
|
||||
) -> MixnodeDescriptionResponse {
|
||||
let output = output.output.unwrap_or_default();
|
||||
output.to_response(description)
|
||||
}
|
||||
|
||||
pub type MixnodeDescriptionResponse = FormattedResponse<NodeDescription>;
|
||||
@@ -1,93 +0,0 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use axum::extract::Query;
|
||||
use cupid::TopologyType;
|
||||
use nym_http_api_common::{FormattedResponse, OutputParams};
|
||||
use serde::Serialize;
|
||||
use sysinfo::System;
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct Hardware {
|
||||
ram: String,
|
||||
num_cores: usize,
|
||||
crypto_hardware: Option<CryptoHardware>,
|
||||
}
|
||||
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
#[derive(Serialize, Debug)]
|
||||
pub(crate) struct CryptoHardware {
|
||||
aesni: bool,
|
||||
avx2: bool,
|
||||
brand_string: String,
|
||||
smt_logical_processor_count: Vec<u32>,
|
||||
osxsave: bool,
|
||||
sgx: bool,
|
||||
xsave: bool,
|
||||
}
|
||||
|
||||
/// Provides hardware information which Nym can use to optimize mixnet speed over time (memory, crypto hardware, CPU, cores, etc).
|
||||
pub(crate) async fn hardware(Query(output): Query<OutputParams>) -> MixnodeHardwareResponse {
|
||||
let output = output.output.unwrap_or_default();
|
||||
output.to_response(hardware_info())
|
||||
}
|
||||
|
||||
pub type MixnodeHardwareResponse = FormattedResponse<Option<Hardware>>;
|
||||
|
||||
/// Gives back a summary report of whatever system hardware info we can get for this platform.
|
||||
fn hardware_info() -> Option<Hardware> {
|
||||
let crypto_hardware = hardware_info_from_cupid();
|
||||
hardware_from_sysinfo(crypto_hardware)
|
||||
}
|
||||
|
||||
/// Sysinfo gives back basic stuff like number of CPU cores and available memory. If available, this includes the hardware encryption
|
||||
/// extensions report
|
||||
fn hardware_from_sysinfo(crypto_hardware: Option<CryptoHardware>) -> Option<Hardware> {
|
||||
if sysinfo::IS_SUPPORTED_SYSTEM {
|
||||
let mut system = System::new_all();
|
||||
system.refresh_all();
|
||||
let ram = format!("{}KB", system.total_memory());
|
||||
let cores = system.cpus();
|
||||
let num_cores = cores.len();
|
||||
Some(Hardware {
|
||||
ram,
|
||||
num_cores,
|
||||
crypto_hardware,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// The `cupid` crate gives back a report on available hardware encryption extensions which may be useful for future mixnet optimizations.
|
||||
///
|
||||
/// Note: this information is generally only available on x86 platforms for Linux.
|
||||
fn hardware_info_from_cupid() -> Option<CryptoHardware> {
|
||||
cupid::master().map(|info| -> CryptoHardware {
|
||||
let smt_logical_processor_count =
|
||||
if let Some(extended_topology) = info.extended_topology_enumeration() {
|
||||
extended_topology
|
||||
.clone()
|
||||
.filter_map(|entry| {
|
||||
if entry.level_type() == TopologyType::SMT {
|
||||
Some(entry.logical_processor_count())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
CryptoHardware {
|
||||
aesni: info.aesni(),
|
||||
avx2: info.avx2(),
|
||||
brand_string: info.brand_string().map(String::from).unwrap_or_default(),
|
||||
smt_logical_processor_count,
|
||||
osxsave: info.osxsave(),
|
||||
sgx: info.sgx(),
|
||||
xsave: info.xsave(),
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
#![allow(unused)]
|
||||
|
||||
use crate::node::http::legacy::description::description;
|
||||
use crate::node::http::legacy::hardware::hardware;
|
||||
use crate::node::http::legacy::state::MixnodeAppState;
|
||||
use crate::node::http::legacy::stats::metrics;
|
||||
use crate::node::http::legacy::stats::stats;
|
||||
use crate::node::http::legacy::verloc::verloc;
|
||||
use crate::node::node_description::NodeDescription;
|
||||
use axum::http::{StatusCode, Uri};
|
||||
use axum::response::IntoResponse;
|
||||
use axum::routing::get;
|
||||
use axum::Router;
|
||||
|
||||
pub(crate) mod description;
|
||||
pub(crate) mod hardware;
|
||||
pub(crate) mod state;
|
||||
pub(crate) mod stats;
|
||||
pub(crate) mod verloc;
|
||||
|
||||
pub(crate) async fn not_found(uri: Uri) -> impl IntoResponse {
|
||||
(
|
||||
StatusCode::NOT_FOUND,
|
||||
format!("I couldn't find '{uri}'. Try something else?"),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) mod api_routes {
|
||||
pub(crate) const VERLOC: &str = "/verloc";
|
||||
pub(crate) const DESCRIPTION: &str = "/description";
|
||||
pub(crate) const STATS: &str = "/stats";
|
||||
pub(crate) const METRICS: &str = "/metrics";
|
||||
pub(crate) const HARDWARE: &str = "/hardware";
|
||||
}
|
||||
|
||||
pub(crate) fn routes<S: Send + Sync + 'static + Clone>(
|
||||
state: MixnodeAppState,
|
||||
descriptor: NodeDescription,
|
||||
) -> Router<S> {
|
||||
Router::new()
|
||||
.route(api_routes::VERLOC, get(verloc))
|
||||
.route(
|
||||
api_routes::DESCRIPTION,
|
||||
get(move |query| description(descriptor, query)),
|
||||
)
|
||||
.route(api_routes::STATS, get(stats))
|
||||
.route(api_routes::HARDWARE, get(hardware))
|
||||
.route(api_routes::METRICS, get(metrics))
|
||||
.fallback(not_found)
|
||||
.with_state(state)
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use axum::extract::FromRef;
|
||||
use nym_node_http_api::state::metrics::{SharedMixingStats, SharedVerlocStats};
|
||||
|
||||
// this is a temporary thing for the transition period
|
||||
#[derive(Clone, Default)]
|
||||
pub(crate) struct MixnodeAppState {
|
||||
pub(crate) verloc: SharedVerlocStats,
|
||||
pub(crate) stats: SharedMixingStats,
|
||||
pub(crate) metrics_key: Option<String>,
|
||||
}
|
||||
|
||||
impl FromRef<MixnodeAppState> for SharedVerlocStats {
|
||||
fn from_ref(app_state: &MixnodeAppState) -> Self {
|
||||
app_state.verloc.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRef<MixnodeAppState> for SharedMixingStats {
|
||||
fn from_ref(app_state: &MixnodeAppState) -> Self {
|
||||
app_state.stats.clone()
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use axum::{
|
||||
extract::{Query, State},
|
||||
http::HeaderMap,
|
||||
};
|
||||
use nym_http_api_common::{FormattedResponse, Output};
|
||||
use nym_metrics::metrics;
|
||||
use nym_node_http_api::api::api_requests::v1::metrics::models::MixingStats;
|
||||
use nym_node_http_api::state::metrics::SharedMixingStats;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::state::MixnodeAppState;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum NodeStatsResponse {
|
||||
Full(String),
|
||||
Simple(MixingStats),
|
||||
}
|
||||
|
||||
pub(crate) async fn metrics(State(state): State<MixnodeAppState>, headers: HeaderMap) -> String {
|
||||
if let Some(metrics_key) = state.metrics_key {
|
||||
if let Some(auth) = headers.get("Authorization") {
|
||||
if auth.to_str().unwrap_or_default() == format!("Bearer {}", metrics_key) {
|
||||
metrics!()
|
||||
} else {
|
||||
"Unauthorized".to_string()
|
||||
}
|
||||
} else {
|
||||
"Unauthorized".to_string()
|
||||
}
|
||||
} else {
|
||||
"Set metrics_key in config to enable Prometheus metrics".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn stats(
|
||||
Query(params): Query<StatsQueryParams>,
|
||||
State(stats): State<SharedMixingStats>,
|
||||
) -> MixnodeStatsResponse {
|
||||
let output = params.output.unwrap_or_default();
|
||||
|
||||
// there's no point in returning the entire hashmap of sending destinations in regular mode
|
||||
let response = generate_stats(params.debug, stats).await;
|
||||
output.to_response(response)
|
||||
}
|
||||
|
||||
async fn generate_stats(full: bool, stats: SharedMixingStats) -> NodeStatsResponse {
|
||||
if full {
|
||||
NodeStatsResponse::Full(metrics!())
|
||||
} else {
|
||||
NodeStatsResponse::Simple(stats.read().await.as_response())
|
||||
}
|
||||
}
|
||||
|
||||
pub type MixnodeStatsResponse = FormattedResponse<NodeStatsResponse>;
|
||||
|
||||
#[derive(Default, Debug, Serialize, Deserialize, Copy, Clone)]
|
||||
// #[derive(Default, Debug, Serialize, Deserialize, Copy, Clone, IntoParams, ToSchema)]
|
||||
#[serde(default)]
|
||||
pub(crate) struct StatsQueryParams {
|
||||
debug: bool,
|
||||
pub output: Option<Output>,
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use axum::extract::{Query, State};
|
||||
use nym_http_api_common::{FormattedResponse, OutputParams};
|
||||
use nym_node_http_api::api::api_requests::v1::metrics::models::VerlocResultData;
|
||||
use nym_node_http_api::state::metrics::SharedVerlocStats;
|
||||
|
||||
/// Provides verifiable location (verloc) measurements for this mixnode - a list of the
|
||||
/// round-trip times, in milliseconds, for all other mixnodes that this node knows about.
|
||||
pub(crate) async fn verloc(
|
||||
State(verloc): State<SharedVerlocStats>,
|
||||
Query(output): Query<OutputParams>,
|
||||
) -> MixnodeVerlocResponse {
|
||||
let output = output.output.unwrap_or_default();
|
||||
output.to_response(verloc.read().await.current_run_data.clone())
|
||||
}
|
||||
|
||||
pub type MixnodeVerlocResponse = FormattedResponse<VerlocResultData>;
|
||||
@@ -1,120 +0,0 @@
|
||||
// Copyright 2023-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::error::MixnodeError;
|
||||
use crate::node::node_description::NodeDescription;
|
||||
use log::{error, info};
|
||||
use nym_bin_common::bin_info_owned;
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
use nym_node_http_api::api::api_requests;
|
||||
use nym_node_http_api::api::api_requests::SignedHostInformation;
|
||||
use nym_node_http_api::state::metrics::{SharedMixingStats, SharedVerlocStats};
|
||||
use nym_node_http_api::NymNodeHttpError;
|
||||
use nym_task::TaskClient;
|
||||
|
||||
pub(crate) mod legacy;
|
||||
|
||||
fn load_host_details(
|
||||
config: &Config,
|
||||
sphinx_key: &encryption::PublicKey,
|
||||
identity_keypair: &identity::KeyPair,
|
||||
) -> Result<api_requests::v1::node::models::SignedHostInformation, MixnodeError> {
|
||||
let host_info = api_requests::v1::node::models::HostInformation {
|
||||
ip_address: config.host.public_ips.clone(),
|
||||
hostname: config.host.hostname.clone(),
|
||||
keys: api_requests::v1::node::models::HostKeys {
|
||||
ed25519_identity: *identity_keypair.public_key(),
|
||||
x25519_sphinx: *sphinx_key,
|
||||
x25519_noise: None,
|
||||
},
|
||||
};
|
||||
|
||||
let signed_info = SignedHostInformation::new(host_info, identity_keypair.private_key())
|
||||
.map_err(NymNodeHttpError::from)?;
|
||||
Ok(signed_info)
|
||||
}
|
||||
|
||||
fn load_mixnode_details(
|
||||
_config: &Config,
|
||||
) -> Result<api_requests::v1::mixnode::models::Mixnode, MixnodeError> {
|
||||
Ok(api_requests::v1::mixnode::models::Mixnode {})
|
||||
}
|
||||
|
||||
pub(crate) struct HttpApiBuilder<'a> {
|
||||
mixnode_config: &'a Config,
|
||||
identity_keypair: &'a identity::KeyPair,
|
||||
sphinx_keypair: &'a encryption::KeyPair,
|
||||
legacy_mixnode: legacy::state::MixnodeAppState,
|
||||
legacy_descriptor: NodeDescription,
|
||||
}
|
||||
|
||||
impl<'a> HttpApiBuilder<'a> {
|
||||
pub(crate) fn new(
|
||||
mixnode_config: &'a Config,
|
||||
identity_keypair: &'a identity::KeyPair,
|
||||
sphinx_keypair: &'a encryption::KeyPair,
|
||||
) -> Self {
|
||||
HttpApiBuilder {
|
||||
mixnode_config,
|
||||
identity_keypair,
|
||||
sphinx_keypair,
|
||||
legacy_mixnode: legacy::state::MixnodeAppState::default(),
|
||||
legacy_descriptor: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub(crate) fn with_metrics_key(mut self, metrics_key: Option<&String>) -> Self {
|
||||
self.legacy_mixnode.metrics_key = metrics_key.map(|k| k.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub(crate) fn with_verloc(mut self, verloc: SharedVerlocStats) -> Self {
|
||||
self.legacy_mixnode.verloc = verloc;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub(crate) fn with_mixing_stats(mut self, stats: SharedMixingStats) -> Self {
|
||||
self.legacy_mixnode.stats = stats;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub(crate) fn with_descriptor(mut self, descriptor: NodeDescription) -> Self {
|
||||
self.legacy_descriptor = descriptor;
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn start(self, task_client: TaskClient) -> Result<(), MixnodeError> {
|
||||
let bind_address = self.mixnode_config.http.bind_address;
|
||||
info!("Starting HTTP API on http://{bind_address}",);
|
||||
|
||||
let config = nym_node_http_api::Config::new(
|
||||
bin_info_owned!(),
|
||||
load_host_details(
|
||||
self.mixnode_config,
|
||||
self.sphinx_keypair.public_key(),
|
||||
self.identity_keypair,
|
||||
)?,
|
||||
)
|
||||
.with_mixnode(load_mixnode_details(self.mixnode_config)?)
|
||||
.with_landing_page_assets(self.mixnode_config.http.landing_page_assets_path.as_ref());
|
||||
|
||||
let router = nym_node_http_api::NymNodeRouter::new(config, None);
|
||||
|
||||
tokio::spawn(async move {
|
||||
let server = match router.build_server(&bind_address).await {
|
||||
Ok(server) => server.with_task_client(task_client),
|
||||
Err(err) => {
|
||||
error!("failed to create http server: {err}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
server.run().await
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,6 @@ use crate::node::listener::connection_handler::packet_processing::PacketProcesso
|
||||
use crate::node::packet_delayforwarder::PacketDelayForwardSender;
|
||||
use crate::node::TaskClient;
|
||||
use futures::StreamExt;
|
||||
use log::debug;
|
||||
use log::{error, info, warn};
|
||||
use nym_metrics::nanos;
|
||||
use nym_sphinx::forwarding::packet::MixPacket;
|
||||
use nym_sphinx::framing::codec::NymCodec;
|
||||
@@ -18,6 +16,7 @@ use std::net::SocketAddr;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::time::Instant;
|
||||
use tokio_util::codec::Framed;
|
||||
use tracing::{debug, error, info, trace, warn};
|
||||
|
||||
pub(crate) mod packet_processing;
|
||||
|
||||
@@ -94,7 +93,7 @@ impl ConnectionHandler {
|
||||
tokio::select! {
|
||||
biased;
|
||||
_ = shutdown.recv() => {
|
||||
log::trace!("ConnectionHandler: received shutdown");
|
||||
trace!("ConnectionHandler: received shutdown");
|
||||
}
|
||||
framed_sphinx_packet = framed_conn.next() => {
|
||||
match framed_sphinx_packet {
|
||||
@@ -125,6 +124,6 @@ impl ConnectionHandler {
|
||||
"Closing connection from {:?}",
|
||||
framed_conn.into_inner().peer_addr()
|
||||
);
|
||||
log::trace!("ConnectionHandler: Exiting");
|
||||
trace!("ConnectionHandler: Exiting");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::node::listener::connection_handler::ConnectionHandler;
|
||||
use log::{error, info, warn};
|
||||
use std::net::SocketAddr;
|
||||
use std::process;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::task::JoinHandle;
|
||||
use tracing::{error, info, trace, warn};
|
||||
|
||||
use super::TaskClient;
|
||||
|
||||
@@ -23,7 +23,7 @@ impl Listener {
|
||||
}
|
||||
|
||||
async fn run(&mut self, connection_handler: ConnectionHandler) {
|
||||
log::trace!("Starting Listener");
|
||||
trace!("Starting Listener");
|
||||
let listener = match TcpListener::bind(self.address).await {
|
||||
Ok(listener) => listener,
|
||||
Err(err) => {
|
||||
@@ -36,7 +36,7 @@ impl Listener {
|
||||
tokio::select! {
|
||||
biased;
|
||||
_ = self.shutdown.recv() => {
|
||||
log::trace!("Listener: Received shutdown");
|
||||
trace!("Listener: Received shutdown");
|
||||
}
|
||||
connection = listener.accept() => {
|
||||
match connection {
|
||||
@@ -49,7 +49,7 @@ impl Listener {
|
||||
},
|
||||
};
|
||||
}
|
||||
log::trace!("Listener: Exiting");
|
||||
trace!("Listener: Exiting");
|
||||
}
|
||||
|
||||
pub(crate) fn start(mut self, connection_handler: ConnectionHandler) -> JoinHandle<()> {
|
||||
|
||||
+21
-107
@@ -2,72 +2,44 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::error::MixnodeError;
|
||||
use crate::node::helpers::{load_identity_keys, load_sphinx_keys};
|
||||
use crate::node::http::HttpApiBuilder;
|
||||
use crate::node::listener::connection_handler::packet_processing::PacketProcessor;
|
||||
use crate::node::listener::connection_handler::ConnectionHandler;
|
||||
use crate::node::listener::Listener;
|
||||
use crate::node::node_description::NodeDescription;
|
||||
use crate::node::packet_delayforwarder::{DelayForwarder, PacketDelayForwardSender};
|
||||
use log::{error, info, warn};
|
||||
use nym_bin_common::output_format::OutputFormat;
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
use nym_mixnode_common::verloc;
|
||||
use nym_mixnode_common::verloc::VerlocMeasurer;
|
||||
use nym_node_http_api::state::metrics::{SharedMixingStats, SharedVerlocStats};
|
||||
use nym_task::{TaskClient, TaskHandle};
|
||||
use rand::seq::SliceRandom;
|
||||
use rand::thread_rng;
|
||||
use std::net::SocketAddr;
|
||||
use std::process;
|
||||
use std::sync::Arc;
|
||||
use tracing::{error, info, warn};
|
||||
|
||||
pub mod helpers;
|
||||
mod http;
|
||||
mod listener;
|
||||
pub mod node_description;
|
||||
mod node_statistics;
|
||||
mod packet_delayforwarder;
|
||||
|
||||
// the MixNode will live for whole duration of this program
|
||||
pub struct MixNode {
|
||||
config: Config,
|
||||
descriptor: NodeDescription,
|
||||
identity_keypair: Arc<identity::KeyPair>,
|
||||
sphinx_keypair: Arc<encryption::KeyPair>,
|
||||
|
||||
run_http_server: bool,
|
||||
task_client: Option<TaskClient>,
|
||||
mixing_stats: Option<SharedMixingStats>,
|
||||
verloc_stats: Option<SharedVerlocStats>,
|
||||
}
|
||||
|
||||
impl MixNode {
|
||||
pub fn new(config: Config) -> Result<Self, MixnodeError> {
|
||||
Ok(MixNode {
|
||||
run_http_server: true,
|
||||
descriptor: Self::load_node_description(&config),
|
||||
identity_keypair: Arc::new(load_identity_keys(&config)?),
|
||||
sphinx_keypair: Arc::new(load_sphinx_keys(&config)?),
|
||||
config,
|
||||
task_client: None,
|
||||
mixing_stats: None,
|
||||
verloc_stats: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new_loaded(
|
||||
config: Config,
|
||||
descriptor: NodeDescription,
|
||||
identity_keypair: Arc<identity::KeyPair>,
|
||||
sphinx_keypair: Arc<encryption::KeyPair>,
|
||||
) -> Self {
|
||||
MixNode {
|
||||
run_http_server: true,
|
||||
task_client: None,
|
||||
config,
|
||||
descriptor,
|
||||
identity_keypair,
|
||||
sphinx_keypair,
|
||||
mixing_stats: None,
|
||||
@@ -75,10 +47,6 @@ impl MixNode {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn disable_http_server(&mut self) {
|
||||
self.run_http_server = false
|
||||
}
|
||||
|
||||
pub fn set_task_client(&mut self, task_client: TaskClient) {
|
||||
self.task_client = Some(task_client)
|
||||
}
|
||||
@@ -91,40 +59,6 @@ impl MixNode {
|
||||
self.verloc_stats = Some(verloc_stats)
|
||||
}
|
||||
|
||||
fn load_node_description(config: &Config) -> NodeDescription {
|
||||
NodeDescription::load_from_file(&config.storage_paths.node_description).unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Prints relevant node details to the console
|
||||
pub fn print_node_details(&self, output: OutputFormat) {
|
||||
let node_details = nym_types::mixnode::MixnodeNodeDetailsResponse {
|
||||
identity_key: self.identity_keypair.public_key().to_base58_string(),
|
||||
sphinx_key: self.sphinx_keypair.public_key().to_base58_string(),
|
||||
bind_address: self.config.mixnode.listening_address,
|
||||
version: self.config.mixnode.version.clone(),
|
||||
mix_port: self.config.mixnode.mix_port,
|
||||
http_api_port: self.config.http.bind_address.port(),
|
||||
verloc_port: self.config.mixnode.verloc_port,
|
||||
};
|
||||
|
||||
println!("{}", output.format(&node_details));
|
||||
}
|
||||
|
||||
fn start_http_api(
|
||||
&self,
|
||||
atomic_verloc_result: SharedVerlocStats,
|
||||
node_stats_pointer: SharedMixingStats,
|
||||
metrics_key: Option<&String>,
|
||||
task_client: TaskClient,
|
||||
) -> Result<(), MixnodeError> {
|
||||
HttpApiBuilder::new(&self.config, &self.identity_keypair, &self.sphinx_keypair)
|
||||
.with_verloc(atomic_verloc_result)
|
||||
.with_mixing_stats(node_stats_pointer)
|
||||
.with_metrics_key(metrics_key)
|
||||
.with_descriptor(self.descriptor.clone())
|
||||
.start(task_client)
|
||||
}
|
||||
|
||||
fn start_node_stats_controller(
|
||||
&mut self,
|
||||
shutdown: TaskClient,
|
||||
@@ -221,41 +155,35 @@ impl MixNode {
|
||||
verloc_state
|
||||
}
|
||||
|
||||
fn random_api_client(&self) -> nym_validator_client::NymApiClient {
|
||||
let endpoints = self.config.get_nym_api_endpoints();
|
||||
let nym_api = endpoints
|
||||
.choose(&mut thread_rng())
|
||||
.expect("The list of validator apis is empty");
|
||||
|
||||
nym_validator_client::NymApiClient::new(nym_api.clone())
|
||||
}
|
||||
|
||||
async fn check_if_bonded(&self) -> bool {
|
||||
// TODO: if anything, this should be getting data directly from the contract
|
||||
// as opposed to the validator API
|
||||
let validator_client = self.random_api_client();
|
||||
let existing_nodes = match validator_client.get_all_basic_nodes(None).await {
|
||||
Ok(nodes) => nodes,
|
||||
Err(err) => {
|
||||
error!(
|
||||
"failed to grab initial network mixnodes - {err}\n \
|
||||
Please try to startup again in few minutes",
|
||||
);
|
||||
process::exit(1);
|
||||
for api_url in self.config.get_nym_api_endpoints() {
|
||||
let client = nym_validator_client::NymApiClient::new(api_url.clone());
|
||||
match client.get_all_basic_nodes(None).await {
|
||||
Ok(nodes) => {
|
||||
return nodes.iter().any(|node| {
|
||||
&node.ed25519_identity_pubkey == self.identity_keypair.public_key()
|
||||
})
|
||||
}
|
||||
Err(err) => {
|
||||
error!("failed to grab initial network mixnodes from {api_url}: {err}",);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
existing_nodes
|
||||
.iter()
|
||||
.any(|node| &node.ed25519_identity_pubkey == self.identity_keypair.public_key())
|
||||
error!(
|
||||
"failed to grab initial network mixnodes from any of the available apis. Please try to startup again in few minutes",
|
||||
);
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
async fn wait_for_interrupt(&self, shutdown: TaskHandle) {
|
||||
let _res = shutdown.wait_for_shutdown().await;
|
||||
log::info!("Stopping nym mixnode");
|
||||
info!("Stopping nym mixnode");
|
||||
}
|
||||
|
||||
pub async fn run(&mut self) -> Result<(), MixnodeError> {
|
||||
pub async fn run(&mut self) {
|
||||
info!("Starting nym mixnode");
|
||||
|
||||
if self.check_if_bonded().await {
|
||||
@@ -270,7 +198,7 @@ impl MixNode {
|
||||
.unwrap_or_default()
|
||||
.name_if_unnamed("mixnode");
|
||||
|
||||
let (node_stats_pointer, node_stats_update_sender) =
|
||||
let (_, node_stats_update_sender) =
|
||||
self.start_node_stats_controller(shutdown.fork("node_statistics::Controller"));
|
||||
let delay_forwarding_channel = self.start_packet_delay_forwarder(
|
||||
node_stats_update_sender.clone(),
|
||||
@@ -281,23 +209,9 @@ impl MixNode {
|
||||
delay_forwarding_channel,
|
||||
shutdown.fork("Listener"),
|
||||
);
|
||||
let atomic_verloc_results = self.start_verloc_measurements(shutdown.fork("VerlocMeasurer"));
|
||||
|
||||
// Rocket handles shutdown on it's own, but its shutdown handling should be incorporated
|
||||
// with that of the rest of the tasks.
|
||||
// Currently it's runtime is forcefully terminated once the mixnode exits.
|
||||
if self.run_http_server {
|
||||
self.start_http_api(
|
||||
atomic_verloc_results,
|
||||
node_stats_pointer,
|
||||
self.config.metrics_key(),
|
||||
shutdown.fork("http-api"),
|
||||
)?;
|
||||
}
|
||||
self.start_verloc_measurements(shutdown.fork("VerlocMeasurer"));
|
||||
|
||||
info!("Finished nym mixnode startup procedure - it should now be able to receive mix traffic!");
|
||||
self.wait_for_interrupt(shutdown).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::fs::create_dir_all;
|
||||
use std::path::Path;
|
||||
use std::{fs, io};
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||
pub struct NodeDescription {
|
||||
#[serde(default)]
|
||||
pub name: String,
|
||||
|
||||
#[serde(default)]
|
||||
pub description: String,
|
||||
|
||||
#[serde(default)]
|
||||
pub link: String,
|
||||
|
||||
#[serde(default)]
|
||||
pub location: String,
|
||||
}
|
||||
|
||||
impl Default for NodeDescription {
|
||||
fn default() -> Self {
|
||||
NodeDescription {
|
||||
name: "This node has not yet set a name".to_string(),
|
||||
description: "This node has not yet set a description".to_string(),
|
||||
link: "https://nymtech.net".to_string(),
|
||||
location: "This node has not yet set a location".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NodeDescription {
|
||||
pub fn load_from_file<P: AsRef<Path>>(path: P) -> io::Result<NodeDescription> {
|
||||
// let description_file_path: PathBuf = [config_path.to_str().unwrap(), DESCRIPTION_FILE]
|
||||
// .iter()
|
||||
// .collect();
|
||||
// let toml = fs::read_to_string(description_file_path)?;
|
||||
// toml::from_str(&toml).map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err))
|
||||
let toml = fs::read_to_string(path)?;
|
||||
toml::from_str(&toml).map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err))
|
||||
}
|
||||
|
||||
pub fn save_to_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
|
||||
// let description_file_path: PathBuf = [config_path.to_str().unwrap(), DESCRIPTION_FILE]
|
||||
// .iter()
|
||||
// .collect();
|
||||
let description_toml = toml::to_string(self).expect("could not encode description to toml");
|
||||
if let Some(parent) = path.as_ref().parent() {
|
||||
create_dir_all(parent)?;
|
||||
}
|
||||
fs::write(path, description_toml)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ use super::TaskClient;
|
||||
use futures::channel::mpsc;
|
||||
use futures::lock::Mutex;
|
||||
use futures::StreamExt;
|
||||
use log::{debug, info, trace};
|
||||
use nym_node_http_api::state::metrics::SharedMixingStats;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::DerefMut;
|
||||
@@ -15,6 +14,7 @@ use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use time::OffsetDateTime;
|
||||
use tracing::{debug, info, trace};
|
||||
|
||||
// convenience aliases
|
||||
type PacketsMap = HashMap<String, u64>;
|
||||
@@ -136,7 +136,7 @@ impl UpdateHandler {
|
||||
}
|
||||
|
||||
async fn run(&mut self) {
|
||||
log::trace!("Starting UpdateHandler");
|
||||
trace!("Starting UpdateHandler");
|
||||
while !self.shutdown.is_shutdown() {
|
||||
tokio::select! {
|
||||
Some(packet_data) = self.update_receiver.next() => {
|
||||
@@ -151,13 +151,13 @@ impl UpdateHandler {
|
||||
}
|
||||
}
|
||||
_ = self.shutdown.recv() => {
|
||||
log::trace!("UpdateHandler: Received shutdown");
|
||||
trace!("UpdateHandler: Received shutdown");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log::trace!("UpdateHandler: Exiting");
|
||||
trace!("UpdateHandler: Exiting");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,11 +230,11 @@ impl StatsUpdater {
|
||||
tokio::select! {
|
||||
_ = tokio::time::sleep(self.updating_delay) => self.update_stats().await,
|
||||
_ = self.shutdown.recv() => {
|
||||
log::trace!("StatsUpdater: Received shutdown");
|
||||
trace!("StatsUpdater: Received shutdown");
|
||||
}
|
||||
}
|
||||
}
|
||||
log::trace!("StatsUpdater: Exiting");
|
||||
trace!("StatsUpdater: Exiting");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,16 +319,16 @@ impl PacketStatsConsoleLogger {
|
||||
}
|
||||
|
||||
async fn run(&mut self) {
|
||||
log::trace!("Starting PacketStatsConsoleLogger");
|
||||
trace!("Starting PacketStatsConsoleLogger");
|
||||
while !self.shutdown.is_shutdown() {
|
||||
tokio::select! {
|
||||
_ = tokio::time::sleep(self.logging_delay) => self.log_running_stats().await,
|
||||
_ = self.shutdown.recv() => {
|
||||
log::trace!("PacketStatsConsoleLogger: Received shutdown");
|
||||
trace!("PacketStatsConsoleLogger: Received shutdown");
|
||||
}
|
||||
};
|
||||
}
|
||||
log::trace!("PacketStatsConsoleLogger: Exiting");
|
||||
trace!("PacketStatsConsoleLogger: Exiting");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use super::TaskClient;
|
||||
use crate::node::node_statistics::UpdateSender;
|
||||
use futures::channel::mpsc;
|
||||
use futures::StreamExt;
|
||||
@@ -8,8 +9,7 @@ use nym_nonexhaustive_delayqueue::{Expired, NonExhaustiveDelayQueue};
|
||||
use nym_sphinx::forwarding::packet::MixPacket;
|
||||
use std::io;
|
||||
use tokio::time::Instant;
|
||||
|
||||
use super::TaskClient;
|
||||
use tracing::trace;
|
||||
|
||||
// Delay + MixPacket vs Instant + MixPacket
|
||||
|
||||
@@ -105,7 +105,7 @@ where
|
||||
}
|
||||
|
||||
pub(crate) async fn run(&mut self) {
|
||||
log::trace!("Starting DelayForwarder");
|
||||
trace!("Starting DelayForwarder");
|
||||
loop {
|
||||
tokio::select! {
|
||||
delayed = self.delay_queue.next() => {
|
||||
@@ -117,12 +117,12 @@ where
|
||||
self.handle_new_packet(new_packet.unwrap())
|
||||
}
|
||||
_ = self.shutdown.recv() => {
|
||||
log::trace!("DelayForwarder: Received shutdown");
|
||||
trace!("DelayForwarder: Received shutdown");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
log::trace!("DelayForwarder: Exiting");
|
||||
trace!("DelayForwarder: Exiting");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user