From 845b5df14c0e4e45964c86ccc3b90f4b320a7ee5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Mon, 14 Oct 2024 10:27:59 +0100 Subject: [PATCH] chore: remove unused rocket code --- nym-api/src/circulating_supply_api/mod.rs | 10 - nym-api/src/circulating_supply_api/routes.rs | 77 --- nym-api/src/ecash/api_routes/aggregation.rs | 106 ++- .../src/ecash/api_routes/aggregation_axum.rs | 143 ---- nym-api/src/ecash/api_routes/handlers.rs | 8 +- nym-api/src/ecash/api_routes/issued.rs | 115 +++- nym-api/src/ecash/api_routes/issued_axum.rs | 147 ----- nym-api/src/ecash/api_routes/mod.rs | 8 +- .../src/ecash/api_routes/partial_signing.rs | 107 ++- .../ecash/api_routes/partial_signing_axum.rs | 179 ----- nym-api/src/ecash/api_routes/spending.rs | 106 ++- nym-api/src/ecash/api_routes/spending_axum.rs | 246 ------- nym-api/src/ecash/mod.rs | 24 - nym-api/src/network/mod.rs | 9 - nym-api/src/network/routes.rs | 61 -- nym-api/src/node_status_api/mod.rs | 47 -- .../src/node_status_api/routes_deprecated.rs | 615 ------------------ .../src/nym_contract_cache/legacy_helpers.rs | 2 - nym-api/src/nym_contract_cache/mod.rs | 19 - nym-api/src/nym_contract_cache/routes.rs | 158 ----- nym-api/src/nym_nodes/mod.rs | 31 - nym-api/src/nym_nodes/routes.rs | 135 ---- nym-api/src/nym_nodes/unstable_routes.rs | 403 ------------ nym-api/src/status/mod.rs | 10 - nym-api/src/status/routes.rs | 52 -- 25 files changed, 341 insertions(+), 2477 deletions(-) delete mode 100644 nym-api/src/circulating_supply_api/routes.rs delete mode 100644 nym-api/src/ecash/api_routes/aggregation_axum.rs delete mode 100644 nym-api/src/ecash/api_routes/issued_axum.rs delete mode 100644 nym-api/src/ecash/api_routes/partial_signing_axum.rs delete mode 100644 nym-api/src/ecash/api_routes/spending_axum.rs delete mode 100644 nym-api/src/network/routes.rs delete mode 100644 nym-api/src/node_status_api/routes_deprecated.rs delete mode 100644 nym-api/src/nym_contract_cache/legacy_helpers.rs delete mode 100644 nym-api/src/nym_contract_cache/routes.rs delete mode 100644 nym-api/src/nym_nodes/routes.rs delete mode 100644 nym-api/src/nym_nodes/unstable_routes.rs delete mode 100644 nym-api/src/status/routes.rs diff --git a/nym-api/src/circulating_supply_api/mod.rs b/nym-api/src/circulating_supply_api/mod.rs index 9c125815cb..3253ccaaab 100644 --- a/nym-api/src/circulating_supply_api/mod.rs +++ b/nym-api/src/circulating_supply_api/mod.rs @@ -7,16 +7,6 @@ use nym_task::TaskManager; pub(crate) mod cache; pub(crate) mod handlers; -// pub(crate) mod routes; - -// /// Merges the routes with http information and returns it to Rocket for serving -// pub(crate) fn circulating_supply_routes(settings: &OpenApiSettings) -> (Vec, OpenApi) { -// openapi_get_routes_spec![ -// settings: routes::get_full_circulating_supply, -// routes::get_total_supply, -// routes::get_circulating_supply -// ] -// } /// Spawn the circulating supply cache refresher. pub(crate) fn start_cache_refresh( diff --git a/nym-api/src/circulating_supply_api/routes.rs b/nym-api/src/circulating_supply_api/routes.rs deleted file mode 100644 index b8b00cb82e..0000000000 --- a/nym-api/src/circulating_supply_api/routes.rs +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2022-2023 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use crate::circulating_supply_api::cache::CirculatingSupplyCache; -use crate::node_status_api::models::RocketErrorResponse; -use nym_api_requests::models::CirculatingSupplyResponse; -use nym_validator_client::nyxd::Coin; -use rocket::http::Status; -use rocket::serde::json::Json; -use rocket::State; -use rocket_okapi::openapi; - -// TODO: this is not the best place to put it, it should be more centralised, -// but for a quick fix, that's good enough for now... -// (for proper solution we should be managing `NymNetworkDetails` via rocket and grabbing display exponent -// value from the mix denom here. -const UNYM_RATIO: f64 = 1000000.; - -fn unym_coin_to_float_unym(coin: Coin) -> f64 { - // our total supply can't exceed 1B so an overflow here is impossible - // (if it happened, then we SHOULD crash) - coin.amount as f64 / UNYM_RATIO -} - -#[openapi(tag = "circulating-supply")] -#[get("/circulating-supply")] -pub(crate) async fn get_full_circulating_supply( - cache: &State, -) -> Result, RocketErrorResponse> { - match cache.get_circulating_supply().await { - Some(value) => Ok(Json(value)), - None => Err(RocketErrorResponse::new( - "unavailable", - Status::InternalServerError, - )), - } -} - -#[openapi(tag = "circulating-supply")] -#[get("/circulating-supply/total-supply-value")] -pub(crate) async fn get_total_supply( - cache: &State, -) -> Result, RocketErrorResponse> { - let full_circulating_supply = match cache.get_circulating_supply().await { - Some(res) => res, - None => { - return Err(RocketErrorResponse::new( - "unavailable", - Status::InternalServerError, - )) - } - }; - - Ok(Json(unym_coin_to_float_unym( - full_circulating_supply.total_supply.into(), - ))) -} - -#[openapi(tag = "circulating-supply")] -#[get("/circulating-supply/circulating-supply-value")] -pub(crate) async fn get_circulating_supply( - cache: &State, -) -> Result, RocketErrorResponse> { - let full_circulating_supply = match cache.get_circulating_supply().await { - Some(res) => res, - None => { - return Err(RocketErrorResponse::new( - "unavailable", - Status::InternalServerError, - )) - } - }; - - Ok(Json(unym_coin_to_float_unym( - full_circulating_supply.circulating_supply.into(), - ))) -} diff --git a/nym-api/src/ecash/api_routes/aggregation.rs b/nym-api/src/ecash/api_routes/aggregation.rs index 9e6018550c..d956f2897d 100644 --- a/nym-api/src/ecash/api_routes/aggregation.rs +++ b/nym-api/src/ecash/api_routes/aggregation.rs @@ -1,28 +1,66 @@ // Copyright 2024 - Nym Technologies SA // SPDX-License-Identifier: GPL-3.0-only -use crate::ecash::error::{EcashError, Result}; +use crate::ecash::api_routes::helpers::EpochIdParam; +use crate::ecash::error::EcashError; use crate::ecash::state::EcashState; +use crate::node_status_api::models::AxumResult; +use crate::support::http::state::AppState; +use axum::extract::Path; +use axum::{Json, Router}; use nym_api_requests::ecash::models::{ AggregatedCoinIndicesSignatureResponse, AggregatedExpirationDateSignatureResponse, }; use nym_api_requests::ecash::VerificationKeyResponse; use nym_ecash_time::{cred_exp_date, EcashTime}; use nym_validator_client::nym_api::rfc_3339_date; -use rocket::serde::json::Json; -use rocket::State as RocketState; -use rocket_okapi::openapi; +use serde::Deserialize; +use std::sync::Arc; use time::Date; use tracing::trace; +use utoipa::IntoParams; -// routes with globally aggregated keys, signatures, etc. +/// routes with globally aggregated keys, signatures, etc. +pub(crate) fn aggregation_routes(ecash_state: Arc) -> Router { + Router::new() + .route( + "/master-verification-key:epoch_id", + axum::routing::get({ + let ecash_state = Arc::clone(&ecash_state); + |epoch_id| master_verification_key(epoch_id, ecash_state) + }), + ) + .route( + "/aggregated-expiration-date-signatures:expiration_date", + axum::routing::get({ + let ecash_state = Arc::clone(&ecash_state); + |expiration_date| expiration_date_signatures(expiration_date, ecash_state) + }), + ) + .route( + "/aggregated-coin-indices-signatures:epoch_id", + axum::routing::get({ + let ecash_state = Arc::clone(&ecash_state); + |epoch_id| coin_indices_signatures(epoch_id, ecash_state) + }), + ) +} -#[openapi(tag = "Ecash Global Data")] -#[get("/master-verification-key?")] -pub async fn master_verification_key( - epoch_id: Option, - state: &RocketState, -) -> Result> { +#[utoipa::path( + tag = "Ecash Global Data", + get, + params( + EpochIdParam + ), + path = "/v1/ecash/master-verification-key/{epoch_id}", + responses( + (status = 200, body = VerificationKeyResponse) + ) +)] +async fn master_verification_key( + Path(EpochIdParam { epoch_id }): Path, + state: Arc, +) -> AxumResult> { trace!("aggregated_verification_key request"); // see if we're not in the middle of new dkg @@ -33,12 +71,27 @@ pub async fn master_verification_key( Ok(Json(VerificationKeyResponse::new(key.clone()))) } -#[openapi(tag = "Ecash Global Data")] -#[get("/aggregated-expiration-date-signatures?")] -pub async fn expiration_date_signatures( +#[derive(Deserialize, IntoParams)] +#[into_params(parameter_in = Path)] +struct ExpirationDateParam { expiration_date: Option, - state: &RocketState, -) -> Result> { +} + +#[utoipa::path( + tag = "Ecash Global Data", + get, + params( + ExpirationDateParam + ), + path = "/v1/ecash/aggregated-expiration-date-signatures/{epoch_id}", + responses( + (status = 200, body = AggregatedExpirationDateSignatureResponse) + ) +)] +async fn expiration_date_signatures( + Path(ExpirationDateParam { expiration_date }): Path, + state: Arc, +) -> AxumResult> { trace!("aggregated_expiration_date_signatures request"); let expiration_date = match expiration_date { @@ -61,12 +114,21 @@ pub async fn expiration_date_signatures( })) } -#[openapi(tag = "Ecash Global Data")] -#[get("/aggregated-coin-indices-signatures?")] -pub async fn coin_indices_signatures( - epoch_id: Option, - state: &RocketState, -) -> Result> { +#[utoipa::path( + tag = "Ecash Global Data", + get, + params( + EpochIdParam + ), + path = "/v1/ecash/aggregated-coin-indices-signatures/{epoch_id}", + responses( + (status = 200, body = AggregatedCoinIndicesSignatureResponse) + ) +)] +async fn coin_indices_signatures( + Path(EpochIdParam { epoch_id }): Path, + state: Arc, +) -> AxumResult> { trace!("aggregated_coin_indices_signatures request"); // see if we're not in the middle of new dkg diff --git a/nym-api/src/ecash/api_routes/aggregation_axum.rs b/nym-api/src/ecash/api_routes/aggregation_axum.rs deleted file mode 100644 index d956f2897d..0000000000 --- a/nym-api/src/ecash/api_routes/aggregation_axum.rs +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2024 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use crate::ecash::api_routes::helpers::EpochIdParam; -use crate::ecash::error::EcashError; -use crate::ecash::state::EcashState; -use crate::node_status_api::models::AxumResult; -use crate::support::http::state::AppState; -use axum::extract::Path; -use axum::{Json, Router}; -use nym_api_requests::ecash::models::{ - AggregatedCoinIndicesSignatureResponse, AggregatedExpirationDateSignatureResponse, -}; -use nym_api_requests::ecash::VerificationKeyResponse; -use nym_ecash_time::{cred_exp_date, EcashTime}; -use nym_validator_client::nym_api::rfc_3339_date; -use serde::Deserialize; -use std::sync::Arc; -use time::Date; -use tracing::trace; -use utoipa::IntoParams; - -/// routes with globally aggregated keys, signatures, etc. -pub(crate) fn aggregation_routes(ecash_state: Arc) -> Router { - Router::new() - .route( - "/master-verification-key:epoch_id", - axum::routing::get({ - let ecash_state = Arc::clone(&ecash_state); - |epoch_id| master_verification_key(epoch_id, ecash_state) - }), - ) - .route( - "/aggregated-expiration-date-signatures:expiration_date", - axum::routing::get({ - let ecash_state = Arc::clone(&ecash_state); - |expiration_date| expiration_date_signatures(expiration_date, ecash_state) - }), - ) - .route( - "/aggregated-coin-indices-signatures:epoch_id", - axum::routing::get({ - let ecash_state = Arc::clone(&ecash_state); - |epoch_id| coin_indices_signatures(epoch_id, ecash_state) - }), - ) -} - -#[utoipa::path( - tag = "Ecash Global Data", - get, - params( - EpochIdParam - ), - path = "/v1/ecash/master-verification-key/{epoch_id}", - responses( - (status = 200, body = VerificationKeyResponse) - ) -)] -async fn master_verification_key( - Path(EpochIdParam { epoch_id }): Path, - state: Arc, -) -> AxumResult> { - trace!("aggregated_verification_key request"); - - // see if we're not in the middle of new dkg - state.ensure_dkg_not_in_progress().await?; - - let key = state.master_verification_key(epoch_id).await?; - - Ok(Json(VerificationKeyResponse::new(key.clone()))) -} - -#[derive(Deserialize, IntoParams)] -#[into_params(parameter_in = Path)] -struct ExpirationDateParam { - expiration_date: Option, -} - -#[utoipa::path( - tag = "Ecash Global Data", - get, - params( - ExpirationDateParam - ), - path = "/v1/ecash/aggregated-expiration-date-signatures/{epoch_id}", - responses( - (status = 200, body = AggregatedExpirationDateSignatureResponse) - ) -)] -async fn expiration_date_signatures( - Path(ExpirationDateParam { expiration_date }): Path, - state: Arc, -) -> AxumResult> { - trace!("aggregated_expiration_date_signatures request"); - - let expiration_date = match expiration_date { - None => cred_exp_date().ecash_date(), - Some(raw) => Date::parse(&raw, &rfc_3339_date()) - .map_err(|_| EcashError::MalformedExpirationDate { raw })?, - }; - - // see if we're not in the middle of new dkg - state.ensure_dkg_not_in_progress().await?; - - let expiration_date_signatures = state - .master_expiration_date_signatures(expiration_date) - .await?; - - Ok(Json(AggregatedExpirationDateSignatureResponse { - epoch_id: expiration_date_signatures.epoch_id, - expiration_date, - signatures: expiration_date_signatures.signatures.clone(), - })) -} - -#[utoipa::path( - tag = "Ecash Global Data", - get, - params( - EpochIdParam - ), - path = "/v1/ecash/aggregated-coin-indices-signatures/{epoch_id}", - responses( - (status = 200, body = AggregatedCoinIndicesSignatureResponse) - ) -)] -async fn coin_indices_signatures( - Path(EpochIdParam { epoch_id }): Path, - state: Arc, -) -> AxumResult> { - trace!("aggregated_coin_indices_signatures request"); - - // see if we're not in the middle of new dkg - state.ensure_dkg_not_in_progress().await?; - - let coin_indices_signatures = state.master_coin_index_signatures(epoch_id).await?; - - Ok(Json(AggregatedCoinIndicesSignatureResponse { - epoch_id: coin_indices_signatures.epoch_id, - signatures: coin_indices_signatures.signatures.clone(), - })) -} diff --git a/nym-api/src/ecash/api_routes/handlers.rs b/nym-api/src/ecash/api_routes/handlers.rs index 5b9d6cee46..e9f97c48b0 100644 --- a/nym-api/src/ecash/api_routes/handlers.rs +++ b/nym-api/src/ecash/api_routes/handlers.rs @@ -1,10 +1,10 @@ // Copyright 2023-2024 - Nym Technologies SA // SPDX-License-Identifier: GPL-3.0-only -use crate::ecash::api_routes::aggregation_axum::aggregation_routes; -use crate::ecash::api_routes::issued_axum::issued_routes; -use crate::ecash::api_routes::partial_signing_axum::partial_signing_routes; -use crate::ecash::api_routes::spending_axum::spending_routes; +use crate::ecash::api_routes::aggregation::aggregation_routes; +use crate::ecash::api_routes::issued::issued_routes; +use crate::ecash::api_routes::partial_signing::partial_signing_routes; +use crate::ecash::api_routes::spending::spending_routes; use crate::ecash::state::EcashState; use crate::support::http::state::AppState; use axum::Router; diff --git a/nym-api/src/ecash/api_routes/issued.rs b/nym-api/src/ecash/api_routes/issued.rs index 5b85e6b0cf..95c1439600 100644 --- a/nym-api/src/ecash/api_routes/issued.rs +++ b/nym-api/src/ecash/api_routes/issued.rs @@ -5,21 +5,64 @@ use crate::ecash::api_routes::helpers::build_credentials_response; use crate::ecash::error::EcashError; use crate::ecash::state::EcashState; use crate::ecash::storage::EcashStorageExt; +use crate::node_status_api::models::AxumResult; +use crate::support::http::state::AppState; +use axum::extract::Path; +use axum::{Json, Router}; use nym_api_requests::ecash::models::{ EpochCredentialsResponse, IssuedCredentialResponse, IssuedCredentialsResponse, }; use nym_api_requests::ecash::CredentialsRequestBody; -use nym_coconut_dkg_common::types::EpochId; -use rocket::serde::json::Json; -use rocket::State as RocketState; -use rocket_okapi::openapi; +use serde::Deserialize; +use std::sync::Arc; +use utoipa::IntoParams; -#[openapi(tag = "Ecash")] -#[get("/epoch-credentials/")] -pub async fn epoch_credentials( - epoch: EpochId, - state: &RocketState, -) -> crate::ecash::error::Result> { +pub(crate) fn issued_routes(ecash_state: Arc) -> Router { + Router::new() + .route( + "/epoch-credentials/:epoch", + axum::routing::get({ + let ecash_state = Arc::clone(&ecash_state); + |epoch| epoch_credentials(epoch, ecash_state) + }), + ) + .route( + "/issued-credential/:id", + axum::routing::get({ + let ecash_state = Arc::clone(&ecash_state); + |id| issued_credential(id, ecash_state) + }), + ) + .route( + "/issued-credentials", + axum::routing::post({ + let ecash_state = Arc::clone(&ecash_state); + |body| issued_credentials(body, ecash_state) + }), + ) +} + +#[derive(Deserialize, IntoParams)] +#[into_params(parameter_in = Path)] +struct EpochParam { + epoch: u64, +} + +#[utoipa::path( + tag = "Ecash", + get, + params( + EpochParam + ), + path = "/v1/ecash/epoch-credentials/{epoch}", + responses( + (status = 200, body = EpochCredentialsResponse) + ) +)] +async fn epoch_credentials( + Path(EpochParam { epoch }): Path, + state: Arc, +) -> AxumResult> { let issued = state.aux.storage.get_epoch_credentials(epoch).await?; let response = if let Some(issued) = issued { @@ -35,12 +78,27 @@ pub async fn epoch_credentials( Ok(Json(response)) } -#[openapi(tag = "Ecash")] -#[get("/issued-credential/")] -pub async fn issued_credential( +#[derive(Deserialize, IntoParams)] +#[into_params(parameter_in = Path)] +struct IdParam { id: i64, - state: &RocketState, -) -> crate::ecash::error::Result> { +} + +#[utoipa::path( + tag = "Ecash", + get, + params( + IdParam + ), + path = "/v1/ecash/issued-credential/{id}", + responses( + (status = 200, body = IssuedCredentialResponse) + ) +)] +async fn issued_credential( + Path(IdParam { id }): Path, + state: Arc, +) -> AxumResult> { let issued = state.aux.storage.get_issued_credential(id).await?; let credential = if let Some(issued) = issued { @@ -52,16 +110,21 @@ pub async fn issued_credential( Ok(Json(IssuedCredentialResponse { credential })) } -#[openapi(tag = "Ecash")] -#[post("/issued-credentials", data = "")] -pub async fn issued_credentials( - params: Json, - state: &RocketState, -) -> crate::ecash::error::Result> { - let params = params.into_inner(); - +#[utoipa::path( + tag = "Ecash", + post, + request_body = CredentialsRequestBody, + path = "/v1/ecash/issued-credentials", + responses( + (status = 200, body = IssuedCredentialsResponse) + ) +)] +async fn issued_credentials( + Json(params): Json, + state: Arc, +) -> AxumResult> { if params.pagination.is_some() && !params.credential_ids.is_empty() { - return Err(EcashError::InvalidQueryArguments); + return Err(EcashError::InvalidQueryArguments.into()); } let credentials = if let Some(pagination) = params.pagination { @@ -78,5 +141,7 @@ pub async fn issued_credentials( .await? }; - build_credentials_response(credentials).map(Json) + build_credentials_response(credentials) + .map(Json) + .map_err(From::from) } diff --git a/nym-api/src/ecash/api_routes/issued_axum.rs b/nym-api/src/ecash/api_routes/issued_axum.rs deleted file mode 100644 index 95c1439600..0000000000 --- a/nym-api/src/ecash/api_routes/issued_axum.rs +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2024 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use crate::ecash::api_routes::helpers::build_credentials_response; -use crate::ecash::error::EcashError; -use crate::ecash::state::EcashState; -use crate::ecash::storage::EcashStorageExt; -use crate::node_status_api::models::AxumResult; -use crate::support::http::state::AppState; -use axum::extract::Path; -use axum::{Json, Router}; -use nym_api_requests::ecash::models::{ - EpochCredentialsResponse, IssuedCredentialResponse, IssuedCredentialsResponse, -}; -use nym_api_requests::ecash::CredentialsRequestBody; -use serde::Deserialize; -use std::sync::Arc; -use utoipa::IntoParams; - -pub(crate) fn issued_routes(ecash_state: Arc) -> Router { - Router::new() - .route( - "/epoch-credentials/:epoch", - axum::routing::get({ - let ecash_state = Arc::clone(&ecash_state); - |epoch| epoch_credentials(epoch, ecash_state) - }), - ) - .route( - "/issued-credential/:id", - axum::routing::get({ - let ecash_state = Arc::clone(&ecash_state); - |id| issued_credential(id, ecash_state) - }), - ) - .route( - "/issued-credentials", - axum::routing::post({ - let ecash_state = Arc::clone(&ecash_state); - |body| issued_credentials(body, ecash_state) - }), - ) -} - -#[derive(Deserialize, IntoParams)] -#[into_params(parameter_in = Path)] -struct EpochParam { - epoch: u64, -} - -#[utoipa::path( - tag = "Ecash", - get, - params( - EpochParam - ), - path = "/v1/ecash/epoch-credentials/{epoch}", - responses( - (status = 200, body = EpochCredentialsResponse) - ) -)] -async fn epoch_credentials( - Path(EpochParam { epoch }): Path, - state: Arc, -) -> AxumResult> { - let issued = state.aux.storage.get_epoch_credentials(epoch).await?; - - let response = if let Some(issued) = issued { - issued.into() - } else { - EpochCredentialsResponse { - epoch_id: epoch, - first_epoch_credential_id: None, - total_issued: 0, - } - }; - - Ok(Json(response)) -} - -#[derive(Deserialize, IntoParams)] -#[into_params(parameter_in = Path)] -struct IdParam { - id: i64, -} - -#[utoipa::path( - tag = "Ecash", - get, - params( - IdParam - ), - path = "/v1/ecash/issued-credential/{id}", - responses( - (status = 200, body = IssuedCredentialResponse) - ) -)] -async fn issued_credential( - Path(IdParam { id }): Path, - state: Arc, -) -> AxumResult> { - let issued = state.aux.storage.get_issued_credential(id).await?; - - let credential = if let Some(issued) = issued { - Some(issued.try_into()?) - } else { - None - }; - - Ok(Json(IssuedCredentialResponse { credential })) -} - -#[utoipa::path( - tag = "Ecash", - post, - request_body = CredentialsRequestBody, - path = "/v1/ecash/issued-credentials", - responses( - (status = 200, body = IssuedCredentialsResponse) - ) -)] -async fn issued_credentials( - Json(params): Json, - state: Arc, -) -> AxumResult> { - if params.pagination.is_some() && !params.credential_ids.is_empty() { - return Err(EcashError::InvalidQueryArguments.into()); - } - - let credentials = if let Some(pagination) = params.pagination { - state - .aux - .storage - .get_issued_credentials_paged(pagination) - .await? - } else { - state - .aux - .storage - .get_issued_credentials(params.credential_ids) - .await? - }; - - build_credentials_response(credentials) - .map(Json) - .map_err(From::from) -} diff --git a/nym-api/src/ecash/api_routes/mod.rs b/nym-api/src/ecash/api_routes/mod.rs index 9c0082748c..6e1db9ffca 100644 --- a/nym-api/src/ecash/api_routes/mod.rs +++ b/nym-api/src/ecash/api_routes/mod.rs @@ -7,8 +7,8 @@ mod helpers; // pub(crate) mod partial_signing; // pub(crate) mod spending; -pub(crate) mod aggregation_axum; +pub(crate) mod aggregation; pub(crate) mod handlers; -pub(crate) mod issued_axum; -pub(crate) mod partial_signing_axum; -pub(crate) mod spending_axum; +pub(crate) mod issued; +pub(crate) mod partial_signing; +pub(crate) mod spending; diff --git a/nym-api/src/ecash/api_routes/partial_signing.rs b/nym-api/src/ecash/api_routes/partial_signing.rs index a0958a2a6b..e42ffcf6e0 100644 --- a/nym-api/src/ecash/api_routes/partial_signing.rs +++ b/nym-api/src/ecash/api_routes/partial_signing.rs @@ -1,27 +1,65 @@ // Copyright 2024 - Nym Technologies SA // SPDX-License-Identifier: GPL-3.0-only +use crate::ecash::api_routes::helpers::EpochIdParam; use crate::ecash::error::EcashError; use crate::ecash::helpers::blind_sign; use crate::ecash::state::EcashState; +use crate::node_status_api::models::AxumResult; +use crate::support::http::state::AppState; +use axum::extract::Path; +use axum::{Json, Router}; use nym_api_requests::ecash::{ BlindSignRequestBody, BlindedSignatureResponse, PartialCoinIndicesSignatureResponse, PartialExpirationDateSignatureResponse, }; use nym_ecash_time::{cred_exp_date, EcashTime}; use nym_validator_client::nym_api::rfc_3339_date; -use rocket::serde::json::Json; -use rocket::State as RocketState; -use rocket_okapi::openapi; +use serde::Deserialize; use std::ops::Deref; +use std::sync::Arc; use time::Date; +use tracing::{debug, trace}; +use utoipa::IntoParams; -#[openapi(tag = "Ecash")] -#[post("/blind-sign", data = "")] -pub async fn post_blind_sign( - blind_sign_request_body: Json, - state: &RocketState, -) -> crate::ecash::error::Result> { +pub(crate) fn partial_signing_routes(ecash_state: Arc) -> Router { + Router::new() + .route( + "/blind-sign", + axum::routing::post({ + let ecash_state = Arc::clone(&ecash_state); + |body| post_blind_sign(body, ecash_state) + }), + ) + .route( + "/partial-expiration-date-signatures:expiration_date", + axum::routing::get({ + let ecash_state = Arc::clone(&ecash_state); + |expiration_date| partial_expiration_date_signatures(expiration_date, ecash_state) + }), + ) + .route( + "/partial-coin-indices-signatures:epoch_id", + axum::routing::get({ + let ecash_state = Arc::clone(&ecash_state); + |epoch_id| partial_coin_indices_signatures(epoch_id, ecash_state) + }), + ) +} + +#[utoipa::path( + tag = "Ecash", + post, + request_body = BlindSignRequestBody, + path = "/v1/ecash/blind-sign", + responses( + (status = 200, body = BlindedSignatureResponse) + ) +)] +async fn post_blind_sign( + Json(blind_sign_request_body): Json, + state: Arc, +) -> AxumResult> { debug!("Received blind sign request"); trace!("body: {:?}", blind_sign_request_body); @@ -31,7 +69,7 @@ pub async fn post_blind_sign( // basic check of expiration date validity if blind_sign_request_body.expiration_date > cred_exp_date().ecash_date() { - return Err(EcashError::ExpirationDateTooLate); + return Err(EcashError::ExpirationDateTooLate.into()); } // see if we're not in the middle of new dkg @@ -62,24 +100,38 @@ pub async fn post_blind_sign( // produce the partial signature debug!("producing the partial credential"); - let blinded_signature = blind_sign(blind_sign_request_body.deref(), signing_key.deref())?; + let blinded_signature = blind_sign(&blind_sign_request_body, signing_key.deref())?; // store the information locally debug!("storing the issued credential in the database"); state - .store_issued_credential(blind_sign_request_body.into_inner(), &blinded_signature) + .store_issued_credential(blind_sign_request_body, &blinded_signature) .await?; // finally return the credential to the client Ok(Json(BlindedSignatureResponse { blinded_signature })) } -#[openapi(tag = "Ecash")] -#[get("/partial-expiration-date-signatures?")] -pub async fn partial_expiration_date_signatures( +#[derive(Deserialize, IntoParams)] +struct ExpirationDateParam { expiration_date: Option, - state: &RocketState, -) -> crate::ecash::error::Result> { +} + +#[utoipa::path( + tag = "Ecash", + get, + params( + ExpirationDateParam + ), + path = "/v1/ecash/partial-expiration-date-signatures/{expiration_date}", + responses( + (status = 200, body = PartialExpirationDateSignatureResponse) + ) +)] +async fn partial_expiration_date_signatures( + Path(ExpirationDateParam { expiration_date }): Path, + state: Arc, +) -> AxumResult> { let expiration_date = match expiration_date { None => cred_exp_date().ecash_date(), Some(raw) => Date::parse(&raw, &rfc_3339_date()) @@ -100,12 +152,21 @@ pub async fn partial_expiration_date_signatures( })) } -#[openapi(tag = "Ecash")] -#[get("/partial-coin-indices-signatures?")] -pub async fn partial_coin_indices_signatures( - epoch_id: Option, - state: &RocketState, -) -> crate::ecash::error::Result> { +#[utoipa::path( + tag = "Ecash", + get, + params( + EpochIdParam + ), + path = "/v1/ecash/partial-coin-indices-signatures/{epoch_id}", + responses( + (status = 200, body = PartialExpirationDateSignatureResponse) + ) +)] +async fn partial_coin_indices_signatures( + Path(EpochIdParam { epoch_id }): Path, + state: Arc, +) -> AxumResult> { // see if we're not in the middle of new dkg state.ensure_dkg_not_in_progress().await?; diff --git a/nym-api/src/ecash/api_routes/partial_signing_axum.rs b/nym-api/src/ecash/api_routes/partial_signing_axum.rs deleted file mode 100644 index e42ffcf6e0..0000000000 --- a/nym-api/src/ecash/api_routes/partial_signing_axum.rs +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2024 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use crate::ecash::api_routes::helpers::EpochIdParam; -use crate::ecash::error::EcashError; -use crate::ecash::helpers::blind_sign; -use crate::ecash::state::EcashState; -use crate::node_status_api::models::AxumResult; -use crate::support::http::state::AppState; -use axum::extract::Path; -use axum::{Json, Router}; -use nym_api_requests::ecash::{ - BlindSignRequestBody, BlindedSignatureResponse, PartialCoinIndicesSignatureResponse, - PartialExpirationDateSignatureResponse, -}; -use nym_ecash_time::{cred_exp_date, EcashTime}; -use nym_validator_client::nym_api::rfc_3339_date; -use serde::Deserialize; -use std::ops::Deref; -use std::sync::Arc; -use time::Date; -use tracing::{debug, trace}; -use utoipa::IntoParams; - -pub(crate) fn partial_signing_routes(ecash_state: Arc) -> Router { - Router::new() - .route( - "/blind-sign", - axum::routing::post({ - let ecash_state = Arc::clone(&ecash_state); - |body| post_blind_sign(body, ecash_state) - }), - ) - .route( - "/partial-expiration-date-signatures:expiration_date", - axum::routing::get({ - let ecash_state = Arc::clone(&ecash_state); - |expiration_date| partial_expiration_date_signatures(expiration_date, ecash_state) - }), - ) - .route( - "/partial-coin-indices-signatures:epoch_id", - axum::routing::get({ - let ecash_state = Arc::clone(&ecash_state); - |epoch_id| partial_coin_indices_signatures(epoch_id, ecash_state) - }), - ) -} - -#[utoipa::path( - tag = "Ecash", - post, - request_body = BlindSignRequestBody, - path = "/v1/ecash/blind-sign", - responses( - (status = 200, body = BlindedSignatureResponse) - ) -)] -async fn post_blind_sign( - Json(blind_sign_request_body): Json, - state: Arc, -) -> AxumResult> { - debug!("Received blind sign request"); - trace!("body: {:?}", blind_sign_request_body); - - // check if we have the signing key available - debug!("checking if we actually have ecash keys derived..."); - let signing_key = state.ecash_signing_key().await?; - - // basic check of expiration date validity - if blind_sign_request_body.expiration_date > cred_exp_date().ecash_date() { - return Err(EcashError::ExpirationDateTooLate.into()); - } - - // see if we're not in the middle of new dkg - state.ensure_dkg_not_in_progress().await?; - - // check if we already issued a credential for this deposit - let deposit_id = blind_sign_request_body.deposit_id; - debug!( - "checking if we have already issued credential for this deposit (deposit_id: {deposit_id})", - ); - if let Some(blinded_signature) = state.already_issued(deposit_id).await? { - return Ok(Json(BlindedSignatureResponse { blinded_signature })); - } - - //check if account was blacklisted - let pub_key_bs58 = blind_sign_request_body.ecash_pubkey.to_base58_string(); - state.aux.ensure_not_blacklisted(&pub_key_bs58).await?; - - // get the deposit details of the claimed id - debug!("getting deposit details from the chain"); - let deposit = state.get_deposit(deposit_id).await?; - - // check validity of the request - debug!("fully validating received request"); - state - .validate_request(&blind_sign_request_body, deposit) - .await?; - - // produce the partial signature - debug!("producing the partial credential"); - let blinded_signature = blind_sign(&blind_sign_request_body, signing_key.deref())?; - - // store the information locally - debug!("storing the issued credential in the database"); - state - .store_issued_credential(blind_sign_request_body, &blinded_signature) - .await?; - - // finally return the credential to the client - Ok(Json(BlindedSignatureResponse { blinded_signature })) -} - -#[derive(Deserialize, IntoParams)] -struct ExpirationDateParam { - expiration_date: Option, -} - -#[utoipa::path( - tag = "Ecash", - get, - params( - ExpirationDateParam - ), - path = "/v1/ecash/partial-expiration-date-signatures/{expiration_date}", - responses( - (status = 200, body = PartialExpirationDateSignatureResponse) - ) -)] -async fn partial_expiration_date_signatures( - Path(ExpirationDateParam { expiration_date }): Path, - state: Arc, -) -> AxumResult> { - let expiration_date = match expiration_date { - None => cred_exp_date().ecash_date(), - Some(raw) => Date::parse(&raw, &rfc_3339_date()) - .map_err(|_| EcashError::MalformedExpirationDate { raw })?, - }; - - // see if we're not in the middle of new dkg - state.ensure_dkg_not_in_progress().await?; - - let expiration_date_signatures = state - .partial_expiration_date_signatures(expiration_date) - .await?; - - Ok(Json(PartialExpirationDateSignatureResponse { - epoch_id: expiration_date_signatures.epoch_id, - expiration_date, - signatures: expiration_date_signatures.signatures.clone(), - })) -} - -#[utoipa::path( - tag = "Ecash", - get, - params( - EpochIdParam - ), - path = "/v1/ecash/partial-coin-indices-signatures/{epoch_id}", - responses( - (status = 200, body = PartialExpirationDateSignatureResponse) - ) -)] -async fn partial_coin_indices_signatures( - Path(EpochIdParam { epoch_id }): Path, - state: Arc, -) -> AxumResult> { - // see if we're not in the middle of new dkg - state.ensure_dkg_not_in_progress().await?; - - let coin_indices_signatures = state.partial_coin_index_signatures(epoch_id).await?; - - Ok(Json(PartialCoinIndicesSignatureResponse { - epoch_id: coin_indices_signatures.epoch_id, - signatures: coin_indices_signatures.signatures.clone(), - })) -} diff --git a/nym-api/src/ecash/api_routes/spending.rs b/nym-api/src/ecash/api_routes/spending.rs index 1ea24efde6..a5110a52fc 100644 --- a/nym-api/src/ecash/api_routes/spending.rs +++ b/nym-api/src/ecash/api_routes/spending.rs @@ -3,6 +3,9 @@ use crate::ecash::error::EcashError; use crate::ecash::state::EcashState; +use crate::node_status_api::models::AxumResult; +use crate::support::http::state::AppState; +use axum::{Json, Router}; use nym_api_requests::constants::MIN_BATCH_REDEMPTION_DELAY; use nym_api_requests::ecash::models::{ BatchRedeemTicketsBody, EcashBatchTicketRedemptionResponse, EcashTicketVerificationRejection, @@ -10,31 +13,62 @@ use nym_api_requests::ecash::models::{ }; use nym_compact_ecash::identify::IdentifyResult; use nym_ecash_time::EcashTime; -use rocket::serde::json::Json; -use rocket::State as RocketState; -use rocket_okapi::openapi; use std::collections::HashSet; use std::ops::Deref; +use std::sync::Arc; use time::macros::time; use time::{OffsetDateTime, Time}; +use tracing::{error, warn}; + +pub(crate) fn spending_routes(ecash_state: Arc) -> Router { + Router::new() + .route( + "/verify-ecash-ticket", + axum::routing::post({ + let ecash_state = Arc::clone(&ecash_state); + |body| verify_ticket(body, ecash_state) + }), + ) + .route( + "/batch-redeem-ecash-tickets", + axum::routing::post({ + let ecash_state = Arc::clone(&ecash_state); + |body| batch_redeem_tickets(body, ecash_state) + }), + ) + .route( + "/double-spending-filter-v1", + axum::routing::get({ + let ecash_state = Arc::clone(&ecash_state); + || double_spending_filter_v1(ecash_state) + }), + ) +} const ONE_AM: Time = time!(1:00); fn reject_ticket( reason: EcashTicketVerificationRejection, -) -> crate::ecash::error::Result> { +) -> AxumResult> { Ok(Json(EcashTicketVerificationResponse::reject(reason))) } // TODO: optimise it; for now it's just dummy split of the original `verify_offline_credential` // introduce bloomfilter checks without touching storage first, etc. -#[openapi(tag = "Ecash")] -#[post("/verify-ecash-ticket", data = "")] -pub async fn verify_ticket( +#[utoipa::path( + tag = "Ecash", + post, + request_body = VerifyEcashTicketBody, + path = "/v1/ecash/verify-ecash-ticket", + responses( + (status = 200, body = EcashTicketVerificationResponse) + ) +)] +async fn verify_ticket( // TODO in the future: make it send binary data rather than json - verify_ticket_body: Json, - state: &RocketState, -) -> crate::ecash::error::Result> { + Json(verify_ticket_body): Json, + state: Arc, +) -> AxumResult> { let credential_data = &verify_ticket_body.credential; let gateway_cosmos_addr = &verify_ticket_body.gateway_cosmos_addr; @@ -117,26 +151,30 @@ pub async fn verify_ticket( } // // for particular SN returns what gateway has submitted it and whether it has been verified correctly -// pub async fn credential_status() -> ! { +// async fn credential_status() -> ! { // todo!() // } -#[openapi(tag = "Ecash")] -#[post( - "/batch-redeem-ecash-tickets", - data = "" +#[utoipa::path( + tag = "Ecash", + post, + request_body = BatchRedeemTicketsBody, + path = "/v1/ecash/batch-redeem-ecash-tickets", + responses( + (status = 200, body = EcashBatchTicketRedemptionResponse) + ) )] -pub async fn batch_redeem_tickets( +async fn batch_redeem_tickets( // TODO in the future: make it send binary data rather than json - batch_redeem_credentials_body: Json, - state: &RocketState, -) -> crate::ecash::error::Result> { + Json(batch_redeem_credentials_body): Json, + state: Arc, +) -> AxumResult> { // 1. see if that gateway has even submitted any tickets let Some(provider_info) = state .get_ticket_provider(batch_redeem_credentials_body.gateway_cosmos_addr.as_ref()) .await? else { - return Err(EcashError::NotTicketsProvided); + return Err(EcashError::NotTicketsProvided.into()); }; // 2. check if the gateway is not trying to spam the redemption requests @@ -149,13 +187,14 @@ pub async fn batch_redeem_tickets( return Err(EcashError::TooFrequentRedemption { last_redemption, next_allowed, - }); + } + .into()); } } // 3. verify the request digest if !batch_redeem_credentials_body.verify_digest() { - return Err(EcashError::MismatchedRequestDigest); + return Err(EcashError::MismatchedRequestDigest.into()); } // 4. verify the associated on-chain proposal (whether it's made by correct sender, has valid messages, etc.) @@ -164,9 +203,7 @@ pub async fn batch_redeem_tickets( .await?; let proposal_id = batch_redeem_credentials_body.proposal_id; - let received = batch_redeem_credentials_body - .into_inner() - .included_serial_numbers; + let received = batch_redeem_credentials_body.included_serial_numbers; // 5. check if **every** serial number included in the request has been verified by us // if we have more than requested, tough luck, they're going to lose them @@ -177,7 +214,8 @@ pub async fn batch_redeem_tickets( if !verified_tickets.contains(sn.deref()) { return Err(EcashError::TicketNotVerified { serial_number_bs58: bs58::encode(sn).into_string(), - }); + } + .into()); } } @@ -190,11 +228,17 @@ pub async fn batch_redeem_tickets( // explicitly mark it as v1 in the URL because the response type WILL change; // it will probably be compressed bincode or something -#[openapi(tag = "Ecash")] -#[get("/double-spending-filter-v1")] -pub async fn double_spending_filter_v1( - state: &RocketState, -) -> crate::ecash::error::Result> { +#[utoipa::path( + tag = "Ecash", + get, + path = "/v1/ecash/double-spending-filter-v1", + responses( + (status = 200, body = SpentCredentialsResponse) + ) +)] +async fn double_spending_filter_v1( + state: Arc, +) -> AxumResult> { let spent_credentials_export = state.get_bloomfilter_bytes().await; Ok(Json(SpentCredentialsResponse::new( spent_credentials_export, diff --git a/nym-api/src/ecash/api_routes/spending_axum.rs b/nym-api/src/ecash/api_routes/spending_axum.rs deleted file mode 100644 index a5110a52fc..0000000000 --- a/nym-api/src/ecash/api_routes/spending_axum.rs +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright 2024 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use crate::ecash::error::EcashError; -use crate::ecash::state::EcashState; -use crate::node_status_api::models::AxumResult; -use crate::support::http::state::AppState; -use axum::{Json, Router}; -use nym_api_requests::constants::MIN_BATCH_REDEMPTION_DELAY; -use nym_api_requests::ecash::models::{ - BatchRedeemTicketsBody, EcashBatchTicketRedemptionResponse, EcashTicketVerificationRejection, - EcashTicketVerificationResponse, SpentCredentialsResponse, VerifyEcashTicketBody, -}; -use nym_compact_ecash::identify::IdentifyResult; -use nym_ecash_time::EcashTime; -use std::collections::HashSet; -use std::ops::Deref; -use std::sync::Arc; -use time::macros::time; -use time::{OffsetDateTime, Time}; -use tracing::{error, warn}; - -pub(crate) fn spending_routes(ecash_state: Arc) -> Router { - Router::new() - .route( - "/verify-ecash-ticket", - axum::routing::post({ - let ecash_state = Arc::clone(&ecash_state); - |body| verify_ticket(body, ecash_state) - }), - ) - .route( - "/batch-redeem-ecash-tickets", - axum::routing::post({ - let ecash_state = Arc::clone(&ecash_state); - |body| batch_redeem_tickets(body, ecash_state) - }), - ) - .route( - "/double-spending-filter-v1", - axum::routing::get({ - let ecash_state = Arc::clone(&ecash_state); - || double_spending_filter_v1(ecash_state) - }), - ) -} - -const ONE_AM: Time = time!(1:00); - -fn reject_ticket( - reason: EcashTicketVerificationRejection, -) -> AxumResult> { - Ok(Json(EcashTicketVerificationResponse::reject(reason))) -} - -// TODO: optimise it; for now it's just dummy split of the original `verify_offline_credential` -// introduce bloomfilter checks without touching storage first, etc. -#[utoipa::path( - tag = "Ecash", - post, - request_body = VerifyEcashTicketBody, - path = "/v1/ecash/verify-ecash-ticket", - responses( - (status = 200, body = EcashTicketVerificationResponse) - ) -)] -async fn verify_ticket( - // TODO in the future: make it send binary data rather than json - Json(verify_ticket_body): Json, - state: Arc, -) -> AxumResult> { - let credential_data = &verify_ticket_body.credential; - let gateway_cosmos_addr = &verify_ticket_body.gateway_cosmos_addr; - - // easy check: is there only a single payment attached? - if credential_data.payment.spend_value != 1 { - return reject_ticket(EcashTicketVerificationRejection::MultipleTickets); - } - - let sn = &credential_data.encoded_serial_number(); - let spend_date = credential_data.spend_date; - let epoch_id = credential_data.epoch_id; - - let now = OffsetDateTime::now_utc(); - let today_ecash = now.ecash_date(); - - #[allow(clippy::unwrap_used)] - let yesterday_ecash = today_ecash.previous_day().unwrap(); - - // only accept yesterday date if we're near the day transition, i.e. before 1AM UTC - if spend_date != today_ecash && now.time() > ONE_AM && spend_date != yesterday_ecash { - return reject_ticket(EcashTicketVerificationRejection::InvalidSpentDate { - today: today_ecash, - yesterday: yesterday_ecash, - received: spend_date, - }); - } - - // check the bloomfilter for obvious double-spending so that we wouldn't need to waste time on crypto verification - // TODO: when blacklisting is implemented, this should get removed - if state.check_bloomfilter(sn).await { - return reject_ticket(EcashTicketVerificationRejection::ReplayedTicket); - } - - // actual double spend detection with storage - if let Some(previous_payment) = state.get_ticket_data_by_serial_number(sn).await? { - match nym_compact_ecash::identify::identify( - &credential_data.payment, - &previous_payment.payment, - credential_data.pay_info, - previous_payment.pay_info, - ) { - IdentifyResult::NotADuplicatePayment => {} //SW NOTE This should never happen, quick message? - IdentifyResult::DuplicatePayInfo(_) => { - warn!("Identical payInfo"); - return reject_ticket(EcashTicketVerificationRejection::ReplayedTicket); - } - IdentifyResult::DoubleSpendingPublicKeys(pub_key) => { - //Actual double spending - warn!( - "Double spending attempt for key {}", - pub_key.to_base58_string() - ); - error!("UNIMPLEMENTED: blacklisting the double spend key"); - return reject_ticket(EcashTicketVerificationRejection::DoubleSpend); - } - } - } - - let verification_key = state.master_verification_key(Some(epoch_id)).await?; - - // perform actual crypto verification - if credential_data.verify(&verification_key).is_err() { - return reject_ticket(EcashTicketVerificationRejection::InvalidTicket); - } - - // finally get EXCLUSIVE lock on the bloomfilter, check if for the final time and insert the SN - let was_present = state - .update_bloomfilter(sn, spend_date, today_ecash) - .await?; - if was_present { - return reject_ticket(EcashTicketVerificationRejection::ReplayedTicket); - } - - //store credential - state - .store_verified_ticket(credential_data, gateway_cosmos_addr) - .await?; - - Ok(Json(EcashTicketVerificationResponse { verified: Ok(()) })) -} - -// // for particular SN returns what gateway has submitted it and whether it has been verified correctly -// async fn credential_status() -> ! { -// todo!() -// } - -#[utoipa::path( - tag = "Ecash", - post, - request_body = BatchRedeemTicketsBody, - path = "/v1/ecash/batch-redeem-ecash-tickets", - responses( - (status = 200, body = EcashBatchTicketRedemptionResponse) - ) -)] -async fn batch_redeem_tickets( - // TODO in the future: make it send binary data rather than json - Json(batch_redeem_credentials_body): Json, - state: Arc, -) -> AxumResult> { - // 1. see if that gateway has even submitted any tickets - let Some(provider_info) = state - .get_ticket_provider(batch_redeem_credentials_body.gateway_cosmos_addr.as_ref()) - .await? - else { - return Err(EcashError::NotTicketsProvided.into()); - }; - - // 2. check if the gateway is not trying to spam the redemption requests - // (we have to protect our poor chain) - if let Some(last_redemption) = provider_info.last_batch_verification { - let now = OffsetDateTime::now_utc(); - let next_allowed = last_redemption + MIN_BATCH_REDEMPTION_DELAY; - - if next_allowed > now { - return Err(EcashError::TooFrequentRedemption { - last_redemption, - next_allowed, - } - .into()); - } - } - - // 3. verify the request digest - if !batch_redeem_credentials_body.verify_digest() { - return Err(EcashError::MismatchedRequestDigest.into()); - } - - // 4. verify the associated on-chain proposal (whether it's made by correct sender, has valid messages, etc.) - state - .validate_redemption_proposal(&batch_redeem_credentials_body) - .await?; - - let proposal_id = batch_redeem_credentials_body.proposal_id; - let received = batch_redeem_credentials_body.included_serial_numbers; - - // 5. check if **every** serial number included in the request has been verified by us - // if we have more than requested, tough luck, they're going to lose them - let verified = state.get_redeemable_tickets(provider_info).await?; - let verified_tickets: HashSet<_> = verified.iter().map(|sn| sn.deref()).collect(); - - for sn in &received { - if !verified_tickets.contains(sn.deref()) { - return Err(EcashError::TicketNotVerified { - serial_number_bs58: bs58::encode(sn).into_string(), - } - .into()); - } - } - - // TODO: offload it to separate task with work queue and batching (of tx messages) to vote for multiple proposals in the same tx - state.accept_proposal(proposal_id).await?; - Ok(Json(EcashBatchTicketRedemptionResponse { - proposal_accepted: true, - })) -} - -// explicitly mark it as v1 in the URL because the response type WILL change; -// it will probably be compressed bincode or something -#[utoipa::path( - tag = "Ecash", - get, - path = "/v1/ecash/double-spending-filter-v1", - responses( - (status = 200, body = SpentCredentialsResponse) - ) -)] -async fn double_spending_filter_v1( - state: Arc, -) -> AxumResult> { - let spent_credentials_export = state.get_bloomfilter_bytes().await; - Ok(Json(SpentCredentialsResponse::new( - spent_credentials_export, - ))) -} diff --git a/nym-api/src/ecash/mod.rs b/nym-api/src/ecash/mod.rs index 2855f8dd7a..6a1d04564a 100644 --- a/nym-api/src/ecash/mod.rs +++ b/nym-api/src/ecash/mod.rs @@ -16,27 +16,3 @@ pub(crate) mod tests; // equivalent of 100nym pub(crate) const MINIMUM_BALANCE: u128 = 100_000000; - -// pub(crate) fn routes_open_api(settings: &OpenApiSettings, enabled: bool) -> (Vec, OpenApi) { -// if enabled { -// openapi_get_routes_spec![ -// settings: -// api_routes::partial_signing::post_blind_sign, -// api_routes::partial_signing::partial_expiration_date_signatures, -// api_routes::partial_signing::partial_coin_indices_signatures, -// api_routes::spending::verify_ticket, -// api_routes::spending::batch_redeem_tickets, -// api_routes::spending::double_spending_filter_v1, -// api_routes::issued::epoch_credentials, -// api_routes::issued::issued_credential, -// api_routes::issued::issued_credentials, -// api_routes::aggregation::master_verification_key, -// api_routes::aggregation::coin_indices_signatures, -// api_routes::aggregation::expiration_date_signatures -// ] -// } else { -// openapi_get_routes_spec![ -// settings: -// ] -// } -// } diff --git a/nym-api/src/network/mod.rs b/nym-api/src/network/mod.rs index 8595827dd0..016fc441b5 100644 --- a/nym-api/src/network/mod.rs +++ b/nym-api/src/network/mod.rs @@ -3,12 +3,3 @@ pub(crate) mod handlers; pub(crate) mod models; -// mod routes; -// -// pub(crate) fn network_routes(settings: &OpenApiSettings) -> (Vec, OpenApi) { -// openapi_get_routes_spec![ -// settings: routes::network_details, -// routes::nym_contracts, -// routes::nym_contracts_detailed -// ] -// } diff --git a/nym-api/src/network/routes.rs b/nym-api/src/network/routes.rs deleted file mode 100644 index 73625f8b88..0000000000 --- a/nym-api/src/network/routes.rs +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2023 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use crate::network::models::{ContractInformation, NetworkDetails}; -use crate::nym_contract_cache::cache::NymContractCache; -use nym_contracts_common::ContractBuildInformation; -use rocket::serde::json::Json; -use rocket::State; -use rocket_okapi::openapi; -use std::collections::HashMap; -use std::ops::Deref; - -#[openapi(tag = "network")] -#[get("/details")] -pub(crate) fn network_details(details: &State) -> Json { - Json(details.deref().clone()) -} - -// I agree, it feels weird to be pulling contract cache here, but I feel like it makes -// more sense to return this information here rather than in the generic cache route -#[openapi(tag = "network")] -#[get("/nym-contracts")] -pub(crate) async fn nym_contracts( - cache: &State, -) -> Json>> { - let info = cache.contract_details().await; - Json( - info.iter() - .map(|(contract, info)| { - ( - contract.to_owned(), - ContractInformation { - address: info.address.as_ref().map(|a| a.to_string()), - details: info.base.clone(), - }, - ) - }) - .collect(), - ) -} - -#[openapi(tag = "network")] -#[get("/nym-contracts-detailed")] -pub(crate) async fn nym_contracts_detailed( - cache: &State, -) -> Json>> { - let info = cache.contract_details().await; - Json( - info.iter() - .map(|(contract, info)| { - ( - contract.to_owned(), - ContractInformation { - address: info.address.as_ref().map(|a| a.to_string()), - details: info.detailed.clone(), - }, - ) - }) - .collect(), - ) -} diff --git a/nym-api/src/node_status_api/mod.rs b/nym-api/src/node_status_api/mod.rs index 3f7ed14b16..942a476467 100644 --- a/nym-api/src/node_status_api/mod.rs +++ b/nym-api/src/node_status_api/mod.rs @@ -16,7 +16,6 @@ pub(crate) mod handlers; pub(crate) mod helpers; pub(crate) mod models; pub(crate) mod reward_estimate; -// pub(crate) mod routes_deprecated; pub(crate) mod uptime_updater; pub(crate) mod utils; @@ -24,52 +23,6 @@ pub(crate) const FIFTEEN_MINUTES: Duration = Duration::from_secs(900); pub(crate) const ONE_HOUR: Duration = Duration::from_secs(3600); pub(crate) const ONE_DAY: Duration = Duration::from_secs(86400); -// pub(crate) fn node_status_routes( -// settings: &OpenApiSettings, -// enabled: bool, -// ) -> (Vec, OpenApi) { -// if enabled { -// openapi_get_routes_spec![ -// settings: routes_deprecated::gateway_report, -// routes_deprecated::gateway_uptime_history, -// routes_deprecated::gateway_core_status_count, -// routes_deprecated::mixnode_report, -// routes_deprecated::mixnode_uptime_history, -// routes_deprecated::mixnode_core_status_count, -// routes_deprecated::get_mixnode_status, -// routes_deprecated::get_mixnode_reward_estimation, -// routes_deprecated::compute_mixnode_reward_estimation, -// routes_deprecated::get_mixnode_stake_saturation, -// routes_deprecated::get_mixnode_inclusion_probability, -// routes_deprecated::get_mixnode_avg_uptime, -// routes_deprecated::get_gateway_avg_uptime, -// routes_deprecated::get_mixnode_inclusion_probabilities, -// routes_deprecated::get_mixnodes_detailed, -// routes_deprecated::get_mixnodes_detailed_unfiltered, -// routes_deprecated::get_rewarded_set_detailed, -// routes_deprecated::get_active_set_detailed, -// routes_deprecated::get_gateways_detailed, -// routes_deprecated::get_gateways_detailed_unfiltered, -// routes_deprecated::unstable::mixnode_test_results, -// routes_deprecated::unstable::gateway_test_results, -// routes_deprecated::submit_gateway_monitoring_results, -// routes_deprecated::submit_node_monitoring_results, -// ] -// } else { -// // in the minimal variant we would not have access to endpoints relying on existence -// // of the network monitor and the associated storage -// openapi_get_routes_spec![ -// settings: routes_deprecated::get_mixnode_status, -// routes_deprecated::get_mixnode_stake_saturation, -// routes_deprecated::get_mixnode_inclusion_probability, -// routes_deprecated::get_mixnode_inclusion_probabilities, -// routes_deprecated::get_mixnodes_detailed, -// routes_deprecated::get_rewarded_set_detailed, -// routes_deprecated::get_active_set_detailed, -// ] -// } -// } - /// Spawn the node status cache refresher. /// /// It is primarily refreshed in-sync with the nym contract cache, however provide a fallback diff --git a/nym-api/src/node_status_api/routes_deprecated.rs b/nym-api/src/node_status_api/routes_deprecated.rs deleted file mode 100644 index fa49ad692a..0000000000 --- a/nym-api/src/node_status_api/routes_deprecated.rs +++ /dev/null @@ -1,615 +0,0 @@ -// Copyright 2021-2024 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use super::NodeStatusCache; -use crate::node_status_api::helpers_deprecated::{ - _compute_mixnode_reward_estimation, _gateway_core_status_count, _gateway_report, - _gateway_uptime_history, _get_gateway_avg_uptime, _get_mixnode_avg_uptime, - _get_mixnode_inclusion_probabilities, _get_mixnode_inclusion_probability, - _get_mixnode_reward_estimation, _get_mixnode_stake_saturation, _get_mixnode_status, - _get_mixnodes_detailed_unfiltered, _mixnode_core_status_count, _mixnode_report, - _mixnode_uptime_history, -}; -use crate::node_status_api::models::RocketErrorResponse; -use crate::storage::NymApiStorage; -use crate::NymContractCache; -use nym_api_requests::models::{ - AllInclusionProbabilitiesResponse, ComputeRewardEstParam, GatewayBondAnnotated, - GatewayCoreStatusResponse, GatewayStatusReportResponse, GatewayUptimeHistoryResponse, - GatewayUptimeResponse, InclusionProbabilityResponse, MixNodeBondAnnotated, - MixnodeCoreStatusResponse, MixnodeStatusReportResponse, MixnodeStatusResponse, - MixnodeUptimeHistoryResponse, RewardEstimationResponse, StakeSaturationResponse, - UptimeResponse, -}; -use nym_mixnet_contract_common::NodeId; -use nym_types::monitoring::MonitorMessage; -use rocket::serde::json::Json; -use rocket::State; -use rocket_okapi::openapi; - -#[openapi(tag = "status", deprecated = true)] -#[post("/submit-gateway-monitoring-results", data = "")] -pub(crate) async fn submit_gateway_monitoring_results( - message: Json, - storage: &State, -) -> Result<(), RocketErrorResponse> { - todo!("rebasing"); - // if !message.from_allowed() { - // return Err(RocketErrorResponse::new( - // "Monitor not registered to submit results".to_string(), - // rocket::http::Status::Forbidden, - // )); - // } - // - // if !message.timely() { - // return Err(RocketErrorResponse::new( - // "Message is too old".to_string(), - // rocket::http::Status::BadRequest, - // )); - // } - // - // if !message.verify() { - // return Err(RocketErrorResponse::new( - // "Invalid signature".to_string(), - // rocket::http::Status::BadRequest, - // )); - // } - // - // match storage - // .manager - // .submit_gateway_statuses_v2(message.results()) - // .await - // { - // Ok(_) => Ok(()), - // Err(err) => { - // error!("failed to submit gateway monitoring results: {}", err); - // Err(RocketErrorResponse::new( - // "failed to submit gateway monitoring results".to_string(), - // rocket::http::Status::InternalServerError, - // )) - // } - // } -} - -#[openapi(tag = "status")] -#[post("/submit-node-monitoring-results", data = "")] -pub(crate) async fn submit_node_monitoring_results( - message: Json, - storage: &State, -) -> Result<(), RocketErrorResponse> { - todo!("rebasing"); - - // if !message.from_allowed() { - // return Err(RocketErrorResponse::new( - // "Monitor not registered to submit results".to_string(), - // rocket::http::Status::Forbidden, - // )); - // } - // - // if !message.timely() { - // return Err(RocketErrorResponse::new( - // "Message is too old".to_string(), - // rocket::http::Status::BadRequest, - // )); - // } - // - // if !message.verify() { - // return Err(RocketErrorResponse::new( - // "Invalid signature".to_string(), - // rocket::http::Status::BadRequest, - // )); - // } - // - // match storage - // .manager - // .submit_mixnode_statuses_v2(message.results()) - // .await - // { - // Ok(_) => Ok(()), - // Err(err) => { - // error!("failed to submit node monitoring results: {}", err); - // Err(RocketErrorResponse::new( - // "failed to submit node monitoring results".to_string(), - // rocket::http::Status::InternalServerError, - // )) - // } - // } -} - -#[openapi(tag = "status", deprecated = true)] -#[get("/gateway//report")] -pub(crate) async fn gateway_report( - cache: &State, - identity: &str, -) -> Result, RocketErrorResponse> { - todo!("rebasing"); - - // Ok(Json(_gateway_report(cache, identity).await?)) -} - -#[openapi(tag = "status", deprecated = true)] -#[get("/gateway//history")] -pub(crate) async fn gateway_uptime_history( - storage: &State, - nym_contract_cache: &State, - identity: &str, -) -> Result, RocketErrorResponse> { - todo!("rebasing"); - - // Ok(Json( - // _gateway_uptime_history(storage, nym_contract_cache, identity).await?, - // )) -} - -#[openapi(tag = "status", deprecated = true)] -#[get("/gateway//core-status-count?")] -pub(crate) async fn gateway_core_status_count( - storage: &State, - identity: &str, - since: Option, -) -> Result, RocketErrorResponse> { - Ok(Json( - _gateway_core_status_count(storage, identity, since).await?, - )) -} - -#[openapi(tag = "status", deprecated = true)] -#[get("/mixnode//report")] -pub(crate) async fn mixnode_report( - cache: &State, - mix_id: NodeId, -) -> Result, RocketErrorResponse> { - Ok(Json(_mixnode_report(cache, mix_id).await?)) -} - -#[openapi(tag = "status", deprecated = true)] -#[get("/mixnode//history")] -pub(crate) async fn mixnode_uptime_history( - storage: &State, - nym_contract_cache: &State, - mix_id: NodeId, -) -> Result, RocketErrorResponse> { - todo!("rebasing"); - - // Ok(Json( - // _mixnode_uptime_history(storage, nym_contract_cache, mix_id).await?, - // )) -} - -#[openapi(tag = "status", deprecated = true)] -#[get("/mixnode//core-status-count?")] -pub(crate) async fn mixnode_core_status_count( - storage: &State, - mix_id: NodeId, - since: Option, -) -> Result, RocketErrorResponse> { - Ok(Json( - _mixnode_core_status_count(storage, mix_id, since).await?, - )) -} - -#[openapi(tag = "status", deprecated = true)] -#[get("/mixnode//status")] -pub(crate) async fn get_mixnode_status( - cache: &State, - mix_id: NodeId, -) -> Json { - Json(_get_mixnode_status(cache, mix_id).await) -} - -#[openapi(tag = "status", deprecated = true)] -#[get("/mixnode//reward-estimation")] -pub(crate) async fn get_mixnode_reward_estimation( - cache: &State, - validator_cache: &State, - mix_id: NodeId, -) -> Result, RocketErrorResponse> { - Ok(Json( - _get_mixnode_reward_estimation(cache, validator_cache, mix_id).await?, - )) -} - -#[openapi(tag = "status", deprecated = true)] -#[post( - "/mixnode//compute-reward-estimation", - data = "" -)] -pub(crate) async fn compute_mixnode_reward_estimation( - user_reward_param: Json, - cache: &State, - validator_cache: &State, - mix_id: NodeId, -) -> Result, RocketErrorResponse> { - Ok(Json( - _compute_mixnode_reward_estimation( - user_reward_param.into_inner(), - cache, - validator_cache, - mix_id, - ) - .await?, - )) -} - -#[openapi(tag = "status", deprecated = true)] -#[get("/mixnode//stake-saturation")] -pub(crate) async fn get_mixnode_stake_saturation( - cache: &State, - validator_cache: &State, - mix_id: NodeId, -) -> Result, RocketErrorResponse> { - Ok(Json( - _get_mixnode_stake_saturation(cache, validator_cache, mix_id).await?, - )) -} - -#[openapi(tag = "status", deprecated = true)] -#[get("/mixnode//inclusion-probability")] -pub(crate) async fn get_mixnode_inclusion_probability( - cache: &State, - mix_id: NodeId, -) -> Result, RocketErrorResponse> { - Ok(Json( - _get_mixnode_inclusion_probability(cache, mix_id).await?, - )) -} - -#[openapi(tag = "status", deprecated = true)] -#[get("/mixnode//avg_uptime")] -pub(crate) async fn get_mixnode_avg_uptime( - cache: &State, - mix_id: NodeId, -) -> Result, RocketErrorResponse> { - Ok(Json(_get_mixnode_avg_uptime(cache, mix_id).await?)) -} - -#[openapi(tag = "status", deprecated = true)] -#[get("/gateway//avg_uptime")] -pub(crate) async fn get_gateway_avg_uptime( - cache: &State, - identity: &str, -) -> Result, RocketErrorResponse> { - Ok(Json(_get_gateway_avg_uptime(cache, identity).await?)) -} - -#[openapi(tag = "status", deprecated = true)] -#[get("/mixnodes/inclusion_probability")] -pub(crate) async fn get_mixnode_inclusion_probabilities( - cache: &State, -) -> Result, RocketErrorResponse> { - Ok(Json(_get_mixnode_inclusion_probabilities(cache).await?)) -} - -#[openapi(tag = "status", deprecated = true)] -#[get("/mixnodes/detailed")] -pub async fn get_mixnodes_detailed( - cache: &State, -) -> Json> { - todo!("rebasing"); - - // Json(_get_legacy_mixnodes_detailed(cache).await) -} - -#[openapi(tag = "status", deprecated = true)] -#[get("/mixnodes/detailed-unfiltered")] -pub async fn get_mixnodes_detailed_unfiltered( - cache: &State, -) -> Json> { - Json(_get_mixnodes_detailed_unfiltered(cache).await) -} - -#[openapi(tag = "status", deprecated = true)] -#[get("/mixnodes/rewarded/detailed")] -pub async fn get_rewarded_set_detailed( - status_cache: &State, - contract_cache: &State, -) -> Json> { - todo!("rebasing"); - - // Json(_get_rewarded_set_legacy_mixnodes_detailed(status_cache, contract_cache).await) -} - -#[openapi(tag = "status", deprecated = true)] -#[get("/mixnodes/active/detailed")] -pub async fn get_active_set_detailed( - status_cache: &State, - contract_cache: &State, -) -> Json> { - todo!("rebasing"); - - // Json(_get_active_set_legacy_mixnodes_detailed(status_cache, contract_cache).await) -} - -#[openapi(tag = "status", deprecated = true)] -#[get("/gateways/detailed")] -pub async fn get_gateways_detailed( - cache: &State, -) -> Json> { - todo!("rebasing"); - - // Json(_get_legacy_gateways_detailed(cache).await) -} - -#[openapi(tag = "status", deprecated = true)] -#[get("/gateways/detailed-unfiltered")] -pub async fn get_gateways_detailed_unfiltered( - cache: &State, -) -> Json> { - todo!("rebasing"); - // Json(_get_legacy_gateways_detailed_unfiltered(cache).await) -} - -pub mod unstable { - use crate::node_status_api::models::RocketErrorResponse; - use crate::support::http::helpers::PaginationRequest; - use crate::support::storage::NymApiStorage; - use nym_api_requests::models::{ - GatewayTestResultResponse, MixnodeTestResultResponse, PartialTestResult, TestNode, - TestRoute, - }; - use nym_api_requests::pagination::Pagination; - use nym_mixnet_contract_common::NodeId; - use rocket::http::Status; - use rocket::serde::json::Json; - use rocket::State; - use rocket_okapi::openapi; - use std::cmp::min; - use std::collections::HashMap; - use std::sync::Arc; - use tokio::sync::RwLock; - - pub type DbId = i64; - - // a simply in-memory cache of node details - #[derive(Debug, Default)] - pub struct NodeInfoCache { - inner: Arc>, - } - - impl NodeInfoCache { - async fn get_mix_node_details(&self, db_id: DbId, storage: &NymApiStorage) -> TestNode { - { - let read_guard = self.inner.read().await; - if let Some(cached) = read_guard.mixnodes.get(&db_id) { - trace!("cache hit for mixnode {db_id}"); - return cached.clone(); - } - } - trace!("cache miss for mixnode {db_id}"); - - let mut write_guard = self.inner.write().await; - // double-check the cache in case somebody already updated it while we were waiting for the lock - if let Some(cached) = write_guard.mixnodes.get(&db_id) { - return cached.clone(); - } - - let details = match storage.get_mixnode_details_by_db_id(db_id).await { - Ok(Some(details)) => details.into(), - Ok(None) => { - error!("somebody has been messing with the database! details for mixnode with database id {db_id} have been removed!"); - TestNode::default() - } - Err(err) => { - // don't insert into the cache in case another request is successful - error!("failed to retrieve details for mixnode {db_id}: {err}"); - return TestNode::default(); - } - }; - - write_guard.mixnodes.insert(db_id, details.clone()); - details - } - - async fn get_gateway_details(&self, db_id: DbId, storage: &NymApiStorage) -> TestNode { - { - let read_guard = self.inner.read().await; - if let Some(cached) = read_guard.gateways.get(&db_id) { - trace!("cache hit for gateway {db_id}"); - return cached.clone(); - } - } - trace!("cache miss for gateway {db_id}"); - - let mut write_guard = self.inner.write().await; - // double-check the cache in case somebody already updated it while we were waiting for the lock - if let Some(cached) = write_guard.gateways.get(&db_id) { - return cached.clone(); - } - - let details = match storage.get_gateway_details_by_db_id(db_id).await { - Ok(Some(details)) => details.into(), - Ok(None) => { - error!("somebody has been messing with the database! details for gateway with database id {db_id} have been removed!"); - TestNode::default() - } - Err(err) => { - // don't insert into the cache in case another request is successful - error!("failed to retrieve details for gateway {db_id}: {err}"); - return TestNode::default(); - } - }; - - write_guard.gateways.insert(db_id, details.clone()); - details - } - } - - #[derive(Debug, Default)] - struct NodeInfoCacheInner { - mixnodes: HashMap, - gateways: HashMap, - } - - const MAX_TEST_RESULTS_PAGE_SIZE: u32 = 100; - const DEFAULT_TEST_RESULTS_PAGE_SIZE: u32 = 50; - - async fn _mixnode_test_results( - mix_id: NodeId, - page: u32, - per_page: u32, - info_cache: &State, - storage: &State, - ) -> anyhow::Result { - // convert to db offset - // we're paging from page 0 like civilised people, - // so we have to skip (page * per_page) results - let offset = page * per_page; - let limit = per_page; - - let raw_results = storage - .get_mixnode_detailed_statuses(mix_id, limit, offset) - .await?; - let total = match raw_results.first() { - None => 0, - Some(r) => storage.get_mixnode_detailed_statuses_count(r.db_id).await?, - }; - - let mut partial_results = Vec::new(); - for result in raw_results { - let gateway = info_cache - .get_gateway_details(result.gateway_id, storage) - .await; - let layer1 = info_cache - .get_mix_node_details(result.layer1_mix_id, storage) - .await; - let layer2 = info_cache - .get_mix_node_details(result.layer2_mix_id, storage) - .await; - let layer3 = info_cache - .get_mix_node_details(result.layer3_mix_id, storage) - .await; - - partial_results.push(PartialTestResult { - monitor_run_id: result.monitor_run_id, - timestamp: result.timestamp, - overall_reliability_for_all_routes_in_monitor_run: result.reliability, - test_routes: TestRoute { - gateway, - layer1, - layer2, - layer3, - }, - }) - } - - Ok(MixnodeTestResultResponse { - pagination: Pagination { - total, - page, - size: partial_results.len(), - }, - data: partial_results, - }) - } - - #[openapi(tag = "UNSTABLE - DO **NOT** USE")] - #[get("/mixnodes/unstable//test-results?")] - pub async fn mixnode_test_results( - mix_id: NodeId, - pagination: PaginationRequest, - info_cache: &State, - storage: &State, - ) -> Result, RocketErrorResponse> { - let page = pagination.page.unwrap_or_default(); - let per_page = min( - pagination - .per_page - .unwrap_or(DEFAULT_TEST_RESULTS_PAGE_SIZE), - MAX_TEST_RESULTS_PAGE_SIZE, - ); - - match _mixnode_test_results(mix_id, page, per_page, info_cache, storage).await { - Ok(res) => Ok(Json(res)), - Err(err) => Err(RocketErrorResponse::new( - format!("failed to retrieve mixnode test results for node {mix_id}: {err}"), - Status::InternalServerError, - )), - } - } - - async fn _gateway_test_results( - gateway_identity: &str, - page: u32, - per_page: u32, - info_cache: &State, - storage: &State, - ) -> anyhow::Result { - // convert to db offset - // we're paging from page 0 like civilised people, - // so we have to skip (page * per_page) results - let offset = page * per_page; - let limit = per_page; - - let raw_results = storage - .get_gateway_detailed_statuses(gateway_identity, limit, offset) - .await?; - let total = match raw_results.first() { - None => 0, - Some(r) => storage.get_gateway_detailed_statuses_count(r.db_id).await?, - }; - - let mut partial_results = Vec::new(); - for result in raw_results { - let gateway = info_cache - .get_gateway_details(result.gateway_id, storage) - .await; - let layer1 = info_cache - .get_mix_node_details(result.layer1_mix_id, storage) - .await; - let layer2 = info_cache - .get_mix_node_details(result.layer2_mix_id, storage) - .await; - let layer3 = info_cache - .get_mix_node_details(result.layer3_mix_id, storage) - .await; - - partial_results.push(PartialTestResult { - monitor_run_id: result.monitor_run_id, - timestamp: result.timestamp, - overall_reliability_for_all_routes_in_monitor_run: result.reliability, - test_routes: TestRoute { - gateway, - layer1, - layer2, - layer3, - }, - }) - } - - Ok(GatewayTestResultResponse { - pagination: Pagination { - total, - page, - size: partial_results.len(), - }, - data: partial_results, - }) - } - - #[openapi(tag = "UNSTABLE - DO **NOT** USE")] - #[get("/gateways/unstable//test-results?")] - pub async fn gateway_test_results( - gateway_identity: &str, - pagination: PaginationRequest, - info_cache: &State, - storage: &State, - ) -> Result, RocketErrorResponse> { - let page = pagination.page.unwrap_or_default(); - let per_page = min( - pagination - .per_page - .unwrap_or(DEFAULT_TEST_RESULTS_PAGE_SIZE), - MAX_TEST_RESULTS_PAGE_SIZE, - ); - - match _gateway_test_results(gateway_identity, page, per_page, info_cache, storage).await { - Ok(res) => Ok(Json(res)), - Err(err) => Err(RocketErrorResponse::new( - format!( - "failed to retrieve mixnode test results for gateway {gateway_identity}: {err}" - ), - Status::InternalServerError, - )), - } - } -} diff --git a/nym-api/src/nym_contract_cache/legacy_helpers.rs b/nym-api/src/nym_contract_cache/legacy_helpers.rs deleted file mode 100644 index 939f19b3a9..0000000000 --- a/nym-api/src/nym_contract_cache/legacy_helpers.rs +++ /dev/null @@ -1,2 +0,0 @@ -// Copyright 2024 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only diff --git a/nym-api/src/nym_contract_cache/mod.rs b/nym-api/src/nym_contract_cache/mod.rs index 49f4147a69..6ca509ef1e 100644 --- a/nym-api/src/nym_contract_cache/mod.rs +++ b/nym-api/src/nym_contract_cache/mod.rs @@ -9,25 +9,6 @@ use self::cache::refresher::NymContractCacheRefresher; pub(crate) mod cache; pub(crate) mod handlers; -pub(crate) mod legacy_helpers; -// pub(crate) mod routes; -// -// pub(crate) fn nym_contract_cache_routes(settings: &OpenApiSettings) -> (Vec, OpenApi) { -// openapi_get_routes_spec![ -// settings: routes::get_mixnodes, -// routes::get_mixnodes_detailed, -// routes::get_gateways, -// routes::get_active_set, -// routes::get_active_set_detailed, -// routes::get_rewarded_set, -// routes::get_rewarded_set_detailed, -// routes::get_blacklisted_mixnodes, -// routes::get_blacklisted_gateways, -// routes::get_blacklisted_gateways_v2, -// routes::get_interval_reward_params, -// routes::get_current_epoch, -// ] -// } pub(crate) fn start_refresher( config: &config::NodeStatusAPI, diff --git a/nym-api/src/nym_contract_cache/routes.rs b/nym-api/src/nym_contract_cache/routes.rs deleted file mode 100644 index 52a9dd70e0..0000000000 --- a/nym-api/src/nym_contract_cache/routes.rs +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2021-2023 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use crate::{node_status_api::NodeStatusCache, nym_contract_cache::cache::NymContractCache}; -use nym_api_requests::models::MixNodeBondAnnotated; -use nym_mixnet_contract_common::{reward_params::RewardingParams, GatewayBond, Interval, NodeId}; - -use nym_api_requests::legacy::LegacyMixNodeDetailsWithLayer; -use rocket::{serde::json::Json, State}; -use rocket_okapi::openapi; -use std::collections::HashSet; - -#[openapi(tag = "contract-cache", deprecated = true)] -#[get("/mixnodes")] -pub async fn get_mixnodes( - cache: &State, -) -> Json> { - Json(cache.legacy_mixnodes_filtered().await) -} - -// DEPRECATED: this endpoint now lives in `node_status_api`. Once all consumers are updated, -// replace this with -// ``` -// pub fn get_mixnodes_detailed() -> Redirect { -// Redirect::to(uri!("/v1/status/mixnodes/detailed")) -// } -// ``` -#[openapi(tag = "contract-cache", deprecated = true)] -#[get("/mixnodes/detailed")] -pub async fn get_mixnodes_detailed( - cache: &State, -) -> Json> { - todo!("rebasing") - // Json(_get_legacy_mixnodes_detailed(cache).await) -} - -#[openapi(tag = "contract-cache", deprecated = true)] -#[get("/gateways")] -pub async fn get_gateways(cache: &State) -> Json> { - Json( - cache - .legacy_gateways_filtered() - .await - .into_iter() - .map(Into::into) - .collect(), - ) -} - -#[openapi(tag = "contract-cache", deprecated = true)] -#[get("/mixnodes/rewarded")] -pub async fn get_rewarded_set( - cache: &State, -) -> Json> { - Json(cache.legacy_v1_rewarded_set_mixnodes().await.clone()) -} - -// DEPRECATED: this endpoint now lives in `node_status_api`. Once all consumers are updated, -// replace this with -// ``` -// pub fn get_mixnodes_set_detailed() -> Redirect { -// Redirect::to(uri!("/v1/status/mixnodes/rewarded/detailed")) -// } -// ``` -#[openapi(tag = "contract-cache", deprecated = true)] -#[get("/mixnodes/rewarded/detailed")] -pub async fn get_rewarded_set_detailed( - status_cache: &State, - contract_cache: &State, -) -> Json> { - todo!("rebasing") - // Json(_get_rewarded_set_legacy_mixnodes_detailed(status_cache, contract_cache).await) -} - -#[openapi(tag = "contract-cache", deprecated = true)] -#[get("/mixnodes/active")] -pub async fn get_active_set( - cache: &State, -) -> Json> { - Json(cache.legacy_v1_active_set_mixnodes().await.clone()) -} - -// DEPRECATED: this endpoint now lives in `node_status_api`. Once all consumers are updated, -// replace this with -// ``` -// pub fn get_active_set_detailed() -> Redirect { -// Redirect::to(uri!("/status/mixnodes/active/detailed")) -// } -// ``` -#[openapi(tag = "contract-cache", deprecated = true)] -#[get("/mixnodes/active/detailed")] -pub async fn get_active_set_detailed( - status_cache: &State, - contract_cache: &State, -) -> Json> { - todo!("rebasing") - // Json(_get_active_set_legacy_mixnodes_detailed(status_cache, contract_cache).await) -} - -#[openapi(tag = "contract-cache", deprecated = true)] -#[get("/mixnodes/blacklisted")] -pub async fn get_blacklisted_mixnodes( - cache: &State, -) -> Json>> { - let blacklist = cache.mixnodes_blacklist().await.clone(); - if blacklist.is_empty() { - Json(None) - } else { - Json(Some(blacklist)) - } -} - -#[openapi(tag = "contract-cache", deprecated = true)] -#[get("/gateways/blacklisted")] -pub async fn get_blacklisted_gateways( - cache: &State, -) -> Json>> { - let blacklist = cache.gateways_blacklist().await.clone(); - if blacklist.is_empty() { - Json(None) - } else { - let gateways = cache.legacy_gateways_all().await; - Json(Some( - gateways - .into_iter() - .filter(|g| blacklist.contains(&g.node_id)) - .map(|g| g.gateway.identity_key.clone()) - .collect(), - )) - } -} - -#[openapi(tag = "contract-cache", deprecated = true)] -#[get("/gateways/blacklisted_v2")] -pub async fn get_blacklisted_gateways_v2( - cache: &State, -) -> Json>> { - let blacklist = cache.gateways_blacklist().await.clone(); - if blacklist.is_empty() { - Json(None) - } else { - Json(Some(blacklist)) - } -} - -#[openapi(tag = "contract-cache")] -#[get("/epoch/reward_params")] -pub async fn get_interval_reward_params( - cache: &State, -) -> Json> { - Json(*cache.interval_reward_params().await) -} - -#[openapi(tag = "contract-cache")] -#[get("/epoch/current")] -pub async fn get_current_epoch(cache: &State) -> Json> { - Json(*cache.current_interval().await) -} diff --git a/nym-api/src/nym_nodes/mod.rs b/nym-api/src/nym_nodes/mod.rs index 33a7ff2fd8..8e3725c7ec 100644 --- a/nym-api/src/nym_nodes/mod.rs +++ b/nym-api/src/nym_nodes/mod.rs @@ -2,34 +2,3 @@ // SPDX-License-Identifier: GPL-3.0-only pub(crate) mod handlers; - -// pub(crate) mod routes; -// mod unstable_routes; - -// /// Merges the routes with http information and returns it to Rocket for serving -// pub(crate) fn nym_node_routes_deprecated(settings: &OpenApiSettings) -> (Vec, OpenApi) { -// openapi_get_routes_spec![ -// settings: -// routes::get_gateways_described, -// routes::get_mixnodes_described, -// ] -// } -// -// pub(crate) fn nym_node_routes_next(settings: &OpenApiSettings) -> (Vec, OpenApi) { -// openapi_get_routes_spec![ -// settings: -// unstable_routes::nodes_basic, -// unstable_routes::nodes_expanded, -// unstable_routes::nodes_detailed, -// unstable_routes::gateways_basic, -// unstable_routes::gateways_expanded, -// unstable_routes::gateways_detailed, -// unstable_routes::mixnodes_basic, -// unstable_routes::mixnodes_expanded, -// unstable_routes::mixnodes_detailed, -// routes::all_described_nodes, -// routes::node_description, -// routes::node_annotation_by_identity, -// routes::node_annotation -// ] -// } diff --git a/nym-api/src/nym_nodes/routes.rs b/nym-api/src/nym_nodes/routes.rs deleted file mode 100644 index f5574418cc..0000000000 --- a/nym-api/src/nym_nodes/routes.rs +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2023 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use crate::node_describe_cache::DescribedNodes; -use crate::node_status_api::NodeStatusCache; -use crate::nym_contract_cache::cache::NymContractCache; -use crate::support::caching::cache::SharedCache; -use nym_api_requests::models::{ - AnnotationResponse, LegacyDescribedGateway, LegacyDescribedMixNode, NymNodeDescription, -}; -use nym_mixnet_contract_common::NodeId; -use rocket::serde::json::Json; -use rocket::State; -use rocket_okapi::openapi; -use std::ops::Deref; - -#[openapi(tag = "Nym Nodes")] -#[get("/all/described")] -pub async fn all_described_nodes( - describe_cache: &State>, -) -> Json> { - let Ok(self_descriptions) = describe_cache.get().await else { - return Json(Vec::new()); - }; - - Json(self_descriptions.all_nodes().cloned().collect()) -} - -#[openapi(tag = "Nym Nodes")] -#[get("/all//described")] -pub async fn node_description( - node_id: NodeId, - describe_cache: &State>, -) -> Json> { - let Ok(self_descriptions) = describe_cache.get().await else { - return Json(None); - }; - - Json(self_descriptions.get_node(&node_id).cloned()) -} - -#[openapi(tag = "Nym Nodes")] -#[get("/annotation-by-identity/")] -pub async fn node_annotation_by_identity( - identity: String, - status_cache: &State, -) -> Json { - let Some(node_id) = status_cache.map_identity_to_node_id(&identity).await else { - return Json(Default::default()); - }; - node_annotation(node_id, status_cache).await -} - -#[openapi(tag = "Nym Nodes")] -#[get("/annotation/")] -pub async fn node_annotation( - node_id: NodeId, - status_cache: &State, -) -> Json { - let Some(annotation) = status_cache.node_annotations().await else { - return Json(Default::default()); - }; - - Json(AnnotationResponse { - node_id: Some(node_id), - annotation: annotation.get(&node_id).cloned(), - }) -} - -/// This only returns descriptions of **legacy** gateways -#[openapi(tag = "Nym Nodes", deprecated = true)] -#[get("/gateways/described")] -pub async fn get_gateways_described( - contract_cache: &State, - describe_cache: &State>, -) -> Json> { - let gateways = contract_cache.legacy_gateways_filtered().await; - if gateways.is_empty() { - return Json(Vec::new()); - } - - // if the self describe cache is unavailable, well, don't attach describe data and only return legacy gateways - let Ok(self_descriptions) = describe_cache.get().await else { - return Json(gateways.into_iter().map(Into::into).collect()); - }; - - Json( - gateways - .into_iter() - .map(|bond| LegacyDescribedGateway { - self_described: self_descriptions - .deref() - .get_description(&bond.node_id) - .cloned(), - bond, - }) - .collect(), - ) -} - -/// This only returns descriptions of **legacy** mixnodes -#[openapi(tag = "Nym Nodes", deprecated = true)] -#[get("/mixnodes/described")] -pub async fn get_mixnodes_described( - contract_cache: &State, - describe_cache: &State>, -) -> Json> { - let mixnodes = contract_cache - .legacy_mixnodes_filtered() - .await - .into_iter() - .map(|m| m.bond_information) - .collect::>(); - if mixnodes.is_empty() { - return Json(Vec::new()); - } - - // if the self describe cache is unavailable, well, don't attach describe data - let Ok(self_descriptions) = describe_cache.get().await else { - return Json(mixnodes.into_iter().map(Into::into).collect()); - }; - - Json( - mixnodes - .into_iter() - .map(|bond| LegacyDescribedMixNode { - self_described: self_descriptions - .deref() - .get_description(&bond.mix_id) - .cloned(), - bond, - }) - .collect(), - ) -} diff --git a/nym-api/src/nym_nodes/unstable_routes.rs b/nym-api/src/nym_nodes/unstable_routes.rs deleted file mode 100644 index 0e178da389..0000000000 --- a/nym-api/src/nym_nodes/unstable_routes.rs +++ /dev/null @@ -1,403 +0,0 @@ -// Copyright 2024 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use crate::node_describe_cache::DescribedNodes; -use crate::node_status_api::models::RocketErrorResponse; -use crate::node_status_api::NodeStatusCache; -use crate::nym_contract_cache::cache::NymContractCache; -use crate::support::caching::cache::SharedCache; -use nym_api_requests::nym_nodes::{ - CachedNodesResponse, FullFatNode, NodeRole, NodeRoleQueryParam, SemiSkimmedNode, SkimmedNode, -}; -use nym_bin_common::version_checker; -use rocket::http::Status; -use rocket::serde::json::Json; -use rocket::State; -use rocket_okapi::openapi; -use std::collections::HashSet; - -/* - routes: - - // all routes/nodes are split into three tiers: - // /skimmed => [used by clients] returns the very basic information for routing purposes - // /semi-skimmed => [used by other nodes/VPN] returns more additional information such noise keys - // /full-fat => [used by explorers, et al.] returns almost everything there is about the nodes - - // there's also additional split based on the role: - ?role => filters based on the specific role (mixnode/gateway/(in the future: entry/exit)) - /mixnodes/ => only returns mixnode role data - /gateway/ => only returns (entry) gateway role data - - -*/ - -#[openapi(tag = "Unstable Nym Nodes")] -#[get("/skimmed?&")] -pub async fn nodes_basic( - status_cache: &State, - contract_cache: &State, - describe_cache: &State>, - role: Option, - semver_compatibility: Option, -) -> Result>, RocketErrorResponse> { - if let Some(role) = role { - match role { - NodeRoleQueryParam::ActiveMixnode => { - return mixnodes_basic( - status_cache, - contract_cache, - describe_cache, - semver_compatibility, - ) - .await - } - NodeRoleQueryParam::EntryGateway => { - return gateways_basic( - status_cache, - contract_cache, - describe_cache, - semver_compatibility, - ) - .await - } - _ => {} - } - } - - Err(RocketErrorResponse::new( - "unimplemented", - Status::NotImplemented, - )) -} - -#[openapi(tag = "Unstable Nym Nodes")] -#[get("/semi-skimmed?&")] -pub async fn nodes_expanded( - cache: &State, - role: Option, - semver_compatibility: Option, -) -> Result>, RocketErrorResponse> { - if let Some(role) = role { - match role { - NodeRoleQueryParam::ActiveMixnode => { - return mixnodes_expanded(cache, semver_compatibility).await - } - NodeRoleQueryParam::EntryGateway => { - return gateways_expanded(cache, semver_compatibility).await - } - _ => {} - } - } - - Err(RocketErrorResponse::new( - "unimplemented", - Status::NotImplemented, - )) -} - -#[openapi(tag = "Unstable Nym Nodes")] -#[get("/full-fat?&")] -pub async fn nodes_detailed( - cache: &State, - role: Option, - semver_compatibility: Option, -) -> Result>, RocketErrorResponse> { - if let Some(role) = role { - match role { - NodeRoleQueryParam::ActiveMixnode => { - return mixnodes_detailed(cache, semver_compatibility).await - } - NodeRoleQueryParam::EntryGateway => { - return gateways_detailed(cache, semver_compatibility).await - } - _ => {} - } - } - - Err(RocketErrorResponse::new( - "unimplemented", - Status::NotImplemented, - )) -} - -#[openapi(tag = "Unstable Nym Nodes")] -#[get("/gateways/skimmed?")] -pub async fn gateways_basic( - status_cache: &State, - contract_cache: &State, - describe_cache: &State>, - semver_compatibility: Option, -) -> Result>, RocketErrorResponse> { - // 1. get the rewarded set - let rewarded_set = contract_cache - .rewarded_set() - .await - .ok_or_else(RocketErrorResponse::internal_server_error)?; - - // determine which gateways are active, i.e. which gateways the clients should be using for connecting and routing the traffic - let active_gateways = rewarded_set.gateways().into_iter().collect::>(); - - // 2. grab all annotations so that we could attach scores to the [nym] nodes - let annotations = status_cache - .node_annotations() - .await - .ok_or_else(RocketErrorResponse::internal_server_error)?; - - // 3. grab all legacy gateways - // due to legacy endpoints we already have fully annotated data on them - let annotated_legacy_gateways = status_cache - .annotated_legacy_gateways() - .await - .ok_or_else(RocketErrorResponse::internal_server_error)?; - - // 4. grab all relevant described nym-nodes - let describe_cache = describe_cache.get().await?; - let gateway_capable_nym_nodes = describe_cache.gateway_capable_nym_nodes(); - - // 5. only return nodes that are present in the active set - let mut active_skimmed_gateways = Vec::new(); - - for (node_id, legacy) in annotated_legacy_gateways.iter() { - if !active_gateways.contains(node_id) { - continue; - } - - if let Some(semver_compat) = semver_compatibility.as_ref() { - let version = legacy.version(); - if !version_checker::is_minor_version_compatible(version, semver_compat) { - continue; - } - } - - // if we have self-described info, prefer it over contract data - if let Some(described) = describe_cache.get_node(node_id) { - active_skimmed_gateways.push( - described.to_skimmed_node(NodeRole::EntryGateway, legacy.node_performance.last_24h), - ) - } else { - active_skimmed_gateways.push(legacy.into()); - } - } - - for nym_node in gateway_capable_nym_nodes { - // if this node is not an active gateway, ignore it - if !active_gateways.contains(&nym_node.node_id) { - continue; - } - - // if we have wrong version, ignore - if let Some(semver_compat) = semver_compatibility.as_ref() { - let version = nym_node.version(); - if !version_checker::is_minor_version_compatible(version, semver_compat) { - continue; - } - } - - // NOTE: if we determined our node IS an active gateway, it MUST be EITHER entry or exit - let role = if rewarded_set.is_exit(&nym_node.node_id) { - NodeRole::ExitGateway - } else { - NodeRole::EntryGateway - }; - - // honestly, not sure under what exact circumstances this value could be missing, - // but in that case just use 0 performance - let annotation = annotations - .get(&nym_node.node_id) - .copied() - .unwrap_or_default(); - - active_skimmed_gateways - .push(nym_node.to_skimmed_node(role, annotation.last_24h_performance)); - } - - // min of all caches - let refreshed_at = [ - rewarded_set.timestamp(), - annotations.timestamp(), - describe_cache.timestamp(), - annotated_legacy_gateways.timestamp(), - ] - .into_iter() - .min() - .unwrap() - .into(); - - Ok(Json(CachedNodesResponse { - refreshed_at, - nodes: active_skimmed_gateways, - })) -} - -#[openapi(tag = "Unstable Nym Nodes")] -#[get("/gateways/semi-skimmed?")] -pub async fn gateways_expanded( - cache: &State, - semver_compatibility: Option, -) -> Result>, RocketErrorResponse> { - let _ = cache; - let _ = semver_compatibility; - Err(RocketErrorResponse::new( - "unimplemented", - Status::NotImplemented, - )) -} - -#[openapi(tag = "Unstable Nym Nodes")] -#[get("/gateways/full-fat?")] -pub async fn gateways_detailed( - cache: &State, - semver_compatibility: Option, -) -> Result>, RocketErrorResponse> { - let _ = cache; - let _ = semver_compatibility; - Err(RocketErrorResponse::new( - "unimplemented", - Status::NotImplemented, - )) -} - -#[openapi(tag = "Unstable Nym Nodes")] -#[get("/mixnodes/skimmed?")] -pub async fn mixnodes_basic( - status_cache: &State, - contract_cache: &State, - describe_cache: &State>, - semver_compatibility: Option, -) -> Result>, RocketErrorResponse> { - // 1. get the rewarded set - let rewarded_set = contract_cache - .rewarded_set() - .await - .ok_or_else(RocketErrorResponse::internal_server_error)?; - - // determine which mixnodes are active, i.e. which mixnodes the clients should be using for routing the traffic - let active_mixnodes = rewarded_set - .active_mixnodes() - .into_iter() - .collect::>(); - - // 2. grab all annotations so that we could attach scores to the [nym] nodes - let annotations = status_cache - .node_annotations() - .await - .ok_or_else(RocketErrorResponse::internal_server_error)?; - - // 3. grab all legacy mixnodes - // due to legacy endpoints we already have fully annotated data on them - let annotated_legacy_mixnodes = status_cache - .annotated_legacy_mixnodes() - .await - .ok_or_else(RocketErrorResponse::internal_server_error)?; - - // 4. grab all relevant described nym-nodes - let describe_cache = describe_cache.get().await?; - let mixing_nym_nodes = describe_cache.mixing_nym_nodes(); - - // TODO: in the future, only use the self-described cache and simply reject mixnodes that did not expose it - - // 5. only return nodes that are present in the active set - let mut active_skimmed_mixnodes = Vec::new(); - - for (mix_id, legacy) in annotated_legacy_mixnodes.iter() { - if !active_mixnodes.contains(mix_id) { - continue; - } - - if let Some(semver_compat) = semver_compatibility.as_ref() { - let version = legacy.version(); - if !version_checker::is_minor_version_compatible(version, semver_compat) { - continue; - } - } - - // if we have self-described info, prefer it over contract data - if let Some(described) = describe_cache.get_node(mix_id) { - active_skimmed_mixnodes.push(described.to_skimmed_node( - NodeRole::Mixnode { - layer: legacy.mixnode_details.bond_information.layer.into(), - }, - legacy.node_performance.last_24h, - )) - } else { - active_skimmed_mixnodes.push(legacy.into()); - } - } - - for nym_node in mixing_nym_nodes { - // if this node is not an active mixnode, ignore it - if !active_mixnodes.contains(&nym_node.node_id) { - continue; - } - - // if we have wrong version, ignore - if let Some(semver_compat) = semver_compatibility.as_ref() { - let version = nym_node.version(); - if !version_checker::is_minor_version_compatible(version, semver_compat) { - continue; - } - } - - // SAFETY: if we determined our node IS active, it MUST have a layer - // no other thread could have updated the rewarded set as we're still holding the [read] lock on the data - #[allow(clippy::unwrap_used)] - let layer = rewarded_set.try_get_mix_layer(&nym_node.node_id).unwrap(); - - // honestly, not sure under what exact circumstances this value could be missing, - // but in that case just use 0 performance - let annotation = annotations - .get(&nym_node.node_id) - .copied() - .unwrap_or_default(); - - active_skimmed_mixnodes.push( - nym_node.to_skimmed_node(NodeRole::Mixnode { layer }, annotation.last_24h_performance), - ); - } - - // min of all caches - let refreshed_at = [ - rewarded_set.timestamp(), - annotations.timestamp(), - describe_cache.timestamp(), - annotated_legacy_mixnodes.timestamp(), - ] - .into_iter() - .min() - .unwrap() - .into(); - - Ok(Json(CachedNodesResponse { - refreshed_at, - nodes: active_skimmed_mixnodes, - })) -} - -#[openapi(tag = "Unstable Nym Nodes")] -#[get("/mixnodes/semi-skimmed?")] -pub async fn mixnodes_expanded( - cache: &State, - semver_compatibility: Option, -) -> Result>, RocketErrorResponse> { - let _ = cache; - let _ = semver_compatibility; - Err(RocketErrorResponse::new( - "unimplemented", - Status::NotImplemented, - )) -} - -#[openapi(tag = "Unstable Nym Nodes")] -#[get("/mixnodes/full-fat?")] -pub async fn mixnodes_detailed( - cache: &State, - semver_compatibility: Option, -) -> Result>, RocketErrorResponse> { - let _ = cache; - let _ = semver_compatibility; - Err(RocketErrorResponse::new( - "unimplemented", - Status::NotImplemented, - )) -} diff --git a/nym-api/src/status/mod.rs b/nym-api/src/status/mod.rs index 5e74eeffb6..0ed94a1819 100644 --- a/nym-api/src/status/mod.rs +++ b/nym-api/src/status/mod.rs @@ -8,7 +8,6 @@ use nym_bin_common::build_information::BinaryBuildInformation; use tokio::time::Instant; pub(crate) mod handlers; -// pub(crate) mod routes; pub(crate) struct ApiStatusState { startup_time: Instant, @@ -40,12 +39,3 @@ impl ApiStatusState { self.signer_information = Some(signer_information) } } - -// pub(crate) fn api_status_routes(settings: &OpenApiSettings) -> (Vec, OpenApi) { -// openapi_get_routes_spec![ -// settings: -// routes::health, -// routes::build_information, -// routes::signer_information -// ] -// } diff --git a/nym-api/src/status/routes.rs b/nym-api/src/status/routes.rs deleted file mode 100644 index 911e9e2a9b..0000000000 --- a/nym-api/src/status/routes.rs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2024 - Nym Technologies SA -// SPDX-License-Identifier: GPL-3.0-only - -use crate::node_status_api::models::RocketErrorResponse; -use crate::status::ApiStatusState; -use nym_api_requests::models::{ApiHealthResponse, SignerInformationResponse}; -use nym_bin_common::build_information::BinaryBuildInformationOwned; -use nym_compact_ecash::Base58; -use rocket::http::Status; -use rocket::serde::json::Json; -use rocket::State; -use rocket_okapi::openapi; - -#[openapi(tag = "Api Status")] -#[get("/health")] -pub(crate) async fn health(state: &State) -> Json { - let uptime = state.startup_time.elapsed(); - let health = ApiHealthResponse::new_healthy(uptime); - Json(health) -} - -#[openapi(tag = "Api Status")] -#[get("/build-information")] -pub(crate) async fn build_information( - state: &State, -) -> Json { - Json(state.build_information.to_owned()) -} - -#[openapi(tag = "Api Status")] -#[get("/signer-information")] -pub(crate) async fn signer_information( - state: &State, -) -> Result, RocketErrorResponse> { - let signer_state = state.signer_information.as_ref().ok_or_else(|| { - RocketErrorResponse::new( - "this api does not expose zk-nym signing functionalities", - Status::InternalServerError, - ) - })?; - - Ok(Json(SignerInformationResponse { - cosmos_address: signer_state.cosmos_address.clone(), - identity: signer_state.identity.clone(), - announce_address: signer_state.announce_address.clone(), - verification_key: signer_state - .ecash_keypair - .verification_key() - .await - .map(|maybe_vk| maybe_vk.to_bs58()), - })) -}