mix throughput tester (#5661)

* wip: sending with single client

* tag packets to measure latency

* constantly logging rates

* concurrency

* adjusting some values

* write results to files upon completion
This commit is contained in:
Jędrzej Stuczyński
2025-03-31 15:57:24 +01:00
committed by GitHub
parent 89eea3100e
commit d062524d32
24 changed files with 1227 additions and 109 deletions
@@ -1,7 +1,6 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::node::mixnet::packet_forwarding::global::is_global_ip;
use crate::node::shared_network::RoutingFilter;
use futures::StreamExt;
use nym_mixnet_client::forwarder::{
@@ -13,38 +12,33 @@ use nym_nonexhaustive_delayqueue::{Expired, NonExhaustiveDelayQueue};
use nym_sphinx_forwarding::packet::MixPacket;
use nym_task::ShutdownToken;
use std::io;
use std::net::IpAddr;
use tokio::time::Instant;
use tracing::{debug, error, trace, warn};
pub(crate) mod global;
pub struct PacketForwarder<C> {
testnet: bool,
pub struct PacketForwarder<C, F> {
delay_queue: NonExhaustiveDelayQueue<MixPacket>,
mixnet_client: C,
metrics: NymNodeMetrics,
routing_filter: RoutingFilter,
routing_filter: F,
packet_sender: MixForwardingSender,
packet_receiver: MixForwardingReceiver,
shutdown: ShutdownToken,
}
impl<C> PacketForwarder<C> {
impl<C, F> PacketForwarder<C, F> {
pub fn new(
client: C,
testnet: bool,
routing_filter: RoutingFilter,
routing_filter: F,
metrics: NymNodeMetrics,
shutdown: ShutdownToken,
) -> Self {
let (packet_sender, packet_receiver) = mix_forwarding_channels();
PacketForwarder {
testnet,
delay_queue: NonExhaustiveDelayQueue::new(),
mixnet_client: client,
metrics,
@@ -59,22 +53,14 @@ impl<C> PacketForwarder<C> {
self.packet_sender.clone()
}
fn should_route(&mut self, ip_addr: IpAddr) -> bool {
// only allow non-global ips on testnets
if self.testnet && !is_global_ip(&ip_addr) {
return true;
}
self.routing_filter.attempt_resolve(ip_addr).should_route()
}
fn forward_packet(&mut self, packet: MixPacket)
where
C: SendWithoutResponse,
F: RoutingFilter,
{
let next_hop = packet.next_hop();
if !self.should_route(next_hop.as_ref().ip()) {
if !self.routing_filter.should_route(next_hop.as_ref().ip()) {
debug!("dropping packet as the egress address does not belong to any known node");
self.metrics
.mixnet
@@ -113,6 +99,7 @@ impl<C> PacketForwarder<C> {
fn handle_done_delaying(&mut self, packet: Expired<MixPacket>)
where
C: SendWithoutResponse,
F: RoutingFilter,
{
let delayed_packet = packet.into_inner();
self.forward_packet(delayed_packet);
@@ -121,6 +108,7 @@ impl<C> PacketForwarder<C> {
fn handle_new_packet(&mut self, new_packet: PacketToForward)
where
C: SendWithoutResponse,
F: RoutingFilter,
{
// in case of a zero delay packet, don't bother putting it in the delay queue,
// just forward it immediately
@@ -152,6 +140,7 @@ impl<C> PacketForwarder<C> {
pub async fn run(&mut self)
where
C: SendWithoutResponse,
F: RoutingFilter,
{
let mut processed = 0;
trace!("starting PacketForwarder");
+29 -5
View File
@@ -29,7 +29,7 @@ use crate::node::mixnet::packet_forwarding::PacketForwarder;
use crate::node::mixnet::shared::ProcessingConfig;
use crate::node::mixnet::SharedFinalHopData;
use crate::node::shared_network::{
CachedNetwork, CachedTopologyProvider, NetworkRefresher, RoutingFilter,
CachedNetwork, CachedTopologyProvider, NetworkRefresher, OpenFilter, RoutingFilter,
};
use nym_bin_common::bin_info;
use nym_crypto::asymmetric::{ed25519, x25519};
@@ -461,6 +461,10 @@ impl NymNode {
&self.config
}
pub(crate) fn shutdown_token<S: Into<String>>(&self, child_suffix: S) -> ShutdownToken {
self.shutdown_manager.clone_token(child_suffix)
}
pub(crate) fn with_accepted_operator_terms_and_conditions(
mut self,
accepted_operator_terms_and_conditions: bool,
@@ -528,12 +532,17 @@ impl NymNode {
self.x25519_sphinx_keys.public_key()
}
pub(crate) fn x25519_sphinx_keys(&self) -> Arc<x25519::KeyPair> {
self.x25519_sphinx_keys.clone()
}
pub(crate) fn x25519_noise_key(&self) -> &x25519::PublicKey {
self.x25519_noise_keys.public_key()
}
async fn build_network_refresher(&self) -> Result<NetworkRefresher, NymNodeError> {
NetworkRefresher::initialise_new(
self.config.debug.testnet,
self.user_agent(),
self.config.mixnet.nym_api_urls.clone(),
self.config.debug.topology_cache_ttl,
@@ -970,12 +979,15 @@ impl NymNode {
events_sender
}
pub(crate) fn start_mixnet_listener(
pub(crate) fn start_mixnet_listener<F>(
&self,
active_clients_store: &ActiveClientsStore,
routing_filter: RoutingFilter,
routing_filter: F,
shutdown: ShutdownToken,
) -> (MixForwardingSender, ActiveConnections) {
) -> (MixForwardingSender, ActiveConnections)
where
F: RoutingFilter + Send + Sync + 'static,
{
let processing_config = ProcessingConfig::new(&self.config);
// we're ALWAYS listening for mixnet packets, either for forward or final hops (or both)
@@ -1002,7 +1014,6 @@ impl NymNode {
let mut packet_forwarder = PacketForwarder::new(
mixnet_client,
self.config.debug.testnet,
routing_filter,
self.metrics.clone(),
shutdown.clone_with_suffix("mix-packet-forwarder"),
@@ -1028,6 +1039,19 @@ impl NymNode {
(mix_packet_sender, active_connections)
}
pub(crate) async fn run_minimal_mixnet_processing(self) -> Result<(), NymNodeError> {
self.start_mixnet_listener(
&ActiveClientsStore::new(),
OpenFilter,
self.shutdown_manager.clone_token("mixnet-traffic"),
);
self.shutdown_manager.close();
self.shutdown_manager.wait_for_shutdown_signal().await;
Ok(())
}
pub(crate) async fn run(mut self) -> Result<(), NymNodeError> {
info!("starting Nym Node {} with the following modes: mixnode: {}, entry: {}, exit: {}, wireguard: {}",
self.ed25519_identity_key(),
+36 -7
View File
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::error::NymNodeError;
use crate::node::mixnet::packet_forwarding::global::is_global_ip;
use arc_swap::ArcSwap;
use async_trait::async_trait;
use nym_gateway::node::UserAgent;
@@ -22,8 +23,34 @@ use tracing::log::error;
use tracing::{debug, trace, warn};
use url::Url;
pub(crate) trait RoutingFilter {
fn should_route(&self, ip: IpAddr) -> bool;
}
#[derive(Debug, Copy, Clone, Default)]
pub(crate) struct OpenFilter;
impl RoutingFilter for OpenFilter {
fn should_route(&self, _: IpAddr) -> bool {
true
}
}
impl RoutingFilter for NetworkRoutingFilter {
fn should_route(&self, ip: IpAddr) -> bool {
// only allow non-global ips on testnets
if self.testnet_mode && !is_global_ip(&ip) {
return true;
}
self.attempt_resolve(ip).should_route()
}
}
#[derive(Clone)]
pub(crate) struct RoutingFilter {
pub(crate) struct NetworkRoutingFilter {
testnet_mode: bool,
resolved: KnownNodes,
// while this is technically behind a lock, it should not be called too often as once resolved it will
@@ -31,9 +58,10 @@ pub(crate) struct RoutingFilter {
pending: UnknownNodes,
}
impl RoutingFilter {
fn new_empty() -> Self {
RoutingFilter {
impl NetworkRoutingFilter {
fn new_empty(testnet_mode: bool) -> Self {
NetworkRoutingFilter {
testnet_mode,
resolved: Default::default(),
pending: Default::default(),
}
@@ -254,11 +282,12 @@ pub struct NetworkRefresher {
shutdown_token: ShutdownToken,
network: CachedNetwork,
routing_filter: RoutingFilter,
routing_filter: NetworkRoutingFilter,
}
impl NetworkRefresher {
pub(crate) async fn initialise_new(
testnet: bool,
user_agent: UserAgent,
nym_api_urls: Vec<Url>,
full_refresh_interval: Duration,
@@ -280,7 +309,7 @@ impl NetworkRefresher {
pending_check_interval,
shutdown_token,
network: CachedNetwork::new_empty(),
routing_filter: RoutingFilter::new_empty(),
routing_filter: NetworkRoutingFilter::new_empty(testnet),
};
this.obtain_initial_network().await?;
@@ -403,7 +432,7 @@ impl NetworkRefresher {
.map_err(|source| NymNodeError::InitialTopologyQueryFailure { source })
}
pub(crate) fn routing_filter(&self) -> RoutingFilter {
pub(crate) fn routing_filter(&self) -> NetworkRoutingFilter {
self.routing_filter.clone()
}