nym-api: uptime rework (#3053)
* nym-api: cache updates as node performance * nym-api: update get mixnode avg_uptime endpoint * nym-api: mixnode report to use cached data * nym-api: annotate gateway bond with node performance * nym-api: gateway report to use cached data * wip * Add get_gateway_avg_uptime * Add comment * update NR gateways to include node_performance on frontend * use node_performance values on frontend * fixup select gateway from list * fix up lint errors --------- Co-authored-by: fmtabbara <fmtabbara@hotmail.co.uk>
This commit is contained in:
@@ -29,7 +29,6 @@ import {
|
||||
GatewayBondAnnotated,
|
||||
GatewayBond,
|
||||
} from '../typeDefs/explorer-api';
|
||||
import { toPercentIntegerString } from '../utils';
|
||||
|
||||
function getFromCache(key: string) {
|
||||
const ts = Number(localStorage.getItem('ts'));
|
||||
@@ -94,9 +93,9 @@ export class Api {
|
||||
static fetchGateways = async (): Promise<GatewayBond[]> => {
|
||||
const res = await fetch(GATEWAYS_API);
|
||||
const gatewaysAnnotated: GatewayBondAnnotated[] = await res.json();
|
||||
return gatewaysAnnotated.map(({ gateway_bond, performance }) => ({
|
||||
return gatewaysAnnotated.map(({ gateway_bond, node_performance }) => ({
|
||||
...gateway_bond,
|
||||
performance: toPercentIntegerString(performance),
|
||||
node_performance,
|
||||
}));
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { GatewayResponse, GatewayBond, GatewayReportResponse } from '../typeDefs/explorer-api';
|
||||
import { toPercentIntegerString } from '../utils';
|
||||
|
||||
export type GatewayRowType = {
|
||||
id: string;
|
||||
@@ -8,7 +9,7 @@ export type GatewayRowType = {
|
||||
host: string;
|
||||
location: string;
|
||||
version: string;
|
||||
performance: string;
|
||||
node_performance: string;
|
||||
};
|
||||
|
||||
export type GatewayEnrichedRowType = GatewayRowType & {
|
||||
@@ -29,7 +30,7 @@ export function gatewayToGridRow(arrayOfGateways: GatewayResponse): GatewayRowTy
|
||||
bond: gw.pledge_amount.amount || 0,
|
||||
host: gw.gateway.host || '',
|
||||
version: gw.gateway.version || '',
|
||||
performance: gw.performance,
|
||||
node_performance: toPercentIntegerString(gw.node_performance.last_24h),
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -46,6 +47,6 @@ export function gatewayEnrichedToGridRow(gateway: GatewayBond, report: GatewayRe
|
||||
mixPort: gateway.gateway.mix_port || 0,
|
||||
routingScore: `${report.most_recent}%`,
|
||||
avgUptime: `${report.last_day || report.last_hour}%`,
|
||||
performance: gateway.performance,
|
||||
node_performance: toPercentIntegerString(gateway.node_performance.most_recent),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ const CustomPagination = () => {
|
||||
color="primary"
|
||||
count={state.pagination.pageCount}
|
||||
page={state.pagination.page + 1}
|
||||
onChange={(event, value) => apiRef.current.setPage(value - 1)}
|
||||
onChange={(_, value) => apiRef.current.setPage(value - 1)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -26,7 +26,7 @@ const columns: ColumnsType[] = [
|
||||
headerAlign: 'left',
|
||||
},
|
||||
{
|
||||
field: 'routingScore',
|
||||
field: 'node_performance',
|
||||
title: 'Routing Score',
|
||||
flex: 1,
|
||||
headerAlign: 'left',
|
||||
@@ -130,13 +130,13 @@ const PageGatewayDetailsWithState = ({ selectedGateway }: { selectedGateway: Gat
|
||||
* Guard component to handle loading and not found states
|
||||
*/
|
||||
const PageGatewayDetailGuard: FCWithChildren = () => {
|
||||
const [selectedGateway, setSelectedGateway] = React.useState<GatewayBond | undefined>();
|
||||
const [selectedGateway, setSelectedGateway] = React.useState<GatewayBond>();
|
||||
const { gateways } = useMainContext();
|
||||
const { id } = useParams<{ id: string | undefined }>();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (gateways?.data) {
|
||||
setSelectedGateway(gateways.data.find((gateway) => gateway.gateway.identity_key === id));
|
||||
setSelectedGateway(gateways.data.find((g) => g.gateway.identity_key === id));
|
||||
}
|
||||
}, [gateways, id]);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import { Link as RRDLink } from 'react-router-dom';
|
||||
import { Box, Button, Card, Grid, Link as MuiLink } from '@mui/material';
|
||||
import { Box, Card, Grid, Link as MuiLink } from '@mui/material';
|
||||
import { CopyToClipboard } from '@nymproject/react/clipboard/CopyToClipboard';
|
||||
import { GridColDef, GridRenderCellParams } from '@mui/x-data-grid';
|
||||
import { SelectChangeEvent } from '@mui/material/Select';
|
||||
@@ -86,7 +86,6 @@ export const PageGateways: FCWithChildren = () => {
|
||||
const columns: GridColDef[] = [
|
||||
{
|
||||
field: 'identity_key',
|
||||
headerName: 'Identity Key',
|
||||
renderHeader: () => <CustomColumnHeading headingTitle="Identity Key" />,
|
||||
headerClassName: 'MuiDataGrid-header-override',
|
||||
width: 380,
|
||||
@@ -119,7 +118,7 @@ export const PageGateways: FCWithChildren = () => {
|
||||
<MuiLink
|
||||
sx={{ ...cellStyles }}
|
||||
component={RRDLink}
|
||||
to={`/network-components/gateway/${params.row.identityKey}`}
|
||||
to={`/network-components/gateway/${params.row.identity_key}`}
|
||||
data-testid="pledge-amount"
|
||||
>
|
||||
{unymToNym(params.value, 6)}
|
||||
@@ -127,8 +126,7 @@ export const PageGateways: FCWithChildren = () => {
|
||||
),
|
||||
},
|
||||
{
|
||||
field: 'performance',
|
||||
headerName: 'Routing Score',
|
||||
field: 'node_performance',
|
||||
renderHeader: () => <CustomColumnHeading headingTitle="Routing Score" />,
|
||||
width: 150,
|
||||
headerAlign: 'left',
|
||||
@@ -137,7 +135,7 @@ export const PageGateways: FCWithChildren = () => {
|
||||
<MuiLink
|
||||
sx={{ ...cellStyles }}
|
||||
component={RRDLink}
|
||||
to={`/network-components/gateway/${params.row.identityKey}`}
|
||||
to={`/network-components/gateway/${params.row.identity_key}`}
|
||||
data-testid="pledge-amount"
|
||||
>
|
||||
{`${params.value}%`}
|
||||
@@ -154,7 +152,7 @@ export const PageGateways: FCWithChildren = () => {
|
||||
<MuiLink
|
||||
sx={{ ...cellStyles }}
|
||||
component={RRDLink}
|
||||
to={`/network-components/gateway/${params.row.identityKey}`}
|
||||
to={`/network-components/gateway/${params.row.identity_key}`}
|
||||
data-testid="host"
|
||||
>
|
||||
{params.value}
|
||||
@@ -168,9 +166,9 @@ export const PageGateways: FCWithChildren = () => {
|
||||
headerAlign: 'left',
|
||||
headerClassName: 'MuiDataGrid-header-override',
|
||||
renderCell: (params: GridRenderCellParams) => (
|
||||
<Button
|
||||
<Box
|
||||
onClick={() => handleSearch(params.value as string)}
|
||||
sx={{ ...cellStyles, justifyContent: 'flex-start' }}
|
||||
sx={{ ...cellStyles, justifyContent: 'flex-start', cursor: 'pointer' }}
|
||||
data-testid="location-button"
|
||||
>
|
||||
<Tooltip text={params.value} id="gateway-location-text">
|
||||
@@ -184,7 +182,7 @@ export const PageGateways: FCWithChildren = () => {
|
||||
{params.value}
|
||||
</Box>
|
||||
</Tooltip>
|
||||
</Button>
|
||||
</Box>
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -207,7 +205,6 @@ export const PageGateways: FCWithChildren = () => {
|
||||
},
|
||||
{
|
||||
field: 'version',
|
||||
headerName: 'Version',
|
||||
renderHeader: () => <CustomColumnHeading headingTitle="Version" />,
|
||||
width: 150,
|
||||
headerAlign: 'left',
|
||||
|
||||
@@ -114,6 +114,12 @@ export interface StatsResponse {
|
||||
packets_explicitly_dropped_since_last_update: number;
|
||||
}
|
||||
|
||||
export interface NodePerformance {
|
||||
most_recent: string;
|
||||
last_hour: string;
|
||||
last_24h: string;
|
||||
}
|
||||
|
||||
export type MixNodeHistoryResponse = StatsResponse;
|
||||
|
||||
export interface GatewayBond {
|
||||
@@ -122,12 +128,12 @@ export interface GatewayBond {
|
||||
total_delegation: Amount;
|
||||
owner: string;
|
||||
gateway: Gateway;
|
||||
performance: string;
|
||||
node_performance: NodePerformance;
|
||||
}
|
||||
|
||||
export interface GatewayBondAnnotated {
|
||||
gateway_bond: GatewayBond;
|
||||
performance: string;
|
||||
node_performance: NodePerformance;
|
||||
}
|
||||
|
||||
export type GatewayResponse = GatewayBond[];
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_std::{Coin, Decimal};
|
||||
use cosmwasm_std::{Addr, Coin, Decimal};
|
||||
use nym_mixnet_contract_common::families::FamilyHead;
|
||||
use nym_mixnet_contract_common::mixnode::MixNodeDetails;
|
||||
use nym_mixnet_contract_common::reward_params::{Performance, RewardingParams};
|
||||
@@ -93,12 +93,21 @@ pub struct MixnodeStatusResponse {
|
||||
pub status: MixnodeStatus,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct NodePerformance {
|
||||
pub most_recent: Performance,
|
||||
pub last_hour: Performance,
|
||||
pub last_24h: Performance,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct MixNodeBondAnnotated {
|
||||
pub mixnode_details: MixNodeDetails,
|
||||
pub stake_saturation: StakeSaturation,
|
||||
pub uncapped_stake_saturation: StakeSaturation,
|
||||
// NOTE: the performance field is deprecated in favour of node_performance
|
||||
pub performance: Performance,
|
||||
pub node_performance: NodePerformance,
|
||||
pub estimated_operator_apy: Decimal,
|
||||
pub estimated_delegators_apy: Decimal,
|
||||
pub family: Option<FamilyHead>,
|
||||
@@ -112,12 +121,32 @@ impl MixNodeBondAnnotated {
|
||||
pub fn mix_id(&self) -> MixId {
|
||||
self.mixnode_details.mix_id()
|
||||
}
|
||||
|
||||
pub fn identity_key(&self) -> &str {
|
||||
self.mixnode_details.bond_information.identity()
|
||||
}
|
||||
|
||||
pub fn owner(&self) -> &Addr {
|
||||
self.mixnode_details.bond_information.owner()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct GatewayBondAnnotated {
|
||||
pub gateway_bond: GatewayBond,
|
||||
// NOTE: the performance field is deprecated in favour of node_performance
|
||||
pub performance: Performance,
|
||||
pub node_performance: NodePerformance,
|
||||
}
|
||||
|
||||
impl GatewayBondAnnotated {
|
||||
pub fn identity(&self) -> &String {
|
||||
self.gateway_bond.identity()
|
||||
}
|
||||
|
||||
pub fn owner(&self) -> &Addr {
|
||||
self.gateway_bond.owner()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||
@@ -147,7 +176,17 @@ pub struct RewardEstimationResponse {
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct UptimeResponse {
|
||||
pub mix_id: MixId,
|
||||
// The same as node_performance.last_24h. Legacy
|
||||
pub avg_uptime: u8,
|
||||
pub node_performance: NodePerformance,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct GatewayUptimeResponse {
|
||||
pub identity: String,
|
||||
// The same as node_performance.last_24h. Legacy
|
||||
pub avg_uptime: u8,
|
||||
pub node_performance: NodePerformance,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
|
||||
+27
-4
@@ -1,6 +1,6 @@
|
||||
use crate::node_status_api::reward_estimate::{compute_apy_from_reward, compute_reward_estimate};
|
||||
use crate::support::storage::NymApiStorage;
|
||||
use nym_api_requests::models::{GatewayBondAnnotated, MixNodeBondAnnotated};
|
||||
use nym_api_requests::models::{GatewayBondAnnotated, MixNodeBondAnnotated, NodePerformance};
|
||||
use nym_mixnet_contract_common::families::FamilyHead;
|
||||
use nym_mixnet_contract_common::{reward_params::Performance, Interval, MixId};
|
||||
use nym_mixnet_contract_common::{
|
||||
@@ -99,16 +99,15 @@ pub(super) async fn annotate_nodes_with_details(
|
||||
.rewarding_details
|
||||
.uncapped_bond_saturation(&interval_reward_params);
|
||||
|
||||
let rewarded_set_status = rewarded_set.get(&mixnode.mix_id()).copied();
|
||||
|
||||
// If the performance can't be obtained, because the nym-api was not started with
|
||||
// the monitoring (and hence, storage), then reward estimates will be all zero
|
||||
|
||||
let performance =
|
||||
get_mixnode_performance_from_storage(storage, mixnode.mix_id(), current_interval)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
|
||||
let rewarded_set_status = rewarded_set.get(&mixnode.mix_id()).copied();
|
||||
|
||||
let reward_estimate = compute_reward_estimate(
|
||||
&mixnode,
|
||||
performance,
|
||||
@@ -117,6 +116,17 @@ pub(super) async fn annotate_nodes_with_details(
|
||||
current_interval,
|
||||
);
|
||||
|
||||
let node_performance = if let Some(storage) = storage {
|
||||
storage
|
||||
.construct_mixnode_report(mixnode.mix_id())
|
||||
.await
|
||||
.map(NodePerformance::from)
|
||||
.ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
.unwrap_or_default();
|
||||
|
||||
let (estimated_operator_apy, estimated_delegators_apy) =
|
||||
compute_apy_from_reward(&mixnode, reward_estimate, current_interval);
|
||||
|
||||
@@ -129,6 +139,7 @@ pub(super) async fn annotate_nodes_with_details(
|
||||
stake_saturation,
|
||||
uncapped_stake_saturation,
|
||||
performance,
|
||||
node_performance,
|
||||
estimated_operator_apy,
|
||||
estimated_delegators_apy,
|
||||
family,
|
||||
@@ -152,9 +163,21 @@ pub(crate) async fn annotate_gateways_with_details(
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
|
||||
let node_performance = if let Some(storage) = storage {
|
||||
storage
|
||||
.construct_gateway_report(gateway_bond.identity())
|
||||
.await
|
||||
.map(NodePerformance::from)
|
||||
.ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
.unwrap_or_default();
|
||||
|
||||
annotated.push(GatewayBondAnnotated {
|
||||
gateway_bond,
|
||||
performance,
|
||||
node_performance,
|
||||
});
|
||||
}
|
||||
annotated
|
||||
|
||||
@@ -8,26 +8,112 @@ use crate::{NodeStatusCache, NymContractCache};
|
||||
use cosmwasm_std::Decimal;
|
||||
use nym_api_requests::models::{
|
||||
AllInclusionProbabilitiesResponse, ComputeRewardEstParam, GatewayBondAnnotated,
|
||||
InclusionProbabilityResponse, MixNodeBondAnnotated, MixnodeCoreStatusResponse,
|
||||
MixnodeStatusReportResponse, MixnodeStatusResponse, MixnodeUptimeHistoryResponse,
|
||||
RewardEstimationResponse, StakeSaturationResponse, UptimeResponse,
|
||||
GatewayCoreStatusResponse, GatewayStatusReportResponse, GatewayUptimeHistoryResponse,
|
||||
GatewayUptimeResponse, InclusionProbabilityResponse, MixNodeBondAnnotated,
|
||||
MixnodeCoreStatusResponse, MixnodeStatusReportResponse, MixnodeStatusResponse,
|
||||
MixnodeUptimeHistoryResponse, RewardEstimationResponse, StakeSaturationResponse,
|
||||
UptimeResponse,
|
||||
};
|
||||
use nym_mixnet_contract_common::reward_params::Performance;
|
||||
use nym_mixnet_contract_common::{Interval, MixId, RewardedSetNodeStatus};
|
||||
use nym_mixnet_contract_common::{MixId, RewardedSetNodeStatus};
|
||||
use rocket::http::Status;
|
||||
use rocket::State;
|
||||
|
||||
use super::reward_estimate::compute_reward_estimate;
|
||||
|
||||
pub(crate) async fn _mixnode_report(
|
||||
async fn get_gateway_bond_annotated(
|
||||
cache: &NodeStatusCache,
|
||||
identity: &str,
|
||||
) -> Result<GatewayBondAnnotated, ErrorResponse> {
|
||||
let gateways = cache.gateways_annotated().await.ok_or(ErrorResponse::new(
|
||||
"no data available",
|
||||
Status::ServiceUnavailable,
|
||||
))?;
|
||||
|
||||
gateways
|
||||
.into_inner()
|
||||
.into_iter()
|
||||
.find(|gateway| gateway.identity() == identity)
|
||||
.ok_or(ErrorResponse::new(
|
||||
"mixnode bond not found",
|
||||
Status::NotFound,
|
||||
))
|
||||
}
|
||||
|
||||
async fn get_mixnode_bond_annotated(
|
||||
cache: &NodeStatusCache,
|
||||
mix_id: MixId,
|
||||
) -> Result<MixNodeBondAnnotated, ErrorResponse> {
|
||||
let mixnodes = cache.mixnodes_annotated().await.ok_or(ErrorResponse::new(
|
||||
"no data available",
|
||||
Status::ServiceUnavailable,
|
||||
))?;
|
||||
|
||||
mixnodes
|
||||
.into_inner()
|
||||
.into_iter()
|
||||
.find(|mixnode| mixnode.mix_id() == mix_id)
|
||||
.ok_or(ErrorResponse::new(
|
||||
"mixnode bond not found",
|
||||
Status::NotFound,
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) async fn _gateway_report(
|
||||
cache: &NodeStatusCache,
|
||||
identity: &str,
|
||||
) -> Result<GatewayStatusReportResponse, ErrorResponse> {
|
||||
let gateway = get_gateway_bond_annotated(cache, identity).await?;
|
||||
|
||||
Ok(GatewayStatusReportResponse {
|
||||
identity: gateway.identity().to_owned(),
|
||||
owner: gateway.owner().to_string(),
|
||||
most_recent: gateway.node_performance.most_recent.round_to_integer(),
|
||||
last_hour: gateway.node_performance.last_hour.round_to_integer(),
|
||||
last_day: gateway.node_performance.last_24h.round_to_integer(),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) async fn _gateway_uptime_history(
|
||||
storage: &NymApiStorage,
|
||||
identity: &str,
|
||||
) -> Result<GatewayUptimeHistoryResponse, ErrorResponse> {
|
||||
storage
|
||||
.get_gateway_uptime_history(identity)
|
||||
.await
|
||||
.map(GatewayUptimeHistoryResponse::from)
|
||||
.map_err(|err| ErrorResponse::new(err.to_string(), Status::NotFound))
|
||||
}
|
||||
|
||||
pub(crate) async fn _gateway_core_status_count(
|
||||
storage: &State<NymApiStorage>,
|
||||
identity: &str,
|
||||
since: Option<i64>,
|
||||
) -> Result<GatewayCoreStatusResponse, ErrorResponse> {
|
||||
let count = storage
|
||||
.get_core_gateway_status_count(identity, since)
|
||||
.await
|
||||
.map_err(|err| ErrorResponse::new(err.to_string(), Status::NotFound))?;
|
||||
|
||||
Ok(GatewayCoreStatusResponse {
|
||||
identity: identity.to_string(),
|
||||
count,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) async fn _mixnode_report(
|
||||
cache: &NodeStatusCache,
|
||||
mix_id: MixId,
|
||||
) -> Result<MixnodeStatusReportResponse, ErrorResponse> {
|
||||
storage
|
||||
.construct_mixnode_report(mix_id)
|
||||
.await
|
||||
.map(MixnodeStatusReportResponse::from)
|
||||
.map_err(|err| ErrorResponse::new(err.to_string(), Status::NotFound))
|
||||
let mixnode = get_mixnode_bond_annotated(cache, mix_id).await?;
|
||||
|
||||
Ok(MixnodeStatusReportResponse {
|
||||
mix_id,
|
||||
identity: mixnode.identity_key().to_owned(),
|
||||
owner: mixnode.owner().to_string(),
|
||||
most_recent: mixnode.node_performance.most_recent.round_to_integer(),
|
||||
last_hour: mixnode.node_performance.last_hour.round_to_integer(),
|
||||
last_day: mixnode.node_performance.last_24h.round_to_integer(),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) async fn _mixnode_uptime_history(
|
||||
@@ -103,21 +189,6 @@ pub(crate) async fn _get_mixnode_reward_estimation(
|
||||
}
|
||||
}
|
||||
|
||||
async fn average_mixnode_performance(
|
||||
mix_id: MixId,
|
||||
current_interval: Interval,
|
||||
storage: &NymApiStorage,
|
||||
) -> Result<Performance, ErrorResponse> {
|
||||
storage
|
||||
.get_average_mixnode_uptime_in_the_last_24hrs(
|
||||
mix_id,
|
||||
current_interval.current_epoch_end_unix_timestamp(),
|
||||
)
|
||||
.await
|
||||
.map_err(|err| ErrorResponse::new(err.to_string(), Status::NotFound))
|
||||
.map(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) async fn _compute_mixnode_reward_estimation(
|
||||
user_reward_param: ComputeRewardEstParam,
|
||||
cache: &NodeStatusCache,
|
||||
@@ -254,21 +325,28 @@ pub(crate) async fn _get_mixnode_inclusion_probability(
|
||||
}
|
||||
|
||||
pub(crate) async fn _get_mixnode_avg_uptime(
|
||||
cache: &NymContractCache,
|
||||
storage: &NymApiStorage,
|
||||
cache: &NodeStatusCache,
|
||||
mix_id: MixId,
|
||||
) -> Result<UptimeResponse, ErrorResponse> {
|
||||
let current_interval = cache
|
||||
.current_interval()
|
||||
.await
|
||||
.into_inner()
|
||||
.ok_or_else(|| ErrorResponse::new("server error", Status::InternalServerError))?;
|
||||
|
||||
let performance = average_mixnode_performance(mix_id, current_interval, storage).await?;
|
||||
let mixnode = get_mixnode_bond_annotated(cache, mix_id).await?;
|
||||
|
||||
Ok(UptimeResponse {
|
||||
mix_id,
|
||||
avg_uptime: performance.round_to_integer(),
|
||||
avg_uptime: mixnode.node_performance.last_24h.round_to_integer(),
|
||||
node_performance: mixnode.node_performance,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) async fn _get_gateway_avg_uptime(
|
||||
cache: &NodeStatusCache,
|
||||
identity: &str,
|
||||
) -> Result<GatewayUptimeResponse, ErrorResponse> {
|
||||
let gateway = get_gateway_bond_annotated(cache, identity).await?;
|
||||
|
||||
Ok(GatewayUptimeResponse {
|
||||
identity: identity.to_string(),
|
||||
avg_uptime: gateway.node_performance.last_24h.round_to_integer(),
|
||||
node_performance: gateway.node_performance,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -288,7 +366,7 @@ pub(crate) async fn _get_mixnode_inclusion_probabilities(
|
||||
})
|
||||
} else {
|
||||
Err(ErrorResponse::new(
|
||||
"No data available".to_string(),
|
||||
"No data available",
|
||||
Status::ServiceUnavailable,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ pub(crate) fn node_status_routes(
|
||||
routes::get_mixnode_stake_saturation,
|
||||
routes::get_mixnode_inclusion_probability,
|
||||
routes::get_mixnode_avg_uptime,
|
||||
routes::get_gateway_avg_uptime,
|
||||
routes::get_mixnode_inclusion_probabilities,
|
||||
routes::get_mixnodes_detailed,
|
||||
routes::get_rewarded_set_detailed,
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::node_status_api::utils::NodeUptimes;
|
||||
use crate::storage::models::NodeStatus;
|
||||
use nym_api_requests::models::{
|
||||
GatewayStatusReportResponse, GatewayUptimeHistoryResponse, HistoricalUptimeResponse,
|
||||
MixnodeStatusReportResponse, MixnodeUptimeHistoryResponse, RequestError,
|
||||
MixnodeStatusReportResponse, MixnodeUptimeHistoryResponse, NodePerformance, RequestError,
|
||||
};
|
||||
use nym_mixnet_contract_common::reward_params::Performance;
|
||||
use nym_mixnet_contract_common::{IdentityKey, MixId};
|
||||
@@ -179,6 +179,16 @@ impl From<MixnodeStatusReport> for MixnodeStatusReportResponse {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MixnodeStatusReport> for NodePerformance {
|
||||
fn from(report: MixnodeStatusReport) -> Self {
|
||||
NodePerformance {
|
||||
most_recent: report.most_recent.into(),
|
||||
last_hour: report.last_hour.into(),
|
||||
last_24h: report.last_day.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)]
|
||||
pub struct GatewayStatusReport {
|
||||
pub(crate) identity: String,
|
||||
@@ -228,6 +238,16 @@ impl From<GatewayStatusReport> for GatewayStatusReportResponse {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GatewayStatusReport> for NodePerformance {
|
||||
fn from(report: GatewayStatusReport) -> Self {
|
||||
NodePerformance {
|
||||
most_recent: report.most_recent.into(),
|
||||
last_hour: report.last_hour.into(),
|
||||
last_24h: report.last_day.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)]
|
||||
pub struct MixnodeUptimeHistory {
|
||||
pub(crate) mix_id: MixId,
|
||||
|
||||
@@ -4,11 +4,13 @@
|
||||
use super::helpers::_get_gateways_detailed;
|
||||
use super::NodeStatusCache;
|
||||
use crate::node_status_api::helpers::{
|
||||
_compute_mixnode_reward_estimation, _get_active_set_detailed, _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, _get_rewarded_set_detailed, _mixnode_core_status_count,
|
||||
_mixnode_report, _mixnode_uptime_history,
|
||||
_compute_mixnode_reward_estimation, _gateway_core_status_count, _gateway_report,
|
||||
_gateway_uptime_history, _get_active_set_detailed, _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,
|
||||
_get_rewarded_set_detailed, _mixnode_core_status_count, _mixnode_report,
|
||||
_mixnode_uptime_history,
|
||||
};
|
||||
use crate::node_status_api::models::ErrorResponse;
|
||||
use crate::storage::NymApiStorage;
|
||||
@@ -16,12 +18,12 @@ use crate::NymContractCache;
|
||||
use nym_api_requests::models::{
|
||||
AllInclusionProbabilitiesResponse, ComputeRewardEstParam, GatewayBondAnnotated,
|
||||
GatewayCoreStatusResponse, GatewayStatusReportResponse, GatewayUptimeHistoryResponse,
|
||||
InclusionProbabilityResponse, MixNodeBondAnnotated, MixnodeCoreStatusResponse,
|
||||
MixnodeStatusReportResponse, MixnodeStatusResponse, MixnodeUptimeHistoryResponse,
|
||||
RewardEstimationResponse, StakeSaturationResponse, UptimeResponse,
|
||||
GatewayUptimeResponse, InclusionProbabilityResponse, MixNodeBondAnnotated,
|
||||
MixnodeCoreStatusResponse, MixnodeStatusReportResponse, MixnodeStatusResponse,
|
||||
MixnodeUptimeHistoryResponse, RewardEstimationResponse, StakeSaturationResponse,
|
||||
UptimeResponse,
|
||||
};
|
||||
use nym_mixnet_contract_common::MixId;
|
||||
use rocket::http::Status;
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::State;
|
||||
use rocket_okapi::openapi;
|
||||
@@ -29,15 +31,10 @@ use rocket_okapi::openapi;
|
||||
#[openapi(tag = "status")]
|
||||
#[get("/gateway/<identity>/report")]
|
||||
pub(crate) async fn gateway_report(
|
||||
storage: &State<NymApiStorage>,
|
||||
cache: &State<NodeStatusCache>,
|
||||
identity: &str,
|
||||
) -> Result<Json<GatewayStatusReportResponse>, ErrorResponse> {
|
||||
storage
|
||||
.construct_gateway_report(identity)
|
||||
.await
|
||||
.map(GatewayStatusReportResponse::from)
|
||||
.map(Json)
|
||||
.map_err(|err| ErrorResponse::new(err.to_string(), Status::NotFound))
|
||||
Ok(Json(_gateway_report(cache, identity).await?))
|
||||
}
|
||||
|
||||
#[openapi(tag = "status")]
|
||||
@@ -46,12 +43,7 @@ pub(crate) async fn gateway_uptime_history(
|
||||
storage: &State<NymApiStorage>,
|
||||
identity: &str,
|
||||
) -> Result<Json<GatewayUptimeHistoryResponse>, ErrorResponse> {
|
||||
storage
|
||||
.get_gateway_uptime_history(identity)
|
||||
.await
|
||||
.map(GatewayUptimeHistoryResponse::from)
|
||||
.map(Json)
|
||||
.map_err(|err| ErrorResponse::new(err.to_string(), Status::NotFound))
|
||||
Ok(Json(_gateway_uptime_history(storage, identity).await?))
|
||||
}
|
||||
|
||||
#[openapi(tag = "status")]
|
||||
@@ -60,25 +52,19 @@ pub(crate) async fn gateway_core_status_count(
|
||||
storage: &State<NymApiStorage>,
|
||||
identity: &str,
|
||||
since: Option<i64>,
|
||||
) -> Json<GatewayCoreStatusResponse> {
|
||||
let count = storage
|
||||
.get_core_gateway_status_count(identity, since)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
|
||||
Json(GatewayCoreStatusResponse {
|
||||
identity: identity.to_string(),
|
||||
count,
|
||||
})
|
||||
) -> Result<Json<GatewayCoreStatusResponse>, ErrorResponse> {
|
||||
Ok(Json(
|
||||
_gateway_core_status_count(storage, identity, since).await?,
|
||||
))
|
||||
}
|
||||
|
||||
#[openapi(tag = "status")]
|
||||
#[get("/mixnode/<mix_id>/report")]
|
||||
pub(crate) async fn mixnode_report(
|
||||
storage: &State<NymApiStorage>,
|
||||
cache: &State<NodeStatusCache>,
|
||||
mix_id: MixId,
|
||||
) -> Result<Json<MixnodeStatusReportResponse>, ErrorResponse> {
|
||||
Ok(Json(_mixnode_report(storage, mix_id).await?))
|
||||
Ok(Json(_mixnode_report(cache, mix_id).await?))
|
||||
}
|
||||
|
||||
#[openapi(tag = "status")]
|
||||
@@ -171,11 +157,19 @@ pub(crate) async fn get_mixnode_inclusion_probability(
|
||||
#[openapi(tag = "status")]
|
||||
#[get("/mixnode/<mix_id>/avg_uptime")]
|
||||
pub(crate) async fn get_mixnode_avg_uptime(
|
||||
cache: &State<NymContractCache>,
|
||||
storage: &State<NymApiStorage>,
|
||||
cache: &State<NodeStatusCache>,
|
||||
mix_id: MixId,
|
||||
) -> Result<Json<UptimeResponse>, ErrorResponse> {
|
||||
Ok(Json(_get_mixnode_avg_uptime(cache, storage, mix_id).await?))
|
||||
Ok(Json(_get_mixnode_avg_uptime(cache, mix_id).await?))
|
||||
}
|
||||
|
||||
#[openapi(tag = "status")]
|
||||
#[get("/gateway/<identity>/avg_uptime")]
|
||||
pub(crate) async fn get_gateway_avg_uptime(
|
||||
cache: &State<NodeStatusCache>,
|
||||
identity: &str,
|
||||
) -> Result<Json<GatewayUptimeResponse>, ErrorResponse> {
|
||||
Ok(Json(_get_gateway_avg_uptime(cache, identity).await?))
|
||||
}
|
||||
|
||||
#[openapi(tag = "status")]
|
||||
|
||||
Reference in New Issue
Block a user