// Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 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; use mixnet_contract_common::Delegation; 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, PrettyDetailedMixNodeBond, SummedDelegations, }; use crate::state::ExplorerApiStateContext; 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, ] } #[openapi(tag = "mix_nodes")] #[get("/")] pub(crate) async fn get_by_id( pubkey: &str, state: &State, ) -> Result, NotFound> { match state .inner .mixnodes .get_detailed_mixnode_by_id(pubkey) .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( pubkey: &str, state: &State, ) -> Json> { Json(get_single_mixnode_delegations(&state.inner.validator_client, pubkey).await) } #[openapi(tag = "mix_node")] #[get("//delegations/summed")] pub(crate) async fn get_delegations_summed( pubkey: &str, state: &State, ) -> Json> { Json(get_single_mixnode_delegations_summed(&state.inner.validator_client, pubkey).await) } #[openapi(tag = "mix_node")] #[get("//description")] pub(crate) async fn get_description( pubkey: &str, state: &State, ) -> Option> { match state.inner.mixnode.clone().get_description(pubkey).await { Some(cache_value) => { trace!("Returning cached value for {}", pubkey); Some(Json(cache_value)) } None => { trace!("No valid cache value for {}", pubkey); match state.inner.get_mix_node(pubkey).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(pubkey, response.clone()) .await; Some(Json(response)) } Err(e) => { error!( "Unable to get description for {} on {}:{} -> {}", pubkey, 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( pubkey: &str, state: &State, ) -> Option> { match state.inner.mixnode.get_node_stats(pubkey).await { Some(cache_value) => { trace!("Returning cached value for {}", pubkey); Some(Json(cache_value)) } None => { trace!("No valid cache value for {}", pubkey); match state.inner.get_mix_node(pubkey).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(pubkey, response.clone()) .await; Some(Json(response)) } Err(e) => { error!( "Unable to get description for {} on {}:{} -> {}", pubkey, 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( pubkey: &str, state: &State, ) -> Option> { match state.inner.mixnode.get_econ_stats(pubkey).await { Some(cache_value) => { trace!("Returning cached value for {}", pubkey); Some(Json(cache_value)) } None => { trace!("No valid cache value for {}", pubkey); // get fresh value from the validator API let econ_stats = retrieve_mixnode_econ_stats(&state.inner.validator_client, pubkey).await?; // update cache state .inner .mixnode .set_econ_stats(pubkey, econ_stats.clone()) .await; Some(Json(econ_stats)) } } } async fn get_mix_node_description(host: &str, port: &u16) -> Result { reqwest::get(format!("http://{}:{}/description", host, port)) .await? .json::() .await } async fn get_mix_node_stats(host: &str, port: &u16) -> Result { reqwest::get(format!("http://{}:{}/stats", host, port)) .await? .json::() .await }