Wireguard metadata client library (#5943)
This commit is contained in:
committed by
GitHub
parent
4fb252c44b
commit
f8337d9b38
Generated
+46
-8
@@ -5718,7 +5718,7 @@ dependencies = [
|
||||
"nym-types",
|
||||
"nym-validator-client",
|
||||
"nym-wireguard",
|
||||
"nym-wireguard-private-metadata",
|
||||
"nym-wireguard-private-metadata-server",
|
||||
"nym-wireguard-types",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
@@ -7312,27 +7312,65 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-wireguard-private-metadata"
|
||||
version = "0.1.0"
|
||||
name = "nym-wireguard-private-metadata-client"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"nym-http-api-client",
|
||||
"nym-wireguard-private-metadata-shared",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-wireguard-private-metadata-server"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"axum 0.7.9",
|
||||
"bincode",
|
||||
"futures",
|
||||
"nym-credential-verification",
|
||||
"nym-credentials-interface",
|
||||
"nym-http-api-common",
|
||||
"nym-wireguard",
|
||||
"schemars 0.8.22",
|
||||
"serde",
|
||||
"thiserror 2.0.12",
|
||||
"nym-wireguard-private-metadata-shared",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tower-http 0.5.2",
|
||||
"utoipa",
|
||||
"utoipa-swagger-ui",
|
||||
"utoipauto",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-wireguard-private-metadata-shared"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"bincode",
|
||||
"nym-credentials-interface",
|
||||
"schemars 0.8.22",
|
||||
"serde",
|
||||
"thiserror 2.0.12",
|
||||
"utoipa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-wireguard-private-metadata-tests"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum 0.7.9",
|
||||
"nym-credential-verification",
|
||||
"nym-credentials-interface",
|
||||
"nym-http-api-client",
|
||||
"nym-http-api-common",
|
||||
"nym-wireguard",
|
||||
"nym-wireguard-private-metadata-client",
|
||||
"nym-wireguard-private-metadata-server",
|
||||
"nym-wireguard-private-metadata-shared",
|
||||
"tokio",
|
||||
"tower-http 0.5.2",
|
||||
"utoipa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
+4
-1
@@ -102,7 +102,10 @@ members = [
|
||||
"common/wasm/storage",
|
||||
"common/wasm/utils",
|
||||
"common/wireguard",
|
||||
"common/wireguard-private-metadata",
|
||||
"common/wireguard-private-metadata/client",
|
||||
"common/wireguard-private-metadata/server",
|
||||
"common/wireguard-private-metadata/shared",
|
||||
"common/wireguard-private-metadata/tests",
|
||||
"common/wireguard-types",
|
||||
"common/zulip-client",
|
||||
"documentation/autodoc",
|
||||
|
||||
@@ -136,6 +136,7 @@
|
||||
//! ```
|
||||
#![warn(missing_docs)]
|
||||
|
||||
pub use reqwest::ClientBuilder as ReqwestClientBuilder;
|
||||
pub use reqwest::StatusCode;
|
||||
|
||||
use crate::path::RequestPath;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "nym-wireguard-private-metadata-client"
|
||||
version = "1.0.0"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
async-trait = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
nym-http-api-client = { path = "../../http-api-client" }
|
||||
nym-wireguard-private-metadata-shared = { path = "../shared" }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -0,0 +1,58 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use async_trait::async_trait;
|
||||
use tracing::instrument;
|
||||
|
||||
use nym_http_api_client::{ApiClient, Client, HttpClientError, NO_PARAMS};
|
||||
|
||||
use nym_wireguard_private_metadata_shared::{
|
||||
routes, Version, {ErrorResponse, Request, Response},
|
||||
};
|
||||
|
||||
pub type WireguardMetadataApiClientError = HttpClientError<ErrorResponse>;
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
pub trait WireguardMetadataApiClient: ApiClient {
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn version(&self) -> Result<Version, WireguardMetadataApiClientError> {
|
||||
let version: u64 = self
|
||||
.get_json(
|
||||
&[routes::V1_API_VERSION, routes::BANDWIDTH, routes::VERSION],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await?;
|
||||
Ok(version.into())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn available_bandwidth(
|
||||
&self,
|
||||
request_body: &Request,
|
||||
) -> Result<Response, WireguardMetadataApiClientError> {
|
||||
self.post_json(
|
||||
&[routes::V1_API_VERSION, routes::BANDWIDTH, routes::AVAILABLE],
|
||||
NO_PARAMS,
|
||||
request_body,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, request_body))]
|
||||
async fn topup_bandwidth(
|
||||
&self,
|
||||
request_body: &Request,
|
||||
) -> Result<Response, WireguardMetadataApiClientError> {
|
||||
self.post_json(
|
||||
&[routes::V1_API_VERSION, routes::BANDWIDTH, routes::TOPUP],
|
||||
NO_PARAMS,
|
||||
request_body,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl WireguardMetadataApiClient for Client {}
|
||||
+11
-11
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-wireguard-private-metadata"
|
||||
version = "0.1.0"
|
||||
name = "nym-wireguard-private-metadata-server"
|
||||
version = "1.0.0"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
@@ -11,11 +11,7 @@ license.workspace = true
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
axum = { workspace = true, features = ["tokio", "macros"] }
|
||||
bincode = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
schemars = { workspace = true, features = ["preserve_order"] }
|
||||
serde = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "net", "io-util"] }
|
||||
tokio-util = { workspace = true }
|
||||
tower-http = { workspace = true, features = [
|
||||
@@ -27,17 +23,21 @@ tower-http = { workspace = true, features = [
|
||||
"compression-zstd",
|
||||
] }
|
||||
utoipa = { workspace = true, features = ["axum_extras", "time"] }
|
||||
utoipauto = { workspace = true }
|
||||
utoipa-swagger-ui = { workspace = true, features = ["axum"] }
|
||||
|
||||
nym-credentials-interface = { path = "../credentials-interface" }
|
||||
nym-credential-verification = { path = "../credential-verification" }
|
||||
nym-http-api-common = { path = "../http-api-common", features = [
|
||||
nym-credentials-interface = { path = "../../credentials-interface" }
|
||||
nym-credential-verification = { path = "../../credential-verification" }
|
||||
nym-http-api-common = { path = "../../http-api-common", features = [
|
||||
"middleware",
|
||||
"utoipa",
|
||||
"output",
|
||||
] }
|
||||
nym-wireguard = { path = "../wireguard" }
|
||||
nym-wireguard = { path = "../../wireguard" }
|
||||
nym-wireguard-private-metadata-shared = { path = "../shared" }
|
||||
|
||||
[dev-dependencies]
|
||||
async-trait = { workspace = true }
|
||||
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
+2
-2
@@ -3,12 +3,12 @@
|
||||
|
||||
use utoipa::OpenApi;
|
||||
|
||||
use crate::models::{AvailableBandwidthResponse, TopUpRequest};
|
||||
use nym_wireguard_private_metadata_shared::{Request, Response};
|
||||
|
||||
#[derive(OpenApi)]
|
||||
#[openapi(
|
||||
info(title = "Nym Wireguard Private Metadata"),
|
||||
tags(),
|
||||
components(schemas(AvailableBandwidthResponse, TopUpRequest))
|
||||
components(schemas(Request, Response))
|
||||
)]
|
||||
pub(crate) struct ApiDoc;
|
||||
+1
-1
@@ -66,7 +66,7 @@ fn setup_cors() -> CorsLayer {
|
||||
}
|
||||
|
||||
pub struct RouterWithState {
|
||||
router: Router,
|
||||
pub router: Router,
|
||||
}
|
||||
|
||||
impl RouterWithState {
|
||||
+8
-16
@@ -5,11 +5,8 @@ use std::net::IpAddr;
|
||||
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
|
||||
use crate::{
|
||||
error::MetadataError,
|
||||
models::{latest, AvailableBandwidthResponse},
|
||||
transceiver::PeerControllerTransceiver,
|
||||
};
|
||||
use crate::transceiver::PeerControllerTransceiver;
|
||||
use nym_wireguard_private_metadata_shared::error::MetadataError;
|
||||
|
||||
#[derive(Clone, axum::extract::FromRef)]
|
||||
pub struct AppState {
|
||||
@@ -21,23 +18,18 @@ impl AppState {
|
||||
Self { transceiver }
|
||||
}
|
||||
|
||||
pub(crate) async fn available_bandwidth(
|
||||
&self,
|
||||
ip: IpAddr,
|
||||
) -> Result<AvailableBandwidthResponse, MetadataError> {
|
||||
let value = self.transceiver.query_bandwidth(ip).await?;
|
||||
let res = latest::InnerAvailableBandwidthResponse::new(value).try_into()?;
|
||||
Ok(res)
|
||||
pub async fn available_bandwidth(&self, ip: IpAddr) -> Result<i64, MetadataError> {
|
||||
self.transceiver.query_bandwidth(ip).await
|
||||
}
|
||||
|
||||
pub(crate) async fn topup_bandwidth(
|
||||
// Top up with a credential and return the afterwards available bandwidth
|
||||
pub async fn topup_bandwidth(
|
||||
&self,
|
||||
ip: IpAddr,
|
||||
credential: CredentialSpendingData,
|
||||
) -> Result<(), MetadataError> {
|
||||
) -> Result<i64, MetadataError> {
|
||||
self.transceiver
|
||||
.topup_bandwidth(ip, Box::new(credential))
|
||||
.await?;
|
||||
Ok(())
|
||||
.await
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
mod http;
|
||||
mod network;
|
||||
mod transceiver;
|
||||
|
||||
pub use http::{
|
||||
router::{ApiHttpServer, RouterBuilder, RouterWithState},
|
||||
state::AppState,
|
||||
ShutdownHandles,
|
||||
};
|
||||
pub use transceiver::PeerControllerTransceiver;
|
||||
@@ -0,0 +1,111 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use axum::{
|
||||
extract::{ConnectInfo, Query, State},
|
||||
Json, Router,
|
||||
};
|
||||
use nym_http_api_common::{FormattedResponse, OutputParams};
|
||||
use nym_wireguard_private_metadata_shared::{
|
||||
interface::{RequestData, ResponseData},
|
||||
latest, AxumErrorResponse, AxumResult, Construct, Extract, Request, Response,
|
||||
};
|
||||
use tower_http::compression::CompressionLayer;
|
||||
|
||||
use crate::http::state::AppState;
|
||||
|
||||
pub(crate) fn bandwidth_routes() -> Router<AppState> {
|
||||
Router::new()
|
||||
.route("/version", axum::routing::get(version))
|
||||
.route("/available", axum::routing::post(available_bandwidth))
|
||||
.route("/topup", axum::routing::post(topup_bandwidth))
|
||||
.layer(CompressionLayer::new())
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
tag = "bandwidth",
|
||||
get,
|
||||
path = "/v1/bandwidth/version",
|
||||
responses(
|
||||
(status = 200, content(
|
||||
(Response = "application/bincode")
|
||||
))
|
||||
),
|
||||
)]
|
||||
async fn version(Query(output): Query<OutputParams>) -> AxumResult<FormattedResponse<u64>> {
|
||||
let output = output.output.unwrap_or_default();
|
||||
Ok(output.to_response(latest::VERSION.into()))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
tag = "bandwidth",
|
||||
post,
|
||||
request_body = Request,
|
||||
path = "/v1/bandwidth/available",
|
||||
responses(
|
||||
(status = 200, content(
|
||||
(Response = "application/bincode")
|
||||
))
|
||||
),
|
||||
)]
|
||||
async fn available_bandwidth(
|
||||
ConnectInfo(addr): ConnectInfo<SocketAddr>,
|
||||
Query(output): Query<OutputParams>,
|
||||
State(state): State<AppState>,
|
||||
Json(request): Json<Request>,
|
||||
) -> AxumResult<FormattedResponse<Response>> {
|
||||
let output = output.output.unwrap_or_default();
|
||||
|
||||
let (RequestData::AvailableBandwidth(_), version) =
|
||||
request.extract().map_err(AxumErrorResponse::bad_request)?
|
||||
else {
|
||||
return Err(AxumErrorResponse::bad_request("incorrect request type"));
|
||||
};
|
||||
let available_bandwidth = state
|
||||
.available_bandwidth(addr.ip())
|
||||
.await
|
||||
.map_err(AxumErrorResponse::bad_request)?;
|
||||
let response = Response::construct(
|
||||
ResponseData::AvailableBandwidth(available_bandwidth),
|
||||
version,
|
||||
)
|
||||
.map_err(AxumErrorResponse::bad_request)?;
|
||||
|
||||
Ok(output.to_response(response))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
tag = "bandwidth",
|
||||
post,
|
||||
request_body = Request,
|
||||
path = "/v1/bandwidth/topup",
|
||||
responses(
|
||||
(status = 200, content(
|
||||
(Response = "application/bincode")
|
||||
))
|
||||
),
|
||||
)]
|
||||
async fn topup_bandwidth(
|
||||
ConnectInfo(addr): ConnectInfo<SocketAddr>,
|
||||
Query(output): Query<OutputParams>,
|
||||
State(state): State<AppState>,
|
||||
Json(request): Json<Request>,
|
||||
) -> AxumResult<FormattedResponse<Response>> {
|
||||
let output = output.output.unwrap_or_default();
|
||||
|
||||
let (RequestData::TopUpBandwidth(credential), version) =
|
||||
request.extract().map_err(AxumErrorResponse::bad_request)?
|
||||
else {
|
||||
return Err(AxumErrorResponse::bad_request("incorrect request type"));
|
||||
};
|
||||
let available_bandwidth = state
|
||||
.topup_bandwidth(addr.ip(), *credential)
|
||||
.await
|
||||
.map_err(AxumErrorResponse::bad_request)?;
|
||||
let response = Response::construct(ResponseData::TopUpBandwidth(available_bandwidth), version)
|
||||
.map_err(AxumErrorResponse::bad_request)?;
|
||||
|
||||
Ok(output.to_response(response))
|
||||
}
|
||||
+31
-16
@@ -1,13 +1,15 @@
|
||||
use futures::channel::oneshot;
|
||||
use nym_credential_verification::ClientBandwidth;
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
use tokio::sync::mpsc;
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::net::IpAddr;
|
||||
|
||||
use nym_wireguard::peer_controller::PeerControlRequest;
|
||||
use futures::channel::oneshot;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use crate::error::MetadataError;
|
||||
use nym_credential_verification::ClientBandwidth;
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
use nym_wireguard::peer_controller::PeerControlRequest;
|
||||
use nym_wireguard_private_metadata_shared::error::MetadataError;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PeerControllerTransceiver {
|
||||
@@ -39,6 +41,7 @@ impl PeerControllerTransceiver {
|
||||
Ok(self.get_client_bandwidth(ip).await?.available().await)
|
||||
}
|
||||
|
||||
// Top up with a credential and return the afterwards available bandwidth
|
||||
pub(crate) async fn topup_bandwidth(
|
||||
&self,
|
||||
ip: IpAddr,
|
||||
@@ -61,20 +64,26 @@ impl PeerControllerTransceiver {
|
||||
.map_err(|err| MetadataError::Unsuccessful {
|
||||
reason: err.to_string(),
|
||||
})?;
|
||||
let available_bandwidth = verifier.verify().await?;
|
||||
let available_bandwidth =
|
||||
verifier
|
||||
.verify()
|
||||
.await
|
||||
.map_err(|err| MetadataError::CredentialVerification {
|
||||
message: err.to_string(),
|
||||
})?;
|
||||
|
||||
Ok(available_bandwidth)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
pub(crate) mod tests {
|
||||
use nym_credential_verification::TicketVerifier;
|
||||
use nym_wireguard::CONTROL_CHANNEL_SIZE;
|
||||
|
||||
use super::*;
|
||||
|
||||
const CREDENTIAL_BYTES: [u8; 1245] = [
|
||||
pub(crate) const CREDENTIAL_BYTES: [u8; 1245] = [
|
||||
0, 0, 4, 133, 96, 179, 223, 185, 136, 23, 213, 166, 59, 203, 66, 69, 209, 181, 227, 254,
|
||||
16, 102, 98, 237, 59, 119, 170, 111, 31, 194, 51, 59, 120, 17, 115, 229, 79, 91, 11, 139,
|
||||
154, 2, 212, 23, 68, 70, 167, 3, 240, 54, 224, 171, 221, 1, 69, 48, 60, 118, 119, 249, 123,
|
||||
@@ -139,10 +148,16 @@ mod tests {
|
||||
0, 0, 0, 0, 0, 1,
|
||||
];
|
||||
|
||||
struct MockVerifier {
|
||||
pub(crate) struct MockVerifier {
|
||||
ret: i64,
|
||||
}
|
||||
|
||||
impl MockVerifier {
|
||||
pub(crate) fn new(ret: i64) -> MockVerifier {
|
||||
Self { ret }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl TicketVerifier for MockVerifier {
|
||||
async fn verify(&mut self) -> nym_credential_verification::Result<i64> {
|
||||
@@ -162,7 +177,7 @@ mod tests {
|
||||
.send(Ok(ClientBandwidth::new(Default::default())))
|
||||
.ok();
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
_ => panic!("Not expected"),
|
||||
}
|
||||
});
|
||||
|
||||
@@ -197,7 +212,7 @@ mod tests {
|
||||
ip: _,
|
||||
response_tx: _,
|
||||
} => {}
|
||||
_ => unimplemented!(),
|
||||
_ => panic!("Not expected"),
|
||||
}
|
||||
});
|
||||
|
||||
@@ -222,7 +237,7 @@ mod tests {
|
||||
)))
|
||||
.ok();
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
_ => panic!("Not expected"),
|
||||
}
|
||||
});
|
||||
|
||||
@@ -247,10 +262,10 @@ mod tests {
|
||||
response_tx,
|
||||
} => {
|
||||
response_tx
|
||||
.send(Ok(Box::new(MockVerifier { ret: verifier_bw })))
|
||||
.send(Ok(Box::new(MockVerifier::new(verifier_bw))))
|
||||
.ok();
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
_ => panic!("Not expected"),
|
||||
}
|
||||
});
|
||||
|
||||
@@ -280,7 +295,7 @@ mod tests {
|
||||
)))
|
||||
.ok();
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
_ => panic!("Not expected"),
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
[package]
|
||||
name = "nym-wireguard-private-metadata-shared"
|
||||
version = "1.0.0"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
axum = { workspace = true }
|
||||
bincode = { workspace = true }
|
||||
schemars = { workspace = true, features = ["preserve_order"] }
|
||||
serde = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
utoipa = { workspace = true }
|
||||
|
||||
nym-credentials-interface = { path = "../../credentials-interface" }
|
||||
|
||||
[features]
|
||||
testing = []
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
-8
@@ -26,11 +26,3 @@ impl From<crate::models::error::Error> for MetadataError {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<nym_credential_verification::Error> for MetadataError {
|
||||
fn from(value: nym_credential_verification::Error) -> Self {
|
||||
Self::CredentialVerification {
|
||||
message: value.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod error;
|
||||
mod models;
|
||||
pub mod routes;
|
||||
|
||||
#[cfg(feature = "testing")]
|
||||
pub use models::v0;
|
||||
pub use models::{
|
||||
error::Error as ModelError, interface, latest, v1, AxumErrorResponse, AxumResult, Construct,
|
||||
ErrorResponse, Extract, Request, Response, Version,
|
||||
};
|
||||
|
||||
fn make_bincode_serializer() -> impl bincode::Options {
|
||||
use bincode::Options;
|
||||
bincode::DefaultOptions::new()
|
||||
.with_big_endian()
|
||||
.with_varint_encoding()
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::models::Version;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
Bincode(#[from] bincode::Error),
|
||||
|
||||
#[error("trying to deserialize from version {source_version:?} into {target_version:?}")]
|
||||
InvalidVersion {
|
||||
source_version: Version,
|
||||
target_version: Version,
|
||||
},
|
||||
|
||||
#[error(
|
||||
"trying to deserialize from query type {source_query_type} query type {target_query_type}"
|
||||
)]
|
||||
InvalidQueryType {
|
||||
source_query_type: String,
|
||||
target_query_type: String,
|
||||
},
|
||||
|
||||
#[error("update not possible from {from:?} to {to:?}")]
|
||||
UpdateNotPossible { from: Version, to: Version },
|
||||
|
||||
#[error("downgrade not possible from {from:?} to {to:?}")]
|
||||
DowngradeNotPossible { from: Version, to: Version },
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
|
||||
#[cfg(feature = "testing")]
|
||||
use crate::models::v0;
|
||||
use crate::models::{v1, Construct, Extract, Request, Response, Version};
|
||||
|
||||
pub enum RequestData {
|
||||
AvailableBandwidth(()),
|
||||
TopUpBandwidth(Box<CredentialSpendingData>),
|
||||
}
|
||||
|
||||
impl From<super::latest::interface::RequestData> for RequestData {
|
||||
fn from(value: super::latest::interface::RequestData) -> Self {
|
||||
match value {
|
||||
super::latest::interface::RequestData::AvailableBandwidth(inner) => {
|
||||
Self::AvailableBandwidth(inner)
|
||||
}
|
||||
super::latest::interface::RequestData::TopUpBandwidth(credential_spending_data) => {
|
||||
Self::TopUpBandwidth(credential_spending_data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RequestData> for super::latest::interface::RequestData {
|
||||
fn from(value: RequestData) -> Self {
|
||||
match value {
|
||||
RequestData::AvailableBandwidth(inner) => Self::AvailableBandwidth(inner),
|
||||
RequestData::TopUpBandwidth(credential_spending_data) => {
|
||||
Self::TopUpBandwidth(credential_spending_data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<super::latest::interface::ResponseData> for ResponseData {
|
||||
fn from(value: super::latest::interface::ResponseData) -> Self {
|
||||
match value {
|
||||
super::latest::interface::ResponseData::AvailableBandwidth(inner) => {
|
||||
Self::AvailableBandwidth(inner)
|
||||
}
|
||||
super::latest::interface::ResponseData::TopUpBandwidth(credential_spending_data) => {
|
||||
Self::TopUpBandwidth(credential_spending_data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ResponseData> for super::latest::interface::ResponseData {
|
||||
fn from(value: ResponseData) -> Self {
|
||||
match value {
|
||||
ResponseData::AvailableBandwidth(inner) => Self::AvailableBandwidth(inner),
|
||||
ResponseData::TopUpBandwidth(credential_spending_data) => {
|
||||
Self::TopUpBandwidth(credential_spending_data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Construct<RequestData> for Request {
|
||||
fn construct(info: RequestData, version: Version) -> Result<Self, super::error::Error> {
|
||||
match version {
|
||||
#[cfg(feature = "testing")]
|
||||
Version::V0 => {
|
||||
let translate_info = super::latest::interface::RequestData::from(info);
|
||||
let downgrade_info = v0::interface::RequestData::try_from(translate_info)?;
|
||||
let versioned_request = v0::VersionedRequest::construct(downgrade_info, version)?;
|
||||
Ok(versioned_request.try_into()?)
|
||||
}
|
||||
Version::V1 => {
|
||||
let versioned_request = v1::VersionedRequest::construct(info.into(), version)?;
|
||||
Ok(versioned_request.try_into()?)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Extract<RequestData> for Request {
|
||||
fn extract(&self) -> Result<(RequestData, Version), crate::models::Error> {
|
||||
match self.version {
|
||||
#[cfg(feature = "testing")]
|
||||
super::Version::V0 => {
|
||||
let versioned_request = v0::VersionedRequest::try_from(self.clone())?;
|
||||
let (request, version) = versioned_request.extract()?;
|
||||
|
||||
let upgrade_request = super::latest::interface::RequestData::try_from(request)?;
|
||||
|
||||
Ok((upgrade_request.into(), version))
|
||||
}
|
||||
super::Version::V1 => {
|
||||
let versioned_request = v1::VersionedRequest::try_from(self.clone())?;
|
||||
let (extracted, version) = versioned_request.extract()?;
|
||||
Ok((extracted.into(), version))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ResponseData {
|
||||
AvailableBandwidth(i64),
|
||||
TopUpBandwidth(i64),
|
||||
}
|
||||
|
||||
impl Construct<ResponseData> for Response {
|
||||
fn construct(info: ResponseData, version: Version) -> Result<Self, super::error::Error> {
|
||||
match version {
|
||||
#[cfg(feature = "testing")]
|
||||
super::Version::V0 => {
|
||||
let translate_response = super::latest::interface::ResponseData::from(info);
|
||||
let downgrade_response = v0::interface::ResponseData::try_from(translate_response)?;
|
||||
let versioned_response =
|
||||
v0::VersionedResponse::construct(downgrade_response, version)?;
|
||||
Ok(versioned_response.try_into()?)
|
||||
}
|
||||
Version::V1 => {
|
||||
let versioned_response = v1::VersionedResponse::construct(info.into(), version)?;
|
||||
Ok(versioned_response.try_into()?)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Extract<ResponseData> for Response {
|
||||
fn extract(&self) -> Result<(ResponseData, Version), super::error::Error> {
|
||||
match self.version {
|
||||
#[cfg(feature = "testing")]
|
||||
super::Version::V0 => {
|
||||
let versioned_response = v0::VersionedResponse::try_from(self.clone())?;
|
||||
let (response, version) = versioned_response.extract()?;
|
||||
|
||||
let upgrade_response = super::latest::interface::ResponseData::try_from(response)?;
|
||||
|
||||
Ok((upgrade_response.into(), version))
|
||||
}
|
||||
super::Version::V1 => {
|
||||
let versioned_response = v1::VersionedResponse::try_from(self.clone())?;
|
||||
let (extracted, version) = versioned_response.extract()?;
|
||||
Ok((extracted.into(), version))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
use axum::http::StatusCode;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
pub(crate) mod error;
|
||||
pub mod interface;
|
||||
#[cfg(feature = "testing")]
|
||||
pub mod v0; // dummy version, only for filling boilerplate code for update/downgrade and testing
|
||||
pub mod v1;
|
||||
|
||||
pub use v1 as latest;
|
||||
|
||||
use crate::models::error::Error;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
pub enum Version {
|
||||
#[cfg(feature = "testing")]
|
||||
/// only used for testing purposes, don't include it in your matching arms
|
||||
V0,
|
||||
V1,
|
||||
}
|
||||
|
||||
impl From<u64> for Version {
|
||||
fn from(value: u64) -> Self {
|
||||
#[cfg(feature = "testing")]
|
||||
let zero_version = Version::V0;
|
||||
#[cfg(not(feature = "testing"))]
|
||||
let zero_version = latest::VERSION;
|
||||
match value {
|
||||
0 => zero_version,
|
||||
1 => Version::V1,
|
||||
_ => latest::VERSION, // if unknown, it means we're behind, so we can use the latest we know about
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Version> for u64 {
|
||||
fn from(value: Version) -> Self {
|
||||
// remember to modify the above match if you're bumping the version
|
||||
match value {
|
||||
#[cfg(feature = "testing")]
|
||||
Version::V0 => 0,
|
||||
Version::V1 => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
pub struct Request {
|
||||
pub version: Version,
|
||||
pub(crate) inner: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, ToSchema)]
|
||||
pub struct Response {
|
||||
pub version: Version,
|
||||
pub(crate) inner: Vec<u8>,
|
||||
}
|
||||
|
||||
pub trait Extract<T> {
|
||||
fn extract(&self) -> Result<(T, Version), Error>;
|
||||
}
|
||||
|
||||
pub trait Construct<T>: Sized {
|
||||
fn construct(info: T, version: Version) -> Result<Self, Error>;
|
||||
}
|
||||
|
||||
pub type AxumResult<T> = Result<T, AxumErrorResponse>;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
|
||||
pub struct ErrorResponse {
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
impl Display for ErrorResponse {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
self.message.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AxumErrorResponse {
|
||||
message: ErrorResponse,
|
||||
status: StatusCode,
|
||||
}
|
||||
|
||||
impl AxumErrorResponse {
|
||||
pub fn bad_request(msg: impl Display) -> Self {
|
||||
Self {
|
||||
message: ErrorResponse {
|
||||
message: msg.to_string(),
|
||||
},
|
||||
status: StatusCode::BAD_REQUEST,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl axum::response::IntoResponse for AxumErrorResponse {
|
||||
fn into_response(self) -> axum::response::Response {
|
||||
(self.status, self.message.message.to_string()).into_response()
|
||||
}
|
||||
}
|
||||
|
||||
mod tests {
|
||||
#[allow(dead_code)]
|
||||
pub(crate) const CREDENTIAL_BYTES: [u8; 1245] = [
|
||||
0, 0, 4, 133, 96, 179, 223, 185, 136, 23, 213, 166, 59, 203, 66, 69, 209, 181, 227, 254,
|
||||
16, 102, 98, 237, 59, 119, 170, 111, 31, 194, 51, 59, 120, 17, 115, 229, 79, 91, 11, 139,
|
||||
154, 2, 212, 23, 68, 70, 167, 3, 240, 54, 224, 171, 221, 1, 69, 48, 60, 118, 119, 249, 123,
|
||||
35, 172, 227, 131, 96, 232, 209, 187, 123, 4, 197, 102, 90, 96, 45, 125, 135, 140, 99, 1,
|
||||
151, 17, 131, 143, 157, 97, 107, 139, 232, 212, 87, 14, 115, 253, 255, 166, 167, 186, 43,
|
||||
90, 96, 173, 105, 120, 40, 10, 163, 250, 224, 214, 200, 178, 4, 160, 16, 130, 59, 76, 193,
|
||||
39, 240, 3, 101, 141, 209, 183, 226, 186, 207, 56, 210, 187, 7, 164, 240, 164, 205, 37, 81,
|
||||
184, 214, 193, 195, 90, 205, 238, 225, 195, 104, 12, 123, 203, 57, 233, 243, 215, 145, 195,
|
||||
196, 57, 38, 125, 172, 18, 47, 63, 165, 110, 219, 180, 40, 58, 116, 92, 254, 160, 98, 48,
|
||||
92, 254, 232, 107, 184, 80, 234, 60, 160, 235, 249, 76, 41, 38, 165, 28, 40, 136, 74, 48,
|
||||
166, 50, 245, 23, 201, 140, 101, 79, 93, 235, 128, 186, 146, 126, 180, 134, 43, 13, 186,
|
||||
19, 195, 48, 168, 201, 29, 216, 95, 176, 198, 132, 188, 64, 39, 212, 150, 32, 52, 53, 38,
|
||||
228, 199, 122, 226, 217, 75, 40, 191, 151, 48, 164, 242, 177, 79, 14, 122, 105, 151, 85,
|
||||
88, 199, 162, 17, 96, 103, 83, 178, 128, 9, 24, 30, 74, 108, 241, 85, 240, 166, 97, 241,
|
||||
85, 199, 11, 198, 226, 234, 70, 107, 145, 28, 208, 114, 51, 12, 234, 108, 101, 202, 112,
|
||||
48, 185, 22, 159, 67, 109, 49, 27, 149, 90, 109, 32, 226, 112, 7, 201, 208, 209, 104, 31,
|
||||
97, 134, 204, 145, 27, 181, 206, 181, 106, 32, 110, 136, 115, 249, 201, 111, 5, 245, 203,
|
||||
71, 121, 169, 126, 151, 178, 236, 59, 221, 195, 48, 135, 115, 6, 50, 227, 74, 97, 107, 107,
|
||||
213, 90, 2, 203, 154, 138, 47, 128, 52, 134, 128, 224, 51, 65, 240, 90, 8, 55, 175, 180,
|
||||
178, 204, 206, 168, 110, 51, 57, 189, 169, 48, 169, 136, 121, 99, 51, 170, 178, 214, 74, 1,
|
||||
96, 151, 167, 25, 173, 180, 171, 155, 10, 55, 142, 234, 190, 113, 90, 79, 80, 244, 71, 166,
|
||||
30, 235, 113, 150, 133, 1, 218, 17, 109, 111, 223, 24, 216, 177, 41, 2, 204, 65, 221, 212,
|
||||
207, 236, 144, 6, 65, 224, 55, 42, 1, 1, 161, 134, 118, 127, 111, 220, 110, 127, 240, 71,
|
||||
223, 129, 12, 93, 20, 220, 60, 56, 71, 146, 184, 95, 132, 69, 28, 56, 53, 192, 213, 22,
|
||||
119, 230, 152, 225, 182, 188, 163, 219, 37, 175, 247, 73, 14, 247, 38, 72, 243, 1, 48, 131,
|
||||
59, 8, 13, 96, 143, 185, 127, 241, 161, 217, 24, 149, 193, 40, 16, 30, 202, 151, 28, 119,
|
||||
240, 153, 101, 156, 61, 193, 72, 245, 199, 181, 12, 231, 65, 166, 67, 142, 121, 207, 202,
|
||||
58, 197, 113, 188, 248, 42, 124, 105, 48, 161, 241, 55, 209, 36, 194, 27, 63, 233, 144,
|
||||
189, 85, 117, 234, 9, 139, 46, 31, 206, 114, 95, 131, 29, 240, 13, 81, 142, 140, 133, 33,
|
||||
30, 41, 141, 37, 80, 217, 95, 221, 76, 115, 86, 201, 165, 51, 252, 9, 28, 209, 1, 48, 150,
|
||||
74, 248, 212, 187, 222, 66, 210, 3, 200, 19, 217, 171, 184, 42, 148, 53, 150, 57, 50, 6,
|
||||
227, 227, 62, 49, 42, 148, 148, 157, 82, 191, 58, 24, 34, 56, 98, 120, 89, 105, 176, 85,
|
||||
15, 253, 241, 41, 153, 195, 136, 1, 48, 142, 126, 213, 101, 223, 79, 133, 230, 105, 38,
|
||||
161, 149, 2, 21, 136, 150, 42, 72, 218, 85, 146, 63, 223, 58, 108, 186, 183, 248, 62, 20,
|
||||
47, 34, 113, 160, 177, 204, 181, 16, 24, 212, 224, 35, 84, 51, 168, 56, 136, 11, 1, 48,
|
||||
135, 242, 62, 149, 230, 178, 32, 224, 119, 26, 234, 163, 237, 224, 114, 95, 112, 140, 170,
|
||||
150, 96, 125, 136, 221, 180, 78, 18, 11, 12, 184, 2, 198, 217, 119, 43, 69, 4, 172, 109,
|
||||
55, 183, 40, 131, 172, 161, 88, 183, 101, 1, 48, 173, 216, 22, 73, 42, 255, 211, 93, 249,
|
||||
87, 159, 115, 61, 91, 55, 130, 17, 216, 60, 34, 122, 55, 8, 244, 244, 153, 151, 57, 5, 144,
|
||||
178, 55, 249, 64, 211, 168, 34, 148, 56, 89, 92, 203, 70, 124, 219, 152, 253, 165, 0, 32,
|
||||
203, 116, 63, 7, 240, 222, 82, 86, 11, 149, 167, 72, 224, 55, 190, 66, 201, 65, 168, 184,
|
||||
96, 47, 194, 241, 168, 124, 7, 74, 214, 250, 37, 76, 32, 218, 69, 122, 103, 215, 145, 169,
|
||||
24, 212, 229, 168, 106, 10, 144, 31, 13, 25, 178, 242, 250, 106, 159, 40, 48, 163, 165, 61,
|
||||
130, 57, 146, 4, 73, 32, 254, 233, 125, 135, 212, 29, 111, 4, 177, 114, 15, 210, 170, 82,
|
||||
108, 110, 62, 166, 81, 209, 106, 176, 156, 14, 133, 242, 60, 127, 120, 242, 28, 97, 0, 1,
|
||||
32, 103, 93, 109, 89, 240, 91, 1, 84, 150, 50, 206, 157, 203, 49, 220, 120, 234, 175, 234,
|
||||
150, 126, 225, 94, 163, 164, 199, 138, 114, 62, 99, 106, 112, 1, 32, 171, 40, 220, 82, 241,
|
||||
203, 76, 146, 111, 139, 182, 179, 237, 182, 115, 75, 128, 201, 107, 43, 214, 0, 135, 217,
|
||||
160, 68, 150, 232, 144, 114, 237, 98, 32, 30, 134, 232, 59, 93, 163, 253, 244, 13, 202, 52,
|
||||
147, 168, 83, 121, 123, 95, 21, 210, 209, 225, 223, 143, 49, 10, 205, 238, 1, 22, 83, 81,
|
||||
70, 1, 32, 26, 76, 6, 234, 160, 50, 139, 102, 161, 232, 155, 106, 130, 171, 226, 210, 233,
|
||||
178, 85, 247, 71, 123, 55, 53, 46, 67, 148, 137, 156, 207, 208, 107, 1, 32, 102, 31, 4, 98,
|
||||
110, 156, 144, 61, 229, 140, 198, 84, 196, 238, 128, 35, 131, 182, 137, 125, 241, 95, 69,
|
||||
131, 170, 27, 2, 144, 75, 72, 242, 102, 3, 32, 121, 80, 45, 173, 56, 65, 218, 27, 40, 251,
|
||||
197, 32, 169, 104, 123, 110, 90, 78, 153, 166, 38, 9, 129, 228, 99, 8, 1, 116, 142, 233,
|
||||
162, 69, 32, 216, 169, 159, 116, 95, 12, 63, 176, 195, 6, 183, 123, 135, 75, 61, 112, 106,
|
||||
83, 235, 176, 41, 27, 248, 48, 71, 165, 170, 12, 92, 103, 103, 81, 32, 58, 74, 75, 145,
|
||||
192, 94, 153, 69, 80, 128, 241, 3, 16, 117, 192, 86, 161, 103, 44, 174, 211, 196, 182, 124,
|
||||
55, 11, 107, 142, 49, 88, 6, 41, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 37, 139, 240, 0, 0,
|
||||
0, 0, 0, 0, 0, 1,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod request;
|
||||
pub mod response;
|
||||
@@ -0,0 +1,86 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use bincode::Options;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{make_bincode_serializer, models::Request};
|
||||
|
||||
use super::super::{Error, QueryType, VersionedRequest};
|
||||
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct InnerAvailableBandwidthRequest {}
|
||||
|
||||
impl TryFrom<VersionedRequest> for InnerAvailableBandwidthRequest {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: VersionedRequest) -> Result<Self, Self::Error> {
|
||||
match value.query_type {
|
||||
QueryType::AvailableBandwidth => {
|
||||
Ok(make_bincode_serializer().deserialize(&value.inner)?)
|
||||
}
|
||||
QueryType::TopupBandwidth => Err(Error::InvalidQueryType {
|
||||
source_query_type: value.query_type.to_string(),
|
||||
target_query_type: QueryType::AvailableBandwidth.to_string(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<InnerAvailableBandwidthRequest> for VersionedRequest {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: InnerAvailableBandwidthRequest) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
query_type: QueryType::AvailableBandwidth,
|
||||
inner: make_bincode_serializer().serialize(&value)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Request> for InnerAvailableBandwidthRequest {
|
||||
type Error = crate::error::MetadataError;
|
||||
|
||||
fn try_from(value: Request) -> Result<Self, Self::Error> {
|
||||
VersionedRequest::try_from(value)?
|
||||
.try_into()
|
||||
.map_err(|err: Error| crate::error::MetadataError::Models {
|
||||
message: err.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<InnerAvailableBandwidthRequest> for Request {
|
||||
type Error = crate::error::MetadataError;
|
||||
|
||||
fn try_from(value: InnerAvailableBandwidthRequest) -> Result<Self, Self::Error> {
|
||||
VersionedRequest::try_from(value)?
|
||||
.try_into()
|
||||
.map_err(|err: Error| crate::error::MetadataError::Models {
|
||||
message: err.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn serde() {
|
||||
let req = InnerAvailableBandwidthRequest {};
|
||||
let ser = VersionedRequest::try_from(req).unwrap();
|
||||
assert_eq!(QueryType::AvailableBandwidth, ser.query_type);
|
||||
let de = InnerAvailableBandwidthRequest::try_from(ser).unwrap();
|
||||
assert_eq!(req, de);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_content() {
|
||||
let future_req = VersionedRequest {
|
||||
query_type: QueryType::AvailableBandwidth,
|
||||
inner: vec![],
|
||||
};
|
||||
assert!(InnerAvailableBandwidthRequest::try_from(future_req).is_ok());
|
||||
}
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use bincode::Options;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{make_bincode_serializer, models::Response};
|
||||
|
||||
use super::super::{Error, QueryType, VersionedResponse};
|
||||
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct InnerAvailableBandwidthResponse {}
|
||||
|
||||
impl TryFrom<VersionedResponse> for InnerAvailableBandwidthResponse {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: VersionedResponse) -> Result<Self, Self::Error> {
|
||||
match value.query_type {
|
||||
QueryType::AvailableBandwidth => {
|
||||
Ok(make_bincode_serializer().deserialize(&value.inner)?)
|
||||
}
|
||||
QueryType::TopupBandwidth => Err(Error::InvalidQueryType {
|
||||
source_query_type: value.query_type.to_string(),
|
||||
target_query_type: QueryType::AvailableBandwidth.to_string(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<InnerAvailableBandwidthResponse> for VersionedResponse {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: InnerAvailableBandwidthResponse) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
query_type: QueryType::AvailableBandwidth,
|
||||
inner: make_bincode_serializer().serialize(&value)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Response> for InnerAvailableBandwidthResponse {
|
||||
type Error = crate::error::MetadataError;
|
||||
|
||||
fn try_from(value: Response) -> Result<Self, Self::Error> {
|
||||
VersionedResponse::try_from(value)?
|
||||
.try_into()
|
||||
.map_err(|err: Error| crate::error::MetadataError::Models {
|
||||
message: err.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<InnerAvailableBandwidthResponse> for Response {
|
||||
type Error = crate::error::MetadataError;
|
||||
|
||||
fn try_from(value: InnerAvailableBandwidthResponse) -> Result<Self, Self::Error> {
|
||||
VersionedResponse::try_from(value)?
|
||||
.try_into()
|
||||
.map_err(|err: Error| crate::error::MetadataError::Models {
|
||||
message: err.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn serde() {
|
||||
let resp = InnerAvailableBandwidthResponse {};
|
||||
let ser = VersionedResponse::try_from(resp).unwrap();
|
||||
assert_eq!(QueryType::AvailableBandwidth, ser.query_type);
|
||||
let de = InnerAvailableBandwidthResponse::try_from(ser).unwrap();
|
||||
assert_eq!(resp, de);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_content() {
|
||||
let future_resp = VersionedResponse {
|
||||
query_type: QueryType::AvailableBandwidth,
|
||||
inner: vec![],
|
||||
};
|
||||
assert!(InnerAvailableBandwidthResponse::try_from(future_resp).is_ok());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::{
|
||||
available_bandwidth::{
|
||||
request::InnerAvailableBandwidthRequest, response::InnerAvailableBandwidthResponse,
|
||||
},
|
||||
topup_bandwidth::{request::InnerTopUpRequest, response::InnerTopUpResponse},
|
||||
QueryType, VersionedRequest, VersionedResponse, VERSION,
|
||||
};
|
||||
use crate::models::{error::Error, Construct, Extract, Version};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum RequestData {
|
||||
AvailableBandwidth(()),
|
||||
TopUpBandwidth(()),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum ResponseData {
|
||||
AvailableBandwidth(()),
|
||||
TopUpBandwidth(()),
|
||||
}
|
||||
|
||||
impl Construct<RequestData> for VersionedRequest {
|
||||
fn construct(info: RequestData, _version: Version) -> Result<Self, Error> {
|
||||
match info {
|
||||
RequestData::AvailableBandwidth(_) => Ok(InnerAvailableBandwidthRequest {}.try_into()?),
|
||||
RequestData::TopUpBandwidth(_) => Ok(InnerTopUpRequest {}.try_into()?),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Extract<RequestData> for VersionedRequest {
|
||||
fn extract(&self) -> Result<(RequestData, Version), Error> {
|
||||
match self.query_type {
|
||||
QueryType::AvailableBandwidth => {
|
||||
let _req = InnerAvailableBandwidthRequest::try_from(self.clone())?;
|
||||
Ok((RequestData::AvailableBandwidth(()), VERSION))
|
||||
}
|
||||
QueryType::TopupBandwidth => {
|
||||
let _req = InnerTopUpRequest::try_from(self.clone())?;
|
||||
Ok((RequestData::TopUpBandwidth(()), VERSION))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Construct<ResponseData> for VersionedResponse {
|
||||
fn construct(info: ResponseData, _version: Version) -> Result<Self, Error> {
|
||||
match info {
|
||||
ResponseData::AvailableBandwidth(()) => {
|
||||
Ok(InnerAvailableBandwidthResponse {}.try_into()?)
|
||||
}
|
||||
ResponseData::TopUpBandwidth(()) => Ok(InnerTopUpResponse {}.try_into()?),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Extract<ResponseData> for VersionedResponse {
|
||||
fn extract(&self) -> Result<(ResponseData, Version), Error> {
|
||||
match self.query_type {
|
||||
QueryType::AvailableBandwidth => {
|
||||
let _resp = InnerAvailableBandwidthResponse::try_from(self.clone())?;
|
||||
Ok((ResponseData::AvailableBandwidth(()), VERSION))
|
||||
}
|
||||
QueryType::TopupBandwidth => {
|
||||
let _resp = InnerTopUpResponse::try_from(self.clone())?;
|
||||
Ok((ResponseData::TopUpBandwidth(()), VERSION))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::fmt::Display;
|
||||
|
||||
use bincode::Options;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use super::error::Error;
|
||||
use crate::{
|
||||
make_bincode_serializer,
|
||||
models::{Request, Response, Version},
|
||||
};
|
||||
|
||||
pub(crate) mod available_bandwidth;
|
||||
pub mod interface;
|
||||
pub(crate) mod topup_bandwidth;
|
||||
|
||||
pub const VERSION: Version = Version::V0;
|
||||
|
||||
pub use available_bandwidth::{
|
||||
request::InnerAvailableBandwidthRequest as AvailableBandwidthRequest,
|
||||
response::InnerAvailableBandwidthResponse as AvailableBandwidthResponse,
|
||||
};
|
||||
pub use topup_bandwidth::{
|
||||
request::InnerTopUpRequest as TopUpRequest, response::InnerTopUpResponse as TopUpResponse,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
pub enum QueryType {
|
||||
AvailableBandwidth,
|
||||
TopupBandwidth,
|
||||
}
|
||||
|
||||
impl Display for QueryType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{self:?}")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
pub struct VersionedRequest {
|
||||
query_type: QueryType,
|
||||
inner: Vec<u8>,
|
||||
}
|
||||
|
||||
impl TryFrom<VersionedRequest> for Request {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: VersionedRequest) -> Result<Self, Self::Error> {
|
||||
Ok(Request {
|
||||
version: VERSION,
|
||||
inner: make_bincode_serializer().serialize(&value)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Request> for VersionedRequest {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: Request) -> Result<Self, Self::Error> {
|
||||
if value.version != VERSION {
|
||||
return Err(Error::InvalidVersion {
|
||||
source_version: value.version,
|
||||
target_version: VERSION,
|
||||
});
|
||||
}
|
||||
Ok(make_bincode_serializer().deserialize(&value.inner)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
pub struct VersionedResponse {
|
||||
query_type: QueryType,
|
||||
inner: Vec<u8>,
|
||||
}
|
||||
|
||||
impl TryFrom<VersionedResponse> for Response {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: VersionedResponse) -> Result<Self, Self::Error> {
|
||||
Ok(Response {
|
||||
version: VERSION,
|
||||
inner: make_bincode_serializer().serialize(&value)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Response> for VersionedResponse {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: Response) -> Result<Self, Self::Error> {
|
||||
if value.version != VERSION {
|
||||
return Err(Error::InvalidVersion {
|
||||
source_version: value.version,
|
||||
target_version: VERSION,
|
||||
});
|
||||
}
|
||||
Ok(make_bincode_serializer().deserialize(&value.inner)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use self::{
|
||||
available_bandwidth::{
|
||||
request::InnerAvailableBandwidthRequest, response::InnerAvailableBandwidthResponse,
|
||||
},
|
||||
topup_bandwidth::{request::InnerTopUpRequest, response::InnerTopUpResponse},
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn serde_request_av_bw() {
|
||||
let req = VersionedRequest {
|
||||
query_type: QueryType::AvailableBandwidth,
|
||||
inner: make_bincode_serializer()
|
||||
.serialize(&InnerAvailableBandwidthRequest {})
|
||||
.unwrap(),
|
||||
};
|
||||
|
||||
let ser = Request::try_from(req.clone()).unwrap();
|
||||
assert_eq!(VERSION, ser.version);
|
||||
let de = VersionedRequest::try_from(ser).unwrap();
|
||||
assert_eq!(req, de);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serde_response_av_bw() {
|
||||
let resp = VersionedResponse {
|
||||
query_type: QueryType::AvailableBandwidth,
|
||||
inner: make_bincode_serializer()
|
||||
.serialize(&InnerAvailableBandwidthResponse {})
|
||||
.unwrap(),
|
||||
};
|
||||
|
||||
let ser = Response::try_from(resp.clone()).unwrap();
|
||||
assert_eq!(VERSION, ser.version);
|
||||
let de = VersionedResponse::try_from(ser).unwrap();
|
||||
assert_eq!(resp, de);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serde_request_topup() {
|
||||
let req = VersionedRequest {
|
||||
query_type: QueryType::TopupBandwidth,
|
||||
inner: make_bincode_serializer()
|
||||
.serialize(&InnerTopUpRequest {})
|
||||
.unwrap(),
|
||||
};
|
||||
|
||||
let ser = Request::try_from(req.clone()).unwrap();
|
||||
assert_eq!(VERSION, ser.version);
|
||||
let de = VersionedRequest::try_from(ser).unwrap();
|
||||
assert_eq!(req, de);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serde_response_topup() {
|
||||
let resp = VersionedResponse {
|
||||
query_type: QueryType::TopupBandwidth,
|
||||
inner: make_bincode_serializer()
|
||||
.serialize(&InnerTopUpResponse {})
|
||||
.unwrap(),
|
||||
};
|
||||
|
||||
let ser = Response::try_from(resp.clone()).unwrap();
|
||||
assert_eq!(VERSION, ser.version);
|
||||
let de = VersionedResponse::try_from(ser).unwrap();
|
||||
assert_eq!(resp, de);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod request;
|
||||
pub mod response;
|
||||
@@ -0,0 +1,84 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use bincode::Options;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{make_bincode_serializer, models::Request};
|
||||
|
||||
use super::super::{Error, QueryType, VersionedRequest};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct InnerTopUpRequest {}
|
||||
|
||||
impl TryFrom<VersionedRequest> for InnerTopUpRequest {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: VersionedRequest) -> Result<Self, Self::Error> {
|
||||
match value.query_type {
|
||||
QueryType::TopupBandwidth => Ok(make_bincode_serializer().deserialize(&value.inner)?),
|
||||
QueryType::AvailableBandwidth => Err(Error::InvalidQueryType {
|
||||
source_query_type: value.query_type.to_string(),
|
||||
target_query_type: QueryType::TopupBandwidth.to_string(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<InnerTopUpRequest> for VersionedRequest {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: InnerTopUpRequest) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
query_type: QueryType::TopupBandwidth,
|
||||
inner: make_bincode_serializer().serialize(&value)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Request> for InnerTopUpRequest {
|
||||
type Error = crate::error::MetadataError;
|
||||
|
||||
fn try_from(value: Request) -> Result<Self, Self::Error> {
|
||||
VersionedRequest::try_from(value)?
|
||||
.try_into()
|
||||
.map_err(|err: Error| crate::error::MetadataError::Models {
|
||||
message: err.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<InnerTopUpRequest> for Request {
|
||||
type Error = crate::error::MetadataError;
|
||||
|
||||
fn try_from(value: InnerTopUpRequest) -> Result<Self, Self::Error> {
|
||||
VersionedRequest::try_from(value)?
|
||||
.try_into()
|
||||
.map_err(|err: Error| crate::error::MetadataError::Models {
|
||||
message: err.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn serde() {
|
||||
let req = InnerTopUpRequest {};
|
||||
let ser = VersionedRequest::try_from(req.clone()).unwrap();
|
||||
assert_eq!(QueryType::TopupBandwidth, ser.query_type);
|
||||
let de = InnerTopUpRequest::try_from(ser).unwrap();
|
||||
assert_eq!(req, de);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_content() {
|
||||
let future_req = VersionedRequest {
|
||||
query_type: QueryType::TopupBandwidth,
|
||||
inner: vec![],
|
||||
};
|
||||
assert!(InnerTopUpRequest::try_from(future_req).is_ok());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use bincode::Options;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{make_bincode_serializer, models::Response};
|
||||
|
||||
use super::super::{Error, QueryType, VersionedResponse};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct InnerTopUpResponse {}
|
||||
|
||||
impl TryFrom<VersionedResponse> for InnerTopUpResponse {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: VersionedResponse) -> Result<Self, Self::Error> {
|
||||
match value.query_type {
|
||||
QueryType::TopupBandwidth => Ok(make_bincode_serializer().deserialize(&value.inner)?),
|
||||
QueryType::AvailableBandwidth => Err(Error::InvalidQueryType {
|
||||
source_query_type: value.query_type.to_string(),
|
||||
target_query_type: QueryType::TopupBandwidth.to_string(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<InnerTopUpResponse> for VersionedResponse {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: InnerTopUpResponse) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
query_type: QueryType::TopupBandwidth,
|
||||
inner: make_bincode_serializer().serialize(&value)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Response> for InnerTopUpResponse {
|
||||
type Error = crate::error::MetadataError;
|
||||
|
||||
fn try_from(value: Response) -> Result<Self, Self::Error> {
|
||||
VersionedResponse::try_from(value)?
|
||||
.try_into()
|
||||
.map_err(|err: Error| crate::error::MetadataError::Models {
|
||||
message: err.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<InnerTopUpResponse> for Response {
|
||||
type Error = crate::error::MetadataError;
|
||||
|
||||
fn try_from(value: InnerTopUpResponse) -> Result<Self, Self::Error> {
|
||||
VersionedResponse::try_from(value)?
|
||||
.try_into()
|
||||
.map_err(|err: Error| crate::error::MetadataError::Models {
|
||||
message: err.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn serde() {
|
||||
let resp = InnerTopUpResponse {};
|
||||
let ser = VersionedResponse::try_from(resp.clone()).unwrap();
|
||||
assert_eq!(QueryType::TopupBandwidth, ser.query_type);
|
||||
let de = InnerTopUpResponse::try_from(ser).unwrap();
|
||||
assert_eq!(resp, de);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_content() {
|
||||
let future_resp = VersionedResponse {
|
||||
query_type: QueryType::TopupBandwidth,
|
||||
inner: vec![],
|
||||
};
|
||||
assert!(InnerTopUpResponse::try_from(future_resp).is_ok());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod request;
|
||||
pub mod response;
|
||||
@@ -0,0 +1,86 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use bincode::Options;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{make_bincode_serializer, models::Request};
|
||||
|
||||
use super::super::{Error, QueryType, VersionedRequest};
|
||||
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct InnerAvailableBandwidthRequest {}
|
||||
|
||||
impl TryFrom<VersionedRequest> for InnerAvailableBandwidthRequest {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: VersionedRequest) -> Result<Self, Self::Error> {
|
||||
match value.query_type {
|
||||
QueryType::AvailableBandwidth => {
|
||||
Ok(make_bincode_serializer().deserialize(&value.inner)?)
|
||||
}
|
||||
QueryType::TopupBandwidth => Err(Error::InvalidQueryType {
|
||||
source_query_type: value.query_type.to_string(),
|
||||
target_query_type: QueryType::AvailableBandwidth.to_string(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<InnerAvailableBandwidthRequest> for VersionedRequest {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: InnerAvailableBandwidthRequest) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
query_type: QueryType::AvailableBandwidth,
|
||||
inner: make_bincode_serializer().serialize(&value)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Request> for InnerAvailableBandwidthRequest {
|
||||
type Error = crate::error::MetadataError;
|
||||
|
||||
fn try_from(value: Request) -> Result<Self, Self::Error> {
|
||||
VersionedRequest::try_from(value)?
|
||||
.try_into()
|
||||
.map_err(|err: Error| crate::error::MetadataError::Models {
|
||||
message: err.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<InnerAvailableBandwidthRequest> for Request {
|
||||
type Error = crate::error::MetadataError;
|
||||
|
||||
fn try_from(value: InnerAvailableBandwidthRequest) -> Result<Self, Self::Error> {
|
||||
VersionedRequest::try_from(value)?
|
||||
.try_into()
|
||||
.map_err(|err: Error| crate::error::MetadataError::Models {
|
||||
message: err.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn serde() {
|
||||
let req = InnerAvailableBandwidthRequest {};
|
||||
let ser = VersionedRequest::try_from(req).unwrap();
|
||||
assert_eq!(QueryType::AvailableBandwidth, ser.query_type);
|
||||
let de = InnerAvailableBandwidthRequest::try_from(ser).unwrap();
|
||||
assert_eq!(req, de);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_content() {
|
||||
let future_req = VersionedRequest {
|
||||
query_type: QueryType::AvailableBandwidth,
|
||||
inner: vec![],
|
||||
};
|
||||
assert!(InnerAvailableBandwidthRequest::try_from(future_req).is_ok());
|
||||
}
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use bincode::Options;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{make_bincode_serializer, models::Response};
|
||||
|
||||
use super::super::{Error, QueryType, VersionedResponse};
|
||||
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct InnerAvailableBandwidthResponse {
|
||||
pub available_bandwidth: i64,
|
||||
}
|
||||
|
||||
impl TryFrom<VersionedResponse> for InnerAvailableBandwidthResponse {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: VersionedResponse) -> Result<Self, Self::Error> {
|
||||
match value.query_type {
|
||||
QueryType::AvailableBandwidth => {
|
||||
Ok(make_bincode_serializer().deserialize(&value.inner)?)
|
||||
}
|
||||
QueryType::TopupBandwidth => Err(Error::InvalidQueryType {
|
||||
source_query_type: value.query_type.to_string(),
|
||||
target_query_type: QueryType::AvailableBandwidth.to_string(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<InnerAvailableBandwidthResponse> for VersionedResponse {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: InnerAvailableBandwidthResponse) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
query_type: QueryType::AvailableBandwidth,
|
||||
inner: make_bincode_serializer().serialize(&value)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Response> for InnerAvailableBandwidthResponse {
|
||||
type Error = crate::error::MetadataError;
|
||||
|
||||
fn try_from(value: Response) -> Result<Self, Self::Error> {
|
||||
VersionedResponse::try_from(value)?
|
||||
.try_into()
|
||||
.map_err(|err: Error| crate::error::MetadataError::Models {
|
||||
message: err.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<InnerAvailableBandwidthResponse> for Response {
|
||||
type Error = crate::error::MetadataError;
|
||||
|
||||
fn try_from(value: InnerAvailableBandwidthResponse) -> Result<Self, Self::Error> {
|
||||
VersionedResponse::try_from(value)?
|
||||
.try_into()
|
||||
.map_err(|err: Error| crate::error::MetadataError::Models {
|
||||
message: err.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn serde() {
|
||||
let resp = InnerAvailableBandwidthResponse {
|
||||
available_bandwidth: 42,
|
||||
};
|
||||
let ser = VersionedResponse::try_from(resp).unwrap();
|
||||
assert_eq!(QueryType::AvailableBandwidth, ser.query_type);
|
||||
let de = InnerAvailableBandwidthResponse::try_from(ser).unwrap();
|
||||
assert_eq!(resp, de);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_content() {
|
||||
let future_resp = VersionedResponse {
|
||||
query_type: QueryType::AvailableBandwidth,
|
||||
inner: vec![],
|
||||
};
|
||||
assert!(InnerAvailableBandwidthResponse::try_from(future_resp).is_err());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
|
||||
#[cfg(feature = "testing")]
|
||||
use super::super::v0 as previous;
|
||||
|
||||
use super::{
|
||||
available_bandwidth::{
|
||||
request::InnerAvailableBandwidthRequest, response::InnerAvailableBandwidthResponse,
|
||||
},
|
||||
topup_bandwidth::{request::InnerTopUpRequest, response::InnerTopUpResponse},
|
||||
QueryType, VersionedRequest, VersionedResponse, VERSION,
|
||||
};
|
||||
use crate::models::{error::Error, Construct, Extract, Version};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum RequestData {
|
||||
AvailableBandwidth(()),
|
||||
TopUpBandwidth(Box<CredentialSpendingData>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum ResponseData {
|
||||
AvailableBandwidth(i64),
|
||||
TopUpBandwidth(i64),
|
||||
}
|
||||
|
||||
impl Construct<RequestData> for VersionedRequest {
|
||||
fn construct(info: RequestData, _version: Version) -> Result<Self, Error> {
|
||||
match info {
|
||||
RequestData::AvailableBandwidth(_) => Ok(InnerAvailableBandwidthRequest {}.try_into()?),
|
||||
RequestData::TopUpBandwidth(credential) => Ok(InnerTopUpRequest {
|
||||
credential: *credential,
|
||||
}
|
||||
.try_into()?),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Extract<RequestData> for VersionedRequest {
|
||||
fn extract(&self) -> Result<(RequestData, Version), Error> {
|
||||
match self.query_type {
|
||||
QueryType::AvailableBandwidth => {
|
||||
let _req = InnerAvailableBandwidthRequest::try_from(self.clone())?;
|
||||
Ok((RequestData::AvailableBandwidth(()), VERSION))
|
||||
}
|
||||
QueryType::TopupBandwidth => {
|
||||
let req = InnerTopUpRequest::try_from(self.clone())?;
|
||||
Ok((
|
||||
RequestData::TopUpBandwidth(Box::new(req.credential)),
|
||||
VERSION,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Construct<ResponseData> for VersionedResponse {
|
||||
fn construct(info: ResponseData, _version: Version) -> Result<Self, Error> {
|
||||
match info {
|
||||
ResponseData::AvailableBandwidth(available_bandwidth) => {
|
||||
Ok(InnerAvailableBandwidthResponse {
|
||||
available_bandwidth,
|
||||
}
|
||||
.try_into()?)
|
||||
}
|
||||
ResponseData::TopUpBandwidth(available_bandwidth) => Ok(InnerTopUpResponse {
|
||||
available_bandwidth,
|
||||
}
|
||||
.try_into()?),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Extract<ResponseData> for VersionedResponse {
|
||||
fn extract(&self) -> Result<(ResponseData, Version), Error> {
|
||||
match self.query_type {
|
||||
QueryType::AvailableBandwidth => {
|
||||
let resp = InnerAvailableBandwidthResponse::try_from(self.clone())?;
|
||||
Ok((
|
||||
ResponseData::AvailableBandwidth(resp.available_bandwidth),
|
||||
VERSION,
|
||||
))
|
||||
}
|
||||
QueryType::TopupBandwidth => {
|
||||
let resp = InnerTopUpResponse::try_from(self.clone())?;
|
||||
Ok((
|
||||
ResponseData::TopUpBandwidth(resp.available_bandwidth),
|
||||
VERSION,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this should be with #[cfg(feature = "testing")] only coming from v0, don't copy this for future versions
|
||||
#[cfg(feature = "testing")]
|
||||
impl TryFrom<previous::interface::RequestData> for RequestData {
|
||||
type Error = super::Error;
|
||||
|
||||
fn try_from(value: previous::interface::RequestData) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
previous::interface::RequestData::AvailableBandwidth(inner) => {
|
||||
Ok(Self::AvailableBandwidth(inner))
|
||||
}
|
||||
previous::interface::RequestData::TopUpBandwidth(_) => {
|
||||
Err(super::Error::UpdateNotPossible {
|
||||
from: previous::VERSION,
|
||||
to: VERSION,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this should be with #[cfg(feature = "testing")] only coming from v0, don't copy this for future versions
|
||||
#[cfg(feature = "testing")]
|
||||
impl TryFrom<RequestData> for previous::interface::RequestData {
|
||||
type Error = super::Error;
|
||||
|
||||
fn try_from(value: RequestData) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
RequestData::AvailableBandwidth(inner) => Ok(Self::AvailableBandwidth(inner)),
|
||||
RequestData::TopUpBandwidth(_) => Ok(Self::TopUpBandwidth(())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this should be with #[cfg(feature = "testing")] only coming from v0, don't copy this for future versions
|
||||
#[cfg(feature = "testing")]
|
||||
impl TryFrom<previous::interface::ResponseData> for ResponseData {
|
||||
type Error = super::Error;
|
||||
|
||||
fn try_from(value: previous::interface::ResponseData) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
previous::interface::ResponseData::AvailableBandwidth(_) => {
|
||||
Err(super::Error::UpdateNotPossible {
|
||||
from: previous::VERSION,
|
||||
to: VERSION,
|
||||
})
|
||||
}
|
||||
previous::interface::ResponseData::TopUpBandwidth(_) => {
|
||||
Err(super::Error::UpdateNotPossible {
|
||||
from: previous::VERSION,
|
||||
to: VERSION,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this should be with #[cfg(feature = "testing")] only coming from v0, don't copy this for future versions
|
||||
#[cfg(feature = "testing")]
|
||||
impl TryFrom<ResponseData> for previous::interface::ResponseData {
|
||||
type Error = super::Error;
|
||||
|
||||
fn try_from(value: ResponseData) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
ResponseData::AvailableBandwidth(_) => Ok(Self::AvailableBandwidth(())),
|
||||
ResponseData::TopUpBandwidth(_) => Ok(Self::TopUpBandwidth(())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::models::tests::CREDENTIAL_BYTES;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn request_upgrade() {
|
||||
assert_eq!(
|
||||
RequestData::try_from(previous::interface::RequestData::AvailableBandwidth(()))
|
||||
.unwrap(),
|
||||
RequestData::AvailableBandwidth(())
|
||||
);
|
||||
assert!(
|
||||
RequestData::try_from(previous::interface::RequestData::TopUpBandwidth(())).is_err(),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn response_upgrade() {
|
||||
assert!(
|
||||
ResponseData::try_from(previous::interface::ResponseData::AvailableBandwidth(()))
|
||||
.is_err()
|
||||
);
|
||||
assert!(
|
||||
ResponseData::try_from(previous::interface::ResponseData::TopUpBandwidth(())).is_err()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn request_downgrade() {
|
||||
assert_eq!(
|
||||
previous::interface::RequestData::try_from(RequestData::AvailableBandwidth(()))
|
||||
.unwrap(),
|
||||
previous::interface::RequestData::AvailableBandwidth(())
|
||||
);
|
||||
assert_eq!(
|
||||
previous::interface::RequestData::try_from(RequestData::TopUpBandwidth(Box::new(
|
||||
CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap()
|
||||
)))
|
||||
.unwrap(),
|
||||
previous::interface::RequestData::TopUpBandwidth(())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn response_downgrade() {
|
||||
assert_eq!(
|
||||
previous::interface::ResponseData::try_from(ResponseData::AvailableBandwidth(42))
|
||||
.unwrap(),
|
||||
previous::interface::ResponseData::AvailableBandwidth(())
|
||||
);
|
||||
assert_eq!(
|
||||
previous::interface::ResponseData::try_from(ResponseData::TopUpBandwidth(42)).unwrap(),
|
||||
previous::interface::ResponseData::TopUpBandwidth(())
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::fmt::Display;
|
||||
|
||||
use bincode::Options;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use super::error::Error;
|
||||
use crate::{
|
||||
make_bincode_serializer,
|
||||
models::{Request, Response, Version},
|
||||
};
|
||||
|
||||
pub use available_bandwidth::{
|
||||
request::InnerAvailableBandwidthRequest as AvailableBandwidthRequest,
|
||||
response::InnerAvailableBandwidthResponse as AvailableBandwidthResponse,
|
||||
};
|
||||
pub use topup_bandwidth::{
|
||||
request::InnerTopUpRequest as TopUpRequest, response::InnerTopUpResponse as TopUpResponse,
|
||||
};
|
||||
|
||||
pub(crate) mod available_bandwidth;
|
||||
pub mod interface;
|
||||
pub(crate) mod topup_bandwidth;
|
||||
|
||||
pub const VERSION: Version = Version::V1;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
pub enum QueryType {
|
||||
AvailableBandwidth,
|
||||
TopupBandwidth,
|
||||
}
|
||||
|
||||
impl Display for QueryType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{self:?}")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
pub struct VersionedRequest {
|
||||
query_type: QueryType,
|
||||
inner: Vec<u8>,
|
||||
}
|
||||
|
||||
impl TryFrom<VersionedRequest> for Request {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: VersionedRequest) -> Result<Self, Self::Error> {
|
||||
Ok(Request {
|
||||
version: VERSION,
|
||||
inner: make_bincode_serializer().serialize(&value)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Request> for VersionedRequest {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: Request) -> Result<Self, Self::Error> {
|
||||
if value.version != VERSION {
|
||||
return Err(Error::InvalidVersion {
|
||||
source_version: value.version,
|
||||
target_version: VERSION,
|
||||
});
|
||||
}
|
||||
Ok(make_bincode_serializer().deserialize(&value.inner)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
pub struct VersionedResponse {
|
||||
query_type: QueryType,
|
||||
inner: Vec<u8>,
|
||||
}
|
||||
|
||||
impl TryFrom<VersionedResponse> for Response {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: VersionedResponse) -> Result<Self, Self::Error> {
|
||||
Ok(Response {
|
||||
version: VERSION,
|
||||
inner: make_bincode_serializer().serialize(&value)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Response> for VersionedResponse {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: Response) -> Result<Self, Self::Error> {
|
||||
if value.version != VERSION {
|
||||
return Err(Error::InvalidVersion {
|
||||
source_version: value.version,
|
||||
target_version: VERSION,
|
||||
});
|
||||
}
|
||||
Ok(make_bincode_serializer().deserialize(&value.inner)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
|
||||
use crate::models::tests::CREDENTIAL_BYTES;
|
||||
|
||||
use self::{
|
||||
available_bandwidth::{
|
||||
request::InnerAvailableBandwidthRequest, response::InnerAvailableBandwidthResponse,
|
||||
},
|
||||
topup_bandwidth::{request::InnerTopUpRequest, response::InnerTopUpResponse},
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn mismatched_request_version() {
|
||||
let version = Version::V0;
|
||||
let future_bw = Request {
|
||||
version,
|
||||
inner: vec![],
|
||||
};
|
||||
if let Err(Error::InvalidVersion {
|
||||
source_version,
|
||||
target_version,
|
||||
}) = VersionedRequest::try_from(future_bw)
|
||||
{
|
||||
assert_eq!(source_version, version);
|
||||
assert_eq!(target_version, VERSION);
|
||||
} else {
|
||||
panic!("failed");
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mismatched_response_version() {
|
||||
let version = Version::V0;
|
||||
let future_bw = Response {
|
||||
version,
|
||||
inner: vec![],
|
||||
};
|
||||
if let Err(Error::InvalidVersion {
|
||||
source_version,
|
||||
target_version,
|
||||
}) = VersionedResponse::try_from(future_bw)
|
||||
{
|
||||
assert_eq!(source_version, version);
|
||||
assert_eq!(target_version, VERSION);
|
||||
} else {
|
||||
panic!("failed");
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serde_request_av_bw() {
|
||||
let req = VersionedRequest {
|
||||
query_type: QueryType::AvailableBandwidth,
|
||||
inner: make_bincode_serializer()
|
||||
.serialize(&InnerAvailableBandwidthResponse {
|
||||
available_bandwidth: 42,
|
||||
})
|
||||
.unwrap(),
|
||||
};
|
||||
|
||||
let ser = Request::try_from(req.clone()).unwrap();
|
||||
assert_eq!(VERSION, ser.version);
|
||||
let de = VersionedRequest::try_from(ser).unwrap();
|
||||
assert_eq!(req, de);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serde_response_av_bw() {
|
||||
let resp = VersionedResponse {
|
||||
query_type: QueryType::AvailableBandwidth,
|
||||
inner: make_bincode_serializer()
|
||||
.serialize(&InnerAvailableBandwidthRequest {})
|
||||
.unwrap(),
|
||||
};
|
||||
|
||||
let ser = Response::try_from(resp.clone()).unwrap();
|
||||
assert_eq!(VERSION, ser.version);
|
||||
let de = VersionedResponse::try_from(ser).unwrap();
|
||||
assert_eq!(resp, de);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serde_request_topup() {
|
||||
let req = VersionedRequest {
|
||||
query_type: QueryType::TopupBandwidth,
|
||||
inner: make_bincode_serializer()
|
||||
.serialize(&InnerTopUpRequest {
|
||||
credential: CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap(),
|
||||
})
|
||||
.unwrap(),
|
||||
};
|
||||
|
||||
let ser = Request::try_from(req.clone()).unwrap();
|
||||
assert_eq!(VERSION, ser.version);
|
||||
let de = VersionedRequest::try_from(ser).unwrap();
|
||||
assert_eq!(req, de);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serde_response_topup() {
|
||||
let resp = VersionedResponse {
|
||||
query_type: QueryType::TopupBandwidth,
|
||||
inner: make_bincode_serializer()
|
||||
.serialize(&InnerTopUpResponse {
|
||||
available_bandwidth: 42,
|
||||
})
|
||||
.unwrap(),
|
||||
};
|
||||
|
||||
let ser = Response::try_from(resp.clone()).unwrap();
|
||||
assert_eq!(VERSION, ser.version);
|
||||
let de = VersionedResponse::try_from(ser).unwrap();
|
||||
assert_eq!(resp, de);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod request;
|
||||
pub mod response;
|
||||
@@ -0,0 +1,92 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use bincode::Options;
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{make_bincode_serializer, models::Request};
|
||||
|
||||
use super::super::{Error, QueryType, VersionedRequest};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct InnerTopUpRequest {
|
||||
/// Ecash credential
|
||||
pub credential: CredentialSpendingData,
|
||||
}
|
||||
|
||||
impl TryFrom<VersionedRequest> for InnerTopUpRequest {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: VersionedRequest) -> Result<Self, Self::Error> {
|
||||
match value.query_type {
|
||||
QueryType::TopupBandwidth => Ok(make_bincode_serializer().deserialize(&value.inner)?),
|
||||
QueryType::AvailableBandwidth => Err(Error::InvalidQueryType {
|
||||
source_query_type: value.query_type.to_string(),
|
||||
target_query_type: QueryType::TopupBandwidth.to_string(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<InnerTopUpRequest> for VersionedRequest {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: InnerTopUpRequest) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
query_type: QueryType::TopupBandwidth,
|
||||
inner: make_bincode_serializer().serialize(&value)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Request> for InnerTopUpRequest {
|
||||
type Error = crate::error::MetadataError;
|
||||
|
||||
fn try_from(value: Request) -> Result<Self, Self::Error> {
|
||||
VersionedRequest::try_from(value)?
|
||||
.try_into()
|
||||
.map_err(|err: Error| crate::error::MetadataError::Models {
|
||||
message: err.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<InnerTopUpRequest> for Request {
|
||||
type Error = crate::error::MetadataError;
|
||||
|
||||
fn try_from(value: InnerTopUpRequest) -> Result<Self, Self::Error> {
|
||||
VersionedRequest::try_from(value)?
|
||||
.try_into()
|
||||
.map_err(|err: Error| crate::error::MetadataError::Models {
|
||||
message: err.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::models::tests::CREDENTIAL_BYTES;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn serde() {
|
||||
let req = InnerTopUpRequest {
|
||||
credential: CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap(),
|
||||
};
|
||||
let ser = VersionedRequest::try_from(req.clone()).unwrap();
|
||||
assert_eq!(QueryType::TopupBandwidth, ser.query_type);
|
||||
let de = InnerTopUpRequest::try_from(ser).unwrap();
|
||||
assert_eq!(req, de);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_content() {
|
||||
let future_req = VersionedRequest {
|
||||
query_type: QueryType::TopupBandwidth,
|
||||
inner: vec![],
|
||||
};
|
||||
assert!(InnerTopUpRequest::try_from(future_req).is_err());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use bincode::Options;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{make_bincode_serializer, models::Response};
|
||||
|
||||
use super::super::{Error, QueryType, VersionedResponse};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct InnerTopUpResponse {
|
||||
pub available_bandwidth: i64,
|
||||
}
|
||||
|
||||
impl TryFrom<VersionedResponse> for InnerTopUpResponse {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: VersionedResponse) -> Result<Self, Self::Error> {
|
||||
match value.query_type {
|
||||
QueryType::TopupBandwidth => Ok(make_bincode_serializer().deserialize(&value.inner)?),
|
||||
QueryType::AvailableBandwidth => Err(Error::InvalidQueryType {
|
||||
source_query_type: value.query_type.to_string(),
|
||||
target_query_type: QueryType::TopupBandwidth.to_string(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<InnerTopUpResponse> for VersionedResponse {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: InnerTopUpResponse) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
query_type: QueryType::TopupBandwidth,
|
||||
inner: make_bincode_serializer().serialize(&value)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Response> for InnerTopUpResponse {
|
||||
type Error = crate::error::MetadataError;
|
||||
|
||||
fn try_from(value: Response) -> Result<Self, Self::Error> {
|
||||
VersionedResponse::try_from(value)?
|
||||
.try_into()
|
||||
.map_err(|err: Error| crate::error::MetadataError::Models {
|
||||
message: err.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<InnerTopUpResponse> for Response {
|
||||
type Error = crate::error::MetadataError;
|
||||
|
||||
fn try_from(value: InnerTopUpResponse) -> Result<Self, Self::Error> {
|
||||
VersionedResponse::try_from(value)?
|
||||
.try_into()
|
||||
.map_err(|err: Error| crate::error::MetadataError::Models {
|
||||
message: err.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn serde() {
|
||||
let resp = InnerTopUpResponse {
|
||||
available_bandwidth: 42,
|
||||
};
|
||||
let ser = VersionedResponse::try_from(resp.clone()).unwrap();
|
||||
assert_eq!(QueryType::TopupBandwidth, ser.query_type);
|
||||
let de = InnerTopUpResponse::try_from(ser).unwrap();
|
||||
assert_eq!(resp, de);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_content() {
|
||||
let future_resp = VersionedResponse {
|
||||
query_type: QueryType::TopupBandwidth,
|
||||
inner: vec![],
|
||||
};
|
||||
assert!(InnerTopUpResponse::try_from(future_resp).is_err());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub const V1_API_VERSION: &str = "v1";
|
||||
|
||||
pub const BANDWIDTH: &str = "bandwidth";
|
||||
|
||||
pub const VERSION: &str = "version";
|
||||
pub const AVAILABLE: &str = "available";
|
||||
pub const TOPUP: &str = "topup";
|
||||
@@ -1,30 +0,0 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
mod error;
|
||||
#[cfg(target_os = "linux")]
|
||||
mod http;
|
||||
#[cfg(target_os = "linux")]
|
||||
mod models;
|
||||
#[cfg(target_os = "linux")]
|
||||
mod network;
|
||||
#[cfg(target_os = "linux")]
|
||||
mod transceiver;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub use http::{
|
||||
router::{ApiHttpServer, RouterBuilder, RouterWithState},
|
||||
state::AppState,
|
||||
ShutdownHandles,
|
||||
};
|
||||
#[cfg(target_os = "linux")]
|
||||
pub use transceiver::PeerControllerTransceiver;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn make_bincode_serializer() -> impl bincode::Options {
|
||||
use bincode::Options;
|
||||
bincode::DefaultOptions::new()
|
||||
.with_big_endian()
|
||||
.with_varint_encoding()
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::models::Version;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
Bincode(#[from] bincode::Error),
|
||||
|
||||
#[error("trying to deserialize from version {source_version} into {target_version}")]
|
||||
InvalidVersion {
|
||||
source_version: Version,
|
||||
target_version: Version,
|
||||
},
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::fmt::Display;
|
||||
|
||||
use axum::http::StatusCode;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
pub(crate) mod error;
|
||||
pub(crate) mod version_1;
|
||||
|
||||
pub(crate) use version_1 as latest;
|
||||
|
||||
pub type Version = usize;
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, ToSchema)]
|
||||
pub struct AvailableBandwidthResponse {
|
||||
pub(crate) version: Version,
|
||||
pub(crate) inner: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
pub struct TopUpRequest {
|
||||
pub(crate) version: Version,
|
||||
pub(crate) inner: Vec<u8>,
|
||||
}
|
||||
|
||||
pub(crate) type AxumResult<T> = Result<T, AxumErrorResponse>;
|
||||
|
||||
pub(crate) struct AxumErrorResponse {
|
||||
message: String,
|
||||
status: StatusCode,
|
||||
}
|
||||
|
||||
impl AxumErrorResponse {
|
||||
pub(crate) fn bad_request(msg: impl Display) -> Self {
|
||||
Self {
|
||||
message: msg.to_string(),
|
||||
status: StatusCode::BAD_REQUEST,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl axum::response::IntoResponse for AxumErrorResponse {
|
||||
fn into_response(self) -> axum::response::Response {
|
||||
(self.status, self.message).into_response()
|
||||
}
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use bincode::Options;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::error::Error;
|
||||
use crate::{
|
||||
make_bincode_serializer,
|
||||
models::{AvailableBandwidthResponse, TopUpRequest},
|
||||
};
|
||||
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
|
||||
pub const VERSION: usize = 1;
|
||||
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct InnerAvailableBandwidthResponse {
|
||||
pub(crate) value: i64,
|
||||
}
|
||||
|
||||
impl InnerAvailableBandwidthResponse {
|
||||
pub(crate) fn new(value: i64) -> Self {
|
||||
Self { value }
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<InnerAvailableBandwidthResponse> for AvailableBandwidthResponse {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: InnerAvailableBandwidthResponse) -> Result<Self, Self::Error> {
|
||||
Ok(AvailableBandwidthResponse {
|
||||
version: VERSION,
|
||||
inner: make_bincode_serializer().serialize(&value)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<AvailableBandwidthResponse> for InnerAvailableBandwidthResponse {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: AvailableBandwidthResponse) -> Result<Self, Self::Error> {
|
||||
if value.version != VERSION {
|
||||
return Err(Error::InvalidVersion {
|
||||
source_version: value.version,
|
||||
target_version: VERSION,
|
||||
});
|
||||
}
|
||||
Ok(make_bincode_serializer().deserialize(&value.inner)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct InnerTopUpRequest {
|
||||
/// Ecash credential
|
||||
pub credential: CredentialSpendingData,
|
||||
}
|
||||
|
||||
impl TryFrom<InnerTopUpRequest> for TopUpRequest {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: InnerTopUpRequest) -> Result<Self, Self::Error> {
|
||||
Ok(TopUpRequest {
|
||||
version: VERSION,
|
||||
inner: make_bincode_serializer().serialize(&value)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<TopUpRequest> for InnerTopUpRequest {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: TopUpRequest) -> Result<Self, Self::Error> {
|
||||
if value.version != VERSION {
|
||||
return Err(Error::InvalidVersion {
|
||||
source_version: value.version,
|
||||
target_version: VERSION,
|
||||
});
|
||||
}
|
||||
Ok(make_bincode_serializer().deserialize(&value.inner)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn serde_available_bandwidth() {
|
||||
let bw = InnerAvailableBandwidthResponse::new(42);
|
||||
let ser = AvailableBandwidthResponse::try_from(bw).unwrap();
|
||||
assert_eq!(VERSION, ser.version);
|
||||
assert_eq!(ser.inner, vec![84]);
|
||||
let de = InnerAvailableBandwidthResponse::try_from(ser).unwrap();
|
||||
assert_eq!(bw, de);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mismatched_version_available_bandwidth() {
|
||||
let version = 4242;
|
||||
let future_bw = AvailableBandwidthResponse {
|
||||
version,
|
||||
inner: vec![],
|
||||
};
|
||||
if let Err(Error::InvalidVersion {
|
||||
source_version,
|
||||
target_version,
|
||||
}) = InnerAvailableBandwidthResponse::try_from(future_bw)
|
||||
{
|
||||
assert_eq!(source_version, version);
|
||||
assert_eq!(target_version, VERSION);
|
||||
} else {
|
||||
panic!("failed");
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_content_available_bandwidth() {
|
||||
let future_bw = AvailableBandwidthResponse {
|
||||
version: VERSION,
|
||||
inner: vec![],
|
||||
};
|
||||
assert!(InnerAvailableBandwidthResponse::try_from(future_bw).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serde_topup() {
|
||||
let bw = InnerAvailableBandwidthResponse::new(42);
|
||||
let ser = AvailableBandwidthResponse::try_from(bw).unwrap();
|
||||
assert_eq!(VERSION, ser.version);
|
||||
assert_eq!(ser.inner, vec![84]);
|
||||
let de = InnerAvailableBandwidthResponse::try_from(ser).unwrap();
|
||||
assert_eq!(bw, de);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mismatched_version_topup() {
|
||||
let version = 4242;
|
||||
let future_bw = AvailableBandwidthResponse {
|
||||
version,
|
||||
inner: vec![],
|
||||
};
|
||||
if let Err(Error::InvalidVersion {
|
||||
source_version,
|
||||
target_version,
|
||||
}) = InnerAvailableBandwidthResponse::try_from(future_bw)
|
||||
{
|
||||
assert_eq!(source_version, version);
|
||||
assert_eq!(target_version, VERSION);
|
||||
} else {
|
||||
panic!("failed");
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_content_topup() {
|
||||
let future_bw = AvailableBandwidthResponse {
|
||||
version: VERSION,
|
||||
inner: vec![],
|
||||
};
|
||||
assert!(InnerAvailableBandwidthResponse::try_from(future_bw).is_err());
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use axum::{
|
||||
extract::{ConnectInfo, Query, State},
|
||||
Json, Router,
|
||||
};
|
||||
use nym_http_api_common::{FormattedResponse, OutputParams};
|
||||
use tower_http::compression::CompressionLayer;
|
||||
|
||||
use crate::{
|
||||
http::state::AppState,
|
||||
models::{
|
||||
latest::InnerTopUpRequest, AvailableBandwidthResponse, AxumErrorResponse, AxumResult,
|
||||
TopUpRequest,
|
||||
},
|
||||
};
|
||||
|
||||
pub(crate) fn bandwidth_routes() -> Router<AppState> {
|
||||
Router::new()
|
||||
.route("/available", axum::routing::get(available_bandwidth))
|
||||
.route("/topup", axum::routing::post(topup_bandwidth))
|
||||
.layer(CompressionLayer::new())
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
tag = "bandwidth",
|
||||
get,
|
||||
path = "/v1/bandwidth/available",
|
||||
responses(
|
||||
(status = 200, content(
|
||||
(AvailableBandwidthResponse = "application/bincode")
|
||||
))
|
||||
|
||||
),
|
||||
)]
|
||||
async fn available_bandwidth(
|
||||
ConnectInfo(addr): ConnectInfo<SocketAddr>,
|
||||
Query(output): Query<OutputParams>,
|
||||
State(state): State<AppState>,
|
||||
) -> AxumResult<FormattedResponse<AvailableBandwidthResponse>> {
|
||||
let output = output.output.unwrap_or_default();
|
||||
Ok(output.to_response(
|
||||
state
|
||||
.available_bandwidth(addr.ip())
|
||||
.await
|
||||
.map_err(AxumErrorResponse::bad_request)?,
|
||||
))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
tag = "bandwidth",
|
||||
post,
|
||||
request_body = TopUpRequest,
|
||||
path = "/v1/bandwidth/topup",
|
||||
responses(
|
||||
(status = 200),
|
||||
),
|
||||
)]
|
||||
async fn topup_bandwidth(
|
||||
ConnectInfo(addr): ConnectInfo<SocketAddr>,
|
||||
Query(output): Query<OutputParams>,
|
||||
State(state): State<AppState>,
|
||||
Json(request): Json<TopUpRequest>,
|
||||
) -> AxumResult<FormattedResponse<()>> {
|
||||
let output = output.output.unwrap_or_default();
|
||||
let credential = InnerTopUpRequest::try_from(request)
|
||||
.map_err(AxumErrorResponse::bad_request)?
|
||||
.credential;
|
||||
state
|
||||
.topup_bandwidth(addr.ip(), credential)
|
||||
.await
|
||||
.map_err(AxumErrorResponse::bad_request)?;
|
||||
Ok(output.to_response(()))
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
[package]
|
||||
name = "nym-wireguard-private-metadata-tests"
|
||||
version = "1.0.0"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
async-trait = { workspace = true }
|
||||
axum = { workspace = true, features = ["tokio", "macros"] }
|
||||
nym-credential-verification = { path = "../../credential-verification" }
|
||||
nym-credentials-interface = { path = "../../credentials-interface" }
|
||||
nym-http-api-client = { path = "../../http-api-client" }
|
||||
nym-http-api-common = { path = "../../http-api-common", features = [
|
||||
"middleware",
|
||||
"utoipa",
|
||||
"output",
|
||||
] }
|
||||
nym-wireguard = { path = "../../wireguard" }
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "net", "io-util"] }
|
||||
tower-http = { workspace = true, features = [
|
||||
"cors",
|
||||
"trace",
|
||||
"compression-br",
|
||||
"compression-deflate",
|
||||
"compression-gzip",
|
||||
"compression-zstd",
|
||||
] }
|
||||
utoipa = { workspace = true, features = ["axum_extras", "time"] }
|
||||
|
||||
nym-wireguard-private-metadata-client = { path = "../client" }
|
||||
nym-wireguard-private-metadata-shared = { path = "../shared", features = [
|
||||
"testing",
|
||||
] }
|
||||
nym-wireguard-private-metadata-server = { path = "../server" }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -0,0 +1,217 @@
|
||||
#[cfg(test)]
|
||||
mod v0;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use nym_credential_verification::{ClientBandwidth, TicketVerifier};
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
use nym_http_api_client::Client;
|
||||
use nym_wireguard::{peer_controller::PeerControlRequest, CONTROL_CHANNEL_SIZE};
|
||||
use nym_wireguard_private_metadata_client::WireguardMetadataApiClient;
|
||||
use nym_wireguard_private_metadata_server::{
|
||||
AppState, PeerControllerTransceiver, RouterBuilder,
|
||||
};
|
||||
use nym_wireguard_private_metadata_shared::{latest, v0, v1, ErrorResponse};
|
||||
use tokio::{net::TcpListener, sync::mpsc};
|
||||
|
||||
pub(crate) const VERIFIER_AVAILABLE_BANDWIDTH: i64 = 42;
|
||||
pub(crate) const CREDENTIAL_BYTES: [u8; 1245] = [
|
||||
0, 0, 4, 133, 96, 179, 223, 185, 136, 23, 213, 166, 59, 203, 66, 69, 209, 181, 227, 254,
|
||||
16, 102, 98, 237, 59, 119, 170, 111, 31, 194, 51, 59, 120, 17, 115, 229, 79, 91, 11, 139,
|
||||
154, 2, 212, 23, 68, 70, 167, 3, 240, 54, 224, 171, 221, 1, 69, 48, 60, 118, 119, 249, 123,
|
||||
35, 172, 227, 131, 96, 232, 209, 187, 123, 4, 197, 102, 90, 96, 45, 125, 135, 140, 99, 1,
|
||||
151, 17, 131, 143, 157, 97, 107, 139, 232, 212, 87, 14, 115, 253, 255, 166, 167, 186, 43,
|
||||
90, 96, 173, 105, 120, 40, 10, 163, 250, 224, 214, 200, 178, 4, 160, 16, 130, 59, 76, 193,
|
||||
39, 240, 3, 101, 141, 209, 183, 226, 186, 207, 56, 210, 187, 7, 164, 240, 164, 205, 37, 81,
|
||||
184, 214, 193, 195, 90, 205, 238, 225, 195, 104, 12, 123, 203, 57, 233, 243, 215, 145, 195,
|
||||
196, 57, 38, 125, 172, 18, 47, 63, 165, 110, 219, 180, 40, 58, 116, 92, 254, 160, 98, 48,
|
||||
92, 254, 232, 107, 184, 80, 234, 60, 160, 235, 249, 76, 41, 38, 165, 28, 40, 136, 74, 48,
|
||||
166, 50, 245, 23, 201, 140, 101, 79, 93, 235, 128, 186, 146, 126, 180, 134, 43, 13, 186,
|
||||
19, 195, 48, 168, 201, 29, 216, 95, 176, 198, 132, 188, 64, 39, 212, 150, 32, 52, 53, 38,
|
||||
228, 199, 122, 226, 217, 75, 40, 191, 151, 48, 164, 242, 177, 79, 14, 122, 105, 151, 85,
|
||||
88, 199, 162, 17, 96, 103, 83, 178, 128, 9, 24, 30, 74, 108, 241, 85, 240, 166, 97, 241,
|
||||
85, 199, 11, 198, 226, 234, 70, 107, 145, 28, 208, 114, 51, 12, 234, 108, 101, 202, 112,
|
||||
48, 185, 22, 159, 67, 109, 49, 27, 149, 90, 109, 32, 226, 112, 7, 201, 208, 209, 104, 31,
|
||||
97, 134, 204, 145, 27, 181, 206, 181, 106, 32, 110, 136, 115, 249, 201, 111, 5, 245, 203,
|
||||
71, 121, 169, 126, 151, 178, 236, 59, 221, 195, 48, 135, 115, 6, 50, 227, 74, 97, 107, 107,
|
||||
213, 90, 2, 203, 154, 138, 47, 128, 52, 134, 128, 224, 51, 65, 240, 90, 8, 55, 175, 180,
|
||||
178, 204, 206, 168, 110, 51, 57, 189, 169, 48, 169, 136, 121, 99, 51, 170, 178, 214, 74, 1,
|
||||
96, 151, 167, 25, 173, 180, 171, 155, 10, 55, 142, 234, 190, 113, 90, 79, 80, 244, 71, 166,
|
||||
30, 235, 113, 150, 133, 1, 218, 17, 109, 111, 223, 24, 216, 177, 41, 2, 204, 65, 221, 212,
|
||||
207, 236, 144, 6, 65, 224, 55, 42, 1, 1, 161, 134, 118, 127, 111, 220, 110, 127, 240, 71,
|
||||
223, 129, 12, 93, 20, 220, 60, 56, 71, 146, 184, 95, 132, 69, 28, 56, 53, 192, 213, 22,
|
||||
119, 230, 152, 225, 182, 188, 163, 219, 37, 175, 247, 73, 14, 247, 38, 72, 243, 1, 48, 131,
|
||||
59, 8, 13, 96, 143, 185, 127, 241, 161, 217, 24, 149, 193, 40, 16, 30, 202, 151, 28, 119,
|
||||
240, 153, 101, 156, 61, 193, 72, 245, 199, 181, 12, 231, 65, 166, 67, 142, 121, 207, 202,
|
||||
58, 197, 113, 188, 248, 42, 124, 105, 48, 161, 241, 55, 209, 36, 194, 27, 63, 233, 144,
|
||||
189, 85, 117, 234, 9, 139, 46, 31, 206, 114, 95, 131, 29, 240, 13, 81, 142, 140, 133, 33,
|
||||
30, 41, 141, 37, 80, 217, 95, 221, 76, 115, 86, 201, 165, 51, 252, 9, 28, 209, 1, 48, 150,
|
||||
74, 248, 212, 187, 222, 66, 210, 3, 200, 19, 217, 171, 184, 42, 148, 53, 150, 57, 50, 6,
|
||||
227, 227, 62, 49, 42, 148, 148, 157, 82, 191, 58, 24, 34, 56, 98, 120, 89, 105, 176, 85,
|
||||
15, 253, 241, 41, 153, 195, 136, 1, 48, 142, 126, 213, 101, 223, 79, 133, 230, 105, 38,
|
||||
161, 149, 2, 21, 136, 150, 42, 72, 218, 85, 146, 63, 223, 58, 108, 186, 183, 248, 62, 20,
|
||||
47, 34, 113, 160, 177, 204, 181, 16, 24, 212, 224, 35, 84, 51, 168, 56, 136, 11, 1, 48,
|
||||
135, 242, 62, 149, 230, 178, 32, 224, 119, 26, 234, 163, 237, 224, 114, 95, 112, 140, 170,
|
||||
150, 96, 125, 136, 221, 180, 78, 18, 11, 12, 184, 2, 198, 217, 119, 43, 69, 4, 172, 109,
|
||||
55, 183, 40, 131, 172, 161, 88, 183, 101, 1, 48, 173, 216, 22, 73, 42, 255, 211, 93, 249,
|
||||
87, 159, 115, 61, 91, 55, 130, 17, 216, 60, 34, 122, 55, 8, 244, 244, 153, 151, 57, 5, 144,
|
||||
178, 55, 249, 64, 211, 168, 34, 148, 56, 89, 92, 203, 70, 124, 219, 152, 253, 165, 0, 32,
|
||||
203, 116, 63, 7, 240, 222, 82, 86, 11, 149, 167, 72, 224, 55, 190, 66, 201, 65, 168, 184,
|
||||
96, 47, 194, 241, 168, 124, 7, 74, 214, 250, 37, 76, 32, 218, 69, 122, 103, 215, 145, 169,
|
||||
24, 212, 229, 168, 106, 10, 144, 31, 13, 25, 178, 242, 250, 106, 159, 40, 48, 163, 165, 61,
|
||||
130, 57, 146, 4, 73, 32, 254, 233, 125, 135, 212, 29, 111, 4, 177, 114, 15, 210, 170, 82,
|
||||
108, 110, 62, 166, 81, 209, 106, 176, 156, 14, 133, 242, 60, 127, 120, 242, 28, 97, 0, 1,
|
||||
32, 103, 93, 109, 89, 240, 91, 1, 84, 150, 50, 206, 157, 203, 49, 220, 120, 234, 175, 234,
|
||||
150, 126, 225, 94, 163, 164, 199, 138, 114, 62, 99, 106, 112, 1, 32, 171, 40, 220, 82, 241,
|
||||
203, 76, 146, 111, 139, 182, 179, 237, 182, 115, 75, 128, 201, 107, 43, 214, 0, 135, 217,
|
||||
160, 68, 150, 232, 144, 114, 237, 98, 32, 30, 134, 232, 59, 93, 163, 253, 244, 13, 202, 52,
|
||||
147, 168, 83, 121, 123, 95, 21, 210, 209, 225, 223, 143, 49, 10, 205, 238, 1, 22, 83, 81,
|
||||
70, 1, 32, 26, 76, 6, 234, 160, 50, 139, 102, 161, 232, 155, 106, 130, 171, 226, 210, 233,
|
||||
178, 85, 247, 71, 123, 55, 53, 46, 67, 148, 137, 156, 207, 208, 107, 1, 32, 102, 31, 4, 98,
|
||||
110, 156, 144, 61, 229, 140, 198, 84, 196, 238, 128, 35, 131, 182, 137, 125, 241, 95, 69,
|
||||
131, 170, 27, 2, 144, 75, 72, 242, 102, 3, 32, 121, 80, 45, 173, 56, 65, 218, 27, 40, 251,
|
||||
197, 32, 169, 104, 123, 110, 90, 78, 153, 166, 38, 9, 129, 228, 99, 8, 1, 116, 142, 233,
|
||||
162, 69, 32, 216, 169, 159, 116, 95, 12, 63, 176, 195, 6, 183, 123, 135, 75, 61, 112, 106,
|
||||
83, 235, 176, 41, 27, 248, 48, 71, 165, 170, 12, 92, 103, 103, 81, 32, 58, 74, 75, 145,
|
||||
192, 94, 153, 69, 80, 128, 241, 3, 16, 117, 192, 86, 161, 103, 44, 174, 211, 196, 182, 124,
|
||||
55, 11, 107, 142, 49, 88, 6, 41, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 37, 139, 240, 0, 0,
|
||||
0, 0, 0, 0, 0, 1,
|
||||
];
|
||||
|
||||
pub(crate) struct MockVerifier {
|
||||
ret: i64,
|
||||
}
|
||||
|
||||
impl MockVerifier {
|
||||
pub(crate) fn new(ret: i64) -> MockVerifier {
|
||||
Self { ret }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl TicketVerifier for MockVerifier {
|
||||
async fn verify(&mut self) -> nym_credential_verification::Result<i64> {
|
||||
Ok(self.ret)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn spawn_server_and_create_client() -> Client {
|
||||
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
|
||||
let addr = listener.local_addr().unwrap();
|
||||
let (request_tx, mut request_rx) = mpsc::channel(CONTROL_CHANNEL_SIZE);
|
||||
let router = RouterBuilder::with_default_routes()
|
||||
.with_state(AppState::new(PeerControllerTransceiver::new(request_tx)))
|
||||
.router;
|
||||
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
match request_rx.recv().await {
|
||||
Some(PeerControlRequest::GetClientBandwidthByIp { ip: _, response_tx }) => {
|
||||
response_tx
|
||||
.send(Ok(ClientBandwidth::new(Default::default())))
|
||||
.ok();
|
||||
}
|
||||
Some(PeerControlRequest::GetVerifierByIp {
|
||||
ip: _,
|
||||
credential: _,
|
||||
response_tx,
|
||||
}) => {
|
||||
response_tx
|
||||
.send(Ok(Box::new(MockVerifier::new(
|
||||
VERIFIER_AVAILABLE_BANDWIDTH,
|
||||
))))
|
||||
.ok();
|
||||
}
|
||||
None => break,
|
||||
_ => panic!("Not expected"),
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
tokio::spawn(async move {
|
||||
axum::serve(
|
||||
listener,
|
||||
router.into_make_service_with_connect_info::<SocketAddr>(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
});
|
||||
Client::new_url::<_, ErrorResponse>(addr.to_string(), None).unwrap()
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn query_latest_version() {
|
||||
let client = spawn_server_and_create_client().await;
|
||||
let version = client.version().await.unwrap();
|
||||
assert_eq!(version, latest::VERSION);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn query_against_server_v0() {
|
||||
let client = super::v0::network::test::spawn_server_and_create_client().await;
|
||||
|
||||
// version check
|
||||
let version = client.version().await.unwrap();
|
||||
assert_eq!(version, v0::VERSION);
|
||||
|
||||
// v0 reqwests
|
||||
let request = v0::AvailableBandwidthRequest {}.try_into().unwrap();
|
||||
let response = client.available_bandwidth(&request).await.unwrap();
|
||||
v0::AvailableBandwidthResponse::try_from(response).unwrap();
|
||||
|
||||
let request = v0::TopUpRequest {}.try_into().unwrap();
|
||||
let response = client.topup_bandwidth(&request).await.unwrap();
|
||||
v0::TopUpResponse::try_from(response).unwrap();
|
||||
|
||||
// v1 reqwests
|
||||
let request = v1::AvailableBandwidthRequest {}.try_into().unwrap();
|
||||
assert!(client.available_bandwidth(&request).await.is_err());
|
||||
|
||||
let request = v1::TopUpRequest {
|
||||
credential: CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap(),
|
||||
}
|
||||
.try_into()
|
||||
.unwrap();
|
||||
assert!(client.topup_bandwidth(&request).await.is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn query_against_server_v1() {
|
||||
let client = spawn_server_and_create_client().await;
|
||||
|
||||
// version check
|
||||
let version = client.version().await.unwrap();
|
||||
assert_eq!(version, v1::VERSION);
|
||||
|
||||
// v0 reqwests
|
||||
let request = v0::AvailableBandwidthRequest {}.try_into().unwrap();
|
||||
let response = client.available_bandwidth(&request).await.unwrap();
|
||||
v0::AvailableBandwidthResponse::try_from(response).unwrap();
|
||||
|
||||
let request = v0::TopUpRequest {}.try_into().unwrap();
|
||||
assert!(client.topup_bandwidth(&request).await.is_err());
|
||||
|
||||
// v1 reqwests
|
||||
let request = v1::AvailableBandwidthRequest {}.try_into().unwrap();
|
||||
let response = client.available_bandwidth(&request).await.unwrap();
|
||||
let available_bandwidth = v1::AvailableBandwidthResponse::try_from(response)
|
||||
.unwrap()
|
||||
.available_bandwidth;
|
||||
assert_eq!(available_bandwidth, 0);
|
||||
|
||||
let request = v1::TopUpRequest {
|
||||
credential: CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap(),
|
||||
}
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let response = client.topup_bandwidth(&request).await.unwrap();
|
||||
let available_bandwidth = v1::TopUpResponse::try_from(response)
|
||||
.unwrap()
|
||||
.available_bandwidth;
|
||||
assert_eq!(available_bandwidth, VERIFIER_AVAILABLE_BANDWIDTH);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_wireguard_private_metadata_shared::{
|
||||
v0 as latest, Construct, Extract, Request, Response, Version,
|
||||
};
|
||||
|
||||
pub enum RequestData {
|
||||
AvailableBandwidth(()),
|
||||
TopUpBandwidth(()),
|
||||
}
|
||||
|
||||
impl From<latest::interface::RequestData> for RequestData {
|
||||
fn from(value: latest::interface::RequestData) -> Self {
|
||||
match value {
|
||||
latest::interface::RequestData::AvailableBandwidth(inner) => {
|
||||
Self::AvailableBandwidth(inner)
|
||||
}
|
||||
latest::interface::RequestData::TopUpBandwidth(credential_spending_data) => {
|
||||
Self::TopUpBandwidth(credential_spending_data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RequestData> for latest::interface::RequestData {
|
||||
fn from(value: RequestData) -> Self {
|
||||
match value {
|
||||
RequestData::AvailableBandwidth(inner) => Self::AvailableBandwidth(inner),
|
||||
RequestData::TopUpBandwidth(credential_spending_data) => {
|
||||
Self::TopUpBandwidth(credential_spending_data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<latest::interface::ResponseData> for ResponseData {
|
||||
fn from(value: latest::interface::ResponseData) -> Self {
|
||||
match value {
|
||||
latest::interface::ResponseData::AvailableBandwidth(inner) => {
|
||||
Self::AvailableBandwidth(inner)
|
||||
}
|
||||
latest::interface::ResponseData::TopUpBandwidth(credential_spending_data) => {
|
||||
Self::TopUpBandwidth(credential_spending_data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ResponseData> for latest::interface::ResponseData {
|
||||
fn from(value: ResponseData) -> Self {
|
||||
match value {
|
||||
ResponseData::AvailableBandwidth(inner) => Self::AvailableBandwidth(inner),
|
||||
ResponseData::TopUpBandwidth(credential_spending_data) => {
|
||||
Self::TopUpBandwidth(credential_spending_data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Construct<RequestData> for Request {
|
||||
fn construct(
|
||||
info: RequestData,
|
||||
version: Version,
|
||||
) -> Result<Self, nym_wireguard_private_metadata_shared::ModelError> {
|
||||
match version {
|
||||
Version::V0 => {
|
||||
let translate_info = latest::interface::RequestData::from(info);
|
||||
let versioned_request =
|
||||
latest::VersionedRequest::construct(translate_info, latest::VERSION)?;
|
||||
Ok(versioned_request.try_into()?)
|
||||
}
|
||||
_ => Err(
|
||||
nym_wireguard_private_metadata_shared::ModelError::DowngradeNotPossible {
|
||||
from: version,
|
||||
to: Version::V0,
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Extract<RequestData> for Request {
|
||||
fn extract(
|
||||
&self,
|
||||
) -> Result<(RequestData, Version), nym_wireguard_private_metadata_shared::ModelError> {
|
||||
match self.version {
|
||||
Version::V0 => {
|
||||
let versioned_request = latest::VersionedRequest::try_from(self.clone())?;
|
||||
let (request, version) = versioned_request.extract()?;
|
||||
|
||||
Ok((request.into(), version))
|
||||
}
|
||||
_ => Err(
|
||||
nym_wireguard_private_metadata_shared::ModelError::UpdateNotPossible {
|
||||
from: self.version,
|
||||
to: Version::V0,
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ResponseData {
|
||||
AvailableBandwidth(()),
|
||||
TopUpBandwidth(()),
|
||||
}
|
||||
|
||||
impl Construct<ResponseData> for Response {
|
||||
fn construct(
|
||||
info: ResponseData,
|
||||
version: Version,
|
||||
) -> Result<Self, nym_wireguard_private_metadata_shared::ModelError> {
|
||||
match version {
|
||||
Version::V0 => {
|
||||
let translate_response = latest::interface::ResponseData::from(info);
|
||||
let versioned_response =
|
||||
latest::VersionedResponse::construct(translate_response, version)?;
|
||||
Ok(versioned_response.try_into()?)
|
||||
}
|
||||
_ => Err(
|
||||
nym_wireguard_private_metadata_shared::ModelError::DowngradeNotPossible {
|
||||
from: version,
|
||||
to: Version::V0,
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Extract<ResponseData> for Response {
|
||||
fn extract(
|
||||
&self,
|
||||
) -> Result<(ResponseData, Version), nym_wireguard_private_metadata_shared::ModelError> {
|
||||
match self.version {
|
||||
Version::V0 => {
|
||||
let versioned_response = latest::VersionedResponse::try_from(self.clone())?;
|
||||
let (response, version) = versioned_response.extract()?;
|
||||
|
||||
Ok((response.into(), version))
|
||||
}
|
||||
_ => Err(
|
||||
nym_wireguard_private_metadata_shared::ModelError::UpdateNotPossible {
|
||||
from: self.version,
|
||||
to: Version::V0,
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub(crate) mod interface;
|
||||
pub(crate) mod network;
|
||||
@@ -0,0 +1,146 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod test {
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use crate::{
|
||||
tests::{MockVerifier, VERIFIER_AVAILABLE_BANDWIDTH},
|
||||
v0::interface::{RequestData, ResponseData},
|
||||
};
|
||||
use axum::{extract::Query, Json, Router};
|
||||
use nym_credential_verification::ClientBandwidth;
|
||||
use nym_http_api_client::Client;
|
||||
use nym_http_api_common::{FormattedResponse, OutputParams};
|
||||
use nym_wireguard::{peer_controller::PeerControlRequest, CONTROL_CHANNEL_SIZE};
|
||||
use nym_wireguard_private_metadata_server::PeerControllerTransceiver;
|
||||
use nym_wireguard_private_metadata_shared::ErrorResponse;
|
||||
use nym_wireguard_private_metadata_shared::{
|
||||
v0 as latest, AxumErrorResponse, AxumResult, Construct, Extract, Request, Response,
|
||||
};
|
||||
use tokio::{net::TcpListener, sync::mpsc};
|
||||
use tower_http::compression::CompressionLayer;
|
||||
|
||||
use nym_wireguard_private_metadata_server::AppState;
|
||||
|
||||
fn bandwidth_routes() -> Router<AppState> {
|
||||
Router::new()
|
||||
.route("/version", axum::routing::get(version))
|
||||
.route("/available", axum::routing::post(available_bandwidth))
|
||||
.route("/topup", axum::routing::post(topup_bandwidth))
|
||||
.layer(CompressionLayer::new())
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
tag = "bandwidth",
|
||||
get,
|
||||
path = "/v1/bandwidth/version",
|
||||
responses(
|
||||
(status = 200, content(
|
||||
(Response = "application/bincode")
|
||||
))
|
||||
),
|
||||
)]
|
||||
async fn version(Query(output): Query<OutputParams>) -> AxumResult<FormattedResponse<u64>> {
|
||||
let output = output.output.unwrap_or_default();
|
||||
Ok(output.to_response(latest::VERSION.into()))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
tag = "bandwidth",
|
||||
post,
|
||||
request_body = Request,
|
||||
path = "/v1/bandwidth/available",
|
||||
responses(
|
||||
(status = 200, content(
|
||||
(Response = "application/bincode")
|
||||
))
|
||||
),
|
||||
)]
|
||||
async fn available_bandwidth(
|
||||
Query(output): Query<OutputParams>,
|
||||
Json(request): Json<Request>,
|
||||
) -> AxumResult<FormattedResponse<Response>> {
|
||||
let output = output.output.unwrap_or_default();
|
||||
|
||||
let (RequestData::AvailableBandwidth(_), version) =
|
||||
request.extract().map_err(AxumErrorResponse::bad_request)?
|
||||
else {
|
||||
return Err(AxumErrorResponse::bad_request("incorrect request type"));
|
||||
};
|
||||
let response = Response::construct(ResponseData::AvailableBandwidth(()), version)
|
||||
.map_err(AxumErrorResponse::bad_request)?;
|
||||
|
||||
Ok(output.to_response(response))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
tag = "bandwidth",
|
||||
post,
|
||||
request_body = Request,
|
||||
path = "/v1/bandwidth/topup",
|
||||
responses(
|
||||
(status = 200, content(
|
||||
(Response = "application/bincode")
|
||||
))
|
||||
),
|
||||
)]
|
||||
async fn topup_bandwidth(
|
||||
Query(output): Query<OutputParams>,
|
||||
Json(request): Json<Request>,
|
||||
) -> AxumResult<FormattedResponse<Response>> {
|
||||
let output = output.output.unwrap_or_default();
|
||||
|
||||
let (RequestData::TopUpBandwidth(_), version) =
|
||||
request.extract().map_err(AxumErrorResponse::bad_request)?
|
||||
else {
|
||||
return Err(AxumErrorResponse::bad_request("incorrect request type"));
|
||||
};
|
||||
let response = Response::construct(ResponseData::TopUpBandwidth(()), version)
|
||||
.map_err(AxumErrorResponse::bad_request)?;
|
||||
|
||||
Ok(output.to_response(response))
|
||||
}
|
||||
|
||||
pub(crate) async fn spawn_server_and_create_client() -> Client {
|
||||
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
|
||||
let addr = listener.local_addr().unwrap();
|
||||
let (request_tx, mut request_rx) = mpsc::channel(CONTROL_CHANNEL_SIZE);
|
||||
let router = Router::new()
|
||||
.nest("/v1", Router::new().nest("/bandwidth", bandwidth_routes()))
|
||||
.with_state(AppState::new(PeerControllerTransceiver::new(request_tx)));
|
||||
|
||||
tokio::spawn(async move {
|
||||
match request_rx.recv().await.unwrap() {
|
||||
PeerControlRequest::GetClientBandwidthByIp { ip: _, response_tx } => {
|
||||
response_tx
|
||||
.send(Ok(ClientBandwidth::new(Default::default())))
|
||||
.ok();
|
||||
}
|
||||
PeerControlRequest::GetVerifierByIp {
|
||||
ip: _,
|
||||
credential: _,
|
||||
response_tx,
|
||||
} => {
|
||||
response_tx
|
||||
.send(Ok(Box::new(MockVerifier::new(
|
||||
VERIFIER_AVAILABLE_BANDWIDTH,
|
||||
))))
|
||||
.ok();
|
||||
}
|
||||
_ => panic!("Not expected"),
|
||||
}
|
||||
});
|
||||
|
||||
tokio::spawn(async move {
|
||||
axum::serve(
|
||||
listener,
|
||||
router.into_make_service_with_connect_info::<SocketAddr>(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
});
|
||||
Client::new_url::<_, ErrorResponse>(addr.to_string(), None).unwrap()
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -72,7 +72,7 @@ nym-ip-packet-router = { path = "../service-providers/ip-packet-router" }
|
||||
nym-node-metrics = { path = "../nym-node/nym-node-metrics" }
|
||||
|
||||
nym-wireguard = { path = "../common/wireguard" }
|
||||
nym-wireguard-private-metadata = { path = "../common/wireguard-private-metadata" }
|
||||
nym-wireguard-private-metadata-server = { path = "../common/wireguard-private-metadata/server" }
|
||||
nym-wireguard-types = { path = "../common/wireguard-types", default-features = false }
|
||||
|
||||
nym-authenticator-requests = { path = "../common/authenticator-requests" }
|
||||
|
||||
+16
-15
@@ -484,7 +484,7 @@ impl GatewayTasksBuilder {
|
||||
pub async fn try_start_wireguard(
|
||||
&mut self,
|
||||
) -> Result<
|
||||
nym_wireguard_private_metadata::ShutdownHandles,
|
||||
nym_wireguard_private_metadata_server::ShutdownHandles,
|
||||
Box<dyn std::error::Error + Send + Sync>,
|
||||
> {
|
||||
let all_peers = self.get_wireguard_peers().await?;
|
||||
@@ -501,9 +501,9 @@ impl GatewayTasksBuilder {
|
||||
);
|
||||
};
|
||||
|
||||
let router = nym_wireguard_private_metadata::RouterBuilder::with_default_routes();
|
||||
let router = router.with_state(nym_wireguard_private_metadata::AppState::new(
|
||||
nym_wireguard_private_metadata::PeerControllerTransceiver::new(
|
||||
let router = nym_wireguard_private_metadata_server::RouterBuilder::with_default_routes();
|
||||
let router = router.with_state(nym_wireguard_private_metadata_server::AppState::new(
|
||||
nym_wireguard_private_metadata_server::PeerControllerTransceiver::new(
|
||||
wireguard_data.inner.peer_tx().clone(),
|
||||
),
|
||||
));
|
||||
@@ -512,16 +512,6 @@ impl GatewayTasksBuilder {
|
||||
wireguard_data.inner.config().private_ipv4.into(),
|
||||
wireguard_data.inner.config().announced_metadata_port,
|
||||
);
|
||||
let server = router.build_server(&bind_address).await?;
|
||||
|
||||
let cancel_token: tokio_util::sync::CancellationToken = (*self.shutdown_token).clone();
|
||||
let axum_shutdown_receiver = cancel_token.clone().cancelled_owned();
|
||||
let server_handle = tokio::spawn(async move {
|
||||
{
|
||||
info!("Started Wireguard Axum HTTP V2 server on {bind_address}");
|
||||
server.run(axum_shutdown_receiver).await
|
||||
}
|
||||
});
|
||||
|
||||
let wg_handle = nym_wireguard::start_wireguard(
|
||||
ecash_manager,
|
||||
@@ -531,7 +521,18 @@ impl GatewayTasksBuilder {
|
||||
wireguard_data,
|
||||
)
|
||||
.await?;
|
||||
let shutdown_handles = nym_wireguard_private_metadata::ShutdownHandles::new(
|
||||
|
||||
let server = router.build_server(&bind_address).await?;
|
||||
let cancel_token: tokio_util::sync::CancellationToken = (*self.shutdown_token).clone();
|
||||
let axum_shutdown_receiver = cancel_token.clone().cancelled_owned();
|
||||
let server_handle = tokio::spawn(async move {
|
||||
{
|
||||
info!("Started Wireguard Axum HTTP V2 server on {bind_address}");
|
||||
server.run(axum_shutdown_receiver).await
|
||||
}
|
||||
});
|
||||
|
||||
let shutdown_handles = nym_wireguard_private_metadata_server::ShutdownHandles::new(
|
||||
server_handle,
|
||||
wg_handle,
|
||||
cancel_token,
|
||||
|
||||
Reference in New Issue
Block a user