Merge pull request #4599 from nymtech/feature/expose-all-api-reports
nym-api: make report/avg_uptime endpoints ignore blacklist
This commit is contained in:
+5
-2
@@ -2,6 +2,9 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use nym_api_requests::models::{GatewayBondAnnotated, MixNodeBondAnnotated};
|
||||
use nym_contracts_common::IdentityKey;
|
||||
use nym_mixnet_contract_common::MixId;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::support::caching::Cache;
|
||||
|
||||
@@ -9,11 +12,11 @@ use super::inclusion_probabilities::InclusionProbabilities;
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct NodeStatusCacheData {
|
||||
pub(crate) mixnodes_annotated: Cache<Vec<MixNodeBondAnnotated>>,
|
||||
pub(crate) mixnodes_annotated: Cache<HashMap<MixId, MixNodeBondAnnotated>>,
|
||||
pub(crate) rewarded_set_annotated: Cache<Vec<MixNodeBondAnnotated>>,
|
||||
pub(crate) active_set_annotated: Cache<Vec<MixNodeBondAnnotated>>,
|
||||
|
||||
pub(crate) gateways_annotated: Cache<Vec<GatewayBondAnnotated>>,
|
||||
pub(crate) gateways_annotated: Cache<HashMap<IdentityKey, GatewayBondAnnotated>>,
|
||||
|
||||
// Estimated active set inclusion probabilities from Monte Carlo simulation
|
||||
pub(crate) inclusion_probabilities: Cache<InclusionProbabilities>,
|
||||
|
||||
+48
-12
@@ -1,13 +1,14 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::support::caching::Cache;
|
||||
|
||||
use self::data::NodeStatusCacheData;
|
||||
use self::inclusion_probabilities::InclusionProbabilities;
|
||||
use crate::support::caching::Cache;
|
||||
use nym_api_requests::models::{GatewayBondAnnotated, MixNodeBondAnnotated, MixnodeStatus};
|
||||
use nym_contracts_common::{IdentityKey, IdentityKeyRef};
|
||||
use nym_mixnet_contract_common::MixId;
|
||||
use rocket::fairing::AdHoc;
|
||||
use std::collections::HashMap;
|
||||
use std::{sync::Arc, time::Duration};
|
||||
use thiserror::Error;
|
||||
use tokio::sync::RwLockReadGuard;
|
||||
@@ -55,10 +56,10 @@ impl NodeStatusCache {
|
||||
/// Updates the cache with the latest data.
|
||||
async fn update(
|
||||
&self,
|
||||
mixnodes: Vec<MixNodeBondAnnotated>,
|
||||
mixnodes: HashMap<MixId, MixNodeBondAnnotated>,
|
||||
rewarded_set: Vec<MixNodeBondAnnotated>,
|
||||
active_set: Vec<MixNodeBondAnnotated>,
|
||||
gateways: Vec<GatewayBondAnnotated>,
|
||||
gateways: HashMap<IdentityKey, GatewayBondAnnotated>,
|
||||
inclusion_probabilities: InclusionProbabilities,
|
||||
) {
|
||||
match time::timeout(Duration::from_millis(CACHE_TIMEOUT_MS), self.inner.write()).await {
|
||||
@@ -76,7 +77,7 @@ impl NodeStatusCache {
|
||||
}
|
||||
|
||||
/// Returns a copy of the current cache data.
|
||||
async fn get<T>(
|
||||
async fn get_owned<T>(
|
||||
&self,
|
||||
fn_arg: impl FnOnce(RwLockReadGuard<'_, NodeStatusCacheData>) -> Cache<T>,
|
||||
) -> Option<Cache<T>> {
|
||||
@@ -89,8 +90,24 @@ impl NodeStatusCache {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn mixnodes_annotated_full(&self) -> Option<Cache<Vec<MixNodeBondAnnotated>>> {
|
||||
self.get(|c| c.mixnodes_annotated.clone_cache()).await
|
||||
async fn get<'a, T: 'a>(
|
||||
&'a self,
|
||||
fn_arg: impl FnOnce(&NodeStatusCacheData) -> &Cache<T>,
|
||||
) -> Option<RwLockReadGuard<'a, Cache<T>>> {
|
||||
match time::timeout(Duration::from_millis(CACHE_TIMEOUT_MS), self.inner.read()).await {
|
||||
Ok(cache) => Some(RwLockReadGuard::map(cache, |item| fn_arg(item))),
|
||||
Err(e) => {
|
||||
error!("{e}");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn mixnodes_annotated_full(&self) -> Option<Vec<MixNodeBondAnnotated>> {
|
||||
let mixnodes = self.get(|c| &c.mixnodes_annotated).await?;
|
||||
|
||||
// just clone everything and return the vec to work with the existing code
|
||||
Some(mixnodes.values().cloned().collect())
|
||||
}
|
||||
|
||||
pub(crate) async fn mixnodes_annotated_filtered(&self) -> Option<Vec<MixNodeBondAnnotated>> {
|
||||
@@ -98,16 +115,26 @@ impl NodeStatusCache {
|
||||
Some(full.iter().filter(|m| !m.blacklisted).cloned().collect())
|
||||
}
|
||||
|
||||
pub(crate) async fn mixnode_annotated(&self, mix_id: MixId) -> Option<MixNodeBondAnnotated> {
|
||||
let mixnodes = self.get(|c| &c.mixnodes_annotated).await?;
|
||||
mixnodes.get(&mix_id).cloned()
|
||||
}
|
||||
|
||||
pub(crate) async fn rewarded_set_annotated(&self) -> Option<Cache<Vec<MixNodeBondAnnotated>>> {
|
||||
self.get(|c| c.rewarded_set_annotated.clone_cache()).await
|
||||
self.get_owned(|c| c.rewarded_set_annotated.clone_cache())
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn active_set_annotated(&self) -> Option<Cache<Vec<MixNodeBondAnnotated>>> {
|
||||
self.get(|c| c.active_set_annotated.clone_cache()).await
|
||||
self.get_owned(|c| c.active_set_annotated.clone_cache())
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn gateways_annotated_full(&self) -> Option<Cache<Vec<GatewayBondAnnotated>>> {
|
||||
self.get(|c| c.gateways_annotated.clone_cache()).await
|
||||
pub(crate) async fn gateways_annotated_full(&self) -> Option<Vec<GatewayBondAnnotated>> {
|
||||
let gateways = self.get(|c| &c.gateways_annotated).await?;
|
||||
|
||||
// just clone everything and return the vec to work with the existing code
|
||||
Some(gateways.values().cloned().collect())
|
||||
}
|
||||
|
||||
pub(crate) async fn gateways_annotated_filtered(&self) -> Option<Vec<GatewayBondAnnotated>> {
|
||||
@@ -115,8 +142,17 @@ impl NodeStatusCache {
|
||||
Some(full.iter().filter(|m| !m.blacklisted).cloned().collect())
|
||||
}
|
||||
|
||||
pub(crate) async fn gateway_annotated(
|
||||
&self,
|
||||
gateway_id: IdentityKeyRef<'_>,
|
||||
) -> Option<GatewayBondAnnotated> {
|
||||
let gateways = self.get(|c| &c.gateways_annotated).await?;
|
||||
gateways.get(gateway_id).cloned()
|
||||
}
|
||||
|
||||
pub(crate) async fn inclusion_probabilities(&self) -> Option<Cache<InclusionProbabilities>> {
|
||||
self.get(|c| c.inclusion_probabilities.clone_cache()).await
|
||||
self.get_owned(|c| c.inclusion_probabilities.clone_cache())
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn mixnode_details(
|
||||
|
||||
+30
-24
@@ -28,11 +28,11 @@ pub(super) fn to_rewarded_set_node_status(
|
||||
}
|
||||
|
||||
pub(super) fn split_into_active_and_rewarded_set(
|
||||
mixnodes_annotated: &[MixNodeBondAnnotated],
|
||||
mixnodes_annotated: &HashMap<MixId, MixNodeBondAnnotated>,
|
||||
rewarded_set_node_status: &HashMap<u32, RewardedSetNodeStatus>,
|
||||
) -> (Vec<MixNodeBondAnnotated>, Vec<MixNodeBondAnnotated>) {
|
||||
let rewarded_set: Vec<_> = mixnodes_annotated
|
||||
.iter()
|
||||
.values()
|
||||
.filter(|mixnode| rewarded_set_node_status.get(&mixnode.mix_id()).is_some())
|
||||
.cloned()
|
||||
.collect();
|
||||
@@ -88,12 +88,12 @@ pub(super) async fn annotate_nodes_with_details(
|
||||
rewarded_set: &HashMap<MixId, RewardedSetNodeStatus>,
|
||||
mix_to_family: Vec<(IdentityKey, FamilyHead)>,
|
||||
blacklist: &HashSet<MixId>,
|
||||
) -> Vec<MixNodeBondAnnotated> {
|
||||
) -> HashMap<MixId, MixNodeBondAnnotated> {
|
||||
let mix_to_family = mix_to_family
|
||||
.into_iter()
|
||||
.collect::<HashMap<IdentityKey, FamilyHead>>();
|
||||
|
||||
let mut annotated = Vec::new();
|
||||
let mut annotated = HashMap::new();
|
||||
for mixnode in mixnodes {
|
||||
let stake_saturation = mixnode
|
||||
.rewarding_details
|
||||
@@ -138,17 +138,20 @@ pub(super) async fn annotate_nodes_with_details(
|
||||
.get(mixnode.bond_information.identity())
|
||||
.cloned();
|
||||
|
||||
annotated.push(MixNodeBondAnnotated {
|
||||
blacklisted: blacklist.contains(&mixnode.mix_id()),
|
||||
mixnode_details: mixnode,
|
||||
stake_saturation,
|
||||
uncapped_stake_saturation,
|
||||
performance,
|
||||
node_performance,
|
||||
estimated_operator_apy,
|
||||
estimated_delegators_apy,
|
||||
family,
|
||||
});
|
||||
annotated.insert(
|
||||
mixnode.mix_id(),
|
||||
MixNodeBondAnnotated {
|
||||
blacklisted: blacklist.contains(&mixnode.mix_id()),
|
||||
mixnode_details: mixnode,
|
||||
stake_saturation,
|
||||
uncapped_stake_saturation,
|
||||
performance,
|
||||
node_performance,
|
||||
estimated_operator_apy,
|
||||
estimated_delegators_apy,
|
||||
family,
|
||||
},
|
||||
);
|
||||
}
|
||||
annotated
|
||||
}
|
||||
@@ -158,8 +161,8 @@ pub(crate) async fn annotate_gateways_with_details(
|
||||
gateway_bonds: Vec<GatewayBond>,
|
||||
current_interval: Interval,
|
||||
blacklist: &HashSet<IdentityKey>,
|
||||
) -> Vec<GatewayBondAnnotated> {
|
||||
let mut annotated = Vec::new();
|
||||
) -> HashMap<IdentityKey, GatewayBondAnnotated> {
|
||||
let mut annotated = HashMap::new();
|
||||
for gateway_bond in gateway_bonds {
|
||||
let performance = get_gateway_performance_from_storage(
|
||||
storage,
|
||||
@@ -180,13 +183,16 @@ pub(crate) async fn annotate_gateways_with_details(
|
||||
}
|
||||
.unwrap_or_default();
|
||||
|
||||
annotated.push(GatewayBondAnnotated {
|
||||
blacklisted: blacklist.contains(&gateway_bond.gateway.identity_key),
|
||||
gateway_bond,
|
||||
self_described: None,
|
||||
performance,
|
||||
node_performance,
|
||||
});
|
||||
annotated.insert(
|
||||
gateway_bond.identity().to_string(),
|
||||
GatewayBondAnnotated {
|
||||
blacklisted: blacklist.contains(&gateway_bond.gateway.identity_key),
|
||||
gateway_bond,
|
||||
self_described: None,
|
||||
performance,
|
||||
node_performance,
|
||||
},
|
||||
);
|
||||
}
|
||||
annotated
|
||||
}
|
||||
|
||||
@@ -24,14 +24,9 @@ async fn get_gateway_bond_annotated(
|
||||
cache: &NodeStatusCache,
|
||||
identity: &str,
|
||||
) -> Result<GatewayBondAnnotated, ErrorResponse> {
|
||||
let gateways = cache
|
||||
.gateways_annotated_filtered()
|
||||
cache
|
||||
.gateway_annotated(identity)
|
||||
.await
|
||||
.ok_or_else(|| ErrorResponse::new("no data available", Status::ServiceUnavailable))?;
|
||||
|
||||
gateways
|
||||
.into_iter()
|
||||
.find(|gateway| gateway.identity() == identity)
|
||||
.ok_or(ErrorResponse::new(
|
||||
"gateway bond not found",
|
||||
Status::NotFound,
|
||||
@@ -42,17 +37,9 @@ async fn get_mixnode_bond_annotated(
|
||||
cache: &NodeStatusCache,
|
||||
mix_id: MixId,
|
||||
) -> Result<MixNodeBondAnnotated, ErrorResponse> {
|
||||
let mixnodes = cache
|
||||
.mixnodes_annotated_filtered()
|
||||
cache
|
||||
.mixnode_annotated(mix_id)
|
||||
.await
|
||||
.ok_or(ErrorResponse::new(
|
||||
"no data available",
|
||||
Status::ServiceUnavailable,
|
||||
))?;
|
||||
|
||||
mixnodes
|
||||
.into_iter()
|
||||
.find(|mixnode| mixnode.mix_id() == mix_id)
|
||||
.ok_or(ErrorResponse::new(
|
||||
"mixnode bond not found",
|
||||
Status::NotFound,
|
||||
@@ -383,11 +370,7 @@ pub(crate) async fn _get_mixnodes_detailed(cache: &NodeStatusCache) -> Vec<MixNo
|
||||
pub(crate) async fn _get_mixnodes_detailed_unfiltered(
|
||||
cache: &NodeStatusCache,
|
||||
) -> Vec<MixNodeBondAnnotated> {
|
||||
cache
|
||||
.mixnodes_annotated_full()
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.into_inner()
|
||||
cache.mixnodes_annotated_full().await.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub(crate) async fn _get_rewarded_set_detailed(
|
||||
@@ -418,9 +401,5 @@ pub(crate) async fn _get_gateways_detailed(cache: &NodeStatusCache) -> Vec<Gatew
|
||||
pub(crate) async fn _get_gateways_detailed_unfiltered(
|
||||
cache: &NodeStatusCache,
|
||||
) -> Vec<GatewayBondAnnotated> {
|
||||
cache
|
||||
.gateways_annotated_full()
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.into_inner()
|
||||
cache.gateways_annotated_full().await.unwrap_or_default()
|
||||
}
|
||||
|
||||
@@ -94,6 +94,18 @@ impl<T> Cache<T> {
|
||||
}
|
||||
}
|
||||
|
||||
// I know, it's dead code for now, but I feel it could be useful code in the future
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn map<F, U>(this: Self, f: F) -> Cache<U>
|
||||
where
|
||||
F: FnOnce(T) -> U,
|
||||
{
|
||||
Cache {
|
||||
value: f(this.value),
|
||||
as_at: this.as_at,
|
||||
}
|
||||
}
|
||||
|
||||
// ugh. I hate to expose it, but it'd have broken pre-existing code
|
||||
pub(crate) fn clone_cache(&self) -> Self
|
||||
where
|
||||
|
||||
Reference in New Issue
Block a user