// Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 use crate::node_status_api::models::{ ErrorResponse, GatewayStatusReport, GatewayUptimeHistory, MixnodeStatusReport, MixnodeUptimeHistory, }; use crate::storage::ValidatorApiStorage; use crate::{Epoch, ValidatorCache}; use rocket::http::Status; use rocket::serde::json::Json; use rocket::State; use std::convert::TryFrom; use time::OffsetDateTime; use validator_api_requests::models::{ CoreNodeStatusResponse, MixnodeStatusResponse, RewardEstimationResponse, StakeSaturationResponse, }; #[get("/mixnode//report")] pub(crate) async fn mixnode_report( storage: &State, identity: &str, ) -> Result, ErrorResponse> { storage .construct_mixnode_report(identity) .await .map(Json) .map_err(|err| ErrorResponse::new(err.to_string(), Status::NotFound)) } #[get("/gateway//report")] pub(crate) async fn gateway_report( storage: &State, identity: &str, ) -> Result, ErrorResponse> { storage .construct_gateway_report(identity) .await .map(Json) .map_err(|err| ErrorResponse::new(err.to_string(), Status::NotFound)) } #[get("/mixnode//history")] pub(crate) async fn mixnode_uptime_history( storage: &State, identity: &str, ) -> Result, ErrorResponse> { storage .get_mixnode_uptime_history(identity) .await .map(Json) .map_err(|err| ErrorResponse::new(err.to_string(), Status::NotFound)) } #[get("/gateway//history")] pub(crate) async fn gateway_uptime_history( storage: &State, identity: &str, ) -> Result, ErrorResponse> { storage .get_gateway_uptime_history(identity) .await .map(Json) .map_err(|err| ErrorResponse::new(err.to_string(), Status::NotFound)) } #[get("/mixnode//core-status-count?")] pub(crate) async fn mixnode_core_status_count( storage: &State, identity: &str, since: Option, ) -> Json { let count = storage .get_core_mixnode_status_count(identity, since) .await .unwrap_or_default(); Json(CoreNodeStatusResponse { identity: identity.to_string(), count, }) } #[get("/gateway//core-status-count?")] pub(crate) async fn gateway_core_status_count( storage: &State, identity: &str, since: Option, ) -> Json { let count = storage .get_core_gateway_status_count(identity, since) .await .unwrap_or_default(); Json(CoreNodeStatusResponse { identity: identity.to_string(), count, }) } #[get("/mixnode//status")] pub(crate) async fn get_mixnode_status( cache: &State, identity: String, ) -> Json { Json(MixnodeStatusResponse { status: cache.mixnode_status(identity).await, }) } #[get("/mixnode//reward-estimation")] pub(crate) async fn get_mixnode_reward_estimation( cache: &State, storage: &State, first_epoch: &State, identity: String, ) -> Result, ErrorResponse> { let (bond, status) = cache.mixnode_details(&identity).await; if let Some(bond) = bond { let epoch_reward_params = cache.epoch_reward_params().await; let as_at = epoch_reward_params.timestamp(); let epoch_reward_params = epoch_reward_params.into_inner(); let current_epoch = first_epoch.current(OffsetDateTime::now_utc()); let uptime = storage .get_average_mixnode_uptime_in_interval( &identity, current_epoch.start_unix_timestamp(), current_epoch.end_unix_timestamp(), ) .await .map_err(|err| ErrorResponse::new(err.to_string(), Status::NotFound))?; let (estimated_total_node_reward, estimated_operator_reward, estimated_delegators_reward) = epoch_reward_params.estimate_reward(&bond, uptime.u8(), status.is_active()); Ok(Json(RewardEstimationResponse { estimated_total_node_reward: estimated_total_node_reward as u64, estimated_operator_reward: estimated_operator_reward as u64, estimated_delegators_reward: estimated_delegators_reward as u64, current_epoch_start: current_epoch.start_unix_timestamp(), current_epoch_end: current_epoch.end_unix_timestamp(), current_epoch_uptime: uptime.u8(), as_at, })) } else { Err(ErrorResponse::new( "mixnode bond not found", Status::NotFound, )) } } #[get("/mixnode//stake-saturation")] pub(crate) async fn get_mixnode_stake_saturation( cache: &State, identity: String, ) -> Result, ErrorResponse> { let (bond, _) = cache.mixnode_details(&identity).await; if let Some(bond) = bond { let epoch_reward_params = cache.epoch_reward_params().await; let as_at = epoch_reward_params.timestamp(); let epoch_reward_params = epoch_reward_params.into_inner(); let saturation = bond.stake_saturation( epoch_reward_params.circulating_supply, epoch_reward_params.rewarded_set_size, ); Ok(Json(StakeSaturationResponse { saturation: saturation.to_num(), as_at, })) } else { Err(ErrorResponse::new( "mixnode bond not found", Status::NotFound, )) } }