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:
Tommy Verrall
2024-05-21 17:28:07 +01:00
committed by GitHub
5 changed files with 101 additions and 65 deletions
+5 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
+6 -27
View File
@@ -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()
}
+12
View File
@@ -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