// Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 use crate::mix_node::delegations::{ get_single_mixnode_delegations, get_single_mixnode_delegations_summed, }; use crate::mix_node::econ_stats::retrieve_mixnode_econ_stats; use crate::mix_node::models::{ EconomicDynamicsStats, NodeDescription, NodeStats, SummedDelegations, }; use crate::state::ExplorerApiStateContext; use nym_explorer_api_requests::PrettyDetailedMixNodeBond; use nym_mixnet_contract_common::{Delegation, MixId}; use reqwest::Error as ReqwestError; use rocket::response::status::NotFound; use rocket::serde::json::Json; use rocket::{Route, State}; use rocket_okapi::okapi::openapi3::OpenApi; use rocket_okapi::openapi_get_routes_spec; use rocket_okapi::settings::OpenApiSettings; pub fn mix_node_make_default_routes(settings: &OpenApiSettings) -> (Vec, OpenApi) { openapi_get_routes_spec![ settings: get_delegations, get_delegations_summed, get_by_id, get_description, get_stats, get_economic_dynamics_stats, ] } async fn get_mix_node_description(host: &str, port: u16) -> Result { reqwest::get(format!("http://{host}:{port}/description")) .await? .json::() .await } async fn get_mix_node_stats(host: &str, port: u16) -> Result { reqwest::get(format!("http://{host}:{port}/stats")) .await? .json::() .await } #[openapi(tag = "mix_nodes")] #[get("/")] pub(crate) async fn get_by_id( mix_id: MixId, state: &State, ) -> Result, NotFound> { match state.inner.mixnodes.get_detailed_mixnode(mix_id).await { Some(mixnode) => Ok(Json(mixnode)), None => Err(NotFound("Mixnode not found".to_string())), } } #[openapi(tag = "mix_node")] #[get("//delegations")] pub(crate) async fn get_delegations( mix_id: MixId, state: &State, ) -> Json> { Json(get_single_mixnode_delegations(&state.inner.validator_client, mix_id).await) } #[openapi(tag = "mix_node")] #[get("//delegations/summed")] pub(crate) async fn get_delegations_summed( mix_id: MixId, state: &State, ) -> Json> { Json(get_single_mixnode_delegations_summed(&state.inner.validator_client, mix_id).await) } #[openapi(tag = "mix_node")] #[get("//description")] pub(crate) async fn get_description( mix_id: MixId, state: &State, ) -> Option> { match state.inner.mixnode.clone().get_description(mix_id).await { Some(cache_value) => { trace!("Returning cached value for {}", mix_id); Some(Json(cache_value)) } None => { trace!("No valid cache value for {}", mix_id); match state.inner.get_mix_node(mix_id).await { Some(bond) => { match get_mix_node_description( &bond.mix_node().host, bond.mix_node().http_api_port, ) .await { Ok(response) => { // cache the response and return as the HTTP response state .inner .mixnode .set_description(mix_id, response.clone()) .await; Some(Json(response)) } Err(e) => { error!( "Unable to get description for {} on {}:{} -> {}", mix_id, bond.mix_node().host, bond.mix_node().http_api_port, e ); Option::None } } } None => Option::None, } } } } #[openapi(tag = "mix_node")] #[get("//stats")] pub(crate) async fn get_stats( mix_id: MixId, state: &State, ) -> Option> { match state.inner.mixnode.get_node_stats(mix_id).await { Some(cache_value) => { trace!("Returning cached value for {}", mix_id); Some(Json(cache_value)) } None => { trace!("No valid cache value for {}", mix_id); match state.inner.get_mix_node(mix_id).await { Some(bond) => { match get_mix_node_stats(&bond.mix_node().host, bond.mix_node().http_api_port) .await { Ok(response) => { // cache the response and return as the HTTP response state .inner .mixnode .set_node_stats(mix_id, response.clone()) .await; Some(Json(response)) } Err(e) => { error!( "Unable to get description for {} on {}:{} -> {}", mix_id, bond.mix_node().host, bond.mix_node().http_api_port, e ); Option::None } } } None => Option::None, } } } } #[openapi(tag = "mix_node")] #[get("//economic-dynamics-stats")] pub(crate) async fn get_economic_dynamics_stats( mix_id: MixId, state: &State, ) -> Option> { match state.inner.mixnode.get_econ_stats(mix_id).await { Some(cache_value) => { trace!("Returning cached value for {}", mix_id); Some(Json(cache_value)) } None => { trace!("No valid cache value for {}", mix_id); // get fresh value from the validator API let econ_stats = retrieve_mixnode_econ_stats(&state.inner.validator_client, mix_id).await?; // update cache state .inner .mixnode .set_econ_stats(mix_id, econ_stats.clone()) .await; Some(Json(econ_stats)) } } }