Compare commits

..

3 Commits

Author SHA1 Message Date
benedettadavico ecd678a849 ... 2026-01-26 18:52:36 +01:00
benedettadavico 6c6e473607 aaaa 2026-01-26 18:24:23 +01:00
benedettadavico d52d728ab8 loggggggging 2026-01-26 15:22:32 +01:00
10 changed files with 294 additions and 224 deletions
-82
View File
@@ -4,88 +4,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [2026.2-oscypek] (2026-01-27)
- bugfix: downgrade gateway protocol to clients proposed version ([#6377])
- bugfix: ack fix ([#6364])
- Cherry pick/api urls oscypek ([#6348])
- Update nix to v0.30.1 ([#6316])
- Deriving Serialize for GatewayData ([#6314])
- chore: remove repetitive words in comment ([#6313])
- [bugfix] Sqlite transaction escalation was causing errors ([#6299])
- DNS static table pre-resolve ([#6297])
- Add Copy+Clone to nym_api_provider::Config ([#6296])
- [chore] clippy fixes and use fixed rust version from REQUIRED_RUSTC_VERSION ([#6295])
- build(deps): bump SonarSource/sonarqube-scan-action from 6 to 7 ([#6294])
- build(deps): bump mikefarah/yq from 4.49.2 to 4.50.1 ([#6293])
- build(deps): bump actions/upload-artifact from 5 to 6 ([#6292])
- build(deps): bump actions/download-artifact from 6 to 7 ([#6291])
- build(deps): bump js-yaml from 3.14.1 to 3.14.2 in /documentation/docs ([#6290])
- build(deps): bump next from 15.4.9 to 15.4.10 in /nym-node-status-api/nym-node-status-ui ([#6289])
- build(deps): bump next from 14.2.33 to 14.2.35 ([#6288])
- LP Registration + Telescoping + Gateway Probe Localnet Mode ([#6286])
- build(deps): bump next from 15.5.7 to 15.5.9 in /documentation/docs ([#6285])
- build(deps): bump next from 15.4.7 to 15.4.9 in /nym-node-status-api/nym-node-status-ui ([#6284])
- Minor DNS improvements ([#6283])
- HTTP client without default features ([#6281])
- DNS: reduce number of attempts ([#6278])
- [bugfix] use proper mixing delay instead of poisson delay in cover traffic ([#6269])
- build(deps): bump node-forge from 1.3.1 to 1.3.3 in /wasm/zknym-lib/internal-dev ([#6261])
- build(deps-dev): bump node-forge from 1.3.1 to 1.3.3 in /wasm/mix-fetch/internal-dev ([#6260])
- build(deps-dev): bump node-forge from 1.3.1 to 1.3.2 in /wasm/client/internal-dev ([#6251])
- build(deps): bump node-forge from 1.3.1 to 1.3.2 in /nym-credential-proxy/vpn-api-lib-wasm/internal-dev ([#6250])
- [Feature] Fallback gateway listener and remove legacy key support ([#6249])
- build(deps-dev): bump node-forge from 1.3.0 to 1.3.2 in /clients/native/examples/js-examples/websocket ([#6248])
- build(deps): bump node-forge from 1.3.1 to 1.3.2 ([#6246])
- build(deps): bump pnpm/action-setup from 4.1.0 to 4.2.0 ([#6245])
- build(deps): bump actions/download-artifact from 5 to 6 ([#6244])
- build(deps): bump actions/checkout from 4 to 6 ([#6243])
- build(deps): bump mikefarah/yq from 4.48.1 to 4.49.2 ([#6242])
- build(deps): bump actions/upload-artifact from 4 to 5 ([#6241])
- fix: fix assertion ([#6238])
- Initial changes to support extra configurable parameters and to print… ([#6237])
- Data Observatory ([#6172])
[#6377]: https://github.com/nymtech/nym/pull/6377
[#6364]: https://github.com/nymtech/nym/pull/6364
[#6348]: https://github.com/nymtech/nym/pull/6348
[#6316]: https://github.com/nymtech/nym/pull/6316
[#6314]: https://github.com/nymtech/nym/pull/6314
[#6313]: https://github.com/nymtech/nym/pull/6313
[#6299]: https://github.com/nymtech/nym/pull/6299
[#6297]: https://github.com/nymtech/nym/pull/6297
[#6296]: https://github.com/nymtech/nym/pull/6296
[#6295]: https://github.com/nymtech/nym/pull/6295
[#6294]: https://github.com/nymtech/nym/pull/6294
[#6293]: https://github.com/nymtech/nym/pull/6293
[#6292]: https://github.com/nymtech/nym/pull/6292
[#6291]: https://github.com/nymtech/nym/pull/6291
[#6290]: https://github.com/nymtech/nym/pull/6290
[#6289]: https://github.com/nymtech/nym/pull/6289
[#6288]: https://github.com/nymtech/nym/pull/6288
[#6286]: https://github.com/nymtech/nym/pull/6286
[#6285]: https://github.com/nymtech/nym/pull/6285
[#6284]: https://github.com/nymtech/nym/pull/6284
[#6283]: https://github.com/nymtech/nym/pull/6283
[#6281]: https://github.com/nymtech/nym/pull/6281
[#6278]: https://github.com/nymtech/nym/pull/6278
[#6269]: https://github.com/nymtech/nym/pull/6269
[#6261]: https://github.com/nymtech/nym/pull/6261
[#6260]: https://github.com/nymtech/nym/pull/6260
[#6251]: https://github.com/nymtech/nym/pull/6251
[#6250]: https://github.com/nymtech/nym/pull/6250
[#6249]: https://github.com/nymtech/nym/pull/6249
[#6248]: https://github.com/nymtech/nym/pull/6248
[#6246]: https://github.com/nymtech/nym/pull/6246
[#6245]: https://github.com/nymtech/nym/pull/6245
[#6244]: https://github.com/nymtech/nym/pull/6244
[#6243]: https://github.com/nymtech/nym/pull/6243
[#6242]: https://github.com/nymtech/nym/pull/6242
[#6241]: https://github.com/nymtech/nym/pull/6241
[#6238]: https://github.com/nymtech/nym/pull/6238
[#6237]: https://github.com/nymtech/nym/pull/6237
[#6172]: https://github.com/nymtech/nym/pull/6172
## [2026.1-niolo] (2026-01-13)
- bugfix: mozzarella -> niolo config migration ([#6259])
@@ -335,9 +335,36 @@ fn process_final_hop(
packet_type: PacketType,
key_rotation: SphinxKeyRotation,
) -> Result<MixProcessingResultData, PacketProcessingError> {
let payload_size = payload.len();
debug!(
"process_final_hop: payload_size={}, packet_size={:?}, packet_type={:?}, destination={}",
payload_size, packet_size, packet_type, destination
);
let (forward_ack, message) =
split_into_ack_and_message(payload, packet_size, packet_type, key_rotation)?;
let message_size = message.len();
let ack_present = forward_ack.is_some();
debug!(
"process_final_hop: after split - message_size={}, ack_present={}, payload_size={}",
message_size, ack_present, payload_size
);
// Verify expected message size
if let PacketSize::RegularPacket = packet_size {
use nym_sphinx_addressing::nodes::MAX_NODE_ADDRESS_UNPADDED_LEN;
let sphinx_ack_overhead = PacketSize::AckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN;
let expected_message_size = packet_size.plaintext_size() - sphinx_ack_overhead;
if message_size != expected_message_size {
use tracing::warn;
warn!(
"process_final_hop: MESSAGE SIZE MISMATCH! message_size={}, expected={}, payload_size={}, ack_present={}",
message_size, expected_message_size, payload_size, ack_present
);
}
}
Ok(MixProcessingResultData::FinalHop {
final_hop_data: ProcessedFinalHop {
destination,
@@ -278,6 +278,31 @@ impl<R, S> FreshHandler<R, S> {
where
S: AsyncRead + AsyncWrite + Unpin,
{
// Log message sizes before sending to client
for (idx, packet) in packets.iter().enumerate() {
use nym_sphinx::addressing::nodes::MAX_NODE_ADDRESS_UNPADDED_LEN;
use nym_sphinx::params::PacketSize;
let sphinx_ack_overhead = PacketSize::AckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN;
let expected_size = PacketSize::RegularPacket.plaintext_size() - sphinx_ack_overhead;
debug!(
"push_packets_to_client: packet[{}] size={}, expected_regular_size={}, ack_overhead={}",
idx,
packet.len(),
expected_size,
sphinx_ack_overhead
);
if packet.len() == PacketSize::RegularPacket.plaintext_size() {
warn!(
"push_packets_to_client: WARNING - packet[{}] has full plaintext size ({}), expected {} (after ACK removal)",
idx,
packet.len(),
expected_size
);
}
}
// note: into_ws_message encrypts the requests and adds a MAC on it. Perhaps it should
// be more explicit in the naming?
let messages: Vec<Result<Message, WsError>> = packets
@@ -496,6 +521,7 @@ impl<R, S> FreshHandler<R, S> {
error!("{incompatible_err}");
Err(incompatible_err)
} else {
// Return the client's version for backwards compatibility.. temporary solution.
debug!("the client is using exactly the same (or older) protocol version as we are. We're good to continue!");
Ok(client_protocol_version)
}
@@ -717,9 +743,9 @@ impl<R, S> FreshHandler<R, S> {
}
pub(crate) fn handle_supported_protocol_request(&self) -> ServerResponse {
debug!("returning gateway protocol version");
use nym_gateway_requests::EMBEDDED_KEY_ROTATION_INFO_VERSION;
ServerResponse::SupportedProtocol {
version: CURRENT_PROTOCOL_VERSION,
version: EMBEDDED_KEY_ROTATION_INFO_VERSION,
}
}
@@ -846,11 +846,23 @@ impl MixnetListener {
| AuthenticatorVersion::V1
| AuthenticatorVersion::V2
| AuthenticatorVersion::V3
| AuthenticatorVersion::V4
| AuthenticatorVersion::V5 => {
// pre v6 this message hasn't existed
| AuthenticatorVersion::V4 => {
// pre v5 this message hasn't existed
return Err(AuthenticatorError::UnknownVersion);
}
AuthenticatorVersion::V5 => {
// V5 clients shouldn't send this message, but for backward compatibility
// with V5 clients connecting to V6 gateways, we allow it and return
// a V6-style response (V5 clients won't understand it, but at least we don't error)
// This enables V5 clients from mainnet (release/2026.1-kiwi) to work with
// V6 gateways (release/2026.2-oscypek) during the transition period
v6::response::AuthenticatorResponse::new_upgrade_mode_check(
request_id,
self.upgrade_mode_enabled(),
)
.to_bytes()
.map_err(AuthenticatorError::response_serialisation)?
}
AuthenticatorVersion::V6 => {
v6::response::AuthenticatorResponse::new_upgrade_mode_check(
request_id,
+23
View File
@@ -151,6 +151,29 @@ impl ConnectionHandler {
let client = final_hop_data.destination;
let message = final_hop_data.message;
let message_size = message.len();
// Log message size for debugging
use nym_sphinx_addressing::nodes::MAX_NODE_ADDRESS_UNPADDED_LEN;
use nym_sphinx_params::PacketSize;
let sphinx_ack_overhead = PacketSize::AckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN;
let expected_size = PacketSize::RegularPacket.plaintext_size() - sphinx_ack_overhead;
debug!(
"handle_final_hop: client={}, message_size={}, expected_regular_size={}, ack_overhead={}, forward_ack_present={}",
client,
message_size,
expected_size,
sphinx_ack_overhead,
final_hop_data.forward_ack.is_some()
);
if message_size == PacketSize::RegularPacket.plaintext_size() {
warn!(
"handle_final_hop: WARNING - message has full plaintext size ({}), expected {} (after ACK removal)",
message_size, expected_size
);
}
// if possible attempt to push message directly to the client
match self.shared.try_push_message_to_client(client, message) {
@@ -75,7 +75,9 @@ impl BuilderConfig {
// Mixnet mode uses 5-hop configuration
RegistrationMode::Mixnet => mixnet_debug_config(&self.mixnet_client_config),
// Wireguard and LP both use 2-hop configuration
RegistrationMode::Wireguard => two_hop_debug_config(&self.mixnet_client_config),
RegistrationMode::Wireguard | RegistrationMode::Lp => {
two_hop_debug_config(&self.mixnet_client_config)
}
}
}
@@ -119,7 +121,7 @@ impl BuilderConfig {
let debug_config = self.mixnet_client_debug_config();
let remember_me = match self.mode {
RegistrationMode::Mixnet => RememberMe::new_mixnet(),
RegistrationMode::Wireguard => RememberMe::new_vpn(),
RegistrationMode::Wireguard | RegistrationMode::Lp => RememberMe::new_vpn(),
};
let identity = self.entry_node.node.identity.to_string();
+15 -1
View File
@@ -8,8 +8,22 @@ use crate::builder::config::NymNodeWithKeys;
pub enum RegistrationMode {
/// 5-hop mixnet with IPR (IP Packet Router)
Mixnet,
/// 2-hop WireGuard
/// 2-hop WireGuard with authenticator
Wireguard,
/// 2-hop WireGuard with LP (Lewes Protocol)
Lp,
}
impl RegistrationMode {
/// Legacy method for backward compatibility
#[deprecated(note = "use explicit enum variant instead")]
pub fn legacy_two_hop(use_two_hop: bool) -> RegistrationMode {
if use_two_hop {
Self::Wireguard
} else {
Self::Mixnet
}
}
}
pub struct RegistrationClientConfig {
+19
View File
@@ -74,6 +74,25 @@ pub enum RegistrationClientError {
#[source]
source: Box<nym_authenticator_client::AuthenticationClientError>,
},
#[error("LP registration not possible for gateway {node_id}: no LP address available")]
LpRegistrationNotPossible { node_id: String },
#[error("failed to register LP with entry gateway {gateway_id} at {lp_address}: {source}")]
EntryGatewayRegisterLp {
gateway_id: String,
lp_address: std::net::SocketAddr,
#[source]
source: Box<crate::lp_client::LpClientError>,
},
#[error("failed to register LP with exit gateway {gateway_id} at {lp_address}: {source}")]
ExitGatewayRegisterLp {
gateway_id: String,
lp_address: std::net::SocketAddr,
#[source]
source: Box<crate::lp_client::LpClientError>,
},
}
impl RegistrationClientError {
+162 -134
View File
@@ -1,15 +1,17 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use config::RegistrationClientConfig;
use nym_authenticator_client::AuthClientMixnetListenerHandle;
use tokio_util::sync::CancellationToken;
use crate::config::RegistrationClientConfig;
use nym_authenticator_client::{AuthClientMixnetListener, AuthenticatorClient};
use nym_bandwidth_controller::BandwidthTicketProvider;
use nym_credentials_interface::TicketType;
use nym_ip_packet_client::IprClientConnect;
use nym_registration_common::AssignedAddresses;
use nym_sdk::mixnet::{EventReceiver, MixnetClient, Recipient};
use tokio_util::sync::CancellationToken;
use std::sync::Arc;
use tokio::net::TcpStream;
mod builder;
mod config;
@@ -38,66 +40,22 @@ pub struct RegistrationClient {
event_rx: EventReceiver,
}
enum MixnetClientHandle {
Authenticator(AuthClientMixnetListenerHandle),
Sdk(Box<MixnetClient>),
}
impl MixnetClientHandle {
async fn stop(self) {
match self {
Self::Authenticator(handle) => handle.stop().await,
Self::Sdk(handle) => handle.disconnect().await,
}
}
}
// Bundle of an actual error and the underlying mixnet client so it can be shutdown correctly if needed
struct RegistrationError {
mixnet_client_handle: MixnetClientHandle,
source: crate::RegistrationClientError,
}
impl RegistrationClient {
async fn register_mix_exit(self) -> Result<RegistrationResult, RegistrationError> {
async fn register_mix_exit(self) -> Result<RegistrationResult, RegistrationClientError> {
let entry_mixnet_gateway_ip = self.config.entry.node.ip_address;
let exit_mixnet_gateway_ip = self.config.exit.node.ip_address;
let Some(ipr_address) = self.config.exit.node.ipr_address else {
return Err(RegistrationError {
mixnet_client_handle: MixnetClientHandle::Sdk(Box::new(self.mixnet_client)),
source: RegistrationClientError::NoIpPacketRouterAddress {
node_id: self.config.exit.node.identity.to_base58_string(),
},
});
};
let mut ipr_client =
IprClientConnect::new(self.mixnet_client, self.cancel_token.child_token());
let interface_addresses = match self
.cancel_token
.run_until_cancelled(ipr_client.connect(ipr_address))
let ipr_address = self.config.exit.node.ipr_address.ok_or(
RegistrationClientError::NoIpPacketRouterAddress {
node_id: self.config.exit.node.identity.to_base58_string(),
},
)?;
let mut ipr_client = IprClientConnect::new(self.mixnet_client, self.cancel_token.clone());
let interface_addresses = ipr_client
.connect(ipr_address)
.await
{
Some(Ok(addr)) => addr,
Some(Err(e)) => {
return Err(RegistrationError {
mixnet_client_handle: MixnetClientHandle::Sdk(Box::new(
ipr_client.into_mixnet_client(),
)),
source: RegistrationClientError::ConnectToIpPacketRouter(e),
});
}
None => {
return Err(RegistrationError {
mixnet_client_handle: MixnetClientHandle::Sdk(Box::new(
ipr_client.into_mixnet_client(),
)),
source: RegistrationClientError::Cancelled,
});
}
};
.map_err(RegistrationClientError::ConnectToIpPacketRouter)?;
Ok(RegistrationResult::Mixnet(Box::new(
MixnetRegistrationResult {
@@ -114,24 +72,18 @@ impl RegistrationClient {
)))
}
async fn register_wg(self) -> Result<RegistrationResult, RegistrationError> {
let Some(entry_auth_address) = self.config.entry.node.authenticator_address else {
return Err(RegistrationError {
mixnet_client_handle: MixnetClientHandle::Sdk(Box::new(self.mixnet_client)),
source: RegistrationClientError::AuthenticationNotPossible {
node_id: self.config.entry.node.identity.to_base58_string(),
},
});
};
async fn register_wg(self) -> Result<RegistrationResult, RegistrationClientError> {
let entry_auth_address = self.config.entry.node.authenticator_address.ok_or(
RegistrationClientError::AuthenticationNotPossible {
node_id: self.config.entry.node.identity.to_base58_string(),
},
)?;
let Some(exit_auth_address) = self.config.exit.node.authenticator_address else {
return Err(RegistrationError {
mixnet_client_handle: MixnetClientHandle::Sdk(Box::new(self.mixnet_client)),
source: RegistrationClientError::AuthenticationNotPossible {
node_id: self.config.exit.node.identity.to_base58_string(),
},
});
};
let exit_auth_address = self.config.exit.node.authenticator_address.ok_or(
RegistrationClientError::AuthenticationNotPossible {
node_id: self.config.exit.node.identity.to_base58_string(),
},
)?;
let entry_version = self.config.entry.node.version;
tracing::debug!("Entry gateway version: {entry_version}");
@@ -141,8 +93,7 @@ impl RegistrationClient {
// Start the auth client mixnet listener, which will listen for incoming messages from the
// mixnet and rebroadcast them to the auth clients.
let mixnet_listener =
AuthClientMixnetListener::new(self.mixnet_client, self.cancel_token.child_token())
.start();
AuthClientMixnetListener::new(self.mixnet_client, self.cancel_token.clone()).start();
let mut entry_auth_client = AuthenticatorClient::new(
mixnet_listener.subscribe(),
@@ -169,50 +120,24 @@ impl RegistrationClient {
let exit_fut = exit_auth_client
.register_wireguard(&*self.bandwidth_controller, TicketType::V1WireguardExit);
let (entry, exit) = match Box::pin(
self.cancel_token
.run_until_cancelled(async { tokio::join!(entry_fut, exit_fut) }),
)
.await
{
Some((entry, exit)) => (entry, exit),
None => {
return Err(RegistrationError {
mixnet_client_handle: MixnetClientHandle::Authenticator(mixnet_listener),
source: RegistrationClientError::Cancelled,
});
}
};
let (entry, exit) = Box::pin(async { tokio::join!(entry_fut, exit_fut) }).await;
let entry = match entry {
Ok(entry) => entry,
Err(source) => {
return Err(RegistrationError {
mixnet_client_handle: MixnetClientHandle::Authenticator(mixnet_listener),
source: RegistrationClientError::from_authenticator_error(
source,
self.config.entry.node.identity.to_base58_string(),
entry_auth_address,
true,
),
});
}
};
let exit = match exit {
Ok(exit) => exit,
Err(source) => {
return Err(RegistrationError {
mixnet_client_handle: MixnetClientHandle::Authenticator(mixnet_listener),
source: RegistrationClientError::from_authenticator_error(
source,
self.config.exit.node.identity.to_base58_string(),
exit_auth_address,
false,
),
});
}
};
let entry = entry.map_err(|source| {
RegistrationClientError::from_authenticator_error(
source,
self.config.entry.node.identity.to_base58_string(),
entry_auth_address,
true, // is entry
)
})?;
let exit = exit.map_err(|source| {
RegistrationClientError::from_authenticator_error(
source,
self.config.exit.node.identity.to_base58_string(),
exit_auth_address,
false, // is exit (not entry)
)
})?;
Ok(RegistrationResult::Wireguard(Box::new(
WireguardRegistrationResult {
@@ -226,22 +151,125 @@ impl RegistrationClient {
)))
}
pub async fn register(self) -> Result<RegistrationResult, RegistrationClientError> {
let registration_result = match self.config.mode {
RegistrationMode::Mixnet => self.register_mix_exit().await,
RegistrationMode::Wireguard => self.register_wg().await,
};
async fn register_lp(self) -> Result<RegistrationResult, RegistrationClientError> {
use crate::lp_client::{LpRegistrationClient, NestedLpSession};
// If we failed to register, shut down the mixnet client and wait for it to exit
match registration_result {
Ok(result) => Ok(result),
Err(error) => {
tracing::debug!("Registration failed");
tracing::debug!("Shutting down mixnet client");
error.mixnet_client_handle.stop().await;
tracing::debug!("Mixnet client stopped");
Err(error.source)
// Extract and validate LP addresses
let entry_lp_address = self.config.entry.node.lp_address.ok_or(
RegistrationClientError::LpRegistrationNotPossible {
node_id: self.config.entry.node.identity.to_base58_string(),
},
)?;
let exit_lp_address = self.config.exit.node.lp_address.ok_or(
RegistrationClientError::LpRegistrationNotPossible {
node_id: self.config.exit.node.identity.to_base58_string(),
},
)?;
tracing::debug!("Entry gateway LP address: {entry_lp_address}");
tracing::debug!("Exit gateway LP address: {exit_lp_address}");
// Generate fresh Ed25519 keypairs for LP registration
// These are ephemeral and used only for the LP handshake protocol
use nym_crypto::asymmetric::ed25519;
use rand::rngs::OsRng;
let entry_lp_keypair = Arc::new(ed25519::KeyPair::new(&mut OsRng));
let exit_lp_keypair = Arc::new(ed25519::KeyPair::new(&mut OsRng));
// STEP 1: Establish outer session with entry gateway
// This creates the LP session that will be used to forward packets to exit.
// Uses packet-per-connection model: each handshake packet on new TCP connection.
tracing::info!("Establishing outer session with entry gateway");
let mut entry_client = LpRegistrationClient::<TcpStream>::new_with_default_psk(
entry_lp_keypair.clone(),
self.config.entry.node.identity,
entry_lp_address,
self.config.entry.node.ip_address,
);
// Perform handshake with entry gateway (outer session now established)
entry_client.perform_handshake().await.map_err(|source| {
RegistrationClientError::EntryGatewayRegisterLp {
gateway_id: self.config.entry.node.identity.to_base58_string(),
lp_address: entry_lp_address,
source: Box::new(source),
}
}
})?;
tracing::info!("Outer session with entry gateway established");
// STEP 2: Use nested session to register with exit gateway via forwarding
// This hides the client's IP address from the exit gateway
tracing::info!("Registering with exit gateway via entry forwarding");
let mut nested_session = NestedLpSession::new(
self.config.exit.node.identity.to_bytes(),
exit_lp_address.to_string(),
exit_lp_keypair,
self.config.exit.node.identity,
);
// Perform handshake and registration with exit gateway (all via entry forwarding)
let exit_gateway_data = nested_session
.handshake_and_register::<TcpStream>(
&mut entry_client,
&self.config.exit.keys,
&self.config.exit.node.identity,
&*self.bandwidth_controller,
TicketType::V1WireguardExit,
self.config.exit.node.ip_address,
)
.await
.map_err(|source| RegistrationClientError::ExitGatewayRegisterLp {
gateway_id: self.config.exit.node.identity.to_base58_string(),
lp_address: exit_lp_address,
source: Box::new(source),
})?;
tracing::info!("Exit gateway registration completed via forwarding");
// STEP 3: Register with entry gateway (packet-per-connection)
tracing::info!("Registering with entry gateway");
let entry_gateway_data = entry_client
.register(
&self.config.entry.keys,
&self.config.entry.node.identity,
&*self.bandwidth_controller,
TicketType::V1WireguardEntry,
)
.await
.map_err(|source| RegistrationClientError::EntryGatewayRegisterLp {
gateway_id: self.config.entry.node.identity.to_base58_string(),
lp_address: entry_lp_address,
source: Box::new(source),
})?;
tracing::info!("Entry gateway registration successful");
tracing::info!("LP registration successful for both gateways");
// LP is registration-only (packet-per-connection model).
// All data flows through WireGuard after this point.
// Each LP packet used its own TCP connection which was closed after the exchange.
// Exit registration was completed via forwarding through entry gateway.
Ok(RegistrationResult::Lp(Box::new(LpRegistrationResult {
entry_gateway_data,
exit_gateway_data,
bw_controller: self.bandwidth_controller,
})))
}
pub async fn register(self) -> Result<RegistrationResult, RegistrationClientError> {
self.cancel_token
.clone()
.run_until_cancelled(async {
match self.config.mode {
RegistrationMode::Mixnet => self.register_mix_exit().await,
RegistrationMode::Wireguard => self.register_wg().await,
RegistrationMode::Lp => self.register_lp().await,
}
})
.await
.ok_or(RegistrationClientError::Cancelled)?
}
}
@@ -38,4 +38,5 @@ mod nested_session;
pub use client::LpRegistrationClient;
pub use config::LpConfig;
pub use error::LpClientError;
pub use nested_session::NestedLpSession;