Feature/additional mixnode endpoints (#1019)
* Moved mixnode status route to node status api module * Introduced validator-api endpoint for estimating mixnode's reward * Stake saturation endpoint
This commit is contained in:
committed by
GitHub
parent
11a458a43d
commit
29340ed00c
@@ -1,9 +1,7 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::storage;
|
||||
use rocket::fairing::AdHoc;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
|
||||
pub(crate) mod local_guard;
|
||||
@@ -16,20 +14,35 @@ 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 stage(database_path: PathBuf) -> AdHoc {
|
||||
AdHoc::on_ignite("SQLx Stage", |rocket| async {
|
||||
rocket
|
||||
.attach(storage::ValidatorApiStorage::stage(database_path))
|
||||
.mount(
|
||||
"/v1/status",
|
||||
routes![
|
||||
routes::mixnode_report,
|
||||
routes::gateway_report,
|
||||
routes::mixnode_uptime_history,
|
||||
routes::gateway_uptime_history,
|
||||
routes::mixnode_core_status_count,
|
||||
routes::gateway_core_status_count,
|
||||
],
|
||||
)
|
||||
pub(crate) fn stage_full() -> AdHoc {
|
||||
AdHoc::on_ignite("Node Status API Stage", |rocket| async {
|
||||
rocket.mount(
|
||||
"/v1/status",
|
||||
routes![
|
||||
routes::mixnode_report,
|
||||
routes::gateway_report,
|
||||
routes::mixnode_uptime_history,
|
||||
routes::gateway_uptime_history,
|
||||
routes::mixnode_core_status_count,
|
||||
routes::gateway_core_status_count,
|
||||
routes::get_mixnode_status,
|
||||
routes::get_mixnode_reward_estimation,
|
||||
routes::get_mixnode_stake_saturation,
|
||||
],
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// in the minimal variant we would not have access to endpoints relying on existence
|
||||
// of the network monitor and the associated storage
|
||||
pub(crate) fn stage_minimal() -> AdHoc {
|
||||
AdHoc::on_ignite("Node Status API Stage", |rocket| async {
|
||||
rocket.mount(
|
||||
"/v1/status",
|
||||
routes![
|
||||
routes::get_mixnode_status,
|
||||
routes::get_mixnode_stake_saturation,
|
||||
],
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::cache::MixnodeStatus;
|
||||
use crate::node_status_api::utils::NodeUptimes;
|
||||
use crate::storage::models::NodeStatus;
|
||||
use rocket::http::{ContentType, Status};
|
||||
@@ -208,22 +209,24 @@ pub struct HistoricalUptime {
|
||||
}
|
||||
|
||||
pub(crate) struct ErrorResponse {
|
||||
error: ValidatorApiStorageError,
|
||||
error_message: String,
|
||||
status: Status,
|
||||
}
|
||||
|
||||
impl ErrorResponse {
|
||||
pub(crate) fn new(error: ValidatorApiStorageError, status: Status) -> Self {
|
||||
ErrorResponse { error, status }
|
||||
pub(crate) fn new(error_message: impl Into<String>, status: Status) -> Self {
|
||||
ErrorResponse {
|
||||
error_message: error_message.into(),
|
||||
status,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r, 'o: 'r> Responder<'r, 'o> for ErrorResponse {
|
||||
fn respond_to(self, _: &'r Request<'_>) -> response::Result<'o> {
|
||||
let message = format!("{}", self.error);
|
||||
Response::build()
|
||||
.header(ContentType::Plain)
|
||||
.sized_body(message.len(), Cursor::new(message))
|
||||
.sized_body(self.error_message.len(), Cursor::new(self.error_message))
|
||||
.status(self.status)
|
||||
.ok()
|
||||
}
|
||||
@@ -275,3 +278,26 @@ pub struct CoreNodeStatus {
|
||||
pub(crate) identity: String,
|
||||
pub(crate) count: i32,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub(crate) struct MixnodeStatusResponse {
|
||||
pub(crate) status: MixnodeStatus,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub(crate) struct RewardEstimationResponse {
|
||||
pub(crate) estimated_total_node_reward: u128,
|
||||
pub(crate) estimated_operator_reward: u128,
|
||||
pub(crate) estimated_delegators_reward: u128,
|
||||
|
||||
pub(crate) current_epoch_start: i64,
|
||||
pub(crate) current_epoch_end: i64,
|
||||
pub(crate) current_epoch_uptime: Uptime,
|
||||
pub(crate) as_at: i64,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub(crate) struct StakeSaturationResponse {
|
||||
pub(crate) saturation: f32,
|
||||
pub(crate) as_at: i64,
|
||||
}
|
||||
|
||||
@@ -3,12 +3,14 @@
|
||||
|
||||
use crate::node_status_api::models::{
|
||||
CoreNodeStatus, ErrorResponse, GatewayStatusReport, GatewayUptimeHistory, MixnodeStatusReport,
|
||||
MixnodeUptimeHistory,
|
||||
MixnodeStatusResponse, MixnodeUptimeHistory, RewardEstimationResponse, StakeSaturationResponse,
|
||||
};
|
||||
use crate::storage::ValidatorApiStorage;
|
||||
use crate::{Epoch, ValidatorCache};
|
||||
use rocket::http::Status;
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::State;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
#[get("/mixnode/<pubkey>/report")]
|
||||
pub(crate) async fn mixnode_report(
|
||||
@@ -19,7 +21,7 @@ pub(crate) async fn mixnode_report(
|
||||
.construct_mixnode_report(pubkey)
|
||||
.await
|
||||
.map(Json)
|
||||
.map_err(|err| ErrorResponse::new(err, Status::NotFound))
|
||||
.map_err(|err| ErrorResponse::new(err.to_string(), Status::NotFound))
|
||||
}
|
||||
|
||||
#[get("/gateway/<pubkey>/report")]
|
||||
@@ -31,7 +33,7 @@ pub(crate) async fn gateway_report(
|
||||
.construct_gateway_report(pubkey)
|
||||
.await
|
||||
.map(Json)
|
||||
.map_err(|err| ErrorResponse::new(err, Status::NotFound))
|
||||
.map_err(|err| ErrorResponse::new(err.to_string(), Status::NotFound))
|
||||
}
|
||||
|
||||
#[get("/mixnode/<pubkey>/history")]
|
||||
@@ -43,7 +45,7 @@ pub(crate) async fn mixnode_uptime_history(
|
||||
.get_mixnode_uptime_history(pubkey)
|
||||
.await
|
||||
.map(Json)
|
||||
.map_err(|err| ErrorResponse::new(err, Status::NotFound))
|
||||
.map_err(|err| ErrorResponse::new(err.to_string(), Status::NotFound))
|
||||
}
|
||||
|
||||
#[get("/gateway/<pubkey>/history")]
|
||||
@@ -55,7 +57,7 @@ pub(crate) async fn gateway_uptime_history(
|
||||
.get_gateway_uptime_history(pubkey)
|
||||
.await
|
||||
.map(Json)
|
||||
.map_err(|err| ErrorResponse::new(err, Status::NotFound))
|
||||
.map_err(|err| ErrorResponse::new(err.to_string(), Status::NotFound))
|
||||
}
|
||||
|
||||
#[get("/mixnode/<pubkey>/core-status-count?<since>")]
|
||||
@@ -91,3 +93,84 @@ pub(crate) async fn gateway_core_status_count(
|
||||
count,
|
||||
})
|
||||
}
|
||||
|
||||
#[get("/mixnode/<identity>/status")]
|
||||
pub(crate) async fn get_mixnode_status(
|
||||
cache: &State<ValidatorCache>,
|
||||
identity: String,
|
||||
) -> Json<MixnodeStatusResponse> {
|
||||
Json(MixnodeStatusResponse {
|
||||
status: cache.mixnode_status(identity).await,
|
||||
})
|
||||
}
|
||||
|
||||
#[get("/mixnode/<identity>/reward_estimation")]
|
||||
pub(crate) async fn get_mixnode_reward_estimation(
|
||||
cache: &State<ValidatorCache>,
|
||||
storage: &State<ValidatorApiStorage>,
|
||||
first_epoch: &State<Epoch>,
|
||||
identity: String,
|
||||
) -> Result<Json<RewardEstimationResponse>, 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_operator_reward,
|
||||
estimated_delegators_reward,
|
||||
current_epoch_start: current_epoch.start_unix_timestamp(),
|
||||
current_epoch_end: current_epoch.end_unix_timestamp(),
|
||||
current_epoch_uptime: uptime,
|
||||
as_at,
|
||||
}))
|
||||
} else {
|
||||
Err(ErrorResponse::new(
|
||||
"mixnode bond not found",
|
||||
Status::NotFound,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/mixnode/<identity>/stake_saturation")]
|
||||
pub(crate) async fn get_mixnode_stake_saturation(
|
||||
cache: &State<ValidatorCache>,
|
||||
identity: String,
|
||||
) -> Result<Json<StakeSaturationResponse>, 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,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user