From 55e891ae517d0481a756456e63b641b3dbf59e63 Mon Sep 17 00:00:00 2001 From: durch Date: Thu, 6 Nov 2025 11:51:24 +0100 Subject: [PATCH] Add LP registration testing to nym-gateway-probe Implement LP (Lewes Protocol) registration flow testing in nym-gateway-probe to validate gateway LP registration capabilities alongside existing WireGuard and mixnet tests. Changes: - Add LpProbeResults struct to track LP registration test results (can_connect, can_handshake, can_register, error) - Add lp_registration_probe() function that tests full registration flow: * TCP connection to LP listener (port 41264) * Noise protocol handshake with PSK derivation * Registration request with bandwidth credentials * Registration response validation - Integrate LP test into main probe flow - runs automatically if gateway has LP address (derived from gateway IP + port 41264) - Export LpRegistrationClient from nym-registration-client for probe use - Add LP address field to TestedNodeDetails The probe tests only successful registration without additional traffic, keeping the implementation simple and focused. --- Cargo.lock | 3 + nym-gateway-probe/Cargo.toml | 3 + nym-gateway-probe/src/lib.rs | 166 +++++++++++++++++++++++++++++ nym-gateway-probe/src/nodes.rs | 5 + nym-gateway-probe/src/types.rs | 10 ++ nym-registration-client/src/lib.rs | 4 +- 6 files changed, 189 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bfed786a2e..da8f2aac92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5935,7 +5935,10 @@ dependencies = [ "nym-http-api-client-macro", "nym-ip-packet-client", "nym-ip-packet-requests", + "nym-lp", "nym-node-status-client", + "nym-registration-client", + "nym-registration-common", "nym-sdk", "nym-topology", "nym-validator-client", diff --git a/nym-gateway-probe/Cargo.toml b/nym-gateway-probe/Cargo.toml index 5c91e35078..4ad46d3d55 100644 --- a/nym-gateway-probe/Cargo.toml +++ b/nym-gateway-probe/Cargo.toml @@ -59,6 +59,9 @@ nym-credentials = { path = "../common/credentials" } nym-http-api-client-macro = { path = "../common/http-api-client-macro" } nym-http-api-client = { path = "../common/http-api-client" } nym-node-status-client = { path = "../nym-node-status-api/nym-node-status-client" } +nym-registration-client = { path = "../nym-registration-client" } +nym-lp = { path = "../common/nym-lp" } +nym-registration-common = { path = "../common/registration" } # TEMP: REMOVE BEFORE PR nym-topology = { path = "../common/topology" } diff --git a/nym-gateway-probe/src/lib.rs b/nym-gateway-probe/src/lib.rs index 76b8c5c567..057d877324 100644 --- a/nym-gateway-probe/src/lib.rs +++ b/nym-gateway-probe/src/lib.rs @@ -146,6 +146,7 @@ pub struct TestedNodeDetails { authenticator_address: Option, authenticator_version: AuthenticatorVersion, ip_address: Option, + lp_address: Option, } pub struct Probe { @@ -361,6 +362,7 @@ impl Probe { }, as_exit: None, wg: None, + lp: None, }, }); } @@ -383,6 +385,7 @@ impl Probe { }, as_exit: None, wg: None, + lp: None, }), mixnet_client, ) @@ -449,9 +452,42 @@ impl Probe { WgProbeResults::default() }; + // Test LP registration if node has LP address + let lp_outcome = if let (Some(lp_address), Some(ip_address)) = + (node_info.lp_address, node_info.ip_address) + { + info!("Node has LP address, testing LP registration..."); + + // Prepare bandwidth credential for LP registration + let config = nym_validator_client::nyxd::Config::try_from_nym_network_details( + &NymNetworkDetails::new_from_env(), + )?; + let client = + nym_validator_client::nyxd::NyxdClient::connect(config, nyxd_url.as_str())?; + let bw_controller = nym_bandwidth_controller::BandwidthController::new( + storage.credential_store().clone(), + client, + ); + + let outcome = lp_registration_probe( + node_info.identity, + lp_address, + ip_address, + &bw_controller, + ) + .await + .unwrap_or_default(); + + Some(outcome) + } else { + info!("Node does not have LP address, skipping LP registration test"); + None + }; + // Disconnect the mixnet client gracefully outcome.map(|mut outcome| { outcome.wg = Some(wg_outcome); + outcome.lp = lp_outcome; ProbeResult { node: node_info.identity.to_string(), used_entry: mixnet_entry_gateway_id.to_string(), @@ -640,6 +676,135 @@ async fn wg_probe( Ok(wg_outcome) } +async fn lp_registration_probe( + gateway_identity: NodeIdentity, + gateway_lp_address: std::net::SocketAddr, + gateway_ip: IpAddr, + bandwidth_controller: &nym_bandwidth_controller::BandwidthController< + nym_validator_client::nyxd::NyxdClient, + St, + >, +) -> anyhow::Result +where + St: nym_sdk::mixnet::CredentialStorage + Clone + Send + Sync + 'static, + ::StorageError: Send + Sync, +{ + use nym_lp::keypair::{Keypair as LpKeypair, PublicKey as LpPublicKey}; + use nym_registration_client::LpRegistrationClient; + + info!("Starting LP registration probe for gateway at {}", gateway_lp_address); + + let mut lp_outcome = types::LpProbeResults::default(); + + // Generate LP keypair for this connection + let client_lp_keypair = std::sync::Arc::new(LpKeypair::default()); + + // Derive gateway LP public key from gateway identity (as done in registration-client) + let gateway_lp_key = match LpPublicKey::from_bytes(&gateway_identity.to_bytes()) { + Ok(key) => key, + Err(e) => { + let error_msg = format!("Failed to derive gateway LP key: {}", e); + error!("{}", error_msg); + lp_outcome.error = Some(error_msg); + return Ok(lp_outcome); + } + }; + + // Create LP registration client + let mut client = LpRegistrationClient::new_with_default_psk( + client_lp_keypair, + gateway_lp_key, + gateway_lp_address, + gateway_ip, + ); + + // Step 1: Connect to gateway + info!("Connecting to LP listener at {}...", gateway_lp_address); + match client.connect().await { + Ok(_) => { + info!("Successfully connected to LP listener"); + lp_outcome.can_connect = true; + } + Err(e) => { + let error_msg = format!("Failed to connect to LP listener: {}", e); + error!("{}", error_msg); + lp_outcome.error = Some(error_msg); + return Ok(lp_outcome); + } + } + + // Step 2: Perform handshake + info!("Performing LP handshake..."); + match client.perform_handshake().await { + Ok(_) => { + info!("LP handshake completed successfully"); + lp_outcome.can_handshake = true; + } + Err(e) => { + let error_msg = format!("LP handshake failed: {}", e); + error!("{}", error_msg); + lp_outcome.error = Some(error_msg); + return Ok(lp_outcome); + } + } + + // Step 3: Send registration request + info!("Sending LP registration request..."); + + // Generate WireGuard keypair for dVPN registration + let mut rng = rand::thread_rng(); + let wg_keypair = nym_crypto::asymmetric::x25519::KeyPair::new(&mut rng); + + // Convert gateway identity to ed25519 public key + let gateway_ed25519_pubkey = match nym_crypto::asymmetric::ed25519::PublicKey::from_bytes(&gateway_identity.to_bytes()) { + Ok(key) => key, + Err(e) => { + let error_msg = format!("Failed to convert gateway identity: {}", e); + error!("{}", error_msg); + lp_outcome.error = Some(error_msg); + return Ok(lp_outcome); + } + }; + + match client.send_registration_request( + &wg_keypair, + &gateway_ed25519_pubkey, + bandwidth_controller, + TicketType::V1WireguardEntry, + ).await { + Ok(_) => { + info!("LP registration request sent successfully"); + } + Err(e) => { + let error_msg = format!("Failed to send LP registration request: {}", e); + error!("{}", error_msg); + lp_outcome.error = Some(error_msg); + return Ok(lp_outcome); + } + } + + // Step 4: Receive registration response + info!("Waiting for LP registration response..."); + match client.receive_registration_response().await { + Ok(gateway_data) => { + info!("LP registration successful! Received gateway data:"); + info!(" - Gateway public key: {:?}", gateway_data.public_key); + info!(" - Private IPv4: {}", gateway_data.private_ipv4); + info!(" - Private IPv6: {}", gateway_data.private_ipv6); + info!(" - Endpoint: {}", gateway_data.endpoint); + lp_outcome.can_register = true; + } + Err(e) => { + let error_msg = format!("Failed to receive LP registration response: {}", e); + error!("{}", error_msg); + lp_outcome.error = Some(error_msg); + return Ok(lp_outcome); + } + } + + Ok(lp_outcome) +} + fn mixnet_debug_config( min_gateway_performance: Option, ignore_egress_epoch_role: bool, @@ -686,6 +851,7 @@ async fn do_ping( as_entry: entry, as_exit: exit, wg: None, + lp: None, }), mixnet_client, ) diff --git a/nym-gateway-probe/src/nodes.rs b/nym-gateway-probe/src/nodes.rs index 0726bb3b7d..5b868bb7df 100644 --- a/nym-gateway-probe/src/nodes.rs +++ b/nym-gateway-probe/src/nodes.rs @@ -118,12 +118,17 @@ impl DirectoryNode { .first() .copied(); + // Derive LP address from gateway IP + default LP control port (41264) + // TODO: Update this when LP address is exposed in node description API + let lp_address = ip_address.map(|ip| std::net::SocketAddr::new(ip, 41264)); + Ok(TestedNodeDetails { identity: self.identity(), exit_router_address, authenticator_address, authenticator_version, ip_address, + lp_address, }) } } diff --git a/nym-gateway-probe/src/types.rs b/nym-gateway-probe/src/types.rs index 17f02b40f8..ec887d61fb 100644 --- a/nym-gateway-probe/src/types.rs +++ b/nym-gateway-probe/src/types.rs @@ -13,6 +13,7 @@ pub struct ProbeOutcome { pub as_entry: Entry, pub as_exit: Option, pub wg: Option, + pub lp: Option, } #[derive(Debug, Clone, Serialize, Deserialize, Default)] @@ -44,6 +45,15 @@ pub struct WgProbeResults { pub download_error_v6: String, } +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[serde(rename = "lp")] +pub struct LpProbeResults { + pub can_connect: bool, + pub can_handshake: bool, + pub can_register: bool, + pub error: Option, +} + #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] #[allow(clippy::enum_variant_names)] diff --git a/nym-registration-client/src/lib.rs b/nym-registration-client/src/lib.rs index 0f4c5fec1a..328d0f6a6a 100644 --- a/nym-registration-client/src/lib.rs +++ b/nym-registration-client/src/lib.rs @@ -13,7 +13,7 @@ use nym_sdk::mixnet::{EventReceiver, MixnetClient, Recipient}; use std::sync::Arc; use crate::config::RegistrationClientConfig; -use crate::lp_client::{LpClientError, LpRegistrationClient, LpTransport}; +use crate::lp_client::{LpClientError, LpTransport}; mod builder; mod config; @@ -28,7 +28,7 @@ pub use builder::config::{ }; pub use config::RegistrationMode; pub use error::RegistrationClientError; -pub use lp_client::LpConfig; +pub use lp_client::{LpConfig, LpRegistrationClient}; pub use types::{ LpRegistrationResult, MixnetRegistrationResult, RegistrationResult, WireguardRegistrationResult, };