Registration Client (#6059)
* removing wg-gateway-client * bandwidth_provider trait * authenticator client * adapt ip-packet-client * nit * registration_client * accomodate new shutdown and bugfix * sdk changes * cleanup and shutdown management * remove credential mode * error cleanup * better error handling * removing useless cover traffic delay * wasm client stuff * cfg unix * more wasm stuff * change authenticator client to not be blocked by mixnet client
This commit is contained in:
Generated
+39
-20
@@ -4923,11 +4923,16 @@ dependencies = [
|
||||
"bincode",
|
||||
"futures",
|
||||
"nym-authenticator-requests",
|
||||
"nym-bandwidth-controller",
|
||||
"nym-credentials-interface",
|
||||
"nym-crypto",
|
||||
"nym-pemstore",
|
||||
"nym-registration-common",
|
||||
"nym-sdk",
|
||||
"nym-service-provider-requests-common",
|
||||
"nym-validator-client",
|
||||
"nym-wireguard-types",
|
||||
"rand 0.8.5",
|
||||
"semver 1.0.26",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
@@ -4949,8 +4954,10 @@ dependencies = [
|
||||
"nym-sphinx",
|
||||
"nym-wireguard-types",
|
||||
"rand 0.8.5",
|
||||
"semver 1.0.26",
|
||||
"serde",
|
||||
"sha2 0.10.9",
|
||||
"strum_macros",
|
||||
"thiserror 2.0.12",
|
||||
"x25519-dalek",
|
||||
]
|
||||
@@ -4959,6 +4966,7 @@ dependencies = [
|
||||
name = "nym-bandwidth-controller"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bip39",
|
||||
"log",
|
||||
"nym-credential-storage",
|
||||
@@ -6007,6 +6015,7 @@ dependencies = [
|
||||
name = "nym-ip-packet-client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"bytes",
|
||||
"futures",
|
||||
"nym-ip-packet-requests",
|
||||
@@ -6659,6 +6668,36 @@ dependencies = [
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-registration-client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"nym-authenticator-client",
|
||||
"nym-bandwidth-controller",
|
||||
"nym-credential-storage",
|
||||
"nym-credentials-interface",
|
||||
"nym-ip-packet-client",
|
||||
"nym-registration-common",
|
||||
"nym-sdk",
|
||||
"nym-validator-client",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-registration-common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"nym-authenticator-requests",
|
||||
"nym-crypto",
|
||||
"nym-ip-packet-requests",
|
||||
"nym-sphinx",
|
||||
"tokio-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-sdk"
|
||||
version = "0.1.0"
|
||||
@@ -7404,26 +7443,6 @@ dependencies = [
|
||||
"ts-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-wg-gateway-client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"nym-authenticator-client",
|
||||
"nym-authenticator-requests",
|
||||
"nym-bandwidth-controller",
|
||||
"nym-credentials-interface",
|
||||
"nym-crypto",
|
||||
"nym-node-requests",
|
||||
"nym-pemstore",
|
||||
"nym-sdk",
|
||||
"nym-statistics-common",
|
||||
"nym-validator-client",
|
||||
"rand 0.8.5",
|
||||
"thiserror 2.0.12",
|
||||
"tracing",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-wireguard"
|
||||
version = "0.1.0"
|
||||
|
||||
+6
-4
@@ -85,6 +85,7 @@ members = [
|
||||
"common/nymsphinx/types",
|
||||
"common/nyxd-scraper",
|
||||
"common/pemstore",
|
||||
"common/registration",
|
||||
"common/serde-helpers",
|
||||
"common/service-provider-requests-common",
|
||||
"common/socks5-client-core",
|
||||
@@ -92,7 +93,8 @@ members = [
|
||||
"common/socks5/requests",
|
||||
"common/statistics",
|
||||
"common/store-cipher",
|
||||
"common/task", "common/test-utils",
|
||||
"common/task",
|
||||
"common/test-utils",
|
||||
"common/ticketbooks-merkle",
|
||||
"common/topology",
|
||||
"common/tun",
|
||||
@@ -125,10 +127,11 @@ members = [
|
||||
"nym-node-status-api/nym-node-status-client",
|
||||
"nym-node/nym-node-metrics",
|
||||
"nym-node/nym-node-requests",
|
||||
"nym-outfox", "nym-signers-monitor",
|
||||
"nym-outfox",
|
||||
"nym-registration-client",
|
||||
"nym-signers-monitor",
|
||||
"nym-statistics-api",
|
||||
"nym-validator-rewarder",
|
||||
"nym-wg-gateway-client",
|
||||
"nyx-chain-watcher",
|
||||
"sdk/ffi/cpp",
|
||||
"sdk/ffi/go",
|
||||
@@ -145,7 +148,6 @@ members = [
|
||||
# "tools/internal/sdk-version-bump",
|
||||
"tools/internal/ssl-inject",
|
||||
"tools/internal/testnet-manager",
|
||||
"tools/internal/testnet-manager",
|
||||
"tools/internal/testnet-manager/dkg-bypass-contract",
|
||||
"tools/internal/validator-status-check",
|
||||
"tools/nym-cli",
|
||||
|
||||
@@ -13,6 +13,8 @@ base64 = { workspace = true }
|
||||
bincode = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
semver = { workspace = true }
|
||||
strum_macros = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
nym-credentials-interface = { path = "../credentials-interface" }
|
||||
|
||||
@@ -0,0 +1,272 @@
|
||||
// Copyright 2025 Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use nym_sphinx::addressing::Recipient;
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
|
||||
use crate::{
|
||||
latest::registration::IpPair,
|
||||
traits::{FinalMessage, InitMessage, QueryBandwidthMessage, TopUpMessage, Versionable},
|
||||
v2, v3, v4, v5, AuthenticatorVersion, Error,
|
||||
};
|
||||
|
||||
// This is very redundant with AuthenticatorRequest and I reckon they could be smooshed.
|
||||
// It is a bit out of scope for me at the moment though
|
||||
#[derive(Debug)]
|
||||
pub enum ClientMessage {
|
||||
Initial(Box<dyn InitMessage + Send + Sync + 'static>),
|
||||
Final(Box<dyn FinalMessage + Send + Sync + 'static>),
|
||||
Query(Box<dyn QueryBandwidthMessage + Send + Sync + 'static>),
|
||||
TopUp(Box<dyn TopUpMessage + Send + Sync + 'static>),
|
||||
}
|
||||
|
||||
impl ClientMessage {
|
||||
// check if message is wasteful e.g. contains a credential
|
||||
pub fn is_wasteful(&self) -> bool {
|
||||
match self {
|
||||
Self::Final(msg) => msg.credential().is_some(),
|
||||
Self::TopUp(_) => true,
|
||||
Self::Initial(_) | Self::Query(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn version(&self) -> AuthenticatorVersion {
|
||||
match self {
|
||||
ClientMessage::Initial(msg) => msg.version(),
|
||||
ClientMessage::Final(msg) => msg.version(),
|
||||
ClientMessage::Query(msg) => msg.version(),
|
||||
ClientMessage::TopUp(msg) => msg.version(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bytes(&self, reply_to: Recipient) -> Result<(Vec<u8>, u64), Error> {
|
||||
match self.version() {
|
||||
AuthenticatorVersion::V1 => Err(Error::UnsupportedVersion),
|
||||
AuthenticatorVersion::V2 => {
|
||||
use v2::{
|
||||
registration::{ClientMac, FinalMessage, GatewayClient, InitMessage},
|
||||
request::AuthenticatorRequest,
|
||||
};
|
||||
match self {
|
||||
ClientMessage::Initial(init_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_initial_request(
|
||||
InitMessage {
|
||||
pub_key: init_message.pub_key(),
|
||||
},
|
||||
reply_to,
|
||||
);
|
||||
Ok((req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::Final(final_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_final_request(
|
||||
FinalMessage {
|
||||
gateway_client: GatewayClient {
|
||||
pub_key: final_message.gateway_client_pub_key(),
|
||||
private_ip: final_message
|
||||
.gateway_client_ipv4()
|
||||
.ok_or(Error::UnsupportedMessage)?
|
||||
.into(),
|
||||
mac: ClientMac::new(final_message.gateway_client_mac()),
|
||||
},
|
||||
credential: final_message.credential(),
|
||||
},
|
||||
reply_to,
|
||||
);
|
||||
Ok((req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::Query(query_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_query_request(
|
||||
query_message.pub_key(),
|
||||
reply_to,
|
||||
);
|
||||
Ok((req.to_bytes()?, id))
|
||||
}
|
||||
_ => Err(Error::UnsupportedMessage),
|
||||
}
|
||||
}
|
||||
AuthenticatorVersion::V3 => {
|
||||
use v3::{
|
||||
registration::{ClientMac, FinalMessage, GatewayClient, InitMessage},
|
||||
request::AuthenticatorRequest,
|
||||
topup::TopUpMessage,
|
||||
};
|
||||
match self {
|
||||
ClientMessage::Initial(init_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_initial_request(
|
||||
InitMessage {
|
||||
pub_key: init_message.pub_key(),
|
||||
},
|
||||
reply_to,
|
||||
);
|
||||
Ok((req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::Final(final_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_final_request(
|
||||
FinalMessage {
|
||||
gateway_client: GatewayClient {
|
||||
pub_key: final_message.gateway_client_pub_key(),
|
||||
private_ip: final_message
|
||||
.gateway_client_ipv4()
|
||||
.ok_or(Error::UnsupportedMessage)?
|
||||
.into(),
|
||||
mac: ClientMac::new(final_message.gateway_client_mac()),
|
||||
},
|
||||
credential: final_message.credential(),
|
||||
},
|
||||
reply_to,
|
||||
);
|
||||
Ok((req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::Query(query_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_query_request(
|
||||
query_message.pub_key(),
|
||||
reply_to,
|
||||
);
|
||||
Ok((req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::TopUp(top_up_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_topup_request(
|
||||
TopUpMessage {
|
||||
pub_key: top_up_message.pub_key(),
|
||||
credential: top_up_message.credential(),
|
||||
},
|
||||
reply_to,
|
||||
);
|
||||
Ok((req.to_bytes()?, id))
|
||||
}
|
||||
}
|
||||
}
|
||||
AuthenticatorVersion::V4 => {
|
||||
use v4::{
|
||||
registration::{ClientMac, FinalMessage, GatewayClient, InitMessage},
|
||||
request::AuthenticatorRequest,
|
||||
topup::TopUpMessage,
|
||||
};
|
||||
match self {
|
||||
ClientMessage::Initial(init_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_initial_request(
|
||||
InitMessage {
|
||||
pub_key: init_message.pub_key(),
|
||||
},
|
||||
reply_to,
|
||||
);
|
||||
Ok((req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::Final(final_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_final_request(
|
||||
FinalMessage {
|
||||
gateway_client: GatewayClient {
|
||||
pub_key: final_message.gateway_client_pub_key(),
|
||||
private_ips: IpPair {
|
||||
ipv4: final_message
|
||||
.gateway_client_ipv4()
|
||||
.ok_or(Error::UnsupportedMessage)?,
|
||||
ipv6: final_message
|
||||
.gateway_client_ipv6()
|
||||
.ok_or(Error::UnsupportedMessage)?,
|
||||
}
|
||||
.into(),
|
||||
mac: ClientMac::new(final_message.gateway_client_mac()),
|
||||
},
|
||||
credential: final_message.credential(),
|
||||
},
|
||||
reply_to,
|
||||
);
|
||||
Ok((req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::Query(query_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_query_request(
|
||||
query_message.pub_key(),
|
||||
reply_to,
|
||||
);
|
||||
Ok((req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::TopUp(top_up_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_topup_request(
|
||||
TopUpMessage {
|
||||
pub_key: top_up_message.pub_key(),
|
||||
credential: top_up_message.credential(),
|
||||
},
|
||||
reply_to,
|
||||
);
|
||||
Ok((req.to_bytes()?, id))
|
||||
}
|
||||
}
|
||||
}
|
||||
AuthenticatorVersion::V5 => {
|
||||
use v5::{
|
||||
registration::{ClientMac, FinalMessage, GatewayClient, InitMessage},
|
||||
request::AuthenticatorRequest,
|
||||
topup::TopUpMessage,
|
||||
};
|
||||
match self {
|
||||
ClientMessage::Initial(init_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_initial_request(InitMessage {
|
||||
pub_key: init_message.pub_key(),
|
||||
});
|
||||
Ok((req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::Final(final_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_final_request(FinalMessage {
|
||||
gateway_client: GatewayClient {
|
||||
pub_key: final_message.gateway_client_pub_key(),
|
||||
private_ips: IpPair {
|
||||
ipv4: final_message
|
||||
.gateway_client_ipv4()
|
||||
.ok_or(Error::UnsupportedMessage)?,
|
||||
ipv6: final_message
|
||||
.gateway_client_ipv6()
|
||||
.ok_or(Error::UnsupportedMessage)?,
|
||||
},
|
||||
mac: ClientMac::new(final_message.gateway_client_mac()),
|
||||
},
|
||||
credential: final_message.credential(),
|
||||
});
|
||||
Ok((req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::Query(query_message) => {
|
||||
let (req, id) =
|
||||
AuthenticatorRequest::new_query_request(query_message.pub_key());
|
||||
Ok((req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::TopUp(top_up_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_topup_request(TopUpMessage {
|
||||
pub_key: top_up_message.pub_key(),
|
||||
credential: top_up_message.credential(),
|
||||
});
|
||||
Ok((req.to_bytes()?, id))
|
||||
}
|
||||
}
|
||||
}
|
||||
AuthenticatorVersion::UNKNOWN => Err(Error::UnknownVersion),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn use_surbs(&self) -> bool {
|
||||
use AuthenticatorVersion::*;
|
||||
match self.version() {
|
||||
V1 | V2 | V3 | V4 => false,
|
||||
V5 => true,
|
||||
UNKNOWN => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Same comment as above struct
|
||||
#[derive(Debug)]
|
||||
pub struct QueryMessageImpl {
|
||||
pub pub_key: PeerPublicKey,
|
||||
pub version: AuthenticatorVersion,
|
||||
}
|
||||
|
||||
impl Versionable for QueryMessageImpl {
|
||||
fn version(&self) -> AuthenticatorVersion {
|
||||
self.version
|
||||
}
|
||||
}
|
||||
|
||||
impl QueryBandwidthMessage for QueryMessageImpl {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.pub_key
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,17 @@ pub enum Error {
|
||||
#[error("conversion: {0}")]
|
||||
Conversion(String),
|
||||
|
||||
#[error("failed to serialize response packet: {source}")]
|
||||
FailedToSerializeResponsePacket { source: Box<bincode::ErrorKind> },
|
||||
// TODO add version number for debugging
|
||||
#[error("unknown version number")]
|
||||
UnknownVersion,
|
||||
|
||||
// TODO add version number for debugging
|
||||
#[error("unsupported request version")]
|
||||
UnsupportedVersion,
|
||||
|
||||
#[error("gateway doesn't support this type of message")]
|
||||
UnsupportedMessage,
|
||||
|
||||
#[error(transparent)]
|
||||
Bincode(#[from] bincode::Error),
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod client_message;
|
||||
pub mod request;
|
||||
pub mod response;
|
||||
pub mod traits;
|
||||
pub mod v1;
|
||||
pub mod v2;
|
||||
@@ -10,11 +13,13 @@ pub mod v5;
|
||||
|
||||
mod error;
|
||||
mod util;
|
||||
mod version;
|
||||
|
||||
pub use error::Error;
|
||||
pub use v5 as latest;
|
||||
pub use version::AuthenticatorVersion;
|
||||
|
||||
pub const CURRENT_VERSION: u8 = 5;
|
||||
pub const CURRENT_VERSION: u8 = latest::VERSION;
|
||||
|
||||
fn make_bincode_serializer() -> impl bincode::Options {
|
||||
use bincode::Options;
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
|
||||
use nym_sphinx::addressing::Recipient;
|
||||
|
||||
use crate::traits::{FinalMessage, InitMessage, QueryBandwidthMessage, TopUpMessage};
|
||||
use crate::{v1, v2, v3, v4, v5};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AuthenticatorRequest {
|
||||
Initial {
|
||||
msg: Box<dyn InitMessage + Send + Sync + 'static>,
|
||||
protocol: Protocol,
|
||||
reply_to: Option<Recipient>,
|
||||
request_id: u64,
|
||||
},
|
||||
Final {
|
||||
msg: Box<dyn FinalMessage + Send + Sync + 'static>,
|
||||
protocol: Protocol,
|
||||
reply_to: Option<Recipient>,
|
||||
request_id: u64,
|
||||
},
|
||||
QueryBandwidth {
|
||||
msg: Box<dyn QueryBandwidthMessage + Send + Sync + 'static>,
|
||||
protocol: Protocol,
|
||||
reply_to: Option<Recipient>,
|
||||
request_id: u64,
|
||||
},
|
||||
TopUpBandwidth {
|
||||
msg: Box<dyn TopUpMessage + Send + Sync + 'static>,
|
||||
protocol: Protocol,
|
||||
reply_to: Option<Recipient>,
|
||||
request_id: u64,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<v1::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
fn from(value: v1::request::AuthenticatorRequest) -> Self {
|
||||
match value.data {
|
||||
v1::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
|
||||
msg: Box::new(init_message),
|
||||
protocol: Protocol {
|
||||
version: value.version,
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
},
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v1::request::AuthenticatorRequestData::Final(gateway_client) => Self::Final {
|
||||
msg: Box::new(gateway_client),
|
||||
protocol: Protocol {
|
||||
version: value.version,
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
},
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v1::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
|
||||
Self::QueryBandwidth {
|
||||
msg: Box::new(peer_public_key),
|
||||
protocol: Protocol {
|
||||
version: value.version,
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
},
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v2::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
fn from(value: v2::request::AuthenticatorRequest) -> Self {
|
||||
match value.data {
|
||||
v2::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
|
||||
msg: Box::new(init_message),
|
||||
protocol: value.protocol,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v2::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
|
||||
msg: final_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v2::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
|
||||
Self::QueryBandwidth {
|
||||
msg: Box::new(peer_public_key),
|
||||
protocol: value.protocol,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v3::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
fn from(value: v3::request::AuthenticatorRequest) -> Self {
|
||||
match value.data {
|
||||
v3::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
|
||||
msg: Box::new(init_message),
|
||||
protocol: value.protocol,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v3::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
|
||||
msg: final_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v3::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
|
||||
Self::QueryBandwidth {
|
||||
msg: Box::new(peer_public_key),
|
||||
protocol: value.protocol,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
v3::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => {
|
||||
Self::TopUpBandwidth {
|
||||
msg: top_up_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
fn from(value: v4::request::AuthenticatorRequest) -> Self {
|
||||
match value.data {
|
||||
v4::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
|
||||
msg: Box::new(init_message),
|
||||
protocol: value.protocol,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v4::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
|
||||
msg: final_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v4::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
|
||||
Self::QueryBandwidth {
|
||||
msg: Box::new(peer_public_key),
|
||||
protocol: value.protocol,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
v4::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => {
|
||||
Self::TopUpBandwidth {
|
||||
msg: top_up_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
fn from(value: v5::request::AuthenticatorRequest) -> Self {
|
||||
match value.data {
|
||||
v5::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
|
||||
msg: Box::new(init_message),
|
||||
protocol: value.protocol,
|
||||
reply_to: None,
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v5::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
|
||||
msg: final_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: None,
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v5::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
|
||||
Self::QueryBandwidth {
|
||||
msg: Box::new(peer_public_key),
|
||||
protocol: value.protocol,
|
||||
reply_to: None,
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
v5::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => {
|
||||
Self::TopUpBandwidth {
|
||||
msg: top_up_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: None,
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::traits::{
|
||||
Id, PendingRegistrationResponse, RegisteredResponse, RemainingBandwidthResponse,
|
||||
TopUpBandwidthResponse,
|
||||
};
|
||||
use crate::{v2, v3, v4, v5};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AuthenticatorResponse {
|
||||
PendingRegistration(Box<dyn PendingRegistrationResponse + Send + Sync + 'static>),
|
||||
Registered(Box<dyn RegisteredResponse + Send + Sync + 'static>),
|
||||
RemainingBandwidth(Box<dyn RemainingBandwidthResponse + Send + Sync + 'static>),
|
||||
TopUpBandwidth(Box<dyn TopUpBandwidthResponse + Send + Sync + 'static>),
|
||||
}
|
||||
|
||||
impl Id for AuthenticatorResponse {
|
||||
fn id(&self) -> u64 {
|
||||
match self {
|
||||
AuthenticatorResponse::PendingRegistration(pending_registration_response) => {
|
||||
pending_registration_response.id()
|
||||
}
|
||||
AuthenticatorResponse::Registered(registered_response) => registered_response.id(),
|
||||
AuthenticatorResponse::RemainingBandwidth(remaining_bandwidth_response) => {
|
||||
remaining_bandwidth_response.id()
|
||||
}
|
||||
AuthenticatorResponse::TopUpBandwidth(top_up_bandwidth_response) => {
|
||||
top_up_bandwidth_response.id()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v2::response::AuthenticatorResponse> for AuthenticatorResponse {
|
||||
fn from(value: v2::response::AuthenticatorResponse) -> Self {
|
||||
match value.data {
|
||||
v2::response::AuthenticatorResponseData::PendingRegistration(
|
||||
pending_registration_response,
|
||||
) => Self::PendingRegistration(Box::new(pending_registration_response)),
|
||||
v2::response::AuthenticatorResponseData::Registered(registered_response) => {
|
||||
Self::Registered(Box::new(registered_response))
|
||||
}
|
||||
v2::response::AuthenticatorResponseData::RemainingBandwidth(
|
||||
remaining_bandwidth_response,
|
||||
) => Self::RemainingBandwidth(Box::new(remaining_bandwidth_response)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v3::response::AuthenticatorResponse> for AuthenticatorResponse {
|
||||
fn from(value: v3::response::AuthenticatorResponse) -> Self {
|
||||
match value.data {
|
||||
v3::response::AuthenticatorResponseData::PendingRegistration(
|
||||
pending_registration_response,
|
||||
) => Self::PendingRegistration(Box::new(pending_registration_response)),
|
||||
v3::response::AuthenticatorResponseData::Registered(registered_response) => {
|
||||
Self::Registered(Box::new(registered_response))
|
||||
}
|
||||
v3::response::AuthenticatorResponseData::RemainingBandwidth(
|
||||
remaining_bandwidth_response,
|
||||
) => Self::RemainingBandwidth(Box::new(remaining_bandwidth_response)),
|
||||
v3::response::AuthenticatorResponseData::TopUpBandwidth(top_up_bandwidth_response) => {
|
||||
Self::TopUpBandwidth(Box::new(top_up_bandwidth_response))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::response::AuthenticatorResponse> for AuthenticatorResponse {
|
||||
fn from(value: v4::response::AuthenticatorResponse) -> Self {
|
||||
match value.data {
|
||||
v4::response::AuthenticatorResponseData::PendingRegistration(
|
||||
pending_registration_response,
|
||||
) => Self::PendingRegistration(Box::new(pending_registration_response)),
|
||||
v4::response::AuthenticatorResponseData::Registered(registered_response) => {
|
||||
Self::Registered(Box::new(registered_response))
|
||||
}
|
||||
v4::response::AuthenticatorResponseData::RemainingBandwidth(
|
||||
remaining_bandwidth_response,
|
||||
) => Self::RemainingBandwidth(Box::new(remaining_bandwidth_response)),
|
||||
v4::response::AuthenticatorResponseData::TopUpBandwidth(top_up_bandwidth_response) => {
|
||||
Self::TopUpBandwidth(Box::new(top_up_bandwidth_response))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::response::AuthenticatorResponse> for AuthenticatorResponse {
|
||||
fn from(value: v5::response::AuthenticatorResponse) -> Self {
|
||||
match value.data {
|
||||
v5::response::AuthenticatorResponseData::PendingRegistration(
|
||||
pending_registration_response,
|
||||
) => Self::PendingRegistration(Box::new(pending_registration_response)),
|
||||
v5::response::AuthenticatorResponseData::Registered(registered_response) => {
|
||||
Self::Registered(Box::new(registered_response))
|
||||
}
|
||||
v5::response::AuthenticatorResponseData::RemainingBandwidth(
|
||||
remaining_bandwidth_response,
|
||||
) => Self::RemainingBandwidth(Box::new(remaining_bandwidth_response)),
|
||||
v5::response::AuthenticatorResponseData::TopUpBandwidth(top_up_bandwidth_response) => {
|
||||
Self::TopUpBandwidth(Box::new(top_up_bandwidth_response))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +1,105 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::fmt;
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
use nym_crypto::asymmetric::x25519::PrivateKey;
|
||||
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
|
||||
use crate::{
|
||||
v1, v2, v3, v4,
|
||||
v5::{self, registration::IpPair},
|
||||
Error,
|
||||
};
|
||||
use crate::latest::registration::IpPair;
|
||||
use crate::{v1, v2, v3, v4, v5, AuthenticatorVersion, Error};
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum AuthenticatorVersion {
|
||||
V1,
|
||||
V2,
|
||||
V3,
|
||||
V4,
|
||||
V5,
|
||||
UNKNOWN,
|
||||
pub trait Versionable {
|
||||
fn version(&self) -> AuthenticatorVersion;
|
||||
}
|
||||
|
||||
impl From<Protocol> for AuthenticatorVersion {
|
||||
fn from(value: Protocol) -> Self {
|
||||
if value.service_provider_type != ServiceProviderType::Authenticator {
|
||||
AuthenticatorVersion::UNKNOWN
|
||||
} else if value.version == v1::VERSION {
|
||||
AuthenticatorVersion::V1
|
||||
} else if value.version == v2::VERSION {
|
||||
AuthenticatorVersion::V2
|
||||
} else if value.version == v3::VERSION {
|
||||
AuthenticatorVersion::V3
|
||||
} else if value.version == v4::VERSION {
|
||||
AuthenticatorVersion::V4
|
||||
} else if value.version == v5::VERSION {
|
||||
AuthenticatorVersion::V5
|
||||
} else {
|
||||
AuthenticatorVersion::UNKNOWN
|
||||
}
|
||||
impl Versionable for v1::GatewayClient {
|
||||
fn version(&self) -> AuthenticatorVersion {
|
||||
AuthenticatorVersion::V1
|
||||
}
|
||||
}
|
||||
|
||||
pub trait InitMessage {
|
||||
impl Versionable for v1::registration::InitMessage {
|
||||
fn version(&self) -> AuthenticatorVersion {
|
||||
AuthenticatorVersion::V1
|
||||
}
|
||||
}
|
||||
|
||||
impl Versionable for v2::registration::InitMessage {
|
||||
fn version(&self) -> AuthenticatorVersion {
|
||||
AuthenticatorVersion::V2
|
||||
}
|
||||
}
|
||||
|
||||
impl Versionable for v3::registration::InitMessage {
|
||||
fn version(&self) -> AuthenticatorVersion {
|
||||
AuthenticatorVersion::V3
|
||||
}
|
||||
}
|
||||
|
||||
impl Versionable for v4::registration::InitMessage {
|
||||
fn version(&self) -> AuthenticatorVersion {
|
||||
AuthenticatorVersion::V4
|
||||
}
|
||||
}
|
||||
|
||||
impl Versionable for v5::registration::InitMessage {
|
||||
fn version(&self) -> AuthenticatorVersion {
|
||||
AuthenticatorVersion::V5
|
||||
}
|
||||
}
|
||||
|
||||
impl Versionable for v2::registration::FinalMessage {
|
||||
fn version(&self) -> AuthenticatorVersion {
|
||||
AuthenticatorVersion::V2
|
||||
}
|
||||
}
|
||||
|
||||
impl Versionable for v3::registration::FinalMessage {
|
||||
fn version(&self) -> AuthenticatorVersion {
|
||||
AuthenticatorVersion::V3
|
||||
}
|
||||
}
|
||||
|
||||
impl Versionable for v4::registration::FinalMessage {
|
||||
fn version(&self) -> AuthenticatorVersion {
|
||||
AuthenticatorVersion::V4
|
||||
}
|
||||
}
|
||||
|
||||
impl Versionable for v5::registration::FinalMessage {
|
||||
fn version(&self) -> AuthenticatorVersion {
|
||||
AuthenticatorVersion::V5
|
||||
}
|
||||
}
|
||||
|
||||
impl Versionable for PeerPublicKey {
|
||||
fn version(&self) -> AuthenticatorVersion {
|
||||
AuthenticatorVersion::V3
|
||||
}
|
||||
}
|
||||
|
||||
impl Versionable for v3::topup::TopUpMessage {
|
||||
fn version(&self) -> AuthenticatorVersion {
|
||||
AuthenticatorVersion::V3
|
||||
}
|
||||
}
|
||||
|
||||
impl Versionable for v4::topup::TopUpMessage {
|
||||
fn version(&self) -> AuthenticatorVersion {
|
||||
AuthenticatorVersion::V4
|
||||
}
|
||||
}
|
||||
|
||||
impl Versionable for v5::topup::TopUpMessage {
|
||||
fn version(&self) -> AuthenticatorVersion {
|
||||
AuthenticatorVersion::V5
|
||||
}
|
||||
}
|
||||
|
||||
pub trait InitMessage: Versionable + fmt::Debug {
|
||||
fn pub_key(&self) -> PeerPublicKey;
|
||||
}
|
||||
|
||||
@@ -77,15 +133,18 @@ impl InitMessage for v5::registration::InitMessage {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FinalMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey;
|
||||
pub trait FinalMessage: Versionable + fmt::Debug {
|
||||
fn gateway_client_pub_key(&self) -> PeerPublicKey;
|
||||
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error>;
|
||||
fn private_ips(&self) -> IpPair;
|
||||
fn gateway_client_ipv4(&self) -> Option<Ipv4Addr>;
|
||||
fn gateway_client_ipv6(&self) -> Option<Ipv6Addr>;
|
||||
fn gateway_client_mac(&self) -> Vec<u8>;
|
||||
fn credential(&self) -> Option<CredentialSpendingData>;
|
||||
}
|
||||
|
||||
impl FinalMessage for v1::GatewayClient {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
fn gateway_client_pub_key(&self) -> PeerPublicKey {
|
||||
self.pub_key
|
||||
}
|
||||
|
||||
@@ -97,13 +156,28 @@ impl FinalMessage for v1::GatewayClient {
|
||||
self.private_ip.into()
|
||||
}
|
||||
|
||||
fn gateway_client_ipv4(&self) -> Option<Ipv4Addr> {
|
||||
match self.private_ip {
|
||||
std::net::IpAddr::V4(ipv4_addr) => Some(ipv4_addr),
|
||||
std::net::IpAddr::V6(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn gateway_client_ipv6(&self) -> Option<Ipv6Addr> {
|
||||
None
|
||||
}
|
||||
|
||||
fn gateway_client_mac(&self) -> Vec<u8> {
|
||||
self.mac.to_vec()
|
||||
}
|
||||
|
||||
fn credential(&self) -> Option<CredentialSpendingData> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl FinalMessage for v2::registration::FinalMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
fn gateway_client_pub_key(&self) -> PeerPublicKey {
|
||||
self.gateway_client.pub_key
|
||||
}
|
||||
|
||||
@@ -115,13 +189,28 @@ impl FinalMessage for v2::registration::FinalMessage {
|
||||
self.gateway_client.private_ip.into()
|
||||
}
|
||||
|
||||
fn gateway_client_ipv4(&self) -> Option<Ipv4Addr> {
|
||||
match self.gateway_client.private_ip {
|
||||
std::net::IpAddr::V4(ipv4_addr) => Some(ipv4_addr),
|
||||
std::net::IpAddr::V6(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn gateway_client_ipv6(&self) -> Option<Ipv6Addr> {
|
||||
None
|
||||
}
|
||||
|
||||
fn gateway_client_mac(&self) -> Vec<u8> {
|
||||
self.gateway_client.mac.to_vec()
|
||||
}
|
||||
|
||||
fn credential(&self) -> Option<CredentialSpendingData> {
|
||||
self.credential.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl FinalMessage for v3::registration::FinalMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
fn gateway_client_pub_key(&self) -> PeerPublicKey {
|
||||
self.gateway_client.pub_key
|
||||
}
|
||||
|
||||
@@ -133,13 +222,28 @@ impl FinalMessage for v3::registration::FinalMessage {
|
||||
self.gateway_client.private_ip.into()
|
||||
}
|
||||
|
||||
fn gateway_client_ipv4(&self) -> Option<Ipv4Addr> {
|
||||
match self.gateway_client.private_ip {
|
||||
std::net::IpAddr::V4(ipv4_addr) => Some(ipv4_addr),
|
||||
std::net::IpAddr::V6(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn gateway_client_ipv6(&self) -> Option<Ipv6Addr> {
|
||||
None
|
||||
}
|
||||
|
||||
fn gateway_client_mac(&self) -> Vec<u8> {
|
||||
self.gateway_client.mac.to_vec()
|
||||
}
|
||||
|
||||
fn credential(&self) -> Option<CredentialSpendingData> {
|
||||
self.credential.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl FinalMessage for v4::registration::FinalMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
fn gateway_client_pub_key(&self) -> PeerPublicKey {
|
||||
self.gateway_client.pub_key
|
||||
}
|
||||
|
||||
@@ -151,13 +255,25 @@ impl FinalMessage for v4::registration::FinalMessage {
|
||||
self.gateway_client.private_ips.into()
|
||||
}
|
||||
|
||||
fn gateway_client_ipv4(&self) -> Option<Ipv4Addr> {
|
||||
Some(self.gateway_client.private_ips.ipv4)
|
||||
}
|
||||
|
||||
fn gateway_client_ipv6(&self) -> Option<Ipv6Addr> {
|
||||
Some(self.gateway_client.private_ips.ipv6)
|
||||
}
|
||||
|
||||
fn gateway_client_mac(&self) -> Vec<u8> {
|
||||
self.gateway_client.mac.to_vec()
|
||||
}
|
||||
|
||||
fn credential(&self) -> Option<CredentialSpendingData> {
|
||||
self.credential.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl FinalMessage for v5::registration::FinalMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
fn gateway_client_pub_key(&self) -> PeerPublicKey {
|
||||
self.gateway_client.pub_key
|
||||
}
|
||||
|
||||
@@ -169,12 +285,24 @@ impl FinalMessage for v5::registration::FinalMessage {
|
||||
self.gateway_client.private_ips
|
||||
}
|
||||
|
||||
fn gateway_client_ipv4(&self) -> Option<Ipv4Addr> {
|
||||
Some(self.gateway_client.private_ips.ipv4)
|
||||
}
|
||||
|
||||
fn gateway_client_ipv6(&self) -> Option<Ipv6Addr> {
|
||||
Some(self.gateway_client.private_ips.ipv6)
|
||||
}
|
||||
|
||||
fn gateway_client_mac(&self) -> Vec<u8> {
|
||||
self.gateway_client.mac.to_vec()
|
||||
}
|
||||
|
||||
fn credential(&self) -> Option<CredentialSpendingData> {
|
||||
self.credential.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait QueryBandwidthMessage {
|
||||
pub trait QueryBandwidthMessage: Versionable + fmt::Debug {
|
||||
fn pub_key(&self) -> PeerPublicKey;
|
||||
}
|
||||
|
||||
@@ -184,7 +312,7 @@ impl QueryBandwidthMessage for PeerPublicKey {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TopUpMessage {
|
||||
pub trait TopUpMessage: Versionable + fmt::Debug {
|
||||
fn pub_key(&self) -> PeerPublicKey;
|
||||
fn credential(&self) -> CredentialSpendingData;
|
||||
}
|
||||
@@ -219,197 +347,286 @@ impl TopUpMessage for v5::topup::TopUpMessage {
|
||||
}
|
||||
}
|
||||
|
||||
pub enum AuthenticatorRequest {
|
||||
Initial {
|
||||
msg: Box<dyn InitMessage + Send + Sync + 'static>,
|
||||
protocol: Protocol,
|
||||
reply_to: Option<Recipient>,
|
||||
request_id: u64,
|
||||
},
|
||||
Final {
|
||||
msg: Box<dyn FinalMessage + Send + Sync + 'static>,
|
||||
protocol: Protocol,
|
||||
reply_to: Option<Recipient>,
|
||||
request_id: u64,
|
||||
},
|
||||
QueryBandwidth {
|
||||
msg: Box<dyn QueryBandwidthMessage + Send + Sync + 'static>,
|
||||
protocol: Protocol,
|
||||
reply_to: Option<Recipient>,
|
||||
request_id: u64,
|
||||
},
|
||||
TopUpBandwidth {
|
||||
msg: Box<dyn TopUpMessage + Send + Sync + 'static>,
|
||||
protocol: Protocol,
|
||||
reply_to: Option<Recipient>,
|
||||
request_id: u64,
|
||||
},
|
||||
pub trait Id {
|
||||
fn id(&self) -> u64;
|
||||
}
|
||||
|
||||
impl From<v1::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
fn from(value: v1::request::AuthenticatorRequest) -> Self {
|
||||
match value.data {
|
||||
v1::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
|
||||
msg: Box::new(init_message),
|
||||
protocol: Protocol {
|
||||
version: value.version,
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
},
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v1::request::AuthenticatorRequestData::Final(gateway_client) => Self::Final {
|
||||
msg: Box::new(gateway_client),
|
||||
protocol: Protocol {
|
||||
version: value.version,
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
},
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v1::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
|
||||
Self::QueryBandwidth {
|
||||
msg: Box::new(peer_public_key),
|
||||
protocol: Protocol {
|
||||
version: value.version,
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
},
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Id for v2::response::PendingRegistrationResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v2::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
fn from(value: v2::request::AuthenticatorRequest) -> Self {
|
||||
match value.data {
|
||||
v2::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
|
||||
msg: Box::new(init_message),
|
||||
protocol: value.protocol,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v2::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
|
||||
msg: final_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v2::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
|
||||
Self::QueryBandwidth {
|
||||
msg: Box::new(peer_public_key),
|
||||
protocol: value.protocol,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Id for v3::response::PendingRegistrationResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v3::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
fn from(value: v3::request::AuthenticatorRequest) -> Self {
|
||||
match value.data {
|
||||
v3::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
|
||||
msg: Box::new(init_message),
|
||||
protocol: value.protocol,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v3::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
|
||||
msg: final_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v3::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
|
||||
Self::QueryBandwidth {
|
||||
msg: Box::new(peer_public_key),
|
||||
protocol: value.protocol,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
v3::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => {
|
||||
Self::TopUpBandwidth {
|
||||
msg: top_up_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Id for v4::response::PendingRegistrationResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
fn from(value: v4::request::AuthenticatorRequest) -> Self {
|
||||
match value.data {
|
||||
v4::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
|
||||
msg: Box::new(init_message),
|
||||
protocol: value.protocol,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v4::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
|
||||
msg: final_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v4::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
|
||||
Self::QueryBandwidth {
|
||||
msg: Box::new(peer_public_key),
|
||||
protocol: value.protocol,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
v4::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => {
|
||||
Self::TopUpBandwidth {
|
||||
msg: top_up_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: Some(value.reply_to),
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Id for v5::response::PendingRegistrationResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
fn from(value: v5::request::AuthenticatorRequest) -> Self {
|
||||
match value.data {
|
||||
v5::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
|
||||
msg: Box::new(init_message),
|
||||
protocol: value.protocol,
|
||||
reply_to: None,
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v5::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
|
||||
msg: final_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: None,
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v5::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
|
||||
Self::QueryBandwidth {
|
||||
msg: Box::new(peer_public_key),
|
||||
protocol: value.protocol,
|
||||
reply_to: None,
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
v5::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => {
|
||||
Self::TopUpBandwidth {
|
||||
msg: top_up_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: None,
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Id for v2::response::RegisteredResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for v3::response::RegisteredResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for v4::response::RegisteredResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for v5::response::RegisteredResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for v2::response::RemainingBandwidthResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for v3::response::RemainingBandwidthResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for v4::response::RemainingBandwidthResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for v5::response::RemainingBandwidthResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for v3::response::TopUpBandwidthResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for v4::response::TopUpBandwidthResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for v5::response::TopUpBandwidthResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PendingRegistrationResponse: Id + fmt::Debug {
|
||||
fn nonce(&self) -> u64;
|
||||
fn verify(&self, gateway_key: &PrivateKey) -> std::result::Result<(), Error>;
|
||||
fn pub_key(&self) -> PeerPublicKey;
|
||||
fn private_ips(&self) -> IpPair;
|
||||
}
|
||||
|
||||
impl PendingRegistrationResponse for v2::response::PendingRegistrationResponse {
|
||||
fn nonce(&self) -> u64 {
|
||||
self.reply.nonce
|
||||
}
|
||||
|
||||
fn verify(&self, gateway_key: &PrivateKey) -> std::result::Result<(), Error> {
|
||||
self.reply.gateway_data.verify(gateway_key, self.nonce())
|
||||
}
|
||||
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.reply.gateway_data.pub_key
|
||||
}
|
||||
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.reply.gateway_data.private_ip.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl PendingRegistrationResponse for v3::response::PendingRegistrationResponse {
|
||||
fn nonce(&self) -> u64 {
|
||||
self.reply.nonce
|
||||
}
|
||||
|
||||
fn verify(&self, gateway_key: &PrivateKey) -> std::result::Result<(), Error> {
|
||||
self.reply.gateway_data.verify(gateway_key, self.nonce())
|
||||
}
|
||||
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.reply.gateway_data.pub_key
|
||||
}
|
||||
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.reply.gateway_data.private_ip.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl PendingRegistrationResponse for v4::response::PendingRegistrationResponse {
|
||||
fn nonce(&self) -> u64 {
|
||||
self.reply.nonce
|
||||
}
|
||||
|
||||
fn verify(&self, gateway_key: &PrivateKey) -> std::result::Result<(), Error> {
|
||||
self.reply.gateway_data.verify(gateway_key, self.nonce())
|
||||
}
|
||||
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.reply.gateway_data.pub_key
|
||||
}
|
||||
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.reply.gateway_data.private_ips.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl PendingRegistrationResponse for v5::response::PendingRegistrationResponse {
|
||||
fn nonce(&self) -> u64 {
|
||||
self.reply.nonce
|
||||
}
|
||||
|
||||
fn verify(&self, gateway_key: &PrivateKey) -> std::result::Result<(), Error> {
|
||||
self.reply.gateway_data.verify(gateway_key, self.nonce())
|
||||
}
|
||||
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.reply.gateway_data.pub_key
|
||||
}
|
||||
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.reply.gateway_data.private_ips
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RegisteredResponse: Id + fmt::Debug {
|
||||
fn private_ips(&self) -> IpPair;
|
||||
fn pub_key(&self) -> PeerPublicKey;
|
||||
fn wg_port(&self) -> u16;
|
||||
}
|
||||
|
||||
impl RegisteredResponse for v2::response::RegisteredResponse {
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.reply.private_ip.into()
|
||||
}
|
||||
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.reply.pub_key
|
||||
}
|
||||
|
||||
fn wg_port(&self) -> u16 {
|
||||
self.reply.wg_port
|
||||
}
|
||||
}
|
||||
|
||||
impl RegisteredResponse for v3::response::RegisteredResponse {
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.reply.private_ip.into()
|
||||
}
|
||||
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.reply.pub_key
|
||||
}
|
||||
|
||||
fn wg_port(&self) -> u16 {
|
||||
self.reply.wg_port
|
||||
}
|
||||
}
|
||||
impl RegisteredResponse for v4::response::RegisteredResponse {
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.reply.private_ips.into()
|
||||
}
|
||||
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.reply.pub_key
|
||||
}
|
||||
|
||||
fn wg_port(&self) -> u16 {
|
||||
self.reply.wg_port
|
||||
}
|
||||
}
|
||||
|
||||
impl RegisteredResponse for v5::response::RegisteredResponse {
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.reply.private_ips
|
||||
}
|
||||
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.reply.pub_key
|
||||
}
|
||||
|
||||
fn wg_port(&self) -> u16 {
|
||||
self.reply.wg_port
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RemainingBandwidthResponse: Id + fmt::Debug {
|
||||
fn available_bandwidth(&self) -> Option<i64>;
|
||||
}
|
||||
|
||||
impl RemainingBandwidthResponse for v2::response::RemainingBandwidthResponse {
|
||||
fn available_bandwidth(&self) -> Option<i64> {
|
||||
self.reply.as_ref().map(|r| r.available_bandwidth)
|
||||
}
|
||||
}
|
||||
|
||||
impl RemainingBandwidthResponse for v3::response::RemainingBandwidthResponse {
|
||||
fn available_bandwidth(&self) -> Option<i64> {
|
||||
self.reply.as_ref().map(|r| r.available_bandwidth)
|
||||
}
|
||||
}
|
||||
|
||||
impl RemainingBandwidthResponse for v4::response::RemainingBandwidthResponse {
|
||||
fn available_bandwidth(&self) -> Option<i64> {
|
||||
self.reply.as_ref().map(|r| r.available_bandwidth)
|
||||
}
|
||||
}
|
||||
|
||||
impl RemainingBandwidthResponse for v5::response::RemainingBandwidthResponse {
|
||||
fn available_bandwidth(&self) -> Option<i64> {
|
||||
self.reply.as_ref().map(|r| r.available_bandwidth)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TopUpBandwidthResponse: Id + fmt::Debug {
|
||||
fn available_bandwidth(&self) -> i64;
|
||||
}
|
||||
|
||||
impl TopUpBandwidthResponse for v3::response::TopUpBandwidthResponse {
|
||||
fn available_bandwidth(&self) -> i64 {
|
||||
self.reply.available_bandwidth
|
||||
}
|
||||
}
|
||||
|
||||
impl TopUpBandwidthResponse for v4::response::TopUpBandwidthResponse {
|
||||
fn available_bandwidth(&self) -> i64 {
|
||||
self.reply.available_bandwidth
|
||||
}
|
||||
}
|
||||
|
||||
impl TopUpBandwidthResponse for v5::response::TopUpBandwidthResponse {
|
||||
fn available_bandwidth(&self) -> i64 {
|
||||
self.reply.available_bandwidth
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::{v1, v2, v3, v4, v5};
|
||||
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, strum_macros::Display)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum AuthenticatorVersion {
|
||||
/// introduced in wispa release (1.1.5)
|
||||
V1,
|
||||
|
||||
/// introduced in aero release (1.1.9)
|
||||
V2,
|
||||
|
||||
/// introduced in magura release (1.1.10)
|
||||
V3,
|
||||
|
||||
/// introduced in crunch release (1.2.0)
|
||||
V4,
|
||||
|
||||
/// introduced in dorina-patched release (1.6.1)
|
||||
V5,
|
||||
|
||||
UNKNOWN,
|
||||
}
|
||||
|
||||
impl AuthenticatorVersion {
|
||||
pub const LATEST: Self = Self::V5;
|
||||
|
||||
pub const fn release_version(&self) -> semver::Version {
|
||||
match self {
|
||||
AuthenticatorVersion::V1 => semver::Version::new(1, 1, 5),
|
||||
AuthenticatorVersion::V2 => semver::Version::new(1, 1, 9),
|
||||
AuthenticatorVersion::V3 => semver::Version::new(1, 1, 10),
|
||||
AuthenticatorVersion::V4 => semver::Version::new(1, 2, 0),
|
||||
AuthenticatorVersion::V5 => semver::Version::new(1, 6, 1),
|
||||
AuthenticatorVersion::UNKNOWN => semver::Version::new(0, 0, 0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Protocol> for AuthenticatorVersion {
|
||||
fn from(value: Protocol) -> Self {
|
||||
if value.service_provider_type != ServiceProviderType::Authenticator {
|
||||
AuthenticatorVersion::UNKNOWN
|
||||
} else if value.version == v1::VERSION {
|
||||
AuthenticatorVersion::V1
|
||||
} else if value.version == v2::VERSION {
|
||||
AuthenticatorVersion::V2
|
||||
} else if value.version == v3::VERSION {
|
||||
AuthenticatorVersion::V3
|
||||
} else if value.version == v4::VERSION {
|
||||
AuthenticatorVersion::V4
|
||||
} else if value.version == v5::VERSION {
|
||||
AuthenticatorVersion::V5
|
||||
} else {
|
||||
AuthenticatorVersion::UNKNOWN
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for AuthenticatorVersion {
|
||||
fn from(value: u8) -> Self {
|
||||
if value == v1::VERSION {
|
||||
AuthenticatorVersion::V1
|
||||
} else if value == v2::VERSION {
|
||||
AuthenticatorVersion::V2
|
||||
} else if value == v3::VERSION {
|
||||
AuthenticatorVersion::V3
|
||||
} else if value == v4::VERSION {
|
||||
AuthenticatorVersion::V4
|
||||
} else if value == v5::VERSION {
|
||||
AuthenticatorVersion::V5
|
||||
} else {
|
||||
AuthenticatorVersion::UNKNOWN
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for AuthenticatorVersion {
|
||||
fn from(value: &str) -> Self {
|
||||
let Ok(semver) = semver::Version::parse(value) else {
|
||||
return Self::UNKNOWN;
|
||||
};
|
||||
|
||||
semver.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Option<&String>> for AuthenticatorVersion {
|
||||
fn from(value: Option<&String>) -> Self {
|
||||
match value {
|
||||
None => Self::UNKNOWN,
|
||||
Some(value) => value.as_str().into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for AuthenticatorVersion {
|
||||
fn from(value: String) -> Self {
|
||||
Self::from(value.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Option<String>> for AuthenticatorVersion {
|
||||
fn from(value: Option<String>) -> Self {
|
||||
value.as_ref().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<semver::Version> for AuthenticatorVersion {
|
||||
fn from(semver: semver::Version) -> Self {
|
||||
if semver < AuthenticatorVersion::V1.release_version() {
|
||||
return Self::UNKNOWN;
|
||||
}
|
||||
if semver < AuthenticatorVersion::V2.release_version() {
|
||||
return Self::V1;
|
||||
}
|
||||
if semver < AuthenticatorVersion::V3.release_version() {
|
||||
return Self::V2;
|
||||
}
|
||||
if semver < AuthenticatorVersion::V4.release_version() {
|
||||
return Self::V3;
|
||||
}
|
||||
if semver < AuthenticatorVersion::V5.release_version() {
|
||||
return Self::V4;
|
||||
}
|
||||
// if provided version is higher (or equal) to release version of V5,
|
||||
// we return the latest (i.e. v5)
|
||||
|
||||
debug_assert_eq!(Self::V5, Self::LATEST, "a new AuthenticatorVersion variant has been introduced without adjusting the `From<semver::Version>` trait");
|
||||
Self::LATEST
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::latest;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn strum_display() {
|
||||
// sanity check on formatting and casing
|
||||
assert_eq!("v1", AuthenticatorVersion::V1.to_string());
|
||||
assert_eq!("v2", AuthenticatorVersion::V2.to_string());
|
||||
assert_eq!("unknown", AuthenticatorVersion::UNKNOWN.to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn u8_conversion() {
|
||||
assert_eq!(AuthenticatorVersion::V1, AuthenticatorVersion::from(1u8));
|
||||
assert_eq!(AuthenticatorVersion::V2, AuthenticatorVersion::from(2u8));
|
||||
|
||||
assert_eq!(
|
||||
AuthenticatorVersion::UNKNOWN,
|
||||
AuthenticatorVersion::from(latest::VERSION + 1)
|
||||
);
|
||||
assert_eq!(
|
||||
AuthenticatorVersion::UNKNOWN,
|
||||
AuthenticatorVersion::from(0u8)
|
||||
);
|
||||
assert_eq!(
|
||||
AuthenticatorVersion::UNKNOWN,
|
||||
AuthenticatorVersion::from(255u8)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn semver_checks() {
|
||||
assert_eq!(AuthenticatorVersion::UNKNOWN, "1.1.4".into());
|
||||
assert_eq!(AuthenticatorVersion::UNKNOWN, "0.1.0".into());
|
||||
assert_eq!(AuthenticatorVersion::UNKNOWN, "1.0.4".into());
|
||||
assert_eq!(AuthenticatorVersion::V1, "1.1.5".into());
|
||||
assert_eq!(AuthenticatorVersion::V1, "1.1.6".into());
|
||||
assert_eq!(AuthenticatorVersion::V1, "1.1.8".into());
|
||||
assert_eq!(AuthenticatorVersion::V2, "1.1.9".into());
|
||||
assert_eq!(AuthenticatorVersion::V3, "1.1.10".into());
|
||||
assert_eq!(AuthenticatorVersion::V3, "1.1.11".into());
|
||||
assert_eq!(AuthenticatorVersion::V3, "1.1.60".into());
|
||||
assert_eq!(AuthenticatorVersion::V4, "1.2.0".into());
|
||||
assert_eq!(AuthenticatorVersion::V4, "1.2.1".into());
|
||||
assert_eq!(AuthenticatorVersion::V4, "1.5.1".into());
|
||||
assert_eq!(AuthenticatorVersion::V4, "1.6.0".into());
|
||||
assert_eq!(AuthenticatorVersion::V5, "1.6.1".into());
|
||||
assert_eq!(AuthenticatorVersion::V5, "1.6.11".into());
|
||||
assert_eq!(AuthenticatorVersion::V5, "1.7.0".into());
|
||||
assert_eq!(AuthenticatorVersion::V5, "1.16.11".into());
|
||||
assert_eq!(AuthenticatorVersion::V5, "1.17.0".into());
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ license.workspace = true
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
async-trait = { workspace = true }
|
||||
bip39 = { workspace = true }
|
||||
log = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
|
||||
@@ -23,10 +23,12 @@ use nym_validator_client::nym_api::EpochId;
|
||||
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
|
||||
|
||||
pub use event::BandwidthStatusMessage;
|
||||
pub use traits::{BandwidthTicketProvider, DEFAULT_TICKETS_TO_SPEND};
|
||||
|
||||
pub mod acquire;
|
||||
pub mod error;
|
||||
mod event;
|
||||
mod traits;
|
||||
mod utils;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use async_trait::async_trait;
|
||||
use nym_credential_storage::storage::Storage;
|
||||
use nym_credentials_interface::TicketType;
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
|
||||
|
||||
use crate::{error::BandwidthControllerError, BandwidthController, PreparedCredential};
|
||||
|
||||
pub const DEFAULT_TICKETS_TO_SPEND: u32 = 1;
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
pub trait BandwidthTicketProvider: Send + Sync {
|
||||
async fn get_ecash_ticket(
|
||||
&self,
|
||||
ticket_type: TicketType,
|
||||
gateway_id: ed25519::PublicKey,
|
||||
tickets_to_spend: u32,
|
||||
) -> Result<PreparedCredential, BandwidthControllerError>;
|
||||
}
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl<C, St> BandwidthTicketProvider for BandwidthController<C, St>
|
||||
where
|
||||
C: DkgQueryClient + Sync + Send,
|
||||
St: nym_credential_storage::storage::Storage,
|
||||
<St as Storage>::StorageError: Send + Sync + 'static,
|
||||
{
|
||||
async fn get_ecash_ticket(
|
||||
&self,
|
||||
ticket_type: TicketType,
|
||||
gateway_id: ed25519::PublicKey,
|
||||
tickets_to_spend: u32,
|
||||
) -> Result<PreparedCredential, BandwidthControllerError> {
|
||||
self.prepare_ecash_ticket(ticket_type, gateway_id.to_bytes(), tickets_to_spend)
|
||||
.await
|
||||
}
|
||||
}
|
||||
@@ -1050,7 +1050,7 @@ where
|
||||
gateway_connection: GatewayConnection { gateway_ws_fd },
|
||||
},
|
||||
stats_reporter,
|
||||
shutdown_handle: Some(shutdown_tracker), // The primary tracker for this client
|
||||
shutdown_handle: shutdown_tracker, // The primary tracker for this client
|
||||
client_request_sender,
|
||||
forget_me: self.config.debug.forget_me,
|
||||
remember_me: self.config.debug.remember_me,
|
||||
@@ -1066,7 +1066,7 @@ pub struct BaseClient {
|
||||
pub client_state: ClientState,
|
||||
pub stats_reporter: ClientStatsSender,
|
||||
pub client_request_sender: ClientRequestSender,
|
||||
pub shutdown_handle: Option<ShutdownTracker>,
|
||||
pub shutdown_handle: ShutdownTracker,
|
||||
pub forget_me: ForgetMe,
|
||||
pub remember_me: RememberMe,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "nym-registration-common"
|
||||
version = "0.1.0"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
tokio-util.workspace = true
|
||||
|
||||
nym-authenticator-requests = { path = "../authenticator-requests" }
|
||||
nym-crypto = { path = "../crypto" }
|
||||
nym-ip-packet-requests = { path = "../ip-packet-requests" }
|
||||
nym-sphinx = { path = "../nymsphinx" }
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||
|
||||
use nym_authenticator_requests::AuthenticatorVersion;
|
||||
use nym_crypto::asymmetric::x25519::PublicKey;
|
||||
use nym_ip_packet_requests::IpPair;
|
||||
use nym_sphinx::addressing::{NodeIdentity, Recipient};
|
||||
|
||||
pub const DEFAULT_PRIVATE_ENTRY_WIREGUARD_KEY_FILENAME: &str = "free_private_entry_wireguard.pem";
|
||||
pub const DEFAULT_PUBLIC_ENTRY_WIREGUARD_KEY_FILENAME: &str = "free_public_entry_wireguard.pem";
|
||||
pub const DEFAULT_PRIVATE_EXIT_WIREGUARD_KEY_FILENAME: &str = "free_private_exit_wireguard.pem";
|
||||
pub const DEFAULT_PUBLIC_EXIT_WIREGUARD_KEY_FILENAME: &str = "free_public_exit_wireguard.pem";
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct NymNode {
|
||||
pub identity: NodeIdentity,
|
||||
pub ip_address: IpAddr,
|
||||
pub ipr_address: Option<Recipient>,
|
||||
pub authenticator_address: Option<Recipient>,
|
||||
pub version: AuthenticatorVersion,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GatewayData {
|
||||
pub public_key: PublicKey,
|
||||
pub endpoint: SocketAddr,
|
||||
pub private_ipv4: Ipv4Addr,
|
||||
pub private_ipv6: Ipv6Addr,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct AssignedAddresses {
|
||||
pub entry_mixnet_gateway_ip: IpAddr,
|
||||
pub exit_mixnet_gateway_ip: IpAddr,
|
||||
pub mixnet_client_address: Recipient,
|
||||
pub exit_mix_address: Recipient,
|
||||
pub interface_addresses: IpPair,
|
||||
}
|
||||
@@ -42,6 +42,40 @@ impl TryFrom<u8> for ServiceProviderType {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ServiceProviderTypeExt {
|
||||
fn is_network_requester(&self) -> bool;
|
||||
fn is_ip_packet_router(&self) -> bool;
|
||||
fn is_authenticator(&self) -> bool;
|
||||
}
|
||||
|
||||
impl ServiceProviderTypeExt for ServiceProviderType {
|
||||
fn is_network_requester(&self) -> bool {
|
||||
matches!(self, Self::NetworkRequester)
|
||||
}
|
||||
|
||||
fn is_ip_packet_router(&self) -> bool {
|
||||
matches!(self, Self::IpPacketRouter)
|
||||
}
|
||||
|
||||
fn is_authenticator(&self) -> bool {
|
||||
matches!(self, Self::Authenticator)
|
||||
}
|
||||
}
|
||||
|
||||
impl ServiceProviderTypeExt for u8 {
|
||||
fn is_network_requester(&self) -> bool {
|
||||
ServiceProviderType::NetworkRequester as u8 == *self
|
||||
}
|
||||
|
||||
fn is_ip_packet_router(&self) -> bool {
|
||||
ServiceProviderType::IpPacketRouter as u8 == *self
|
||||
}
|
||||
|
||||
fn is_authenticator(&self) -> bool {
|
||||
ServiceProviderType::Authenticator as u8 == *self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct Protocol {
|
||||
pub version: u8,
|
||||
|
||||
@@ -19,11 +19,9 @@ use nym_authenticator_requests::{
|
||||
};
|
||||
use nym_authenticator_requests::{
|
||||
latest::registration::{GatewayClient, PendingRegistrations, PrivateIPs},
|
||||
traits::{
|
||||
AuthenticatorRequest, AuthenticatorVersion, FinalMessage, InitMessage,
|
||||
QueryBandwidthMessage, TopUpMessage,
|
||||
},
|
||||
v1, v2, v3, v4, v5, CURRENT_VERSION,
|
||||
request::AuthenticatorRequest,
|
||||
traits::{FinalMessage, InitMessage, QueryBandwidthMessage, TopUpMessage},
|
||||
v1, v2, v3, v4, v5, AuthenticatorVersion, CURRENT_VERSION,
|
||||
};
|
||||
use nym_credential_verification::ecash::traits::EcashManager;
|
||||
use nym_credential_verification::{
|
||||
@@ -480,7 +478,7 @@ impl MixnetListener {
|
||||
let mut registred_and_free = self.registred_and_free.write().await;
|
||||
let registration_data = registred_and_free
|
||||
.registration_in_progres
|
||||
.get(&final_message.pub_key())
|
||||
.get(&final_message.gateway_client_pub_key())
|
||||
.ok_or(AuthenticatorError::RegistrationNotInProgress)?
|
||||
.clone();
|
||||
|
||||
@@ -491,7 +489,7 @@ impl MixnetListener {
|
||||
return Err(AuthenticatorError::MacVerificationFailure);
|
||||
}
|
||||
|
||||
let mut peer = Peer::new(Key::new(final_message.pub_key().to_bytes()));
|
||||
let mut peer = Peer::new(Key::new(final_message.gateway_client_pub_key().to_bytes()));
|
||||
peer.allowed_ips
|
||||
.push(IpAddrMask::new(final_message.private_ips().ipv4.into(), 32));
|
||||
peer.allowed_ips.push(IpAddrMask::new(
|
||||
@@ -532,7 +530,7 @@ impl MixnetListener {
|
||||
|
||||
registred_and_free
|
||||
.registration_in_progres
|
||||
.remove(&final_message.pub_key());
|
||||
.remove(&final_message.gateway_client_pub_key());
|
||||
|
||||
let bytes = match AuthenticatorVersion::from(protocol) {
|
||||
AuthenticatorVersion::V1 => v1::response::AuthenticatorResponse::new_registered(
|
||||
|
||||
@@ -14,6 +14,7 @@ workspace = true
|
||||
[dependencies]
|
||||
bincode.workspace = true
|
||||
futures.workspace = true
|
||||
rand.workspace = true
|
||||
semver.workspace = true
|
||||
thiserror.workspace = true
|
||||
tokio-util.workspace = true
|
||||
@@ -21,8 +22,12 @@ tokio.workspace = true
|
||||
tracing.workspace = true
|
||||
|
||||
nym-authenticator-requests = { path = "../common/authenticator-requests" }
|
||||
nym-bandwidth-controller = { path = "../common/bandwidth-controller" }
|
||||
nym-credentials-interface = { path = "../common/credentials-interface" }
|
||||
nym-crypto = { path = "../common/crypto" }
|
||||
nym-pemstore = { path = "../common/pemstore" }
|
||||
nym-registration-common = { path = "../common/registration" }
|
||||
nym-sdk = { path = "../sdk/rust/nym-sdk" }
|
||||
nym-service-provider-requests-common = { path = "../common/service-provider-requests-common" }
|
||||
nym-validator-client = { path = "../common/client-libs/validator-client" }
|
||||
nym-wireguard-types = { path = "../common/wireguard-types" }
|
||||
|
||||
@@ -1,41 +1,50 @@
|
||||
use nym_credentials_interface::TicketType;
|
||||
use nym_sdk::mixnet::InputMessage;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("mixnet client stopped returning responses")]
|
||||
NoMixnetMessagesReceived,
|
||||
|
||||
#[error("failed to get version from message")]
|
||||
NoVersionInMessage,
|
||||
|
||||
#[error(
|
||||
"received response with version v{received}, the client is too new and can only understand v{expected}"
|
||||
)]
|
||||
ReceivedResponseWithOldVersion { expected: u8, received: u8 },
|
||||
|
||||
#[error(
|
||||
"received response with version v{received}, the client is too old and can only understand v{expected}"
|
||||
)]
|
||||
ReceivedResponseWithNewVersion { expected: u8, received: u8 },
|
||||
|
||||
#[error("failed to send mixnet message")]
|
||||
SendMixnetMessage(#[source] Box<nym_sdk::Error>),
|
||||
SendMixnetMessage(#[source] Box<tokio::sync::mpsc::error::SendError<InputMessage>>),
|
||||
|
||||
#[error("timeout waiting for connect response from exit gateway (authenticator)")]
|
||||
TimeoutWaitingForConnectResponse,
|
||||
|
||||
#[error("unable to get mixnet handle when sending authenticator message")]
|
||||
UnableToGetMixnetHandle,
|
||||
|
||||
#[error("unknown version number")]
|
||||
UnknownVersion,
|
||||
|
||||
#[error("unsupported request version")]
|
||||
UnsupportedVersion,
|
||||
|
||||
#[error(transparent)]
|
||||
Bincode(#[from] bincode::Error),
|
||||
|
||||
#[error("gateway doesn't support this type of message")]
|
||||
UnsupportedMessage,
|
||||
|
||||
#[error(transparent)]
|
||||
AuthenticatorRequests(#[from] nym_authenticator_requests::Error),
|
||||
|
||||
#[error("verification failure")]
|
||||
VerificationFailed(#[source] nym_authenticator_requests::Error),
|
||||
|
||||
#[error("failed to parse entry gateway socket addr")]
|
||||
FailedToParseEntryGatewaySocketAddr(#[source] std::net::AddrParseError),
|
||||
|
||||
#[error("received invalid response from gateway authenticator")]
|
||||
InvalidGatewayAuthResponse,
|
||||
|
||||
#[error("failed to get {ticketbook_type} ticket")]
|
||||
GetTicket {
|
||||
ticketbook_type: TicketType,
|
||||
#[source]
|
||||
source: nym_bandwidth_controller::error::BandwidthControllerError,
|
||||
},
|
||||
|
||||
#[error("unknown authenticator version number")]
|
||||
UnsupportedAuthenticatorVersion,
|
||||
|
||||
#[error("failed to wait on AuthenticatorClientListener")]
|
||||
FailedToJoinOnTask(#[from] tokio::task::JoinError),
|
||||
}
|
||||
|
||||
// Result type based on our error type
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_crypto::asymmetric::x25519::KeyPair;
|
||||
use nym_pemstore::KeyPairPath;
|
||||
use nym_sdk::mixnet::{IncludedSurbs, Recipient, TransmissionLane};
|
||||
use rand::{CryptoRng, RngCore};
|
||||
|
||||
pub(crate) fn create_input_message(
|
||||
recipient: Recipient,
|
||||
data: Vec<u8>,
|
||||
surbs: IncludedSurbs,
|
||||
) -> nym_sdk::mixnet::InputMessage {
|
||||
match surbs {
|
||||
IncludedSurbs::Amount(surbs) => nym_sdk::mixnet::InputMessage::new_anonymous(
|
||||
recipient,
|
||||
data,
|
||||
surbs,
|
||||
TransmissionLane::General,
|
||||
None,
|
||||
),
|
||||
IncludedSurbs::ExposeSelfAddress => nym_sdk::mixnet::InputMessage::new_regular(
|
||||
recipient,
|
||||
data,
|
||||
TransmissionLane::General,
|
||||
None,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn load_or_generate_keypair<R: RngCore + CryptoRng>(
|
||||
rng: &mut R,
|
||||
paths: KeyPairPath,
|
||||
) -> KeyPair {
|
||||
match nym_pemstore::load_keypair(&paths) {
|
||||
Ok(keypair) => keypair,
|
||||
Err(_) => {
|
||||
let keypair = KeyPair::new(rng);
|
||||
if let Err(e) = nym_pemstore::store_keypair(&keypair, &paths) {
|
||||
tracing::error!(
|
||||
"could not store generated keypair at {:?} - {:?}; will use ephemeral keys",
|
||||
paths,
|
||||
e
|
||||
);
|
||||
}
|
||||
keypair
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::time::Duration;
|
||||
use tracing::{debug, error};
|
||||
|
||||
use crate::mixnet_listener::{MixnetMessageBroadcastReceiver, MixnetMessageInputSender};
|
||||
use crate::{helpers, ClientMessage, Error, Result};
|
||||
use nym_authenticator_requests::{
|
||||
client_message::QueryMessageImpl, response::AuthenticatorResponse, traits::Id, v2, v3, v4, v5,
|
||||
AuthenticatorVersion,
|
||||
};
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
use nym_crypto::asymmetric::x25519::{KeyPair, PublicKey};
|
||||
use nym_sdk::mixnet::{IncludedSurbs, Recipient};
|
||||
use nym_service_provider_requests_common::{Protocol, ServiceProviderTypeExt};
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
|
||||
impl crate::AuthenticatorClient {
|
||||
pub fn into_legacy_and_keypair(self) -> (LegacyAuthenticatorClient, KeyPair) {
|
||||
(
|
||||
LegacyAuthenticatorClient {
|
||||
public_key: *self.keypair.public_key(),
|
||||
mixnet_listener: self.mixnet_listener,
|
||||
mixnet_sender: self.mixnet_sender,
|
||||
our_nym_address: self.our_nym_address,
|
||||
auth_recipient: self.auth_recipient,
|
||||
auth_version: self.auth_version,
|
||||
},
|
||||
self.keypair,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// This is the legacy Authenticator that has to be used to handle bandwidth top up for legacy gateaways
|
||||
pub struct LegacyAuthenticatorClient {
|
||||
public_key: PublicKey,
|
||||
mixnet_listener: MixnetMessageBroadcastReceiver,
|
||||
mixnet_sender: MixnetMessageInputSender,
|
||||
our_nym_address: Recipient,
|
||||
pub auth_recipient: Recipient,
|
||||
auth_version: AuthenticatorVersion,
|
||||
}
|
||||
|
||||
impl LegacyAuthenticatorClient {
|
||||
pub async fn send_and_wait_for_response(
|
||||
&mut self,
|
||||
message: &ClientMessage,
|
||||
) -> Result<AuthenticatorResponse> {
|
||||
let request_id = self.send_request(message).await?;
|
||||
|
||||
debug!("Waiting for reply...");
|
||||
self.listen_for_response(request_id).await
|
||||
}
|
||||
|
||||
async fn send_request(&self, message: &ClientMessage) -> Result<u64> {
|
||||
let (data, request_id) = message.bytes(self.our_nym_address)?;
|
||||
|
||||
// We use 20 surbs for the connect request because typically the
|
||||
// authenticator mixnet client on the nym-node is configured to have a min
|
||||
// threshold of 10 surbs that it reserves for itself to request additional
|
||||
// surbs.
|
||||
let surbs = if message.use_surbs() {
|
||||
match &message {
|
||||
ClientMessage::Initial(_) => IncludedSurbs::new(20),
|
||||
_ => IncludedSurbs::new(1),
|
||||
}
|
||||
} else {
|
||||
IncludedSurbs::ExposeSelfAddress
|
||||
};
|
||||
let input_message = helpers::create_input_message(self.auth_recipient, data, surbs);
|
||||
|
||||
self.mixnet_sender
|
||||
.send(input_message)
|
||||
.await
|
||||
.map_err(|e| Error::SendMixnetMessage(Box::new(e)))?;
|
||||
|
||||
Ok(request_id)
|
||||
}
|
||||
|
||||
async fn listen_for_response(&mut self, request_id: u64) -> Result<AuthenticatorResponse> {
|
||||
let timeout = tokio::time::sleep(Duration::from_secs(10));
|
||||
tokio::pin!(timeout);
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
_ = &mut timeout => {
|
||||
error!("Timed out waiting for reply to connect request");
|
||||
return Err(Error::TimeoutWaitingForConnectResponse);
|
||||
}
|
||||
msg = self.mixnet_listener.recv() => match msg {
|
||||
Err(_) => {
|
||||
return Err(Error::NoMixnetMessagesReceived);
|
||||
}
|
||||
Ok(msg) => {
|
||||
let Some(header) = msg.message.first_chunk::<2>() else {
|
||||
debug!("received too short message that couldn't have been from the authenticator while waiting for connect response");
|
||||
continue;
|
||||
};
|
||||
|
||||
let Ok(protocol) = Protocol::try_from(header) else {
|
||||
debug!("received a message not meant to any service provider while waiting for connect response");
|
||||
continue;
|
||||
};
|
||||
|
||||
if !protocol.service_provider_type.is_authenticator() {
|
||||
debug!("Received non-authenticator message while waiting for connect response");
|
||||
continue;
|
||||
}
|
||||
// Confirm that the version is correct
|
||||
let version = AuthenticatorVersion::from(protocol.version);
|
||||
|
||||
// Then we deserialize the message
|
||||
debug!("AuthClient: got message while waiting for connect response with version {version:?}");
|
||||
let ret: Result<AuthenticatorResponse> = match version {
|
||||
AuthenticatorVersion::V1 => Err(Error::UnsupportedVersion),
|
||||
AuthenticatorVersion::V2 => v2::response::AuthenticatorResponse::from_reconstructed_message(&msg).map(Into::into).map_err(Into::into),
|
||||
AuthenticatorVersion::V3 => v3::response::AuthenticatorResponse::from_reconstructed_message(&msg).map(Into::into).map_err(Into::into),
|
||||
AuthenticatorVersion::V4 => v4::response::AuthenticatorResponse::from_reconstructed_message(&msg).map(Into::into).map_err(Into::into),
|
||||
AuthenticatorVersion::V5 => v5::response::AuthenticatorResponse::from_reconstructed_message(&msg).map(Into::into).map_err(Into::into),
|
||||
AuthenticatorVersion::UNKNOWN => Err(Error::UnknownVersion),
|
||||
};
|
||||
let Ok(response) = ret else {
|
||||
// This is ok, it's likely just one of our self-pings
|
||||
debug!("Failed to deserialize reconstructed message");
|
||||
continue;
|
||||
};
|
||||
|
||||
if response.id() == request_id {
|
||||
debug!("Got response with matching id");
|
||||
return Ok(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn query_bandwidth(&mut self) -> Result<Option<i64>> {
|
||||
let query_message = match self.auth_version {
|
||||
AuthenticatorVersion::V1 => return Err(Error::UnsupportedAuthenticatorVersion),
|
||||
AuthenticatorVersion::V2 => ClientMessage::Query(Box::new(QueryMessageImpl {
|
||||
pub_key: PeerPublicKey::new(self.public_key.to_bytes().into()),
|
||||
version: AuthenticatorVersion::V2,
|
||||
})),
|
||||
AuthenticatorVersion::V3 => ClientMessage::Query(Box::new(QueryMessageImpl {
|
||||
pub_key: PeerPublicKey::new(self.public_key.to_bytes().into()),
|
||||
version: AuthenticatorVersion::V3,
|
||||
})),
|
||||
AuthenticatorVersion::V4 => ClientMessage::Query(Box::new(QueryMessageImpl {
|
||||
pub_key: PeerPublicKey::new(self.public_key.to_bytes().into()),
|
||||
version: AuthenticatorVersion::V4,
|
||||
})),
|
||||
AuthenticatorVersion::V5 => ClientMessage::Query(Box::new(QueryMessageImpl {
|
||||
pub_key: PeerPublicKey::new(self.public_key.to_bytes().into()),
|
||||
version: AuthenticatorVersion::V5,
|
||||
})),
|
||||
AuthenticatorVersion::UNKNOWN => return Err(Error::UnsupportedAuthenticatorVersion),
|
||||
};
|
||||
let response = self.send_and_wait_for_response(&query_message).await?;
|
||||
|
||||
let available_bandwidth = match response {
|
||||
AuthenticatorResponse::RemainingBandwidth(remaining_bandwidth_response) => {
|
||||
if let Some(available_bandwidth) =
|
||||
remaining_bandwidth_response.available_bandwidth()
|
||||
{
|
||||
available_bandwidth
|
||||
} else {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
_ => return Err(Error::InvalidGatewayAuthResponse),
|
||||
};
|
||||
|
||||
let remaining_pretty = if available_bandwidth > 1024 * 1024 {
|
||||
format!("{:.2} MB", available_bandwidth as f64 / 1024.0 / 1024.0)
|
||||
} else {
|
||||
format!("{} KB", available_bandwidth / 1024)
|
||||
};
|
||||
tracing::debug!(
|
||||
"Remaining wireguard bandwidth with gateway {} for today: {}",
|
||||
self.auth_recipient.gateway(),
|
||||
remaining_pretty
|
||||
);
|
||||
if available_bandwidth < 1024 * 1024 {
|
||||
tracing::warn!(
|
||||
"Remaining bandwidth is under 1 MB. The wireguard mode will get suspended after that until tomorrow, UTC time. The client might shutdown with timeout soon"
|
||||
);
|
||||
}
|
||||
Ok(Some(available_bandwidth))
|
||||
}
|
||||
|
||||
pub async fn top_up(&mut self, credential: CredentialSpendingData) -> Result<i64> {
|
||||
let top_up_message = match self.auth_version {
|
||||
AuthenticatorVersion::V3 => ClientMessage::TopUp(Box::new(v3::topup::TopUpMessage {
|
||||
pub_key: PeerPublicKey::new(self.public_key.to_bytes().into()),
|
||||
credential,
|
||||
})),
|
||||
// NOTE: looks like a bug here using v3. But we're leaving it as is since it's working
|
||||
// and V4 is deprecated in favour of V5
|
||||
AuthenticatorVersion::V4 => ClientMessage::TopUp(Box::new(v4::topup::TopUpMessage {
|
||||
pub_key: PeerPublicKey::new(self.public_key.to_bytes().into()),
|
||||
credential,
|
||||
})),
|
||||
AuthenticatorVersion::V5 => ClientMessage::TopUp(Box::new(v5::topup::TopUpMessage {
|
||||
pub_key: PeerPublicKey::new(self.public_key.to_bytes().into()),
|
||||
credential,
|
||||
})),
|
||||
AuthenticatorVersion::V1 | AuthenticatorVersion::V2 | AuthenticatorVersion::UNKNOWN => {
|
||||
return Err(Error::UnsupportedAuthenticatorVersion);
|
||||
}
|
||||
};
|
||||
let response = self.send_and_wait_for_response(&top_up_message).await?;
|
||||
|
||||
let remaining_bandwidth = match response {
|
||||
AuthenticatorResponse::TopUpBandwidth(top_up_bandwidth_response) => {
|
||||
top_up_bandwidth_response.available_bandwidth()
|
||||
}
|
||||
_ => return Err(Error::InvalidGatewayAuthResponse),
|
||||
};
|
||||
|
||||
Ok(remaining_bandwidth)
|
||||
}
|
||||
}
|
||||
+265
-1129
File diff suppressed because it is too large
Load Diff
@@ -1,113 +1,160 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
// To remove with the Registration Client PR
|
||||
#![allow(clippy::unwrap_used)]
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use futures::StreamExt;
|
||||
use nym_sdk::mixnet::{MixnetClient, ReconstructedMessage};
|
||||
use tokio::{sync::broadcast, task::JoinHandle};
|
||||
use nym_sdk::mixnet::{InputMessage, MixnetClient, MixnetMessageSender, ReconstructedMessage};
|
||||
use tokio::{
|
||||
sync::{broadcast, mpsc},
|
||||
task::JoinHandle,
|
||||
};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
use crate::AuthenticatorMixnetClient;
|
||||
|
||||
pub type SharedMixnetClient = Arc<tokio::sync::Mutex<Option<MixnetClient>>>;
|
||||
pub type MixnetMessageBroadcastSender = broadcast::Sender<Arc<ReconstructedMessage>>;
|
||||
pub type MixnetMessageBroadcastReceiver = broadcast::Receiver<Arc<ReconstructedMessage>>;
|
||||
pub type MixnetMessageInputSender = mpsc::Sender<InputMessage>;
|
||||
pub type MixnetMessageInputReceiver = mpsc::Receiver<InputMessage>; // This could be another type, to abstract the mixnet message creation to here
|
||||
|
||||
// The AuthClientsMixnetListener listens to mixnet messages and rebroadcasts them to the
|
||||
// AuthClients, or whoever else is interested.
|
||||
// While it is running, it has a lock on the shared mixnet client. This is the reason it's
|
||||
// designed to be able to start and stop, so that the lock can be released when it's not needed.
|
||||
// It also manages the message input for the mixnet so it can keep the sole ownership of the MixnetClient
|
||||
//
|
||||
// NOTE: this is potentially bit wasteful. Ideally we should have proper channels where the
|
||||
// recipient only gets messages they're interested in.
|
||||
pub struct AuthClientMixnetListener {
|
||||
// The shared mixnet client that we're listening to
|
||||
mixnet_client: SharedMixnetClient,
|
||||
// The mixnet client that we're listening to
|
||||
mixnet_client: MixnetClient,
|
||||
|
||||
// Broadcast channel for the messages that we re-broadcast to the AuthClients
|
||||
message_broadcast: MixnetMessageBroadcastSender,
|
||||
|
||||
// Channel for message to send to the mixnet
|
||||
input_message_tx: MixnetMessageInputSender, // we keep on to make sure it's open
|
||||
input_message_rx: MixnetMessageInputReceiver,
|
||||
|
||||
// Listen to cancel from the outside world
|
||||
shutdown_token: CancellationToken,
|
||||
}
|
||||
|
||||
impl AuthClientMixnetListener {
|
||||
pub fn new(mixnet_client: SharedMixnetClient, shutdown_token: CancellationToken) -> Self {
|
||||
pub fn new(mixnet_client: MixnetClient, shutdown_token: CancellationToken) -> Self {
|
||||
let (message_broadcast, _) = broadcast::channel(100);
|
||||
let (input_message_tx, input_message_rx) = mpsc::channel(100);
|
||||
Self {
|
||||
mixnet_client,
|
||||
message_broadcast,
|
||||
input_message_tx,
|
||||
input_message_rx,
|
||||
shutdown_token,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn subscribe(&self) -> MixnetMessageBroadcastReceiver {
|
||||
self.message_broadcast.subscribe()
|
||||
}
|
||||
async fn run(mut self) -> Self {
|
||||
let mixnet_cancel_token = self.mixnet_client.cancellation_token();
|
||||
self.shutdown_token.run_until_cancelled(async {
|
||||
loop {
|
||||
tokio::select! {
|
||||
biased;
|
||||
_ = mixnet_cancel_token.cancelled() => {
|
||||
tracing::debug!("AuthClientMixnetListener: mixnet client was shutdown");
|
||||
break;
|
||||
}
|
||||
|
||||
async fn run(self) {
|
||||
let mut mixnet_client = self.mixnet_client.lock().await.take().unwrap();
|
||||
self.shutdown_token
|
||||
.run_until_cancelled(async {
|
||||
while let Some(event) = mixnet_client.next().await {
|
||||
if let Err(err) = self.message_broadcast.send(Arc::new(event)) {
|
||||
tracing::error!("Failed to broadcast mixnet message: {err}");
|
||||
// Sending loop
|
||||
input_msg = self.input_message_rx.recv() => {
|
||||
match input_msg {
|
||||
None => {
|
||||
tracing::error!("All senders were dropped. It shouldn't happen as we're holding one");
|
||||
break;
|
||||
},
|
||||
Some(mix_msg) => {
|
||||
if let Err(err) = self.mixnet_client.send(mix_msg).await {
|
||||
tracing::error!("Failed to send mixnet message: {err}");
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
// Receiving loop
|
||||
msg = self.mixnet_client.next() => {
|
||||
match msg {
|
||||
None => {
|
||||
tracing::error!("Mixnet client stream ended unexpectedly");
|
||||
break;
|
||||
},
|
||||
Some(event) => {
|
||||
if let Err(err) = self.message_broadcast.send(Arc::new(event)) {
|
||||
tracing::error!("Failed to broadcast mixnet message: {err}");
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
tracing::error!("Mixnet client stream ended unexpectedly");
|
||||
})
|
||||
.await;
|
||||
self.mixnet_client.lock().await.replace(mixnet_client);
|
||||
}
|
||||
tracing::debug!("AuthClientMixnetListener is shutting down");
|
||||
}).await;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
// Disconnects the mixnet client and effectively drop itself, since it doesn't work without one, and reconnecting isn't supported
|
||||
pub async fn disconnect_mixnet_client(self) {
|
||||
if !self.mixnet_client.cancellation_token().is_cancelled() {
|
||||
self.mixnet_client.disconnect().await;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(self) -> AuthClientMixnetListenerHandle {
|
||||
let mixnet_client = self.mixnet_client.clone();
|
||||
let message_broadcast = self.message_broadcast.clone();
|
||||
let message_sender = self.input_message_tx.clone();
|
||||
// Allows stopping only this, e.g. if we don't need it in the new bandwidth controller
|
||||
let cancellation_token = self.shutdown_token.clone();
|
||||
let mixnet_cancellation_token = self.mixnet_client.cancellation_token();
|
||||
let handle = tokio::spawn(self.run());
|
||||
|
||||
AuthClientMixnetListenerHandle {
|
||||
mixnet_client,
|
||||
message_broadcast,
|
||||
message_sender,
|
||||
cancellation_token,
|
||||
mixnet_cancellation_token,
|
||||
handle,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AuthClientMixnetListenerHandle {
|
||||
mixnet_client: SharedMixnetClient,
|
||||
message_broadcast: MixnetMessageBroadcastSender,
|
||||
handle: JoinHandle<()>,
|
||||
message_sender: MixnetMessageInputSender,
|
||||
cancellation_token: CancellationToken,
|
||||
mixnet_cancellation_token: CancellationToken,
|
||||
handle: JoinHandle<AuthClientMixnetListener>,
|
||||
}
|
||||
|
||||
impl AuthClientMixnetListenerHandle {
|
||||
/// Returns new `AuthClient` or `None` if `MixnetClient` is already moved from shared reference.
|
||||
pub async fn new_auth_client(&self) -> Option<AuthenticatorMixnetClient> {
|
||||
let mixnet_client_guard = self.mixnet_client.lock().await;
|
||||
let mixnet_client_ref = mixnet_client_guard.as_ref()?;
|
||||
let mixnet_sender = mixnet_client_ref.split_sender();
|
||||
let nym_address = *mixnet_client_ref.nym_address();
|
||||
|
||||
Some(
|
||||
AuthenticatorMixnetClient::new(
|
||||
mixnet_sender,
|
||||
self.message_broadcast.subscribe(),
|
||||
nym_address,
|
||||
)
|
||||
.await,
|
||||
)
|
||||
pub fn mixnet_sender(&self) -> MixnetMessageInputSender {
|
||||
self.message_sender.clone()
|
||||
}
|
||||
|
||||
pub fn subscribe(&self) -> MixnetMessageBroadcastReceiver {
|
||||
self.message_broadcast.subscribe()
|
||||
}
|
||||
|
||||
pub async fn wait(self) {
|
||||
if let Err(err) = self.handle.await {
|
||||
tracing::error!("Error waiting for auth clients mixnet listener to stop: {err}");
|
||||
pub fn mixnet_cancel_token(&self) -> CancellationToken {
|
||||
self.mixnet_cancellation_token.clone()
|
||||
}
|
||||
|
||||
pub async fn stop(self) {
|
||||
// If shutdown was externally called, that call is a no-op
|
||||
// If we're only stopping this, it is very much needed
|
||||
self.cancellation_token.cancel();
|
||||
match self.handle.await {
|
||||
Ok(auth_client_mixnet_listener) => {
|
||||
auth_client_mixnet_listener.disconnect_mixnet_client().await;
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("Error waiting for auth clients mixnet listener to stop: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ license.workspace = true
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
bincode.workspace = true
|
||||
bytes.workspace = true
|
||||
futures.workspace = true
|
||||
thiserror.workspace = true
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
// Copyright 2023-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
// To remove with the Registration Client PR
|
||||
#![allow(clippy::unwrap_used)]
|
||||
|
||||
use std::{sync::Arc, time::Duration};
|
||||
use std::time::Duration;
|
||||
|
||||
use nym_ip_packet_requests::IpPair;
|
||||
use nym_sdk::mixnet::{
|
||||
InputMessage, MixnetClient, MixnetClientSender, MixnetMessageSender, Recipient,
|
||||
TransmissionLane,
|
||||
InputMessage, MixnetClient, MixnetMessageSender, Recipient, TransmissionLane,
|
||||
};
|
||||
use tokio::time::sleep;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
@@ -27,8 +23,6 @@ use crate::{
|
||||
helpers::check_ipr_message_version,
|
||||
};
|
||||
|
||||
pub type SharedMixnetClient = Arc<tokio::sync::Mutex<Option<MixnetClient>>>;
|
||||
|
||||
const IPR_CONNECT_TIMEOUT: Duration = Duration::from_secs(10);
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
@@ -43,24 +37,24 @@ enum ConnectionState {
|
||||
pub struct IprClientConnect {
|
||||
// During connection we need the mixnet client, but once connected we expect to setup a channel
|
||||
// from the main mixnet listener at the top-level.
|
||||
// As such, we drop the shared mixnet client once we're connected.
|
||||
mixnet_client: SharedMixnetClient,
|
||||
mixnet_sender: MixnetClientSender,
|
||||
mixnet_client: MixnetClient,
|
||||
connected: ConnectionState,
|
||||
cancel_token: CancellationToken,
|
||||
}
|
||||
|
||||
impl IprClientConnect {
|
||||
pub async fn new(mixnet_client: SharedMixnetClient, cancel_token: CancellationToken) -> Self {
|
||||
let mixnet_sender = mixnet_client.lock().await.as_ref().unwrap().split_sender();
|
||||
pub async fn new(mixnet_client: MixnetClient, cancel_token: CancellationToken) -> Self {
|
||||
Self {
|
||||
mixnet_client,
|
||||
mixnet_sender,
|
||||
connected: ConnectionState::Disconnected,
|
||||
cancel_token,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_mixnet_client(self) -> MixnetClient {
|
||||
self.mixnet_client
|
||||
}
|
||||
|
||||
pub async fn connect(&mut self, ip_packet_router_address: Recipient) -> Result<IpPair> {
|
||||
if self.connected != ConnectionState::Disconnected {
|
||||
return Err(Error::AlreadyConnected);
|
||||
@@ -95,12 +89,12 @@ impl IprClientConnect {
|
||||
// We use 20 surbs for the connect request because typically the IPR is configured to have
|
||||
// a min threshold of 10 surbs that it reserves for itself to request additional surbs.
|
||||
let surbs = 20;
|
||||
self.mixnet_sender
|
||||
self.mixnet_client
|
||||
.send(create_input_message(
|
||||
ip_packet_router_address,
|
||||
request,
|
||||
surbs,
|
||||
))
|
||||
)?)
|
||||
.await
|
||||
.map_err(|err| Error::SdkError(Box::new(err)))?;
|
||||
|
||||
@@ -133,26 +127,31 @@ impl IprClientConnect {
|
||||
}
|
||||
}
|
||||
|
||||
async fn listen_for_connect_response(&self, request_id: u64) -> Result<IpPair> {
|
||||
async fn listen_for_connect_response(&mut self, request_id: u64) -> Result<IpPair> {
|
||||
// Connecting is basically synchronous from the perspective of the mixnet client, so it's safe
|
||||
// to just grab ahold of the mutex and keep it until we get the response.
|
||||
let mut mixnet_client_handle = self.mixnet_client.lock().await;
|
||||
let mixnet_client = mixnet_client_handle.as_mut().unwrap();
|
||||
|
||||
let timeout = sleep(IPR_CONNECT_TIMEOUT);
|
||||
tokio::pin!(timeout);
|
||||
|
||||
let mixnet_cancel_token = self.mixnet_client.cancellation_token();
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
_ = self.cancel_token.cancelled() => {
|
||||
error!("Cancelled while waiting for reply to connect request");
|
||||
return Err(Error::Cancelled);
|
||||
},
|
||||
|
||||
_ = mixnet_cancel_token.cancelled() => {
|
||||
error!("Mixnet client stopped while waiting for reply to connect request");
|
||||
return Err(Error::Cancelled);
|
||||
},
|
||||
_ = &mut timeout => {
|
||||
error!("Timed out waiting for reply to connect request");
|
||||
return Err(Error::TimeoutWaitingForConnectResponse);
|
||||
},
|
||||
msgs = mixnet_client.wait_for_messages() => match msgs {
|
||||
msgs = self.mixnet_client.wait_for_messages() => match msgs {
|
||||
None => {
|
||||
return Err(Error::NoMixnetMessagesReceived);
|
||||
}
|
||||
@@ -188,12 +187,12 @@ fn create_input_message(
|
||||
recipient: Recipient,
|
||||
request: IpPacketRequest,
|
||||
surbs: u32,
|
||||
) -> InputMessage {
|
||||
InputMessage::new_anonymous(
|
||||
) -> Result<InputMessage> {
|
||||
Ok(InputMessage::new_anonymous(
|
||||
recipient,
|
||||
request.to_bytes().unwrap(),
|
||||
request.to_bytes()?,
|
||||
surbs,
|
||||
TransmissionLane::General,
|
||||
None,
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
@@ -18,9 +18,6 @@ pub enum Error {
|
||||
)]
|
||||
ReceivedResponseWithNewVersion { expected: u8, received: u8 },
|
||||
|
||||
#[error("got reply for connect request, but it appears intended for the wrong address?")]
|
||||
GotReplyIntendedForWrongAddress,
|
||||
|
||||
#[error("unexpected connect response")]
|
||||
UnexpectedConnectResponse,
|
||||
|
||||
@@ -42,10 +39,8 @@ pub enum Error {
|
||||
#[error("already connected to the mixnet")]
|
||||
AlreadyConnected,
|
||||
|
||||
#[error("failed to create connect request")]
|
||||
FailedToCreateConnectRequest {
|
||||
source: nym_ip_packet_requests::sign::SignatureError,
|
||||
},
|
||||
#[error(transparent)]
|
||||
Bincode(#[from] bincode::Error),
|
||||
}
|
||||
|
||||
// Result type based on our error type
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "nym-wg-gateway-client"
|
||||
name = "nym-registration-client"
|
||||
version = "0.1.0"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
@@ -12,17 +12,17 @@ license.workspace = true
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
nym-authenticator-client = { path = "../nym-authenticator-client" }
|
||||
nym-authenticator-requests = { path = "../common/authenticator-requests" }
|
||||
nym-bandwidth-controller = { path = "../common/bandwidth-controller" }
|
||||
nym-credentials-interface = { path = "../common/credentials-interface" }
|
||||
nym-crypto = { path = "../common/crypto" }
|
||||
nym-node-requests = { path = "../nym-node/nym-node-requests" }
|
||||
nym-pemstore = { path = "../common/pemstore" }
|
||||
nym-sdk = { path = "../sdk/rust/nym-sdk" }
|
||||
nym-statistics-common = { path = "../common/statistics" }
|
||||
nym-validator-client = { path = "../common/client-libs/validator-client" }
|
||||
rand.workspace = true
|
||||
thiserror.workspace = true
|
||||
tokio.workspace = true
|
||||
tokio-util.workspace = true
|
||||
tracing.workspace = true
|
||||
url.workspace = true
|
||||
|
||||
nym-authenticator-client = { path = "../nym-authenticator-client" }
|
||||
nym-bandwidth-controller = { path = "../common/bandwidth-controller" }
|
||||
nym-credential-storage = { path = "../common/credential-storage" }
|
||||
nym-credentials-interface = { path = "../common/credentials-interface" }
|
||||
nym-ip-packet-client = { path = "../nym-ip-packet-client" }
|
||||
nym-registration-common = { path = "../common/registration" }
|
||||
nym-sdk = { path = "../sdk/rust/nym-sdk" }
|
||||
nym-validator-client = { path = "../common/client-libs/validator-client" }
|
||||
@@ -0,0 +1,206 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use nym_credential_storage::persistent_storage::PersistentStorage;
|
||||
use nym_registration_common::NymNode;
|
||||
use nym_sdk::{
|
||||
mixnet::{
|
||||
CredentialStorage, GatewaysDetailsStore, KeyStore, MixnetClient, MixnetClientBuilder,
|
||||
MixnetClientStorage, OnDiskPersistent, ReplyStorageBackend, StoragePaths,
|
||||
},
|
||||
DebugConfig, NymNetworkDetails, RememberMe, TopologyProvider, UserAgent,
|
||||
};
|
||||
|
||||
#[cfg(unix)]
|
||||
use std::{os::fd::RawFd, sync::Arc};
|
||||
use std::{path::PathBuf, time::Duration};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
use crate::error::RegistrationClientError;
|
||||
|
||||
const VPN_AVERAGE_PACKET_DELAY: Duration = Duration::from_millis(15);
|
||||
|
||||
pub struct BuilderConfig {
|
||||
pub entry_node: NymNode,
|
||||
pub exit_node: NymNode,
|
||||
pub data_path: Option<PathBuf>,
|
||||
pub mixnet_client_config: MixnetClientConfig,
|
||||
pub two_hops: bool,
|
||||
pub user_agent: UserAgent,
|
||||
pub custom_topology_provider: Box<dyn TopologyProvider + Send + Sync>,
|
||||
pub network_env: NymNetworkDetails,
|
||||
pub cancel_token: CancellationToken,
|
||||
#[cfg(unix)]
|
||||
pub connection_fd_callback: Arc<dyn Fn(RawFd) + Send + Sync>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug, Eq, PartialEq)]
|
||||
pub struct MixnetClientConfig {
|
||||
/// Disable Poission process rate limiting of outbound traffic.
|
||||
pub disable_poisson_rate: bool,
|
||||
|
||||
/// Disable constant rate background loop cover traffic
|
||||
pub disable_background_cover_traffic: bool,
|
||||
|
||||
/// The minimum performance of mixnodes to use.
|
||||
pub min_mixnode_performance: Option<u8>,
|
||||
|
||||
/// The minimum performance of gateways to use.
|
||||
pub min_gateway_performance: Option<u8>,
|
||||
}
|
||||
|
||||
impl BuilderConfig {
|
||||
pub fn mixnet_client_debug_config(&self) -> DebugConfig {
|
||||
if self.two_hops {
|
||||
two_hop_debug_config(&self.mixnet_client_config)
|
||||
} else {
|
||||
mixnet_debug_config(&self.mixnet_client_config)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn setup_storage(
|
||||
&self,
|
||||
) -> Result<Option<(OnDiskPersistent, PersistentStorage)>, RegistrationClientError> {
|
||||
if let Some(path) = &self.data_path {
|
||||
tracing::debug!("Using custom key storage path: {}", path.display());
|
||||
|
||||
let storage_paths = StoragePaths::new_from_dir(path)
|
||||
.map_err(|err| RegistrationClientError::BuildMixnetClient(Box::new(err)))?;
|
||||
|
||||
let mixnet_client_storage = storage_paths
|
||||
.initialise_persistent_storage(&self.mixnet_client_debug_config())
|
||||
.await
|
||||
.map_err(|err| RegistrationClientError::BuildMixnetClient(Box::new(err)))?;
|
||||
let credential_storage = storage_paths
|
||||
.persistent_credential_storage()
|
||||
.await
|
||||
.map_err(|err| RegistrationClientError::BuildMixnetClient(Box::new(err)))?;
|
||||
|
||||
Ok(Some((mixnet_client_storage, credential_storage)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn build_and_connect_mixnet_client<S>(
|
||||
self,
|
||||
builder: MixnetClientBuilder<S>,
|
||||
) -> Result<MixnetClient, RegistrationClientError>
|
||||
where
|
||||
S: MixnetClientStorage + Clone + 'static,
|
||||
S::ReplyStore: Send + Sync,
|
||||
S::GatewaysDetailsStore: Sync,
|
||||
<S::ReplyStore as ReplyStorageBackend>::StorageError: Sync + Send,
|
||||
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync,
|
||||
<S::KeyStore as KeyStore>::StorageError: Send + Sync,
|
||||
<S::GatewaysDetailsStore as GatewaysDetailsStore>::StorageError: Send + Sync,
|
||||
{
|
||||
let debug_config = self.mixnet_client_debug_config();
|
||||
let remember_me = if self.two_hops {
|
||||
RememberMe::new_vpn()
|
||||
} else {
|
||||
RememberMe::new_mixnet()
|
||||
};
|
||||
|
||||
let builder = builder
|
||||
.with_user_agent(self.user_agent)
|
||||
.request_gateway(self.entry_node.identity.to_string())
|
||||
.network_details(self.network_env)
|
||||
.debug_config(debug_config)
|
||||
.credentials_mode(true)
|
||||
.with_remember_me(remember_me)
|
||||
.custom_topology_provider(self.custom_topology_provider);
|
||||
|
||||
#[cfg(unix)]
|
||||
let builder = builder.with_connection_fd_callback(self.connection_fd_callback);
|
||||
|
||||
builder
|
||||
.build()
|
||||
.map_err(|err| RegistrationClientError::BuildMixnetClient(Box::new(err)))?
|
||||
.connect_to_mixnet()
|
||||
.await
|
||||
.map_err(|err| RegistrationClientError::ConnectToMixnet(Box::new(err)))
|
||||
}
|
||||
}
|
||||
|
||||
fn two_hop_debug_config(mixnet_client_config: &MixnetClientConfig) -> DebugConfig {
|
||||
let mut debug_config = DebugConfig::default();
|
||||
|
||||
debug_config.traffic.average_packet_delay = VPN_AVERAGE_PACKET_DELAY;
|
||||
|
||||
// We disable mix hops for the mixnet connection.
|
||||
debug_config.traffic.disable_mix_hops = true;
|
||||
// Always disable poisson process for outbound traffic in wireguard.
|
||||
debug_config
|
||||
.traffic
|
||||
.disable_main_poisson_packet_distribution = true;
|
||||
// Always disable background cover traffic in wireguard.
|
||||
debug_config.cover_traffic.disable_loop_cover_traffic_stream = true;
|
||||
|
||||
if let Some(min_mixnode_performance) = mixnet_client_config.min_mixnode_performance {
|
||||
debug_config.topology.minimum_mixnode_performance = min_mixnode_performance;
|
||||
}
|
||||
|
||||
if let Some(min_gateway_performance) = mixnet_client_config.min_gateway_performance {
|
||||
debug_config.topology.minimum_gateway_performance = min_gateway_performance;
|
||||
}
|
||||
|
||||
log_mixnet_client_config(&debug_config);
|
||||
debug_config
|
||||
}
|
||||
|
||||
fn mixnet_debug_config(mixnet_client_config: &MixnetClientConfig) -> DebugConfig {
|
||||
let mut debug_config = DebugConfig::default();
|
||||
debug_config.traffic.average_packet_delay = VPN_AVERAGE_PACKET_DELAY;
|
||||
|
||||
debug_config
|
||||
.traffic
|
||||
.disable_main_poisson_packet_distribution = mixnet_client_config.disable_poisson_rate;
|
||||
|
||||
debug_config.cover_traffic.disable_loop_cover_traffic_stream =
|
||||
mixnet_client_config.disable_background_cover_traffic;
|
||||
|
||||
if let Some(min_mixnode_performance) = mixnet_client_config.min_mixnode_performance {
|
||||
debug_config.topology.minimum_mixnode_performance = min_mixnode_performance;
|
||||
}
|
||||
|
||||
if let Some(min_gateway_performance) = mixnet_client_config.min_gateway_performance {
|
||||
debug_config.topology.minimum_gateway_performance = min_gateway_performance;
|
||||
}
|
||||
log_mixnet_client_config(&debug_config);
|
||||
debug_config
|
||||
}
|
||||
|
||||
fn log_mixnet_client_config(debug_config: &DebugConfig) {
|
||||
tracing::info!(
|
||||
"mixnet client poisson rate limiting: {}",
|
||||
true_to_disabled(
|
||||
debug_config
|
||||
.traffic
|
||||
.disable_main_poisson_packet_distribution
|
||||
)
|
||||
);
|
||||
|
||||
tracing::info!(
|
||||
"mixnet client background loop cover traffic stream: {}",
|
||||
true_to_disabled(debug_config.cover_traffic.disable_loop_cover_traffic_stream)
|
||||
);
|
||||
|
||||
tracing::info!(
|
||||
"mixnet client minimum mixnode performance: {}",
|
||||
debug_config.topology.minimum_mixnode_performance,
|
||||
);
|
||||
|
||||
tracing::info!(
|
||||
"mixnet client minimum gateway performance: {}",
|
||||
debug_config.topology.minimum_gateway_performance,
|
||||
);
|
||||
}
|
||||
|
||||
fn true_to_disabled(val: bool) -> &'static str {
|
||||
if val {
|
||||
"disabled"
|
||||
} else {
|
||||
"enabled"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_bandwidth_controller::{BandwidthController, BandwidthTicketProvider};
|
||||
use nym_credential_storage::ephemeral_storage::EphemeralCredentialStorage;
|
||||
use nym_sdk::{
|
||||
mixnet::{MixnetClient, MixnetClientBuilder},
|
||||
NymNetworkDetails,
|
||||
};
|
||||
use nym_validator_client::{
|
||||
nyxd::{Config as NyxdClientConfig, NyxdClient},
|
||||
QueryHttpRpcNyxdClient,
|
||||
};
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::{config::RegistrationClientConfig, error::RegistrationClientError, RegistrationClient};
|
||||
use config::BuilderConfig;
|
||||
|
||||
pub(crate) mod config;
|
||||
|
||||
pub(crate) const MIXNET_CLIENT_STARTUP_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
|
||||
pub struct RegistrationClientBuilder {
|
||||
pub config: BuilderConfig,
|
||||
}
|
||||
|
||||
impl RegistrationClientBuilder {
|
||||
pub fn new(config: BuilderConfig) -> Self {
|
||||
Self { config }
|
||||
}
|
||||
|
||||
pub async fn build(self) -> Result<RegistrationClient, RegistrationClientError> {
|
||||
let storage = self.config.setup_storage().await?;
|
||||
let config = RegistrationClientConfig {
|
||||
entry: self.config.entry_node,
|
||||
exit: self.config.exit_node,
|
||||
two_hops: self.config.two_hops,
|
||||
data_path: self.config.data_path.clone(),
|
||||
};
|
||||
let cancel_token = self.config.cancel_token.clone();
|
||||
|
||||
let nyxd_client = get_nyxd_client(&self.config.network_env)?;
|
||||
|
||||
let (mixnet_client, bandwidth_controller): (
|
||||
MixnetClient,
|
||||
Box<dyn BandwidthTicketProvider>,
|
||||
) = if let Some((mixnet_client_storage, credential_storage)) = storage {
|
||||
let builder = MixnetClientBuilder::new_with_storage(mixnet_client_storage);
|
||||
let mixnet_client = tokio::time::timeout(
|
||||
MIXNET_CLIENT_STARTUP_TIMEOUT,
|
||||
self.config.build_and_connect_mixnet_client(builder),
|
||||
)
|
||||
.await??;
|
||||
let bandwidth_controller =
|
||||
Box::new(BandwidthController::new(credential_storage, nyxd_client));
|
||||
(mixnet_client, bandwidth_controller)
|
||||
} else {
|
||||
let builder = MixnetClientBuilder::new_ephemeral();
|
||||
let mixnet_client = tokio::time::timeout(
|
||||
MIXNET_CLIENT_STARTUP_TIMEOUT,
|
||||
self.config.build_and_connect_mixnet_client(builder),
|
||||
)
|
||||
.await??;
|
||||
let bandwidth_controller = Box::new(BandwidthController::new(
|
||||
EphemeralCredentialStorage::default(),
|
||||
nyxd_client,
|
||||
));
|
||||
(mixnet_client, bandwidth_controller)
|
||||
};
|
||||
let mixnet_client_address = *mixnet_client.nym_address();
|
||||
|
||||
Ok(RegistrationClient {
|
||||
mixnet_client,
|
||||
config,
|
||||
cancel_token,
|
||||
mixnet_client_address,
|
||||
bandwidth_controller,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// temporary while we use the legacy bandwidth-controller
|
||||
fn get_nyxd_client(
|
||||
network: &NymNetworkDetails,
|
||||
) -> Result<QueryHttpRpcNyxdClient, RegistrationClientError> {
|
||||
let config = NyxdClientConfig::try_from_nym_network_details(network)
|
||||
.map_err(RegistrationClientError::FailedToCreateNyxdClientConfig)?;
|
||||
let nyxd_url = network
|
||||
.endpoints
|
||||
.first()
|
||||
.map(|ep| ep.nyxd_url())
|
||||
.ok_or(RegistrationClientError::InvalidNyxdUrl)?;
|
||||
|
||||
NyxdClient::connect(config, nyxd_url.as_str())
|
||||
.map_err(RegistrationClientError::FailedToConnectUsingNyxdClient)
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use nym_registration_common::NymNode;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub struct RegistrationClientConfig {
|
||||
pub(crate) entry: NymNode,
|
||||
pub(crate) exit: NymNode,
|
||||
pub(crate) two_hops: bool,
|
||||
pub(crate) data_path: Option<PathBuf>,
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum RegistrationClientError {
|
||||
#[error("failed to build mixnet client")]
|
||||
BuildMixnetClient(#[source] Box<nym_sdk::Error>),
|
||||
|
||||
#[error("failed to connect to mixnet")]
|
||||
ConnectToMixnet(#[source] Box<nym_sdk::Error>),
|
||||
|
||||
#[error("failed to connect to ip packet router")]
|
||||
ConnectToIpPacketRouter(#[source] nym_ip_packet_client::Error),
|
||||
|
||||
#[error("the selected node does not have an IP packet router : {node_id}")]
|
||||
NoIpPacketRouterAddress { node_id: String },
|
||||
|
||||
#[error(
|
||||
"wireguard authentication is not possible due to one of the gateways not running the authenticator process: {node_id} "
|
||||
)]
|
||||
AuthenticationNotPossible { node_id: String },
|
||||
|
||||
#[error("Failed to create nyxd client config")]
|
||||
FailedToCreateNyxdClientConfig(nym_validator_client::nyxd::error::NyxdError),
|
||||
|
||||
#[error("failed to parse nyxd_url")]
|
||||
InvalidNyxdUrl,
|
||||
|
||||
#[error("Failed to connect using nyxd client")]
|
||||
FailedToConnectUsingNyxdClient(nym_validator_client::nyxd::error::NyxdError),
|
||||
|
||||
#[error("connection cancelled")]
|
||||
Cancelled,
|
||||
|
||||
#[error("timeout connecting the mixnet client")]
|
||||
Timeout(#[from] tokio::time::error::Elapsed),
|
||||
|
||||
#[error("failed to register wireguard with the gateway for {gateway_id}")]
|
||||
EntryGatewayRegisterWireguard {
|
||||
gateway_id: String,
|
||||
authenticator_address: Box<nym_sdk::mixnet::Recipient>,
|
||||
#[source]
|
||||
source: Box<nym_authenticator_client::Error>,
|
||||
},
|
||||
|
||||
#[error("failed to register wireguard with the gateway for {gateway_id}")]
|
||||
ExitGatewayRegisterWireguard {
|
||||
gateway_id: String,
|
||||
authenticator_address: Box<nym_sdk::mixnet::Recipient>,
|
||||
#[source]
|
||||
source: Box<nym_authenticator_client::Error>,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
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::{MixnetClient, Recipient};
|
||||
|
||||
use crate::config::RegistrationClientConfig;
|
||||
|
||||
mod builder;
|
||||
mod config;
|
||||
mod error;
|
||||
mod types;
|
||||
|
||||
pub use builder::config::{BuilderConfig as RegistrationClientBuilderConfig, MixnetClientConfig};
|
||||
pub use builder::RegistrationClientBuilder;
|
||||
pub use error::RegistrationClientError;
|
||||
pub use types::{MixnetRegistrationResult, RegistrationResult, WireguardRegistrationResult};
|
||||
|
||||
pub struct RegistrationClient {
|
||||
mixnet_client: MixnetClient,
|
||||
config: RegistrationClientConfig,
|
||||
mixnet_client_address: Recipient,
|
||||
bandwidth_controller: Box<dyn BandwidthTicketProvider>,
|
||||
cancel_token: CancellationToken,
|
||||
}
|
||||
|
||||
impl RegistrationClient {
|
||||
async fn register_mix_exit(self) -> Result<RegistrationResult, RegistrationClientError> {
|
||||
let entry_mixnet_gateway_ip = self.config.entry.ip_address;
|
||||
|
||||
let exit_mixnet_gateway_ip = self.config.exit.ip_address;
|
||||
|
||||
let ipr_address = self.config.exit.ipr_address.ok_or(
|
||||
RegistrationClientError::NoIpPacketRouterAddress {
|
||||
node_id: self.config.exit.identity.to_base58_string(),
|
||||
},
|
||||
)?;
|
||||
let mut ipr_client =
|
||||
IprClientConnect::new(self.mixnet_client, self.cancel_token.clone()).await;
|
||||
let interface_addresses = ipr_client
|
||||
.connect(ipr_address)
|
||||
.await
|
||||
.map_err(RegistrationClientError::ConnectToIpPacketRouter)?;
|
||||
|
||||
Ok(RegistrationResult::Mixnet(Box::new(
|
||||
MixnetRegistrationResult {
|
||||
mixnet_client: ipr_client.into_mixnet_client(),
|
||||
assigned_addresses: AssignedAddresses {
|
||||
interface_addresses,
|
||||
exit_mix_address: ipr_address,
|
||||
mixnet_client_address: self.mixnet_client_address,
|
||||
entry_mixnet_gateway_ip,
|
||||
exit_mixnet_gateway_ip,
|
||||
},
|
||||
},
|
||||
)))
|
||||
}
|
||||
|
||||
async fn register_wg(self) -> Result<RegistrationResult, RegistrationClientError> {
|
||||
let entry_auth_address = self.config.entry.authenticator_address.ok_or(
|
||||
RegistrationClientError::AuthenticationNotPossible {
|
||||
node_id: self.config.entry.identity.to_base58_string(),
|
||||
},
|
||||
)?;
|
||||
|
||||
let exit_auth_address = self.config.exit.authenticator_address.ok_or(
|
||||
RegistrationClientError::AuthenticationNotPossible {
|
||||
node_id: self.config.exit.identity.to_base58_string(),
|
||||
},
|
||||
)?;
|
||||
|
||||
let entry_version = self.config.entry.version;
|
||||
tracing::debug!("Entry gateway version: {entry_version}");
|
||||
let exit_version = self.config.exit.version;
|
||||
tracing::debug!("Exit gateway version: {exit_version}");
|
||||
|
||||
// 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.clone()).start();
|
||||
|
||||
let mut entry_auth_client = AuthenticatorClient::new_entry(
|
||||
&self.config.data_path,
|
||||
mixnet_listener.subscribe(),
|
||||
mixnet_listener.mixnet_sender(),
|
||||
self.mixnet_client_address,
|
||||
entry_auth_address,
|
||||
entry_version,
|
||||
self.config.entry.ip_address,
|
||||
);
|
||||
|
||||
let mut exit_auth_client = AuthenticatorClient::new_exit(
|
||||
&self.config.data_path,
|
||||
mixnet_listener.subscribe(),
|
||||
mixnet_listener.mixnet_sender(),
|
||||
self.mixnet_client_address,
|
||||
exit_auth_address,
|
||||
exit_version,
|
||||
self.config.exit.ip_address,
|
||||
);
|
||||
|
||||
let entry_fut = entry_auth_client
|
||||
.register_wireguard(&*self.bandwidth_controller, TicketType::V1WireguardEntry);
|
||||
let exit_fut = exit_auth_client
|
||||
.register_wireguard(&*self.bandwidth_controller, TicketType::V1WireguardExit);
|
||||
|
||||
let (entry, exit) = Box::pin(async { tokio::join!(entry_fut, exit_fut) }).await;
|
||||
|
||||
let entry =
|
||||
entry.map_err(
|
||||
|source| RegistrationClientError::EntryGatewayRegisterWireguard {
|
||||
gateway_id: self.config.entry.identity.to_base58_string(),
|
||||
authenticator_address: Box::new(entry_auth_address),
|
||||
source: Box::new(source),
|
||||
},
|
||||
)?;
|
||||
let exit =
|
||||
exit.map_err(
|
||||
|source| RegistrationClientError::ExitGatewayRegisterWireguard {
|
||||
gateway_id: self.config.exit.identity.to_base58_string(),
|
||||
authenticator_address: Box::new(exit_auth_address),
|
||||
source: Box::new(source),
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(RegistrationResult::Wireguard(Box::new(
|
||||
WireguardRegistrationResult {
|
||||
entry_gateway_client: entry_auth_client,
|
||||
exit_gateway_client: exit_auth_client,
|
||||
entry_gateway_data: entry,
|
||||
exit_gateway_data: exit,
|
||||
authenticator_listener_handle: mixnet_listener,
|
||||
bw_controller: self.bandwidth_controller,
|
||||
},
|
||||
)))
|
||||
}
|
||||
|
||||
pub async fn register(self) -> Result<RegistrationResult, RegistrationClientError> {
|
||||
self.cancel_token
|
||||
.clone()
|
||||
.run_until_cancelled(async {
|
||||
if self.config.two_hops {
|
||||
self.register_wg().await
|
||||
} else {
|
||||
self.register_mix_exit().await
|
||||
}
|
||||
})
|
||||
.await
|
||||
.ok_or(RegistrationClientError::Cancelled)?
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_authenticator_client::{AuthClientMixnetListenerHandle, AuthenticatorClient};
|
||||
use nym_bandwidth_controller::BandwidthTicketProvider;
|
||||
use nym_registration_common::{AssignedAddresses, GatewayData};
|
||||
use nym_sdk::mixnet::MixnetClient;
|
||||
|
||||
pub enum RegistrationResult {
|
||||
Mixnet(Box<MixnetRegistrationResult>),
|
||||
Wireguard(Box<WireguardRegistrationResult>),
|
||||
}
|
||||
|
||||
pub struct MixnetRegistrationResult {
|
||||
pub assigned_addresses: AssignedAddresses,
|
||||
pub mixnet_client: MixnetClient,
|
||||
}
|
||||
|
||||
pub struct WireguardRegistrationResult {
|
||||
pub entry_gateway_client: AuthenticatorClient,
|
||||
pub exit_gateway_client: AuthenticatorClient,
|
||||
pub entry_gateway_data: GatewayData,
|
||||
pub exit_gateway_data: GatewayData,
|
||||
pub authenticator_listener_handle: AuthClientMixnetListenerHandle,
|
||||
pub bw_controller: Box<dyn BandwidthTicketProvider>,
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use nym_authenticator_client::{
|
||||
AuthenticatorClient, AuthenticatorResponse, AuthenticatorVersion, ClientMessage,
|
||||
QueryMessageImpl,
|
||||
};
|
||||
use nym_authenticator_requests::{v3, v4, v5};
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
use nym_crypto::asymmetric::encryption;
|
||||
use nym_node_requests::api::v1::gateway::client_interfaces::wireguard::models::PeerPublicKey;
|
||||
use nym_sdk::mixnet::Recipient;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
|
||||
const RETRY_PERIOD: Duration = Duration::from_secs(30);
|
||||
|
||||
impl crate::WgGatewayClient {
|
||||
pub fn light_client(&self) -> WgGatewayLightClient {
|
||||
WgGatewayLightClient {
|
||||
public_key: *self.keypair.public_key(),
|
||||
auth_client: self.auth_client.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WgGatewayLightClient {
|
||||
public_key: encryption::PublicKey,
|
||||
auth_client: AuthenticatorClient,
|
||||
}
|
||||
impl WgGatewayLightClient {
|
||||
pub fn auth_recipient(&self) -> Recipient {
|
||||
self.auth_client.auth_recipient()
|
||||
}
|
||||
|
||||
pub fn auth_client(&self) -> &AuthenticatorClient {
|
||||
&self.auth_client
|
||||
}
|
||||
|
||||
pub fn set_auth_client(&mut self, auth_client: AuthenticatorClient) {
|
||||
self.auth_client = auth_client;
|
||||
}
|
||||
pub async fn query_bandwidth(&mut self) -> Result<Option<i64>> {
|
||||
let query_message = match self.auth_client.auth_version() {
|
||||
AuthenticatorVersion::V2 => ClientMessage::Query(Box::new(QueryMessageImpl {
|
||||
pub_key: PeerPublicKey::new(self.public_key.to_bytes().into()),
|
||||
version: AuthenticatorVersion::V2,
|
||||
})),
|
||||
AuthenticatorVersion::V3 => ClientMessage::Query(Box::new(QueryMessageImpl {
|
||||
pub_key: PeerPublicKey::new(self.public_key.to_bytes().into()),
|
||||
version: AuthenticatorVersion::V3,
|
||||
})),
|
||||
AuthenticatorVersion::V4 => ClientMessage::Query(Box::new(QueryMessageImpl {
|
||||
pub_key: PeerPublicKey::new(self.public_key.to_bytes().into()),
|
||||
version: AuthenticatorVersion::V4,
|
||||
})),
|
||||
AuthenticatorVersion::V5 => ClientMessage::Query(Box::new(QueryMessageImpl {
|
||||
pub_key: PeerPublicKey::new(self.public_key.to_bytes().into()),
|
||||
version: AuthenticatorVersion::V5,
|
||||
})),
|
||||
AuthenticatorVersion::UNKNOWN => return Err(Error::UnsupportedAuthenticatorVersion),
|
||||
};
|
||||
let response = self.auth_client.send(&query_message).await?;
|
||||
|
||||
let available_bandwidth = match response {
|
||||
nym_authenticator_client::AuthenticatorResponse::RemainingBandwidth(
|
||||
remaining_bandwidth_response,
|
||||
) => {
|
||||
if let Some(available_bandwidth) =
|
||||
remaining_bandwidth_response.available_bandwidth()
|
||||
{
|
||||
available_bandwidth
|
||||
} else {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
_ => return Err(Error::InvalidGatewayAuthResponse),
|
||||
};
|
||||
|
||||
let remaining_pretty = if available_bandwidth > 1024 * 1024 {
|
||||
format!("{:.2} MB", available_bandwidth as f64 / 1024.0 / 1024.0)
|
||||
} else {
|
||||
format!("{} KB", available_bandwidth / 1024)
|
||||
};
|
||||
tracing::debug!(
|
||||
"Remaining wireguard bandwidth with gateway {} for today: {}",
|
||||
self.auth_client.auth_recipient().gateway(),
|
||||
remaining_pretty
|
||||
);
|
||||
if available_bandwidth < 1024 * 1024 {
|
||||
tracing::warn!(
|
||||
"Remaining bandwidth is under 1 MB. The wireguard mode will get suspended after that until tomorrow, UTC time. The client might shutdown with timeout soon"
|
||||
);
|
||||
}
|
||||
Ok(Some(available_bandwidth))
|
||||
}
|
||||
async fn send(&mut self, msg: ClientMessage) -> Result<AuthenticatorResponse> {
|
||||
let now = std::time::Instant::now();
|
||||
while now.elapsed() < RETRY_PERIOD {
|
||||
match self.auth_client.send(&msg).await {
|
||||
Ok(response) => return Ok(response),
|
||||
Err(nym_authenticator_client::Error::TimeoutWaitingForConnectResponse) => continue,
|
||||
Err(source) => {
|
||||
if msg.is_wasteful() {
|
||||
return Err(Error::NoRetry { source });
|
||||
} else {
|
||||
return Err(Error::AuthenticatorClientError(source));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if msg.is_wasteful() {
|
||||
Err(Error::NoRetry {
|
||||
source: nym_authenticator_client::Error::TimeoutWaitingForConnectResponse,
|
||||
})
|
||||
} else {
|
||||
Err(Error::AuthenticatorClientError(
|
||||
nym_authenticator_client::Error::TimeoutWaitingForConnectResponse,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn top_up(&mut self, credential: CredentialSpendingData) -> Result<i64> {
|
||||
let top_up_message = match self.auth_client.auth_version() {
|
||||
AuthenticatorVersion::V3 => ClientMessage::TopUp(Box::new(v3::topup::TopUpMessage {
|
||||
pub_key: PeerPublicKey::new(self.public_key.to_bytes().into()),
|
||||
credential,
|
||||
})),
|
||||
// NOTE: looks like a bug here using v3. But we're leaving it as is since it's working
|
||||
// and V4 is deprecated in favour of V5
|
||||
AuthenticatorVersion::V4 => ClientMessage::TopUp(Box::new(v4::topup::TopUpMessage {
|
||||
pub_key: PeerPublicKey::new(self.public_key.to_bytes().into()),
|
||||
credential,
|
||||
})),
|
||||
AuthenticatorVersion::V5 => ClientMessage::TopUp(Box::new(v5::topup::TopUpMessage {
|
||||
pub_key: PeerPublicKey::new(self.public_key.to_bytes().into()),
|
||||
credential,
|
||||
})),
|
||||
AuthenticatorVersion::V2 | AuthenticatorVersion::UNKNOWN => {
|
||||
return Err(Error::UnsupportedAuthenticatorVersion);
|
||||
}
|
||||
};
|
||||
let response = self.send(top_up_message).await?;
|
||||
|
||||
let remaining_bandwidth = match response {
|
||||
AuthenticatorResponse::TopUpBandwidth(top_up_bandwidth_response) => {
|
||||
top_up_bandwidth_response.available_bandwidth()
|
||||
}
|
||||
_ => return Err(Error::InvalidGatewayAuthResponse),
|
||||
};
|
||||
|
||||
Ok(remaining_bandwidth)
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use nym_credentials_interface::TicketType;
|
||||
use nym_sdk::mixnet::NodeIdentity;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error("received invalid response from gateway authenticator")]
|
||||
InvalidGatewayAuthResponse,
|
||||
|
||||
#[error("unknown authenticator version number")]
|
||||
UnsupportedAuthenticatorVersion,
|
||||
|
||||
#[error(transparent)]
|
||||
AuthenticatorClientError(#[from] nym_authenticator_client::Error),
|
||||
|
||||
#[error("error that should stop auto retrying")]
|
||||
NoRetry {
|
||||
#[source]
|
||||
source: nym_authenticator_client::Error,
|
||||
},
|
||||
|
||||
#[error("verification failure")]
|
||||
VerificationFailed(#[source] nym_authenticator_requests::Error),
|
||||
|
||||
#[error("failed to parse entry gateway socket addr")]
|
||||
FailedToParseEntryGatewaySocketAddr(#[source] std::net::AddrParseError),
|
||||
|
||||
#[error("failed to get {ticketbook_type} ticket")]
|
||||
GetTicket {
|
||||
ticketbook_type: TicketType,
|
||||
#[source]
|
||||
source: nym_bandwidth_controller::error::BandwidthControllerError,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ErrorMessage {
|
||||
#[error("out of bandwidth for gateway: {gateway_id}")]
|
||||
OutOfBandwidth { gateway_id: Box<NodeIdentity> },
|
||||
|
||||
#[error("gateway {gateway_id} is erroring out")]
|
||||
ErrorsFromGateway { gateway_id: Box<NodeIdentity> },
|
||||
}
|
||||
|
||||
// Result type based on our error type
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
@@ -1,302 +0,0 @@
|
||||
// Copyright 2023-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
pub mod deprecated;
|
||||
mod error;
|
||||
|
||||
use std::{
|
||||
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
|
||||
path::PathBuf,
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
pub use error::{Error, ErrorMessage};
|
||||
use nym_authenticator_client::{
|
||||
AuthenticatorClient, AuthenticatorMixnetClient, AuthenticatorResponse, AuthenticatorVersion,
|
||||
ClientMessage,
|
||||
};
|
||||
use nym_authenticator_requests::{v2, v3, v4, v5};
|
||||
use nym_bandwidth_controller::PreparedCredential;
|
||||
use nym_credentials_interface::TicketType;
|
||||
use nym_crypto::asymmetric::{encryption, x25519::KeyPair, x25519::PublicKey};
|
||||
use nym_node_requests::api::v1::gateway::client_interfaces::wireguard::models::PeerPublicKey;
|
||||
use nym_pemstore::KeyPairPath;
|
||||
use nym_sdk::mixnet::{CredentialStorage, NodeIdentity, Recipient};
|
||||
use nym_validator_client::QueryHttpRpcNyxdClient;
|
||||
use rand::{rngs::OsRng, CryptoRng, RngCore};
|
||||
use tracing::{debug, error, trace};
|
||||
|
||||
use crate::error::Result;
|
||||
|
||||
pub const DEFAULT_PRIVATE_ENTRY_WIREGUARD_KEY_FILENAME: &str = "free_private_entry_wireguard.pem";
|
||||
pub const DEFAULT_PUBLIC_ENTRY_WIREGUARD_KEY_FILENAME: &str = "free_public_entry_wireguard.pem";
|
||||
pub const DEFAULT_PRIVATE_EXIT_WIREGUARD_KEY_FILENAME: &str = "free_private_exit_wireguard.pem";
|
||||
pub const DEFAULT_PUBLIC_EXIT_WIREGUARD_KEY_FILENAME: &str = "free_public_exit_wireguard.pem";
|
||||
|
||||
pub const TICKETS_TO_SPEND: u32 = 1;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GatewayData {
|
||||
pub public_key: PublicKey,
|
||||
pub endpoint: SocketAddr,
|
||||
pub private_ipv4: Ipv4Addr,
|
||||
pub private_ipv6: Ipv6Addr,
|
||||
}
|
||||
|
||||
pub struct WgGatewayClient {
|
||||
keypair: encryption::KeyPair,
|
||||
auth_client: AuthenticatorClient,
|
||||
}
|
||||
|
||||
impl WgGatewayClient {
|
||||
fn new_type(
|
||||
data_path: &Option<PathBuf>,
|
||||
auth_mix_client: AuthenticatorMixnetClient,
|
||||
auth_recipient: Recipient,
|
||||
auth_version: AuthenticatorVersion,
|
||||
private_file_name: &str,
|
||||
public_file_name: &str,
|
||||
) -> Self {
|
||||
let mut rng = OsRng;
|
||||
let auth_client = AuthenticatorClient::new(auth_mix_client, auth_recipient, auth_version);
|
||||
if let Some(data_path) = data_path {
|
||||
let paths = KeyPairPath::new(
|
||||
data_path.join(private_file_name),
|
||||
data_path.join(public_file_name),
|
||||
);
|
||||
let keypair = load_or_generate_keypair(&mut rng, paths);
|
||||
WgGatewayClient {
|
||||
keypair,
|
||||
auth_client,
|
||||
}
|
||||
} else {
|
||||
WgGatewayClient {
|
||||
keypair: KeyPair::new(&mut rng),
|
||||
auth_client,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_entry(
|
||||
data_path: &Option<PathBuf>,
|
||||
auth_mix_client: AuthenticatorMixnetClient,
|
||||
auth_recipient: Recipient,
|
||||
auth_version: AuthenticatorVersion,
|
||||
) -> Self {
|
||||
Self::new_type(
|
||||
data_path,
|
||||
auth_mix_client,
|
||||
auth_recipient,
|
||||
auth_version,
|
||||
DEFAULT_PRIVATE_ENTRY_WIREGUARD_KEY_FILENAME,
|
||||
DEFAULT_PUBLIC_ENTRY_WIREGUARD_KEY_FILENAME,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_exit(
|
||||
data_path: &Option<PathBuf>,
|
||||
auth_mix_client: AuthenticatorMixnetClient,
|
||||
auth_recipient: Recipient,
|
||||
auth_version: AuthenticatorVersion,
|
||||
) -> Self {
|
||||
Self::new_type(
|
||||
data_path,
|
||||
auth_mix_client,
|
||||
auth_recipient,
|
||||
auth_version,
|
||||
DEFAULT_PRIVATE_EXIT_WIREGUARD_KEY_FILENAME,
|
||||
DEFAULT_PUBLIC_EXIT_WIREGUARD_KEY_FILENAME,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn keypair(&self) -> &encryption::KeyPair {
|
||||
&self.keypair
|
||||
}
|
||||
|
||||
pub fn auth_recipient(&self) -> Recipient {
|
||||
self.auth_client.auth_recipient()
|
||||
}
|
||||
|
||||
pub fn auth_version(&self) -> AuthenticatorVersion {
|
||||
self.auth_client.auth_version()
|
||||
}
|
||||
|
||||
pub async fn request_bandwidth<St: CredentialStorage>(
|
||||
gateway_id: NodeIdentity,
|
||||
controller: &nym_bandwidth_controller::BandwidthController<QueryHttpRpcNyxdClient, St>,
|
||||
ticketbook_type: TicketType,
|
||||
) -> Result<PreparedCredential>
|
||||
where
|
||||
<St as CredentialStorage>::StorageError: Send + Sync + 'static,
|
||||
{
|
||||
let credential = controller
|
||||
.prepare_ecash_ticket(ticketbook_type, gateway_id.to_bytes(), TICKETS_TO_SPEND)
|
||||
.await
|
||||
.map_err(|source| Error::GetTicket {
|
||||
ticketbook_type,
|
||||
source,
|
||||
})?;
|
||||
Ok(credential)
|
||||
}
|
||||
|
||||
pub async fn register_wireguard<St: CredentialStorage>(
|
||||
&mut self,
|
||||
gateway_host: IpAddr,
|
||||
controller: &nym_bandwidth_controller::BandwidthController<QueryHttpRpcNyxdClient, St>,
|
||||
ticketbook_type: TicketType,
|
||||
) -> Result<GatewayData>
|
||||
where
|
||||
<St as CredentialStorage>::StorageError: Send + Sync + 'static,
|
||||
{
|
||||
debug!("Registering with the wg gateway...");
|
||||
let init_message = match self.auth_version() {
|
||||
AuthenticatorVersion::V2 => {
|
||||
ClientMessage::Initial(Box::new(v2::registration::InitMessage {
|
||||
pub_key: PeerPublicKey::new(self.keypair.public_key().to_bytes().into()),
|
||||
}))
|
||||
}
|
||||
AuthenticatorVersion::V3 => {
|
||||
ClientMessage::Initial(Box::new(v3::registration::InitMessage {
|
||||
pub_key: PeerPublicKey::new(self.keypair.public_key().to_bytes().into()),
|
||||
}))
|
||||
}
|
||||
AuthenticatorVersion::V4 => {
|
||||
ClientMessage::Initial(Box::new(v4::registration::InitMessage {
|
||||
pub_key: PeerPublicKey::new(self.keypair.public_key().to_bytes().into()),
|
||||
}))
|
||||
}
|
||||
AuthenticatorVersion::V5 => {
|
||||
ClientMessage::Initial(Box::new(v5::registration::InitMessage {
|
||||
pub_key: PeerPublicKey::new(self.keypair.public_key().to_bytes().into()),
|
||||
}))
|
||||
}
|
||||
AuthenticatorVersion::UNKNOWN => return Err(Error::UnsupportedAuthenticatorVersion),
|
||||
};
|
||||
trace!("sending init msg to {}: {:?}", &gateway_host, &init_message);
|
||||
let response = self.auth_client.send(&init_message).await?;
|
||||
let registered_data = match response {
|
||||
AuthenticatorResponse::PendingRegistration(pending_registration_response) => {
|
||||
// Unwrap since we have already checked that we have the keypair.
|
||||
debug!("Verifying data");
|
||||
if let Err(e) = pending_registration_response.verify(self.keypair.private_key()) {
|
||||
return Err(Error::VerificationFailed(e));
|
||||
}
|
||||
|
||||
trace!(
|
||||
"received \"pending-registration\" msg from {}: {:?}",
|
||||
&gateway_host,
|
||||
&pending_registration_response
|
||||
);
|
||||
|
||||
let credential = Some(
|
||||
Self::request_bandwidth(
|
||||
self.auth_recipient().gateway(),
|
||||
controller,
|
||||
ticketbook_type,
|
||||
)
|
||||
.await?
|
||||
.data,
|
||||
);
|
||||
|
||||
let finalized_message = match self.auth_version() {
|
||||
AuthenticatorVersion::V2 => {
|
||||
ClientMessage::Final(Box::new(v2::registration::FinalMessage {
|
||||
gateway_client: v2::registration::GatewayClient::new(
|
||||
self.keypair.private_key(),
|
||||
pending_registration_response.pub_key().inner(),
|
||||
pending_registration_response.private_ips().ipv4.into(),
|
||||
pending_registration_response.nonce(),
|
||||
),
|
||||
credential,
|
||||
}))
|
||||
}
|
||||
AuthenticatorVersion::V3 => {
|
||||
ClientMessage::Final(Box::new(v3::registration::FinalMessage {
|
||||
gateway_client: v3::registration::GatewayClient::new(
|
||||
self.keypair.private_key(),
|
||||
pending_registration_response.pub_key().inner(),
|
||||
pending_registration_response.private_ips().ipv4.into(),
|
||||
pending_registration_response.nonce(),
|
||||
),
|
||||
credential,
|
||||
}))
|
||||
}
|
||||
AuthenticatorVersion::V4 => {
|
||||
ClientMessage::Final(Box::new(v4::registration::FinalMessage {
|
||||
gateway_client: v4::registration::GatewayClient::new(
|
||||
self.keypair.private_key(),
|
||||
pending_registration_response.pub_key().inner(),
|
||||
pending_registration_response.private_ips().into(),
|
||||
pending_registration_response.nonce(),
|
||||
),
|
||||
credential,
|
||||
}))
|
||||
}
|
||||
AuthenticatorVersion::V5 => {
|
||||
ClientMessage::Final(Box::new(v5::registration::FinalMessage {
|
||||
gateway_client: v5::registration::GatewayClient::new(
|
||||
self.keypair.private_key(),
|
||||
pending_registration_response.pub_key().inner(),
|
||||
pending_registration_response.private_ips(),
|
||||
pending_registration_response.nonce(),
|
||||
),
|
||||
credential,
|
||||
}))
|
||||
}
|
||||
AuthenticatorVersion::UNKNOWN => {
|
||||
return Err(Error::UnsupportedAuthenticatorVersion);
|
||||
}
|
||||
};
|
||||
trace!(
|
||||
"sending final msg to {}: {:?}",
|
||||
&gateway_host,
|
||||
&finalized_message
|
||||
);
|
||||
|
||||
let response = self.auth_client.send(&finalized_message).await?;
|
||||
let AuthenticatorResponse::Registered(registered_response) = response else {
|
||||
return Err(Error::InvalidGatewayAuthResponse);
|
||||
};
|
||||
registered_response
|
||||
}
|
||||
AuthenticatorResponse::Registered(registered_response) => registered_response,
|
||||
_ => return Err(Error::InvalidGatewayAuthResponse),
|
||||
};
|
||||
|
||||
trace!(
|
||||
"received \"registered\" msg from {}: {:?}",
|
||||
&gateway_host,
|
||||
®istered_data
|
||||
);
|
||||
|
||||
let gateway_data = GatewayData {
|
||||
public_key: registered_data.pub_key().inner().into(),
|
||||
endpoint: SocketAddr::from_str(&format!(
|
||||
"{}:{}",
|
||||
gateway_host,
|
||||
registered_data.wg_port()
|
||||
))
|
||||
.map_err(Error::FailedToParseEntryGatewaySocketAddr)?,
|
||||
private_ipv4: registered_data.private_ips().ipv4,
|
||||
private_ipv6: registered_data.private_ips().ipv6,
|
||||
};
|
||||
|
||||
Ok(gateway_data)
|
||||
}
|
||||
}
|
||||
|
||||
fn load_or_generate_keypair<R: RngCore + CryptoRng>(rng: &mut R, paths: KeyPairPath) -> KeyPair {
|
||||
match nym_pemstore::load_keypair(&paths) {
|
||||
Ok(keypair) => keypair,
|
||||
Err(_) => {
|
||||
let keypair = KeyPair::new(rng);
|
||||
if let Err(e) = nym_pemstore::store_keypair(&keypair, &paths) {
|
||||
error!(
|
||||
"could not store generated keypair at {:?} - {:?}; will use ephemeral keys",
|
||||
paths, e
|
||||
);
|
||||
}
|
||||
keypair
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ pub use nym_client_core::{
|
||||
NymApiTopologyProvider, NymApiTopologyProviderConfig, TopologyProvider,
|
||||
},
|
||||
},
|
||||
config::DebugConfig,
|
||||
config::{DebugConfig, RememberMe},
|
||||
};
|
||||
pub use nym_network_defaults::{
|
||||
ChainDetails, DenomDetails, DenomDetailsOwned, NymContracts, NymNetworkDetails,
|
||||
|
||||
@@ -37,7 +37,6 @@ use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
#[cfg(unix)]
|
||||
use std::sync::Arc;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use url::Url;
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
@@ -119,11 +118,6 @@ where
|
||||
<S::KeyStore as KeyStore>::StorageError: Send + Sync,
|
||||
<S::GatewaysDetailsStore as GatewaysDetailsStore>::StorageError: Send + Sync,
|
||||
{
|
||||
pub fn with_shutdown_token(self, token: CancellationToken) -> Self {
|
||||
let shutdown_tracker = ShutdownTracker::new_from_external_shutdown_token(token.into());
|
||||
self.custom_shutdown(shutdown_tracker)
|
||||
}
|
||||
|
||||
/// Creates a client builder with the provided client storage implementation.
|
||||
#[must_use]
|
||||
pub fn new_with_storage(storage: S) -> MixnetClientBuilder<S> {
|
||||
@@ -755,13 +749,6 @@ where
|
||||
let packet_type = self.config.debug_config.traffic.packet_type;
|
||||
let (mut started_client, nym_address) = self.connect_to_mixnet_common().await?;
|
||||
|
||||
// TODO: more graceful handling here, surely both variants should work... I think?
|
||||
let Some(tracker) = started_client.shutdown_handle else {
|
||||
return Err(Error::new_unsupported(
|
||||
"connecting with socks5 is currently unsupported with custom shutdown",
|
||||
));
|
||||
};
|
||||
|
||||
let client_input = started_client.client_input.register_producer();
|
||||
let client_output = started_client.client_output.register_consumer();
|
||||
let client_state = started_client.client_state;
|
||||
@@ -773,14 +760,14 @@ where
|
||||
client_output,
|
||||
client_state.clone(),
|
||||
nym_address,
|
||||
tracker.child_tracker(),
|
||||
started_client.shutdown_handle.child_tracker(),
|
||||
packet_type,
|
||||
);
|
||||
|
||||
Ok(Socks5MixnetClient {
|
||||
nym_address,
|
||||
client_state,
|
||||
task_handle: tracker,
|
||||
task_handle: started_client.shutdown_handle,
|
||||
socks5_config,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
use tokio::sync::RwLockReadGuard;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
/// Client connected to the Nym mixnet.
|
||||
pub struct MixnetClient {
|
||||
@@ -52,7 +53,7 @@ pub struct MixnetClient {
|
||||
pub(crate) stats_events_reporter: ClientStatsSender,
|
||||
|
||||
/// The task manager that controls all the spawned tasks that the clients uses to do it's job.
|
||||
pub(crate) shutdown_handle: Option<ShutdownTracker>,
|
||||
pub(crate) shutdown_handle: ShutdownTracker,
|
||||
pub(crate) packet_type: Option<PacketType>,
|
||||
|
||||
// internal state used for the `Stream` implementation
|
||||
@@ -72,7 +73,7 @@ impl MixnetClient {
|
||||
client_state: ClientState,
|
||||
reconstructed_receiver: ReconstructedMessagesReceiver,
|
||||
stats_events_reporter: ClientStatsSender,
|
||||
task_handle: Option<ShutdownTracker>,
|
||||
task_handle: ShutdownTracker,
|
||||
packet_type: Option<PacketType>,
|
||||
client_request_sender: ClientRequestSender,
|
||||
forget_me: ForgetMe,
|
||||
@@ -122,6 +123,11 @@ impl MixnetClient {
|
||||
&self.nym_address
|
||||
}
|
||||
|
||||
/// Get a child token of the root, to monitor unexpected shutdown, without causing one
|
||||
pub fn cancellation_token(&self) -> CancellationToken {
|
||||
self.shutdown_handle.child_shutdown_token().inner().clone()
|
||||
}
|
||||
|
||||
pub fn client_request_sender(&self) -> ClientRequestSender {
|
||||
self.client_request_sender.clone()
|
||||
}
|
||||
@@ -238,9 +244,7 @@ impl MixnetClient {
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
|
||||
}
|
||||
|
||||
if let Some(tracker) = self.shutdown_handle {
|
||||
tracker.shutdown().await;
|
||||
}
|
||||
self.shutdown_handle.shutdown().await;
|
||||
}
|
||||
|
||||
pub async fn send_forget_me(&self) -> Result<()> {
|
||||
|
||||
@@ -249,10 +249,7 @@ impl NymClientBuilder {
|
||||
client_input: Arc::new(client_input),
|
||||
client_state: Arc::new(started_client.client_state),
|
||||
_full_topology: None,
|
||||
// this cannot fail as we haven't passed an external task manager
|
||||
_task_manager: started_client
|
||||
.shutdown_handle
|
||||
.expect("shutdown manager missing"),
|
||||
_task_manager: started_client.shutdown_handle,
|
||||
packet_type,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -187,12 +187,7 @@ impl MixFetchClientBuilder {
|
||||
self_address,
|
||||
client_input,
|
||||
requests: active_requests,
|
||||
// this cannot fail as we haven't passed an external task manager
|
||||
_shutdown_manager: Mutex::new(
|
||||
started_client
|
||||
.shutdown_handle
|
||||
.expect("shutdown manager missing"),
|
||||
),
|
||||
_shutdown_manager: Mutex::new(started_client.shutdown_handle),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user