Feature/explorer 2979 (#3147)
* additional unfiltered endpoints for nym-api * add poor performance UI * display appropriate UI when node is blacklisted * update explorer api with blacklisted nodes * add new unfiltered endpoint add new unfiltered endpoint * show blacklisted detail even when node description is unavailable remove console.log --------- Co-authored-by: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com>
This commit is contained in:
@@ -749,6 +749,12 @@ where
|
||||
Ok(self.nym_api.get_mixnodes_detailed().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_mixnodes_detailed_unfiltered(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorClientError> {
|
||||
Ok(self.nym_api.get_mixnodes_detailed_unfiltered().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_rewarded_mixnodes(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
|
||||
|
||||
@@ -144,6 +144,21 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_mixnodes_detailed_unfiltered(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
|
||||
self.query_nym_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS,
|
||||
routes::MIXNODES,
|
||||
routes::DETAILED_UNFILTERED,
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_gateways(&self) -> Result<Vec<GatewayBond>, NymAPIError> {
|
||||
self.query_nym_api(&[routes::API_VERSION, routes::GATEWAYS], NO_PARAMS)
|
||||
.await
|
||||
|
||||
@@ -8,6 +8,7 @@ pub const MIXNODES: &str = "mixnodes";
|
||||
pub const GATEWAYS: &str = "gateways";
|
||||
|
||||
pub const DETAILED: &str = "detailed";
|
||||
pub const DETAILED_UNFILTERED: &str = "detailed-unfiltered";
|
||||
pub const ACTIVE: &str = "active";
|
||||
pub const REWARDED: &str = "rewarded";
|
||||
pub const COCONUT_ROUTES: &str = "coconut";
|
||||
|
||||
@@ -42,6 +42,7 @@ pub(crate) struct PrettyDetailedMixNodeBond {
|
||||
pub operating_cost: Coin,
|
||||
pub profit_margin_percent: Percent,
|
||||
pub family_id: Option<u16>,
|
||||
pub blacklisted: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, JsonSchema)]
|
||||
|
||||
@@ -162,6 +162,7 @@ impl ThreadsafeMixNodesCache {
|
||||
operating_cost: rewarding_info.cost_params.interval_operating_cost.clone(),
|
||||
profit_margin_percent: rewarding_info.cost_params.profit_margin_percent,
|
||||
family_id,
|
||||
blacklisted: node.blacklisted,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ impl ExplorerApiTasks {
|
||||
|
||||
async fn retrieve_all_mixnodes(&self) -> Vec<MixNodeBondAnnotated> {
|
||||
info!("About to retrieve all mixnode bonds...");
|
||||
self.retrieve_mixnodes(validator_client::Client::get_cached_mixnodes_detailed)
|
||||
self.retrieve_mixnodes(validator_client::Client::get_cached_mixnodes_detailed_unfiltered)
|
||||
.await
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,6 @@ export const DelegatorsInfoTable: FCWithChildren<UniversalTableProps<EconomicsIn
|
||||
{columnsData?.map((_, index: number) => {
|
||||
const { field } = columnsData[index];
|
||||
const value: EconomicsRowsType = (eachRow as any)[field];
|
||||
console.log(value);
|
||||
return (
|
||||
<TableCell
|
||||
key={_.title}
|
||||
|
||||
@@ -20,6 +20,7 @@ export type MixnodeRowType = {
|
||||
stake_saturation: React.ReactNode;
|
||||
operating_cost: string;
|
||||
node_performance: NodePerformance['most_recent'];
|
||||
blacklisted: boolean;
|
||||
};
|
||||
|
||||
export function mixnodeToGridRow(arrayOfMixnodes?: MixNodeResponse): MixnodeRowType[] {
|
||||
@@ -51,5 +52,6 @@ export function mixNodeResponseItemToMixnodeRowType(item: MixNodeResponseItem):
|
||||
stake_saturation: uncappedSaturation.toFixed(2),
|
||||
operating_cost: `${unymToNym(item.operating_cost?.amount, 6)} NYM`,
|
||||
node_performance: `${toPercentIntegerString(item.node_performance.most_recent)}%`,
|
||||
blacklisted: item.blacklisted,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import { WorldMap } from '../../components/WorldMap';
|
||||
import { MixNodeDetailSection } from '../../components/MixNodes/DetailSection';
|
||||
import { MixnodeContextProvider, useMixnodeContext } from '../../context/mixnode';
|
||||
import { Title } from '../../components/Title';
|
||||
import { useIsMobile } from '../../hooks/useIsMobile';
|
||||
|
||||
const columns: ColumnsType[] = [
|
||||
{
|
||||
@@ -64,7 +65,7 @@ const columns: ColumnsType[] = [
|
||||
*/
|
||||
const PageMixnodeDetailWithState: FCWithChildren = () => {
|
||||
const { mixNode, mixNodeRow, description, stats, status, uptimeStory, uniqDelegations } = useMixnodeContext();
|
||||
console.log(mixNodeRow);
|
||||
const isMobile = useIsMobile();
|
||||
return (
|
||||
<Box component="main">
|
||||
<Title text="Mixnode Detail" />
|
||||
@@ -73,6 +74,11 @@ const PageMixnodeDetailWithState: FCWithChildren = () => {
|
||||
{mixNodeRow && description?.data && (
|
||||
<MixNodeDetailSection mixNodeRow={mixNodeRow} mixnodeDescription={description.data} />
|
||||
)}
|
||||
{mixNodeRow?.blacklisted && (
|
||||
<Typography textAlign={isMobile ? 'left' : 'right'} fontSize="smaller" sx={{ color: 'error.main' }}>
|
||||
This node is having a poor performance
|
||||
</Typography>
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid container>
|
||||
|
||||
@@ -89,6 +89,7 @@ export interface MixNodeResponseItem {
|
||||
uncapped_saturation: number;
|
||||
operating_cost: Amount;
|
||||
profit_margin_percent: string;
|
||||
blacklisted: boolean;
|
||||
}
|
||||
|
||||
export type MixNodeResponse = MixNodeResponseItem[];
|
||||
|
||||
@@ -111,6 +111,7 @@ pub struct MixNodeBondAnnotated {
|
||||
pub estimated_operator_apy: Decimal,
|
||||
pub estimated_delegators_apy: Decimal,
|
||||
pub family: Option<FamilyHead>,
|
||||
pub blacklisted: bool,
|
||||
}
|
||||
|
||||
impl MixNodeBondAnnotated {
|
||||
@@ -137,6 +138,7 @@ pub struct GatewayBondAnnotated {
|
||||
// NOTE: the performance field is deprecated in favour of node_performance
|
||||
pub performance: Performance,
|
||||
pub node_performance: NodePerformance,
|
||||
pub blacklisted: bool,
|
||||
}
|
||||
|
||||
impl GatewayBondAnnotated {
|
||||
|
||||
@@ -102,7 +102,7 @@ impl RewardedSetUpdater {
|
||||
|
||||
let epoch_end = interval.current_epoch_end();
|
||||
|
||||
let all_mixnodes = self.nym_contract_cache.mixnodes().await;
|
||||
let all_mixnodes = self.nym_contract_cache.mixnodes_filtered().await;
|
||||
if all_mixnodes.is_empty() {
|
||||
// that's a bit weird, but
|
||||
log::warn!("there don't seem to be any mixnodes on the network!")
|
||||
|
||||
+13
-3
@@ -89,10 +89,15 @@ impl NodeStatusCache {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn mixnodes_annotated(&self) -> Option<Cache<Vec<MixNodeBondAnnotated>>> {
|
||||
pub(crate) async fn mixnodes_annotated_full(&self) -> Option<Cache<Vec<MixNodeBondAnnotated>>> {
|
||||
self.get(|c| c.mixnodes_annotated.clone()).await
|
||||
}
|
||||
|
||||
pub(crate) async fn mixnodes_annotated_filtered(&self) -> Option<Vec<MixNodeBondAnnotated>> {
|
||||
let full = self.mixnodes_annotated_full().await?;
|
||||
Some(full.value.into_iter().filter(|m| !m.blacklisted).collect())
|
||||
}
|
||||
|
||||
pub(crate) async fn rewarded_set_annotated(&self) -> Option<Cache<Vec<MixNodeBondAnnotated>>> {
|
||||
self.get(|c| c.rewarded_set_annotated.clone()).await
|
||||
}
|
||||
@@ -101,10 +106,15 @@ impl NodeStatusCache {
|
||||
self.get(|c| c.active_set_annotated.clone()).await
|
||||
}
|
||||
|
||||
pub(crate) async fn gateways_annotated(&self) -> Option<Cache<Vec<GatewayBondAnnotated>>> {
|
||||
pub(crate) async fn gateways_annotated_full(&self) -> Option<Cache<Vec<GatewayBondAnnotated>>> {
|
||||
self.get(|c| c.gateways_annotated.clone()).await
|
||||
}
|
||||
|
||||
pub(crate) async fn gateways_annotated_filtered(&self) -> Option<Vec<GatewayBondAnnotated>> {
|
||||
let full = self.gateways_annotated_full().await?;
|
||||
Some(full.value.into_iter().filter(|m| !m.blacklisted).collect())
|
||||
}
|
||||
|
||||
pub(crate) async fn inclusion_probabilities(&self) -> Option<Cache<InclusionProbabilities>> {
|
||||
self.get(|c| c.inclusion_probabilities.clone()).await
|
||||
}
|
||||
@@ -126,7 +136,7 @@ impl NodeStatusCache {
|
||||
return (Some(bond.clone()), MixnodeStatus::Standby);
|
||||
}
|
||||
|
||||
let all_bonded = &self.mixnodes_annotated().await.unwrap().into_inner();
|
||||
let all_bonded = &self.mixnodes_annotated_filtered().await.unwrap();
|
||||
if let Some(bond) = all_bonded.iter().find(|mix| mix.mix_id() == mix_id) {
|
||||
(Some(bond.clone()), MixnodeStatus::Inactive)
|
||||
} else {
|
||||
|
||||
+5
-1
@@ -6,7 +6,7 @@ use nym_mixnet_contract_common::{reward_params::Performance, Interval, MixId};
|
||||
use nym_mixnet_contract_common::{
|
||||
GatewayBond, IdentityKey, MixNodeDetails, RewardedSetNodeStatus, RewardingParams,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
pub(super) fn to_rewarded_set_node_status(
|
||||
rewarded_set: &[MixNodeDetails],
|
||||
@@ -84,6 +84,7 @@ pub(super) async fn annotate_nodes_with_details(
|
||||
current_interval: Interval,
|
||||
rewarded_set: &HashMap<MixId, RewardedSetNodeStatus>,
|
||||
mix_to_family: Vec<(IdentityKey, FamilyHead)>,
|
||||
blacklist: &HashSet<MixId>,
|
||||
) -> Vec<MixNodeBondAnnotated> {
|
||||
let mix_to_family = mix_to_family
|
||||
.into_iter()
|
||||
@@ -135,6 +136,7 @@ pub(super) async fn annotate_nodes_with_details(
|
||||
.cloned();
|
||||
|
||||
annotated.push(MixNodeBondAnnotated {
|
||||
blacklisted: blacklist.contains(&mixnode.mix_id()),
|
||||
mixnode_details: mixnode,
|
||||
stake_saturation,
|
||||
uncapped_stake_saturation,
|
||||
@@ -152,6 +154,7 @@ pub(crate) async fn annotate_gateways_with_details(
|
||||
storage: &Option<NymApiStorage>,
|
||||
gateway_bonds: Vec<GatewayBond>,
|
||||
current_interval: Interval,
|
||||
blacklist: &HashSet<IdentityKey>,
|
||||
) -> Vec<GatewayBondAnnotated> {
|
||||
let mut annotated = Vec::new();
|
||||
for gateway_bond in gateway_bonds {
|
||||
@@ -175,6 +178,7 @@ pub(crate) async fn annotate_gateways_with_details(
|
||||
.unwrap_or_default();
|
||||
|
||||
annotated.push(GatewayBondAnnotated {
|
||||
blacklisted: blacklist.contains(&gateway_bond.gateway.identity_key),
|
||||
gateway_bond,
|
||||
performance,
|
||||
node_performance,
|
||||
|
||||
+14
-4
@@ -109,13 +109,17 @@ impl NodeStatusCacheRefresher {
|
||||
log::info!("Updating node status cache");
|
||||
|
||||
// Fetch contract cache data to work with
|
||||
let mixnode_details = self.contract_cache.mixnodes().await;
|
||||
let mixnode_details = self.contract_cache.mixnodes_all().await;
|
||||
let interval_reward_params = self.contract_cache.interval_reward_params().await;
|
||||
let current_interval = self.contract_cache.current_interval().await;
|
||||
let rewarded_set = self.contract_cache.rewarded_set().await;
|
||||
let active_set = self.contract_cache.active_set().await;
|
||||
let mix_to_family = self.contract_cache.mix_to_family().await;
|
||||
let gateway_bonds = self.contract_cache.gateways().await;
|
||||
let gateway_bonds = self.contract_cache.gateways_all().await;
|
||||
|
||||
// get blacklists
|
||||
let mixnodes_blacklist = self.contract_cache.mixnodes_blacklist().await;
|
||||
let gateways_blacklist = self.contract_cache.gateways_blacklist().await;
|
||||
|
||||
let interval_reward_params =
|
||||
interval_reward_params.ok_or(NodeStatusCacheError::SourceDataMissing)?;
|
||||
@@ -140,6 +144,7 @@ impl NodeStatusCacheRefresher {
|
||||
current_interval,
|
||||
&rewarded_set_node_status,
|
||||
mix_to_family.to_vec(),
|
||||
&mixnodes_blacklist,
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -147,8 +152,13 @@ impl NodeStatusCacheRefresher {
|
||||
let (rewarded_set, active_set) =
|
||||
split_into_active_and_rewarded_set(&mixnodes_annotated, &rewarded_set_node_status);
|
||||
|
||||
let gateways_annotated =
|
||||
annotate_gateways_with_details(&self.storage, gateway_bonds, current_interval).await;
|
||||
let gateways_annotated = annotate_gateways_with_details(
|
||||
&self.storage,
|
||||
gateway_bonds,
|
||||
current_interval,
|
||||
&gateways_blacklist,
|
||||
)
|
||||
.await;
|
||||
|
||||
// Update the cache
|
||||
self.cache
|
||||
|
||||
@@ -24,17 +24,19 @@ 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,
|
||||
))?;
|
||||
let gateways = cache
|
||||
.gateways_annotated_filtered()
|
||||
.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",
|
||||
"gateway bond not found",
|
||||
Status::NotFound,
|
||||
))
|
||||
}
|
||||
@@ -43,13 +45,15 @@ 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,
|
||||
))?;
|
||||
let mixnodes = cache
|
||||
.mixnodes_annotated_filtered()
|
||||
.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(
|
||||
@@ -374,7 +378,16 @@ pub(crate) async fn _get_mixnode_inclusion_probabilities(
|
||||
|
||||
pub(crate) async fn _get_mixnodes_detailed(cache: &NodeStatusCache) -> Vec<MixNodeBondAnnotated> {
|
||||
cache
|
||||
.mixnodes_annotated()
|
||||
.mixnodes_annotated_filtered()
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub(crate) async fn _get_mixnodes_detailed_unfiltered(
|
||||
cache: &NodeStatusCache,
|
||||
) -> Vec<MixNodeBondAnnotated> {
|
||||
cache
|
||||
.mixnodes_annotated_full()
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.into_inner()
|
||||
@@ -400,7 +413,16 @@ pub(crate) async fn _get_active_set_detailed(cache: &NodeStatusCache) -> Vec<Mix
|
||||
|
||||
pub(crate) async fn _get_gateways_detailed(cache: &NodeStatusCache) -> Vec<GatewayBondAnnotated> {
|
||||
cache
|
||||
.gateways_annotated()
|
||||
.gateways_annotated_filtered()
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub(crate) async fn _get_gateways_detailed_unfiltered(
|
||||
cache: &NodeStatusCache,
|
||||
) -> Vec<GatewayBondAnnotated> {
|
||||
cache
|
||||
.gateways_annotated_full()
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.into_inner()
|
||||
|
||||
@@ -48,9 +48,11 @@ pub(crate) fn node_status_routes(
|
||||
routes::get_gateway_avg_uptime,
|
||||
routes::get_mixnode_inclusion_probabilities,
|
||||
routes::get_mixnodes_detailed,
|
||||
routes::get_mixnodes_detailed_unfiltered,
|
||||
routes::get_rewarded_set_detailed,
|
||||
routes::get_active_set_detailed,
|
||||
routes::get_gateways_detailed,
|
||||
routes::get_gateways_detailed_unfiltered,
|
||||
]
|
||||
} else {
|
||||
// in the minimal variant we would not have access to endpoints relying on existence
|
||||
|
||||
@@ -6,11 +6,11 @@ use super::NodeStatusCache;
|
||||
use crate::node_status_api::helpers::{
|
||||
_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,
|
||||
_get_gateways_detailed_unfiltered, _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_mixnodes_detailed_unfiltered, _get_rewarded_set_detailed,
|
||||
_mixnode_core_status_count, _mixnode_report, _mixnode_uptime_history,
|
||||
};
|
||||
use crate::node_status_api::models::ErrorResponse;
|
||||
use crate::storage::NymApiStorage;
|
||||
@@ -188,6 +188,14 @@ pub async fn get_mixnodes_detailed(
|
||||
Json(_get_mixnodes_detailed(cache).await)
|
||||
}
|
||||
|
||||
#[openapi(tag = "status")]
|
||||
#[get("/mixnodes/detailed-unfiltered")]
|
||||
pub async fn get_mixnodes_detailed_unfiltered(
|
||||
cache: &State<NodeStatusCache>,
|
||||
) -> Json<Vec<MixNodeBondAnnotated>> {
|
||||
Json(_get_mixnodes_detailed_unfiltered(cache).await)
|
||||
}
|
||||
|
||||
#[openapi(tag = "status")]
|
||||
#[get("/mixnodes/rewarded/detailed")]
|
||||
pub async fn get_rewarded_set_detailed(
|
||||
@@ -211,3 +219,11 @@ pub async fn get_gateways_detailed(
|
||||
) -> Json<Vec<GatewayBondAnnotated>> {
|
||||
Json(_get_gateways_detailed(cache).await)
|
||||
}
|
||||
|
||||
#[openapi(tag = "status")]
|
||||
#[get("/gateways/detailed-unfiltered")]
|
||||
pub async fn get_gateways_detailed_unfiltered(
|
||||
cache: &State<NodeStatusCache>,
|
||||
) -> Json<Vec<GatewayBondAnnotated>> {
|
||||
Json(_get_gateways_detailed_unfiltered(cache).await)
|
||||
}
|
||||
|
||||
+69
-72
@@ -67,50 +67,48 @@ impl NymContractCache {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn mixnodes_blacklist(&self) -> Option<Cache<HashSet<MixId>>> {
|
||||
pub async fn mixnodes_blacklist(&self) -> Cache<HashSet<MixId>> {
|
||||
match time::timeout(Duration::from_millis(100), self.inner.read()).await {
|
||||
Ok(cache) => Some(cache.mixnodes_blacklist.clone()),
|
||||
Ok(cache) => cache.mixnodes_blacklist.clone(),
|
||||
Err(err) => {
|
||||
error!("{err}");
|
||||
None
|
||||
Cache::new(HashSet::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn gateways_blacklist(&self) -> Option<Cache<HashSet<IdentityKey>>> {
|
||||
pub async fn gateways_blacklist(&self) -> Cache<HashSet<IdentityKey>> {
|
||||
match time::timeout(Duration::from_millis(100), self.inner.read()).await {
|
||||
Ok(cache) => Some(cache.gateways_blacklist.clone()),
|
||||
Ok(cache) => cache.gateways_blacklist.clone(),
|
||||
Err(err) => {
|
||||
error!("{err}");
|
||||
None
|
||||
Cache::new(HashSet::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn update_mixnodes_blacklist(&self, add: HashSet<MixId>, remove: HashSet<MixId>) {
|
||||
let blacklist = self.mixnodes_blacklist().await;
|
||||
if let Some(blacklist) = blacklist {
|
||||
let mut blacklist = blacklist
|
||||
.value
|
||||
.union(&add)
|
||||
.cloned()
|
||||
.collect::<HashSet<MixId>>();
|
||||
let to_remove = blacklist
|
||||
.intersection(&remove)
|
||||
.cloned()
|
||||
.collect::<HashSet<MixId>>();
|
||||
for key in to_remove {
|
||||
blacklist.remove(&key);
|
||||
let mut blacklist = blacklist
|
||||
.value
|
||||
.union(&add)
|
||||
.cloned()
|
||||
.collect::<HashSet<MixId>>();
|
||||
let to_remove = blacklist
|
||||
.intersection(&remove)
|
||||
.cloned()
|
||||
.collect::<HashSet<MixId>>();
|
||||
for key in to_remove {
|
||||
blacklist.remove(&key);
|
||||
}
|
||||
match time::timeout(Duration::from_millis(100), self.inner.write()).await {
|
||||
Ok(mut cache) => {
|
||||
cache.mixnodes_blacklist.update(blacklist);
|
||||
}
|
||||
match time::timeout(Duration::from_millis(100), self.inner.write()).await {
|
||||
Ok(mut cache) => {
|
||||
cache.mixnodes_blacklist.update(blacklist);
|
||||
return;
|
||||
}
|
||||
Err(err) => error!("{err}"),
|
||||
Err(err) => {
|
||||
error!("Failed to update mixnodes blacklist: {err}");
|
||||
}
|
||||
}
|
||||
error!("Failed to update mixnodes blacklist");
|
||||
}
|
||||
|
||||
pub async fn update_gateways_blacklist(
|
||||
@@ -119,49 +117,52 @@ impl NymContractCache {
|
||||
remove: HashSet<IdentityKey>,
|
||||
) {
|
||||
let blacklist = self.gateways_blacklist().await;
|
||||
if let Some(blacklist) = blacklist {
|
||||
let mut blacklist = blacklist
|
||||
.value
|
||||
.union(&add)
|
||||
.cloned()
|
||||
.collect::<HashSet<IdentityKey>>();
|
||||
let to_remove = blacklist
|
||||
.intersection(&remove)
|
||||
.cloned()
|
||||
.collect::<HashSet<IdentityKey>>();
|
||||
for key in to_remove {
|
||||
blacklist.remove(&key);
|
||||
let mut blacklist = blacklist
|
||||
.value
|
||||
.union(&add)
|
||||
.cloned()
|
||||
.collect::<HashSet<IdentityKey>>();
|
||||
let to_remove = blacklist
|
||||
.intersection(&remove)
|
||||
.cloned()
|
||||
.collect::<HashSet<IdentityKey>>();
|
||||
for key in to_remove {
|
||||
blacklist.remove(&key);
|
||||
}
|
||||
match time::timeout(Duration::from_millis(100), self.inner.write()).await {
|
||||
Ok(mut cache) => {
|
||||
cache.gateways_blacklist.update(blacklist);
|
||||
}
|
||||
match time::timeout(Duration::from_millis(100), self.inner.write()).await {
|
||||
Ok(mut cache) => {
|
||||
cache.gateways_blacklist.update(blacklist);
|
||||
return;
|
||||
}
|
||||
Err(err) => error!("{err}"),
|
||||
Err(err) => {
|
||||
error!("Failed to update gateways blacklist: {err}");
|
||||
}
|
||||
}
|
||||
error!("Failed to update gateways blacklist");
|
||||
}
|
||||
|
||||
pub async fn mixnodes(&self) -> Vec<MixNodeDetails> {
|
||||
pub async fn mixnodes_filtered(&self) -> Vec<MixNodeDetails> {
|
||||
let mixnodes = self.mixnodes_all().await;
|
||||
if mixnodes.is_empty() {
|
||||
return Vec::new();
|
||||
}
|
||||
let blacklist = self.mixnodes_blacklist().await;
|
||||
let mixnodes = match time::timeout(Duration::from_millis(100), self.inner.read()).await {
|
||||
Ok(cache) => cache.mixnodes.clone(),
|
||||
Err(err) => {
|
||||
error!("{err}");
|
||||
return Vec::new();
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(blacklist) = blacklist {
|
||||
if !blacklist.is_empty() {
|
||||
mixnodes
|
||||
.value
|
||||
.iter()
|
||||
.into_iter()
|
||||
.filter(|mix| !blacklist.value.contains(&mix.mix_id()))
|
||||
.cloned()
|
||||
.collect()
|
||||
} else {
|
||||
mixnodes.value
|
||||
mixnodes
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn mixnodes_all(&self) -> Vec<MixNodeDetails> {
|
||||
match time::timeout(Duration::from_millis(100), self.inner.read()).await {
|
||||
Ok(cache) => cache.mixnodes.clone().value,
|
||||
Err(err) => {
|
||||
error!("{err}");
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,25 +182,21 @@ impl NymContractCache {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn gateways(&self) -> Vec<GatewayBond> {
|
||||
let blacklist = self.gateways_blacklist().await;
|
||||
let gateways = match time::timeout(Duration::from_millis(100), self.inner.read()).await {
|
||||
Ok(cache) => cache.gateways.clone(),
|
||||
Err(err) => {
|
||||
error!("{err}");
|
||||
return Vec::new();
|
||||
}
|
||||
};
|
||||
pub async fn gateways_filtered(&self) -> Vec<GatewayBond> {
|
||||
let gateways = self.gateways_all().await;
|
||||
if gateways.is_empty() {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
if let Some(blacklist) = blacklist {
|
||||
let blacklist = self.gateways_blacklist().await;
|
||||
|
||||
if !blacklist.is_empty() {
|
||||
gateways
|
||||
.value
|
||||
.iter()
|
||||
.into_iter()
|
||||
.filter(|mix| !blacklist.value.contains(mix.identity()))
|
||||
.cloned()
|
||||
.collect()
|
||||
} else {
|
||||
gateways.value
|
||||
gateways
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,7 +274,7 @@ impl NymContractCache {
|
||||
return (Some(bond.clone()), MixnodeStatus::Standby);
|
||||
}
|
||||
|
||||
let all_bonded = &self.mixnodes().await;
|
||||
let all_bonded = &self.mixnodes_filtered().await;
|
||||
if let Some(bond) = all_bonded.iter().find(|mix| mix.mix_id() == mix_id) {
|
||||
(Some(bond.clone()), MixnodeStatus::Inactive)
|
||||
} else {
|
||||
|
||||
@@ -20,7 +20,7 @@ use std::collections::HashSet;
|
||||
#[openapi(tag = "contract-cache")]
|
||||
#[get("/mixnodes")]
|
||||
pub async fn get_mixnodes(cache: &State<NymContractCache>) -> Json<Vec<MixNodeDetails>> {
|
||||
Json(cache.mixnodes().await)
|
||||
Json(cache.mixnodes_filtered().await)
|
||||
}
|
||||
|
||||
// DEPRECATED: this endpoint now lives in `node_status_api`. Once all consumers are updated,
|
||||
@@ -41,7 +41,7 @@ pub async fn get_mixnodes_detailed(
|
||||
#[openapi(tag = "contract-cache")]
|
||||
#[get("/gateways")]
|
||||
pub async fn get_gateways(cache: &State<NymContractCache>) -> Json<Vec<GatewayBond>> {
|
||||
Json(cache.gateways().await)
|
||||
Json(cache.gateways_filtered().await)
|
||||
}
|
||||
|
||||
#[openapi(tag = "contract-cache")]
|
||||
@@ -91,7 +91,12 @@ pub async fn get_active_set_detailed(
|
||||
pub async fn get_blacklisted_mixnodes(
|
||||
cache: &State<NymContractCache>,
|
||||
) -> Json<Option<HashSet<MixId>>> {
|
||||
Json(cache.mixnodes_blacklist().await.map(|c| c.value))
|
||||
let blacklist = cache.mixnodes_blacklist().await.value;
|
||||
if blacklist.is_empty() {
|
||||
Json(None)
|
||||
} else {
|
||||
Json(Some(blacklist))
|
||||
}
|
||||
}
|
||||
|
||||
#[openapi(tag = "contract-cache")]
|
||||
@@ -99,7 +104,12 @@ pub async fn get_blacklisted_mixnodes(
|
||||
pub async fn get_blacklisted_gateways(
|
||||
cache: &State<NymContractCache>,
|
||||
) -> Json<Option<HashSet<String>>> {
|
||||
Json(cache.gateways_blacklist().await.map(|c| c.value))
|
||||
let blacklist = cache.gateways_blacklist().await.value;
|
||||
if blacklist.is_empty() {
|
||||
Json(None)
|
||||
} else {
|
||||
Json(Some(blacklist))
|
||||
}
|
||||
}
|
||||
|
||||
#[openapi(tag = "contract-cache")]
|
||||
|
||||
Reference in New Issue
Block a user