From 128f69a5d67a9e87d1691d9cf9d3a9365fdcc5bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Fri, 28 Feb 2025 13:04:53 +0100 Subject: [PATCH] Simplify IPR v8 (#5532) * Purge stuff from v8 * Adapt to v8 changes * Use protocol in ipr header * Remove commented out code * Remove unused error --- Cargo.lock | 4 + common/ip-packet-requests/Cargo.toml | 1 + common/ip-packet-requests/src/v8/request.rs | 384 ++++-------------- common/ip-packet-requests/src/v8/response.rs | 66 +-- .../Cargo.toml | 4 + .../src/lib.rs | 103 +++++ service-providers/ip-packet-router/Cargo.toml | 1 + .../ip-packet-router/src/clients/client_id.rs | 14 - .../ip-packet-router/src/error.rs | 16 +- .../ip-packet-router/src/messages/request.rs | 25 +- .../src/messages/request/v8.rs | 170 +++----- .../ip-packet-router/src/messages/response.rs | 2 +- .../src/messages/response/v8.rs | 88 ++-- 13 files changed, 311 insertions(+), 567 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6cc16f142c..e45cc5e1f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5913,6 +5913,7 @@ dependencies = [ "bytes", "nym-bin-common", "nym-crypto", + "nym-service-provider-requests-common", "nym-sphinx", "rand 0.8.5", "serde", @@ -5944,6 +5945,7 @@ dependencies = [ "nym-network-defaults", "nym-network-requester", "nym-sdk", + "nym-service-provider-requests-common", "nym-service-providers-common", "nym-sphinx", "nym-task", @@ -6517,7 +6519,9 @@ dependencies = [ name = "nym-service-provider-requests-common" version = "0.1.0" dependencies = [ + "bincode", "serde", + "thiserror 2.0.11", ] [[package]] diff --git a/common/ip-packet-requests/Cargo.toml b/common/ip-packet-requests/Cargo.toml index 60581a4278..cd9663dc90 100644 --- a/common/ip-packet-requests/Cargo.toml +++ b/common/ip-packet-requests/Cargo.toml @@ -13,6 +13,7 @@ bincode = { workspace = true } bytes = { workspace = true } nym-bin-common = { path = "../bin-common" } nym-crypto = { path = "../crypto" } +nym-service-provider-requests-common = { path = "../service-provider-requests-common" } nym-sphinx = { path = "../nymsphinx" } rand = { workspace = true } serde = { workspace = true, features = ["derive"] } diff --git a/common/ip-packet-requests/src/v8/request.rs b/common/ip-packet-requests/src/v8/request.rs index 15cf47827f..c5e49e83b7 100644 --- a/common/ip-packet-requests/src/v8/request.rs +++ b/common/ip-packet-requests/src/v8/request.rs @@ -1,20 +1,14 @@ -use std::{fmt, sync::Arc}; +use std::fmt; -use nym_crypto::asymmetric::ed25519; -use nym_sphinx::addressing::Recipient; +use nym_service_provider_requests_common::{Protocol, ServiceProviderType}; use serde::{Deserialize, Serialize}; use time::OffsetDateTime; -use crate::{ - sign::{SignatureError, SignedRequest}, - IpPair, -}; - use super::VERSION; #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct IpPacketRequest { - pub version: u8, + pub protocol: Protocol, pub data: IpPacketRequestData, } @@ -26,9 +20,8 @@ pub enum IpPacketRequestData { #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub enum ControlRequest { - StaticConnect(SignedStaticConnectRequest), - DynamicConnect(SignedDynamicConnectRequest), - Disconnect(SignedDisconnectRequest), + Connect(ConnectRequest), + Disconnect(DisconnectRequest), Ping(PingRequest), Health(HealthRequest), } @@ -39,35 +32,10 @@ pub struct DataRequest { pub ip_packets: bytes::Bytes, } -// A static connect request is when the client provides the internal IP address it will use on the -// ip packet router. -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -pub struct StaticConnectRequest { - pub request_id: u64, - - // The requested internal IP addresses. - pub ips: IpPair, - - // The maximum time in milliseconds the IPR should wait when filling up a mix packet - // with ip packets. - pub buffer_timeout: Option, - - // Timestamp of when the request was sent by the client. - pub timestamp: OffsetDateTime, - - pub sender: SentBy, -} - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -pub struct SignedStaticConnectRequest { - pub request: StaticConnectRequest, - pub signature: Option, -} - // A dynamic connect request is when the client does not provide the internal IP address it will use // on the ip packet router, and instead requests one to be assigned to it. #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -pub struct DynamicConnectRequest { +pub struct ConnectRequest { pub request_id: u64, // The maximum time in milliseconds the IPR should wait when filling up a mix packet @@ -76,14 +44,6 @@ pub struct DynamicConnectRequest { // Timestamp of when the request was sent by the client. pub timestamp: OffsetDateTime, - - pub sender: SentBy, -} - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -pub struct SignedDynamicConnectRequest { - pub request: DynamicConnectRequest, - pub signature: Option, } // A disconnect request is when the client wants to disconnect from the ip packet router and free @@ -94,14 +54,6 @@ pub struct DisconnectRequest { // Timestamp of when the request was sent by the client. pub timestamp: OffsetDateTime, - - pub sender: SentBy, -} - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -pub struct SignedDisconnectRequest { - pub request: DisconnectRequest, - pub signature: Option, } // A ping request is when the client wants to check if the ip packet router is still alive. @@ -109,8 +61,6 @@ pub struct SignedDisconnectRequest { pub struct PingRequest { pub request_id: u64, - pub sender: SentBy, - // Timestamp of when the request was sent by the client. pub timestamp: OffsetDateTime, } @@ -119,142 +69,89 @@ pub struct PingRequest { pub struct HealthRequest { pub request_id: u64, - pub sender: SentBy, - // Timestamp of when the request was sent by the client. pub timestamp: OffsetDateTime, } -// When constructing the request, use this return address. It has the keypair to be able to sign -// the request if we reveal the sender. -#[derive(Clone, Debug)] -pub enum ReturnAddress { - AnonymousSenderTag, - NymAddress { - reply_to: Box, - signing_keypair: Arc, - }, -} - -// The serialized sender field in the request, that does not contain the keypair. -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -pub enum SentBy { - AnonymousSenderTag, - NymAddress(Box), -} - impl IpPacketRequest { - pub fn new_connect_request( - ips: Option, - buffer_timeout: Option, - return_address: ReturnAddress, - ) -> Result<(Self, u64), SignatureError> { + pub fn new_connect_request(buffer_timeout: Option) -> (Self, u64) { + let protocol = Protocol { + version: VERSION, + service_provider_type: ServiceProviderType::IpPacketRouter, + }; let request_id = rand::random(); let timestamp = OffsetDateTime::now_utc(); - let sender = return_address.clone().into(); - let request = if let Some(ips) = ips { - let request = StaticConnectRequest { - request_id, - ips, - buffer_timeout, - timestamp, - sender, - }; - let signature = return_address - .signing_key() - .map(|keypair| { - request - .to_bytes() - .map(|bytes| keypair.private_key().sign(bytes)) - }) - .transpose()?; - ControlRequest::StaticConnect(SignedStaticConnectRequest { request, signature }) - } else { - let request = DynamicConnectRequest { - request_id, - buffer_timeout, - timestamp, - sender, - }; - let signature = return_address - .signing_key() - .map(|keypair| { - request - .to_bytes() - .map(|bytes| keypair.private_key().sign(bytes)) - }) - .transpose()?; - ControlRequest::DynamicConnect(SignedDynamicConnectRequest { request, signature }) + let connect = ConnectRequest { + request_id, + buffer_timeout, + timestamp, }; let request = Self { - version: VERSION, - data: IpPacketRequestData::Control(Box::new(request)), + protocol, + data: IpPacketRequestData::Control(Box::new(ControlRequest::Connect(connect))), }; - Ok((request, request_id)) + (request, request_id) } - pub fn new_disconnect_request( - return_address: ReturnAddress, - ) -> Result<(Self, u64), SignatureError> { + pub fn new_disconnect_request() -> (Self, u64) { + let protocol = Protocol { + version: VERSION, + service_provider_type: ServiceProviderType::IpPacketRouter, + }; let request_id = rand::random(); let timestamp = OffsetDateTime::now_utc(); - let sender = return_address.clone().into(); - let request = DisconnectRequest { + let disconnect = DisconnectRequest { request_id, timestamp, - sender, }; - let signature = return_address - .signing_key() - .map(|keypair| { - request - .to_bytes() - .map(|bytes| keypair.private_key().sign(bytes)) - }) - .transpose()?; let request = Self { - version: VERSION, - data: IpPacketRequestData::Control(Box::new(ControlRequest::Disconnect( - SignedDisconnectRequest { request, signature }, - ))), + protocol, + data: IpPacketRequestData::Control(Box::new(ControlRequest::Disconnect(disconnect))), }; - Ok((request, request_id)) + (request, request_id) } pub fn new_data_request(ip_packets: bytes::Bytes) -> Self { Self { - version: VERSION, + protocol: Protocol { + version: VERSION, + service_provider_type: ServiceProviderType::IpPacketRouter, + }, data: IpPacketRequestData::Data(DataRequest { ip_packets }), } } - pub fn new_ping(return_address: ReturnAddress) -> (Self, u64) { + pub fn new_ping() -> (Self, u64) { + let protocol = Protocol { + version: VERSION, + service_provider_type: ServiceProviderType::IpPacketRouter, + }; let request_id = rand::random(); let timestamp = OffsetDateTime::now_utc(); - let sender = return_address.into(); let ping_request = PingRequest { request_id, - sender, timestamp, }; let request = Self { - version: VERSION, + protocol, data: IpPacketRequestData::Control(Box::new(ControlRequest::Ping(ping_request))), }; (request, request_id) } - pub fn new_health_request(return_address: ReturnAddress) -> (Self, u64) { + pub fn new_health_request() -> (Self, u64) { + let protocol = Protocol { + version: VERSION, + service_provider_type: ServiceProviderType::IpPacketRouter, + }; let request_id = rand::random(); let timestamp = OffsetDateTime::now_utc(); - let sender = return_address.into(); let health_request = HealthRequest { request_id, - sender, timestamp, }; let request = Self { - version: VERSION, + protocol, data: IpPacketRequestData::Control(Box::new(ControlRequest::Health(health_request))), }; (request, request_id) @@ -267,13 +164,6 @@ impl IpPacketRequest { } } - pub fn verify(&self) -> Result<(), SignatureError> { - match &self.data { - IpPacketRequestData::Control(c) => c.verify(), - IpPacketRequestData::Data(_) => Ok(()), - } - } - pub fn to_bytes(&self) -> Result, bincode::Error> { use bincode::Options; crate::make_bincode_serializer().serialize(self) @@ -292,7 +182,7 @@ impl fmt::Display for IpPacketRequest { write!( f, "IpPacketRequest {{ version: {}, data: {} }}", - self.version, self.data + self.protocol.version, self.data ) } } @@ -309,30 +199,18 @@ impl fmt::Display for IpPacketRequestData { impl ControlRequest { fn id(&self) -> u64 { match self { - ControlRequest::StaticConnect(request) => request.request.request_id, - ControlRequest::DynamicConnect(request) => request.request.request_id, - ControlRequest::Disconnect(request) => request.request.request_id, + ControlRequest::Connect(request) => request.request_id, + ControlRequest::Disconnect(request) => request.request_id, ControlRequest::Ping(request) => request.request_id, ControlRequest::Health(request) => request.request_id, } } - - fn verify(&self) -> Result<(), SignatureError> { - match self { - ControlRequest::StaticConnect(request) => request.verify(), - ControlRequest::DynamicConnect(request) => request.verify(), - ControlRequest::Disconnect(request) => request.verify(), - ControlRequest::Ping(_) => Ok(()), - ControlRequest::Health(_) => Ok(()), - } - } } impl fmt::Display for ControlRequest { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - ControlRequest::StaticConnect(_) => write!(f, "StaticConnect"), - ControlRequest::DynamicConnect(_) => write!(f, "DynamicConnect"), + ControlRequest::Connect(_) => write!(f, "Connect"), ControlRequest::Disconnect(_) => write!(f, "Disconnect"), ControlRequest::Ping(_) => write!(f, "Ping"), ControlRequest::Health(_) => write!(f, "Health"), @@ -340,128 +218,17 @@ impl fmt::Display for ControlRequest { } } -impl StaticConnectRequest { - pub fn to_bytes(&self) -> Result, SignatureError> { +impl ConnectRequest { + pub fn to_bytes(&self) -> Result, bincode::Error> { use bincode::Options; - crate::make_bincode_serializer() - .serialize(self) - .map_err(|error| SignatureError::RequestSerializationError { - message: "failed to serialize request to binary".to_string(), - error, - }) - } -} - -impl SignedRequest for SignedStaticConnectRequest { - fn request_as_bytes(&self) -> Result, SignatureError> { - self.request.to_bytes() - } - - fn timestamp(&self) -> OffsetDateTime { - self.request.timestamp - } - - fn identity(&self) -> Option<&ed25519::PublicKey> { - self.request.sender.identity() - } - - fn signature(&self) -> Option<&ed25519::Signature> { - self.signature.as_ref() - } -} - -impl DynamicConnectRequest { - pub fn to_bytes(&self) -> Result, SignatureError> { - use bincode::Options; - crate::make_bincode_serializer() - .serialize(self) - .map_err(|error| SignatureError::RequestSerializationError { - message: "failed to serialize request to binary".to_string(), - error, - }) - } -} - -impl SignedRequest for SignedDynamicConnectRequest { - fn request_as_bytes(&self) -> Result, SignatureError> { - self.request.to_bytes() - } - - fn timestamp(&self) -> OffsetDateTime { - self.request.timestamp - } - - fn identity(&self) -> Option<&ed25519::PublicKey> { - self.request.sender.identity() - } - - fn signature(&self) -> Option<&ed25519::Signature> { - self.signature.as_ref() + crate::make_bincode_serializer().serialize(self) } } impl DisconnectRequest { - pub fn to_bytes(&self) -> Result, SignatureError> { + pub fn to_bytes(&self) -> Result, bincode::Error> { use bincode::Options; - crate::make_bincode_serializer() - .serialize(self) - .map_err(|error| SignatureError::RequestSerializationError { - message: "failed to serialize request to binary".to_string(), - error, - }) - } -} - -impl SignedRequest for SignedDisconnectRequest { - fn request_as_bytes(&self) -> Result, SignatureError> { - self.request.to_bytes() - } - - fn timestamp(&self) -> OffsetDateTime { - self.request.timestamp - } - - fn identity(&self) -> Option<&ed25519::PublicKey> { - self.request.sender.identity() - } - - fn signature(&self) -> Option<&ed25519::Signature> { - self.signature.as_ref() - } -} - -impl SentBy { - fn identity(&self) -> Option<&ed25519::PublicKey> { - match self { - SentBy::AnonymousSenderTag => None, - SentBy::NymAddress(recipient) => Some(recipient.identity()), - } - } -} - -impl From for SentBy { - fn from(recipient: Recipient) -> Self { - SentBy::NymAddress(Box::new(recipient)) - } -} - -impl ReturnAddress { - fn signing_key(&self) -> Option<&ed25519::KeyPair> { - match self { - ReturnAddress::AnonymousSenderTag => None, - ReturnAddress::NymAddress { - signing_keypair, .. - } => Some(signing_keypair.as_ref()), - } - } -} - -impl From for SentBy { - fn from(return_address: ReturnAddress) -> Self { - match return_address { - ReturnAddress::AnonymousSenderTag => SentBy::AnonymousSenderTag, - ReturnAddress::NymAddress { reply_to, .. } => SentBy::NymAddress(reply_to), - } + crate::make_bincode_serializer().serialize(self) } } @@ -470,47 +237,44 @@ mod tests { use time::macros::datetime; use super::*; - use std::net::{Ipv4Addr, Ipv6Addr}; - use std::str::FromStr; #[test] fn check_size_of_request() { let connect = IpPacketRequest { - version: 4, - data: IpPacketRequestData::Control(Box::new(ControlRequest::StaticConnect( - SignedStaticConnectRequest { - request: StaticConnectRequest { - request_id: 123, - ips: IpPair::new( - Ipv4Addr::from_str("10.0.0.1").unwrap(), - Ipv6Addr::from_str("fc00::1").unwrap(), - ), - buffer_timeout: None, - timestamp: datetime!(2024-01-01 12:59:59.5 UTC), - sender: SentBy::AnonymousSenderTag, - }, - signature: None, - }, - ))), + protocol: Protocol { + version: 4, + service_provider_type: ServiceProviderType::IpPacketRouter, + }, + data: IpPacketRequestData::Control(Box::new(ControlRequest::Connect(ConnectRequest { + request_id: 123, + buffer_timeout: None, + timestamp: datetime!(2024-01-01 12:59:59.5 UTC), + }))), }; - assert_eq!(connect.to_bytes().unwrap().len(), 42); + assert_eq!(connect.to_bytes().unwrap().len(), 21); } #[test] fn check_size_of_data() { let data = IpPacketRequest { - version: 4, + protocol: Protocol { + version: 4, + service_provider_type: ServiceProviderType::IpPacketRouter, + }, data: IpPacketRequestData::Data(DataRequest { ip_packets: bytes::Bytes::from(vec![1u8; 32]), }), }; - assert_eq!(data.to_bytes().unwrap().len(), 35); + assert_eq!(data.to_bytes().unwrap().len(), 36); } #[test] fn serialize_and_deserialize_data_request() { let data = IpPacketRequest { - version: 4, + protocol: Protocol { + version: 4, + service_provider_type: ServiceProviderType::IpPacketRouter, + }, data: IpPacketRequestData::Data(DataRequest { ip_packets: bytes::Bytes::from(vec![1, 2, 4, 2, 5]), }), @@ -525,7 +289,11 @@ mod tests { ) .unwrap(); - assert_eq!(deserialized.version, 4); + assert_eq!(deserialized.protocol.version, 4); + assert_eq!( + deserialized.protocol.service_provider_type, + ServiceProviderType::IpPacketRouter + ); assert_eq!( deserialized.data, IpPacketRequestData::Data(DataRequest { diff --git a/common/ip-packet-requests/src/v8/response.rs b/common/ip-packet-requests/src/v8/response.rs index a919d2110f..946a8621be 100644 --- a/common/ip-packet-requests/src/v8/response.rs +++ b/common/ip-packet-requests/src/v8/response.rs @@ -24,11 +24,8 @@ pub struct DataResponse { #[derive(Clone, Debug, Serialize, Deserialize)] pub enum ControlResponse { - // Response for a static connect request - StaticConnect(StaticConnectResponse), - - // Response for a dynamic connect request - DynamicConnect(DynamicConnectResponse), + // Response for a connect request + Connect(ConnectResponse), // Response for a disconnect initiqated by the client Disconnect(DisconnectResponse), @@ -47,51 +44,24 @@ pub enum ControlResponse { } #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct StaticConnectResponse { +pub struct ConnectResponse { pub request_id: u64, - pub reply: StaticConnectResponseReply, + pub reply: ConnectResponseReply, } #[derive(Clone, Debug, Serialize, Deserialize)] -pub enum StaticConnectResponseReply { - Success, - Failure(StaticConnectFailureReason), -} - -#[derive(Clone, Debug, Serialize, Deserialize, thiserror::Error)] -pub enum StaticConnectFailureReason { - #[error("requested ip address is already in use")] - RequestedIpAlreadyInUse, - - #[error("client is already connected")] - ClientAlreadyConnected, - - #[error("request timestamp is out of date")] - OutOfDateTimestamp, - - #[error("{0}")] - Other(String), +pub enum ConnectResponseReply { + Success(ConnectSuccess), + Failure(ConnectFailureReason), } #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct DynamicConnectResponse { - pub request_id: u64, - pub reply: DynamicConnectResponseReply, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum DynamicConnectResponseReply { - Success(DynamicConnectSuccess), - Failure(DynamicConnectFailureReason), -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct DynamicConnectSuccess { +pub struct ConnectSuccess { pub ips: IpPair, } #[derive(Clone, Debug, Serialize, Deserialize, thiserror::Error)] -pub enum DynamicConnectFailureReason { +pub enum ConnectFailureReason { #[error("client is already connected")] ClientAlreadyConnected, @@ -229,8 +199,7 @@ impl IpPacketResponseData { impl ControlResponse { fn id(&self) -> Option { match self { - ControlResponse::StaticConnect(response) => Some(response.request_id), - ControlResponse::DynamicConnect(response) => Some(response.request_id), + ControlResponse::Connect(response) => Some(response.request_id), ControlResponse::Disconnect(response) => Some(response.request_id), ControlResponse::UnrequestedDisconnect(_) => None, ControlResponse::Pong(response) => Some(response.request_id), @@ -240,20 +209,11 @@ impl ControlResponse { } } -impl StaticConnectResponseReply { +impl ConnectResponseReply { pub fn is_success(&self) -> bool { match self { - StaticConnectResponseReply::Success => true, - StaticConnectResponseReply::Failure(_) => false, - } - } -} - -impl DynamicConnectResponseReply { - pub fn is_success(&self) -> bool { - match self { - DynamicConnectResponseReply::Success(_) => true, - DynamicConnectResponseReply::Failure(_) => false, + ConnectResponseReply::Success(_) => true, + ConnectResponseReply::Failure(_) => false, } } } diff --git a/common/service-provider-requests-common/Cargo.toml b/common/service-provider-requests-common/Cargo.toml index f88fdcc0e1..63aef2eb76 100644 --- a/common/service-provider-requests-common/Cargo.toml +++ b/common/service-provider-requests-common/Cargo.toml @@ -12,3 +12,7 @@ readme.workspace = true [dependencies] serde = { workspace = true, features = ["derive"] } +thiserror.workspace = true + +[dev-dependencies] +bincode.workspace = true diff --git a/common/service-provider-requests-common/src/lib.rs b/common/service-provider-requests-common/src/lib.rs index 3b294baba7..2840a9321c 100644 --- a/common/service-provider-requests-common/src/lib.rs +++ b/common/service-provider-requests-common/src/lib.rs @@ -1,8 +1,16 @@ // Copyright 2024 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use std::fmt; + use serde::{Deserialize, Serialize}; +#[derive(thiserror::Error, Debug)] +pub enum ProtocolError { + #[error("invalid service provider type: {0}")] + InvalidServiceProviderType(u8), +} + #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[repr(u8)] pub enum ServiceProviderType { @@ -11,8 +19,103 @@ pub enum ServiceProviderType { Authenticator = 2, } +impl fmt::Display for ServiceProviderType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::NetworkRequester => write!(f, "NetworkRequester"), + Self::IpPacketRouter => write!(f, "IpPacketRouter"), + Self::Authenticator => write!(f, "Authenticator"), + } + } +} + +impl TryFrom for ServiceProviderType { + type Error = ProtocolError; + + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(Self::NetworkRequester), + 1 => Ok(Self::IpPacketRouter), + 2 => Ok(Self::Authenticator), + _ => Err(ProtocolError::InvalidServiceProviderType(value)), + } + } +} + #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct Protocol { pub version: u8, pub service_provider_type: ServiceProviderType, } + +impl TryFrom<&[u8; 2]> for Protocol { + type Error = ProtocolError; + + fn try_from(bytes: &[u8; 2]) -> Result { + let version = bytes[0]; + let service_provider_type = ServiceProviderType::try_from(bytes[1]) + .map_err(|_| ProtocolError::InvalidServiceProviderType(bytes[1]))?; + + Ok(Self { + version, + service_provider_type, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use bincode::Options; + + fn make_bincode_serializer() -> impl bincode::Options { + bincode::DefaultOptions::new() + .with_big_endian() + .with_varint_encoding() + } + + #[test] + fn protocol_serialization() { + let protocol = Protocol { + version: 4, + service_provider_type: ServiceProviderType::NetworkRequester, + }; + + let serialized = make_bincode_serializer().serialize(&protocol).unwrap(); + let deserialized: Protocol = make_bincode_serializer().deserialize(&serialized).unwrap(); + + assert_eq!(protocol, deserialized); + } + + #[test] + fn compact_serialization() { + let protocol = Protocol { + version: 4, + service_provider_type: ServiceProviderType::NetworkRequester, + }; + + let serialized = make_bincode_serializer().serialize(&protocol).unwrap(); + assert_eq!(serialized.len(), 2); + } + + #[test] + fn protocol_deserialization() { + let bytes = [4, ServiceProviderType::NetworkRequester as u8]; + let deserialized = Protocol::try_from(&bytes).unwrap(); + + let expected = Protocol { + version: 4, + service_provider_type: ServiceProviderType::NetworkRequester, + }; + + assert_eq!(expected, deserialized); + } + + #[test] + fn invalid_protocol_deserialization() { + let bytes = [4, 3]; + let deserialized = Protocol::try_from(&bytes); + + assert!(deserialized.is_err()); + } +} diff --git a/service-providers/ip-packet-router/Cargo.toml b/service-providers/ip-packet-router/Cargo.toml index fc7303a872..dd7d953714 100644 --- a/service-providers/ip-packet-router/Cargo.toml +++ b/service-providers/ip-packet-router/Cargo.toml @@ -27,6 +27,7 @@ nym-ip-packet-requests = { path = "../../common/ip-packet-requests" } nym-network-defaults = { path = "../../common/network-defaults" } nym-network-requester = { path = "../network-requester" } nym-sdk = { path = "../../sdk/rust/nym-sdk" } +nym-service-provider-requests-common = { path = "../../common/service-provider-requests-common" } nym-service-providers-common = { path = "../common" } nym-sphinx = { path = "../../common/nymsphinx" } nym-task = { path = "../../common/task" } diff --git a/service-providers/ip-packet-router/src/clients/client_id.rs b/service-providers/ip-packet-router/src/clients/client_id.rs index 038efbda76..5add99a724 100644 --- a/service-providers/ip-packet-router/src/clients/client_id.rs +++ b/service-providers/ip-packet-router/src/clients/client_id.rs @@ -3,7 +3,6 @@ use std::fmt; -use nym_ip_packet_requests::v8::request::SentBy; use nym_sdk::mixnet::{AnonymousSenderTag, Recipient}; use crate::error::{IpPacketRouterError, Result}; @@ -43,16 +42,3 @@ impl From for ConnectedClientId { ConnectedClientId::AnonymousSenderTag(tag) } } - -impl TryFrom<(SentBy, Option)> for ConnectedClientId { - type Error = IpPacketRouterError; - - fn try_from((sent_by, sender_tag): (SentBy, Option)) -> Result { - match sent_by { - SentBy::NymAddress(nym_address) => Ok(ConnectedClientId::NymAddress(nym_address)), - SentBy::AnonymousSenderTag => sender_tag - .map(ConnectedClientId::AnonymousSenderTag) - .ok_or(IpPacketRouterError::InvalidReplyTo), - } - } -} diff --git a/service-providers/ip-packet-router/src/error.rs b/service-providers/ip-packet-router/src/error.rs index 0a81bde0ed..49965b8ad8 100644 --- a/service-providers/ip-packet-router/src/error.rs +++ b/service-providers/ip-packet-router/src/error.rs @@ -7,6 +7,7 @@ pub use nym_client_core::error::ClientCoreError; use nym_exit_policy::PolicyError; use nym_id::NymIdError; use nym_ip_packet_requests::sign::SignatureError; +use nym_service_provider_requests_common::{ProtocolError, ServiceProviderType}; #[derive(thiserror::Error, Debug)] pub enum IpPacketRouterError { @@ -96,11 +97,20 @@ pub enum IpPacketRouterError { #[error("failed to verify request: {source}")] FailedToVerifyRequest { source: SignatureError }, - #[error("client is connected with an invalid version: {version}")] - InvalidConnectedClientVersion { version: u8 }, - #[error("invalid reply-to address in the response")] InvalidReplyTo, + + #[error("missing sender tag in the request")] + MissingSenderTag, + + #[error("unsupported response: {0}")] + UnsupportedResponse(String), + + #[error("invalid service provider type: {0}")] + InvalidServiceProviderType(ServiceProviderType), + + #[error("failed to deserialize protocol: {source}")] + FailedToDeserializeProtocol { source: ProtocolError }, } pub type Result = std::result::Result; diff --git a/service-providers/ip-packet-router/src/messages/request.rs b/service-providers/ip-packet-router/src/messages/request.rs index 3b781275a7..665abe024a 100644 --- a/service-providers/ip-packet-router/src/messages/request.rs +++ b/service-providers/ip-packet-router/src/messages/request.rs @@ -11,6 +11,7 @@ use nym_ip_packet_requests::{ v8::request::IpPacketRequest as IpPacketRequestV8, IpPair, }; use nym_sdk::mixnet::ReconstructedMessage; +use nym_service_provider_requests_common::{Protocol, ServiceProviderType}; use std::fmt; use crate::{clients::ConnectedClientId, error::IpPacketRouterError}; @@ -83,9 +84,23 @@ impl TryFrom<&ReconstructedMessage> for IpPacketRequest { fn try_from(reconstructed: &ReconstructedMessage) -> Result { let request_version = *reconstructed .message - .first() + .first_chunk::<2>() .ok_or(IpPacketRouterError::EmptyPacket)?; + // With version v8 and onwards, the type of the service provider is included in the + // header. + if request_version[0] >= 8 { + let protocol = Protocol::try_from(&request_version) + .map_err(|source| IpPacketRouterError::FailedToDeserializeProtocol { source })?; + + if protocol.service_provider_type != ServiceProviderType::IpPacketRouter { + return Err(IpPacketRouterError::InvalidServiceProviderType( + protocol.service_provider_type, + )); + } + } + + let request_version = request_version[0]; match request_version { 6 => { let request_v6 = IpPacketRequestV6::from_reconstructed_message(reconstructed) @@ -109,10 +124,10 @@ impl TryFrom<&ReconstructedMessage> for IpPacketRequest { .map_err( |source| IpPacketRouterError::FailedToDeserializeTaggedPacket { source }, )?; - request_v8 - .verify() - .map_err(|source| IpPacketRouterError::FailedToVerifyRequest { source })?; - IpPacketRequest::try_from((request_v8, reconstructed.sender_tag)) + let sender_tag = reconstructed + .sender_tag + .ok_or(IpPacketRouterError::MissingSenderTag)?; + Ok(IpPacketRequest::from((request_v8, sender_tag))) } _ => { log::info!("Received packet with invalid version: v{request_version}"); diff --git a/service-providers/ip-packet-router/src/messages/request/v8.rs b/service-providers/ip-packet-router/src/messages/request/v8.rs index e7190faae7..d54eb22d7c 100644 --- a/service-providers/ip-packet-router/src/messages/request/v8.rs +++ b/service-providers/ip-packet-router/src/messages/request/v8.rs @@ -2,34 +2,27 @@ // SPDX-License-Identifier: GPL-3.0-only use nym_ip_packet_requests::v8::request::{ - ControlRequest as ControlRequestV8, DataRequest as DataRequestV8, - DisconnectRequest as DisconnectRequestV8, DynamicConnectRequest as DynamicConnectRequestV8, + ConnectRequest as ConnectRequestV8, ControlRequest as ControlRequestV8, + DataRequest as DataRequestV8, DisconnectRequest as DisconnectRequestV8, HealthRequest as HealthRequestV8, IpPacketRequest as IpPacketRequestV8, IpPacketRequestData as IpPacketRequestDataV8, PingRequest as PingRequestV8, - StaticConnectRequest as StaticConnectRequestV8, }; use nym_sdk::mixnet::AnonymousSenderTag; -use crate::error::IpPacketRouterError; - use super::{ - ClientVersion, ConnectedClientId, ControlRequest, DataRequest, DisconnectRequest, - DynamicConnectRequest, HealthRequest, IpPacketRequest, PingRequest, StaticConnectRequest, + ClientVersion, ControlRequest, DataRequest, DisconnectRequest, DynamicConnectRequest, + HealthRequest, IpPacketRequest, PingRequest, }; -impl TryFrom<(IpPacketRequestV8, Option)> for IpPacketRequest { - type Error = IpPacketRouterError; - - fn try_from( - (request, sender_tag): (IpPacketRequestV8, Option), - ) -> Result { +impl From<(IpPacketRequestV8, AnonymousSenderTag)> for IpPacketRequest { + fn from((request, sender_tag): (IpPacketRequestV8, AnonymousSenderTag)) -> Self { let version = ClientVersion::V8; - Ok(match request.data { + match request.data { IpPacketRequestDataV8::Data(inner) => Self::Data((inner, version).into()), IpPacketRequestDataV8::Control(inner) => { - Self::Control((*inner, sender_tag, version).try_into()?) + Self::Control((*inner, sender_tag, version).into()) } - }) + } } } @@ -42,139 +35,72 @@ impl From<(DataRequestV8, ClientVersion)> for DataRequest { } } -impl TryFrom<(ControlRequestV8, Option, ClientVersion)> for ControlRequest { - type Error = IpPacketRouterError; - - fn try_from( - (request, sender_tag, version): ( - ControlRequestV8, - Option, - ClientVersion, - ), - ) -> Result { - Ok(match request { - ControlRequestV8::StaticConnect(inner) => { - ControlRequest::StaticConnect((inner.request, sender_tag, version).try_into()?) - } - ControlRequestV8::DynamicConnect(inner) => { - ControlRequest::DynamicConnect((inner.request, sender_tag, version).try_into()?) +impl From<(ControlRequestV8, AnonymousSenderTag, ClientVersion)> for ControlRequest { + fn from( + (request, sender_tag, version): (ControlRequestV8, AnonymousSenderTag, ClientVersion), + ) -> Self { + match request { + ControlRequestV8::Connect(inner) => { + ControlRequest::DynamicConnect((inner, sender_tag, version).into()) } ControlRequestV8::Disconnect(inner) => { - ControlRequest::Disconnect((inner.request, sender_tag, version).try_into()?) + ControlRequest::Disconnect((inner, sender_tag, version).into()) } ControlRequestV8::Ping(inner) => { - ControlRequest::Ping((inner, sender_tag, version).try_into()?) + ControlRequest::Ping((inner, sender_tag, version).into()) } ControlRequestV8::Health(inner) => { - ControlRequest::Health((inner, sender_tag, version).try_into()?) + ControlRequest::Health((inner, sender_tag, version).into()) } - }) + } } } -impl - TryFrom<( - StaticConnectRequestV8, - Option, - ClientVersion, - )> for StaticConnectRequest -{ - type Error = IpPacketRouterError; - - fn try_from( - (request, sender_tag, version): ( - StaticConnectRequestV8, - Option, - ClientVersion, - ), - ) -> Result { - Ok(Self { +impl From<(ConnectRequestV8, AnonymousSenderTag, ClientVersion)> for DynamicConnectRequest { + fn from( + (request, sender_tag, version): (ConnectRequestV8, AnonymousSenderTag, ClientVersion), + ) -> Self { + Self { version, request_id: request.request_id, - sent_by: ConnectedClientId::try_from((request.sender, sender_tag))?, - ips: request.ips, + sent_by: sender_tag.into(), buffer_timeout: request.buffer_timeout, - }) + } } } -impl - TryFrom<( - DynamicConnectRequestV8, - Option, - ClientVersion, - )> for DynamicConnectRequest -{ - type Error = IpPacketRouterError; - - fn try_from( - (request, sender_tag, version): ( - DynamicConnectRequestV8, - Option, - ClientVersion, - ), - ) -> Result { - Ok(Self { +impl From<(DisconnectRequestV8, AnonymousSenderTag, ClientVersion)> for DisconnectRequest { + fn from( + (request, sender_tag, version): (DisconnectRequestV8, AnonymousSenderTag, ClientVersion), + ) -> Self { + Self { version, request_id: request.request_id, - sent_by: ConnectedClientId::try_from((request.sender, sender_tag))?, - buffer_timeout: request.buffer_timeout, - }) + sent_by: sender_tag.into(), + } } } -impl - TryFrom<( - DisconnectRequestV8, - Option, - ClientVersion, - )> for DisconnectRequest -{ - type Error = IpPacketRouterError; - - fn try_from( - (request, sender_tag, version): ( - DisconnectRequestV8, - Option, - ClientVersion, - ), - ) -> Result { - Ok(Self { +impl From<(PingRequestV8, AnonymousSenderTag, ClientVersion)> for PingRequest { + fn from( + (request, sender_tag, version): (PingRequestV8, AnonymousSenderTag, ClientVersion), + ) -> Self { + Self { version, request_id: request.request_id, - sent_by: ConnectedClientId::try_from((request.sender, sender_tag))?, - }) + sent_by: sender_tag.into(), + } } } -impl TryFrom<(PingRequestV8, Option, ClientVersion)> for PingRequest { - type Error = IpPacketRouterError; - - fn try_from( - (request, sender_tag, version): (PingRequestV8, Option, ClientVersion), - ) -> Result { - Ok(Self { +impl From<(HealthRequestV8, AnonymousSenderTag, ClientVersion)> for HealthRequest { + fn from( + (request, sender_tag, version): (HealthRequestV8, AnonymousSenderTag, ClientVersion), + ) -> Self { + Self { version, request_id: request.request_id, - sent_by: ConnectedClientId::try_from((request.sender, sender_tag))?, - }) - } -} - -impl TryFrom<(HealthRequestV8, Option, ClientVersion)> for HealthRequest { - type Error = IpPacketRouterError; - - fn try_from( - (request, sender_tag, version): ( - HealthRequestV8, - Option, - ClientVersion, - ), - ) -> Result { - Ok(Self { - version, - request_id: request.request_id, - sent_by: ConnectedClientId::try_from((request.sender, sender_tag))?, - }) + sent_by: sender_tag.into(), + } } } diff --git a/service-providers/ip-packet-router/src/messages/response.rs b/service-providers/ip-packet-router/src/messages/response.rs index 3743323166..6e60d41433 100644 --- a/service-providers/ip-packet-router/src/messages/response.rs +++ b/service-providers/ip-packet-router/src/messages/response.rs @@ -131,7 +131,7 @@ impl VersionedResponse { match self.version { ClientVersion::V6 => IpPacketResponseV6::try_from(self)?.to_bytes(), ClientVersion::V7 => IpPacketResponseV7::try_from(self)?.to_bytes(), - ClientVersion::V8 => IpPacketResponseV8::from(self).to_bytes(), + ClientVersion::V8 => IpPacketResponseV8::try_from(self)?.to_bytes(), } .map_err(|err| IpPacketRouterError::FailedToSerializeResponsePacket { source: err }) } diff --git a/service-providers/ip-packet-router/src/messages/response/v8.rs b/service-providers/ip-packet-router/src/messages/response/v8.rs index 5ad984e355..3f73479a05 100644 --- a/service-providers/ip-packet-router/src/messages/response/v8.rs +++ b/service-providers/ip-packet-router/src/messages/response/v8.rs @@ -2,46 +2,44 @@ // SPDX-License-Identifier: GPL-3.0-only use nym_ip_packet_requests::v8::response::{ + ConnectFailureReason as ConnectFailureReasonV8, ConnectResponse as ConnectResponseV8, + ConnectResponseReply as ConnectResponseReplyV8, ConnectSuccess as ConnectSuccessV8, ControlResponse as ControlResponseV8, DisconnectFailureReason as DisconnectFailureReasonV8, DisconnectResponse as DisconnectResponseV8, - DisconnectResponseReply as DisconnectResponseReplyV8, - DynamicConnectFailureReason as DynamicConnectFailureReasonV8, - DynamicConnectResponse as DynamicConnectResponseV8, - DynamicConnectResponseReply as DynamicConnectResponseReplyV8, - DynamicConnectSuccess as DynamicConnectSuccessV8, HealthResponse as HealthResponseV8, + DisconnectResponseReply as DisconnectResponseReplyV8, HealthResponse as HealthResponseV8, HealthResponseReply as HealthResponseReplyV8, InfoLevel as InfoLevelV8, InfoResponse as InfoResponseV8, InfoResponseReply as InfoResponseReplyV8, IpPacketResponse as IpPacketResponseV8, IpPacketResponseData as IpPacketResponseDataV8, - PongResponse as PongResponseV8, StaticConnectFailureReason as StaticConnectFailureReasonV8, - StaticConnectResponse as StaticConnectResponseV8, - StaticConnectResponseReply as StaticConnectResponseReplyV8, + PongResponse as PongResponseV8, }; +use crate::error::IpPacketRouterError; + use super::{ DisconnectFailureReason, DisconnectResponse, DynamicConnectFailureReason, DynamicConnectResponse, DynamicConnectSuccess, HealthResponse, InfoLevel, InfoResponseReply, - Response, StaticConnectFailureReason, StaticConnectResponse, VersionedResponse, + Response, VersionedResponse, }; -impl From for IpPacketResponseV8 { - fn from(response: VersionedResponse) -> Self { +impl TryFrom for IpPacketResponseV8 { + type Error = IpPacketRouterError; + + fn try_from(response: VersionedResponse) -> Result { let version = response.version.into_u8(); let data = match response.response { - Response::StaticConnect { request_id, reply } => IpPacketResponseDataV8::Control( - Box::new(ControlResponseV8::StaticConnect(StaticConnectResponseV8 { + Response::StaticConnect { .. } => { + return Err(IpPacketRouterError::UnsupportedResponse(format!( + "Static connect response is not supported in version {}", + version + ))) + } + Response::DynamicConnect { request_id, reply } => IpPacketResponseDataV8::Control( + Box::new(ControlResponseV8::Connect(ConnectResponseV8 { request_id, reply: reply.into(), })), ), - Response::DynamicConnect { request_id, reply } => { - IpPacketResponseDataV8::Control(Box::new(ControlResponseV8::DynamicConnect( - DynamicConnectResponseV8 { - request_id, - reply: reply.into(), - }, - ))) - } Response::Disconnect { request_id, reply } => IpPacketResponseDataV8::Control( Box::new(ControlResponseV8::Disconnect(DisconnectResponseV8 { request_id, @@ -66,61 +64,29 @@ impl From for IpPacketResponseV8 { )), }; - IpPacketResponseV8 { version, data } + Ok(IpPacketResponseV8 { version, data }) } } -impl From for StaticConnectResponseReplyV8 { - fn from(reply: StaticConnectResponse) -> Self { - match reply { - StaticConnectResponse::Success => StaticConnectResponseReplyV8::Success, - StaticConnectResponse::Failure(err) => { - StaticConnectResponseReplyV8::Failure(err.into()) - } - } - } -} - -impl From for StaticConnectFailureReasonV8 { - fn from(reason: StaticConnectFailureReason) -> Self { - match reason { - StaticConnectFailureReason::RequestedIpAlreadyInUse => { - StaticConnectFailureReasonV8::RequestedIpAlreadyInUse - } - StaticConnectFailureReason::ClientAlreadyConnected => { - StaticConnectFailureReasonV8::ClientAlreadyConnected - } - StaticConnectFailureReason::OutOfDateTimestamp => { - StaticConnectFailureReasonV8::OutOfDateTimestamp - } - StaticConnectFailureReason::Other(err) => StaticConnectFailureReasonV8::Other(err), - } - } -} - -impl From for DynamicConnectResponseReplyV8 { +impl From for ConnectResponseReplyV8 { fn from(reply: DynamicConnectResponse) -> Self { match reply { DynamicConnectResponse::Success(DynamicConnectSuccess { ips }) => { - DynamicConnectResponseReplyV8::Success(DynamicConnectSuccessV8 { ips }) - } - DynamicConnectResponse::Failure(err) => { - DynamicConnectResponseReplyV8::Failure(err.into()) + ConnectResponseReplyV8::Success(ConnectSuccessV8 { ips }) } + DynamicConnectResponse::Failure(err) => ConnectResponseReplyV8::Failure(err.into()), } } } -impl From for DynamicConnectFailureReasonV8 { +impl From for ConnectFailureReasonV8 { fn from(reason: DynamicConnectFailureReason) -> Self { match reason { DynamicConnectFailureReason::ClientAlreadyConnected => { - DynamicConnectFailureReasonV8::ClientAlreadyConnected + ConnectFailureReasonV8::ClientAlreadyConnected } - DynamicConnectFailureReason::NoAvailableIp => { - DynamicConnectFailureReasonV8::NoAvailableIp - } - DynamicConnectFailureReason::Other(err) => DynamicConnectFailureReasonV8::Other(err), + DynamicConnectFailureReason::NoAvailableIp => ConnectFailureReasonV8::NoAvailableIp, + DynamicConnectFailureReason::Other(err) => ConnectFailureReasonV8::Other(err), } } }