basic handlers with a lot of repeated code
This commit is contained in:
@@ -29,6 +29,7 @@ use std::fmt::{Debug, Display, Formatter};
|
||||
use std::net::IpAddr;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::{fmt, time::Duration};
|
||||
use thiserror::Error;
|
||||
use time::{Date, OffsetDateTime};
|
||||
use utoipa::{IntoParams, ToResponse, ToSchema};
|
||||
|
||||
@@ -192,6 +193,15 @@ pub struct MixNodeBondAnnotated {
|
||||
pub ip_addresses: Vec<IpAddr>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum MalformedNodeBond {
|
||||
#[error("the associated ed25519 identity key is malformed")]
|
||||
InvalidEd25519Key,
|
||||
|
||||
#[error("the associated x25519 sphinx key is malformed")]
|
||||
InvalidX25519Key,
|
||||
}
|
||||
|
||||
impl MixNodeBondAnnotated {
|
||||
pub fn mix_node(&self) -> &MixNode {
|
||||
&self.mixnode_details.bond_information.mix_node
|
||||
@@ -212,6 +222,32 @@ impl MixNodeBondAnnotated {
|
||||
pub fn version(&self) -> &str {
|
||||
&self.mixnode_details.bond_information.mix_node.version
|
||||
}
|
||||
|
||||
pub fn try_to_skimmed_node(&self, role: NodeRole) -> Result<SkimmedNode, MalformedNodeBond> {
|
||||
Ok(SkimmedNode {
|
||||
node_id: self.mix_id(),
|
||||
ed25519_identity_pubkey: self
|
||||
.identity_key()
|
||||
.parse()
|
||||
.map_err(|_| MalformedNodeBond::InvalidEd25519Key)?,
|
||||
ip_addresses: self.ip_addresses.clone(),
|
||||
mix_port: self.mix_node().mix_port,
|
||||
x25519_sphinx_pubkey: self
|
||||
.mix_node()
|
||||
.sphinx_key
|
||||
.parse()
|
||||
.map_err(|_| MalformedNodeBond::InvalidX25519Key)?,
|
||||
epoch_role: role,
|
||||
supported_roles: DeclaredRoles {
|
||||
mixnode: true,
|
||||
entry: false,
|
||||
exit_nr: false,
|
||||
exit_ipr: false,
|
||||
},
|
||||
entry: None,
|
||||
performance: self.node_performance.last_24h,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, ToSchema)]
|
||||
@@ -242,6 +278,39 @@ impl GatewayBondAnnotated {
|
||||
pub fn owner(&self) -> &Addr {
|
||||
self.gateway_bond.bond.owner()
|
||||
}
|
||||
|
||||
pub fn try_to_skimmed_node(&self, role: NodeRole) -> Result<SkimmedNode, MalformedNodeBond> {
|
||||
Ok(SkimmedNode {
|
||||
node_id: self.gateway_bond.node_id,
|
||||
ip_addresses: self.ip_addresses.clone(),
|
||||
ed25519_identity_pubkey: self
|
||||
.gateway_bond
|
||||
.gateway
|
||||
.identity_key
|
||||
.parse()
|
||||
.map_err(|_| MalformedNodeBond::InvalidEd25519Key)?,
|
||||
mix_port: self.gateway_bond.bond.gateway.mix_port,
|
||||
x25519_sphinx_pubkey: self
|
||||
.gateway_bond
|
||||
.gateway
|
||||
.sphinx_key
|
||||
.parse()
|
||||
.map_err(|_| MalformedNodeBond::InvalidX25519Key)?,
|
||||
epoch_role: role,
|
||||
supported_roles: DeclaredRoles {
|
||||
mixnode: false,
|
||||
entry: true,
|
||||
exit_nr: false,
|
||||
exit_ipr: false,
|
||||
},
|
||||
entry: Some(BasicEntryInformation {
|
||||
hostname: None,
|
||||
ws_port: self.gateway_bond.bond.gateway.clients_port,
|
||||
wss_port: None,
|
||||
}),
|
||||
performance: self.node_performance.last_24h,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
@@ -640,7 +709,7 @@ impl NymNodeDescription {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_to_skimmed_node(&self, role: NodeRole, performance: Performance) -> SkimmedNode {
|
||||
pub fn to_skimmed_node(&self, role: NodeRole, performance: Performance) -> SkimmedNode {
|
||||
let keys = &self.description.host_information.keys;
|
||||
let entry = if self.description.declared_role.entry {
|
||||
Some(self.entry_information())
|
||||
@@ -650,15 +719,15 @@ impl NymNodeDescription {
|
||||
|
||||
SkimmedNode {
|
||||
node_id: self.node_id,
|
||||
ed25519_identity_pubkey: keys.ed25519.clone(),
|
||||
ed25519_identity_pubkey: keys.ed25519,
|
||||
ip_addresses: self.description.host_information.ip_address.clone(),
|
||||
mix_port: self.description.mix_port(),
|
||||
x25519_sphinx_pubkey: keys.x25519.clone(),
|
||||
x25519_sphinx_pubkey: keys.x25519,
|
||||
// we can't use the declared roles, we have to take whatever was provided in the contract.
|
||||
// why? say this node COULD operate as an exit, but it might be the case the contract decided
|
||||
// to assign it an ENTRY role only. we have to use that one instead.
|
||||
epoch_role: role,
|
||||
supported_roles: self.description.declared_role.into(),
|
||||
supported_roles: self.description.declared_role,
|
||||
entry,
|
||||
performance,
|
||||
}
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::models::{
|
||||
DeclaredRoles, GatewayBondAnnotated, MixNodeBondAnnotated, NymNodeData,
|
||||
OffsetDateTimeJsonSchemaWrapper,
|
||||
};
|
||||
use crate::pagination::PaginatedResponse;
|
||||
use crate::models::{DeclaredRoles, NymNodeData, OffsetDateTimeJsonSchemaWrapper};
|
||||
use crate::pagination::{PaginatedResponse, Pagination};
|
||||
use nym_crypto::asymmetric::ed25519::serde_helpers::bs58_ed25519_pubkey;
|
||||
use nym_crypto::asymmetric::x25519::serde_helpers::bs58_x25519_pubkey;
|
||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
@@ -43,6 +40,25 @@ pub struct PaginatedCachedNodesResponse<T> {
|
||||
pub nodes: PaginatedResponse<T>,
|
||||
}
|
||||
|
||||
impl<T> PaginatedCachedNodesResponse<T> {
|
||||
pub fn new_full(
|
||||
refreshed_at: impl Into<OffsetDateTimeJsonSchemaWrapper>,
|
||||
nodes: Vec<T>,
|
||||
) -> Self {
|
||||
PaginatedCachedNodesResponse {
|
||||
refreshed_at: refreshed_at.into(),
|
||||
nodes: PaginatedResponse {
|
||||
pagination: Pagination {
|
||||
total: nodes.len(),
|
||||
page: 0,
|
||||
size: nodes.len(),
|
||||
},
|
||||
data: nodes,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, schemars::JsonSchema, utoipa::ToSchema)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum NodeRoleQueryParam {
|
||||
@@ -125,86 +141,8 @@ impl SkimmedNode {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn from_described_gateway(
|
||||
// annotated: &GatewayBondAnnotated,
|
||||
// description: Option<&NymNodeData>,
|
||||
// ) -> Self {
|
||||
// let mut base: SkimmedNode = annotated.into();
|
||||
// let Some(description) = description else {
|
||||
// return base;
|
||||
// };
|
||||
//
|
||||
// // safety: the conversion always sets the entry field
|
||||
// let entry = base.entry.as_mut().unwrap();
|
||||
// entry
|
||||
// .hostname
|
||||
// .clone_from(&description.host_information.hostname);
|
||||
// entry.ws_port = description.mixnet_websockets.ws_port;
|
||||
// entry.wss_port = description.mixnet_websockets.wss_port;
|
||||
//
|
||||
// // always prefer self-described data
|
||||
// if !description.host_information.ip_address.is_empty() {
|
||||
// base.ip_addresses
|
||||
// .clone_from(&description.host_information.ip_address)
|
||||
// }
|
||||
//
|
||||
// base.supported_roles = description.declared_role;
|
||||
//
|
||||
// base
|
||||
// }
|
||||
}
|
||||
|
||||
// impl<'a> From<&'a MixNodeBondAnnotated> for SkimmedNode {
|
||||
// fn from(value: &'a MixNodeBondAnnotated) -> Self {
|
||||
// todo!()
|
||||
// // SkimmedNode {
|
||||
// // node_id: value.mix_id(),
|
||||
// // ed25519_identity_pubkey: value.identity_key().to_string(),
|
||||
// // ip_addresses: value.ip_addresses.clone(),
|
||||
// // mix_port: value.mix_node().mix_port,
|
||||
// // x25519_sphinx_pubkey: value.mix_node().sphinx_key.clone(),
|
||||
// // epoch_role: NodeRole::Mixnode {
|
||||
// // layer: value.mixnode_details.bond_information.layer.into(),
|
||||
// // },
|
||||
// // supported_roles: DeclaredRoles {
|
||||
// // mixnode: true,
|
||||
// // entry: false,
|
||||
// // exit_nr: false,
|
||||
// // exit_ipr: false,
|
||||
// // },
|
||||
// // entry: None,
|
||||
// // performance: value.node_performance.last_24h,
|
||||
// // }
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl<'a> From<&'a GatewayBondAnnotated> for SkimmedNode {
|
||||
// fn from(value: &'a GatewayBondAnnotated) -> Self {
|
||||
// todo!()
|
||||
// // SkimmedNode {
|
||||
// // node_id: value.gateway_bond.node_id,
|
||||
// // ip_addresses: value.ip_addresses.clone(),
|
||||
// // ed25519_identity_pubkey: value.gateway_bond.bond.identity().clone(),
|
||||
// // mix_port: value.gateway_bond.bond.gateway.mix_port,
|
||||
// // x25519_sphinx_pubkey: value.gateway_bond.bond.gateway.sphinx_key.clone(),
|
||||
// // epoch_role: NodeRole::EntryGateway,
|
||||
// // supported_roles: DeclaredRoles {
|
||||
// // mixnode: false,
|
||||
// // entry: true,
|
||||
// // exit_nr: false,
|
||||
// // exit_ipr: false,
|
||||
// // },
|
||||
// // entry: Some(BasicEntryInformation {
|
||||
// // hostname: None,
|
||||
// // ws_port: value.gateway_bond.bond.gateway.clients_port,
|
||||
// // wss_port: None,
|
||||
// // }),
|
||||
// // performance: value.node_performance.last_24h,
|
||||
// // }
|
||||
// }
|
||||
// }
|
||||
|
||||
// an intermediate variant that exposes additional data such as noise keys but without
|
||||
// the full fat of the self-described data
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
#![warn(clippy::todo)]
|
||||
#![warn(clippy::dbg_macro)]
|
||||
|
||||
use crate::epoch_operations::EpochAdvancer;
|
||||
use crate::support::cli;
|
||||
use crate::support::storage;
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::support::caching::cache::SharedCache;
|
||||
use nym_api_requests::legacy::{LegacyGatewayBondWithId, LegacyMixNodeBondWithLayer};
|
||||
use nym_api_requests::models::NymNodeDescription;
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
use nym_mixnet_contract_common::{LegacyMixLayer, NodeId, RewardedSet};
|
||||
use nym_mixnet_contract_common::{LegacyMixLayer, NodeId};
|
||||
use nym_node_tester_utils::node::TestableNode;
|
||||
use nym_node_tester_utils::NodeTester;
|
||||
use nym_sphinx::acknowledgements::AckKey;
|
||||
@@ -344,7 +344,7 @@ impl PacketPreparer {
|
||||
|
||||
let mixing_nym_nodes = descriptions.mixing_nym_nodes();
|
||||
// last I checked `gatewaying` wasn't a word : )
|
||||
let gateway_capable_nym_nodes = descriptions.gateway_capable_nym_nodes();
|
||||
let gateway_capable_nym_nodes = descriptions.entry_capable_nym_nodes();
|
||||
|
||||
let mut rng = thread_rng();
|
||||
|
||||
@@ -527,7 +527,7 @@ impl PacketPreparer {
|
||||
.await
|
||||
.expect("the cache must have been initialised!");
|
||||
let mixing_nym_nodes = descriptions.mixing_nym_nodes();
|
||||
let gateway_capable_nym_nodes = descriptions.gateway_capable_nym_nodes();
|
||||
let gateway_capable_nym_nodes = descriptions.entry_capable_nym_nodes();
|
||||
|
||||
let (mixnodes, invalid_mixnodes) = self.filter_outdated_and_malformed_mixnodes(mixnodes);
|
||||
let (gateways, invalid_gateways) = self.filter_outdated_and_malformed_gateways(gateways);
|
||||
|
||||
@@ -11,7 +11,6 @@ use async_trait::async_trait;
|
||||
use futures::{stream, StreamExt};
|
||||
use nym_api_requests::models::{DescribedNodeType, NymNodeData, NymNodeDescription};
|
||||
use nym_config::defaults::DEFAULT_NYM_NODE_HTTP_PORT;
|
||||
use nym_crypto::asymmetric::{ed25519, encryption, identity, x25519};
|
||||
use nym_mixnet_contract_common::{LegacyMixLayer, NodeId};
|
||||
use nym_node_requests::api::client::{NymNodeApiClientError, NymNodeApiClientExt};
|
||||
use nym_topology::gateway::GatewayConversionError;
|
||||
@@ -159,25 +158,30 @@ impl DescribedNodes {
|
||||
self.nodes.get(node_id)
|
||||
}
|
||||
|
||||
pub fn all_nodes<'a>(&'a self) -> impl Iterator<Item = &'a NymNodeDescription> + 'a {
|
||||
pub fn all_nodes(&self) -> impl Iterator<Item = &NymNodeDescription> {
|
||||
self.nodes.values()
|
||||
}
|
||||
|
||||
pub fn mixing_nym_nodes<'a>(&'a self) -> impl Iterator<Item = &'a NymNodeDescription> + 'a {
|
||||
pub fn mixing_nym_nodes(&self) -> impl Iterator<Item = &NymNodeDescription> {
|
||||
self.nodes
|
||||
.values()
|
||||
.filter(|n| n.contract_node_type == DescribedNodeType::NymNode)
|
||||
.filter(|n| n.description.declared_role.mixnode)
|
||||
}
|
||||
|
||||
pub fn gateway_capable_nym_nodes<'a>(
|
||||
&'a self,
|
||||
) -> impl Iterator<Item = &'a NymNodeDescription> + 'a {
|
||||
pub fn entry_capable_nym_nodes(&self) -> impl Iterator<Item = &NymNodeDescription> {
|
||||
self.nodes
|
||||
.values()
|
||||
.filter(|n| n.contract_node_type == DescribedNodeType::NymNode)
|
||||
.filter(|n| n.description.declared_role.entry)
|
||||
}
|
||||
|
||||
pub fn exit_capable_nym_nodes(&self) -> impl Iterator<Item = &NymNodeDescription> {
|
||||
self.nodes
|
||||
.values()
|
||||
.filter(|n| n.contract_node_type == DescribedNodeType::NymNode)
|
||||
.filter(|n| n.description.declared_role.can_operate_exit_gateway())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NodeDescriptionProvider {
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use nym_api_requests::models::OffsetDateTimeJsonSchemaWrapper;
|
||||
use nym_bin_common::version_checker;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
pub(crate) fn refreshed_at(
|
||||
iter: impl IntoIterator<Item = OffsetDateTime>,
|
||||
) -> OffsetDateTimeJsonSchemaWrapper {
|
||||
iter.into_iter().min().unwrap().into()
|
||||
}
|
||||
|
||||
pub(crate) fn semver(requirement: &Option<String>, declared: &str) -> bool {
|
||||
if let Some(semver_compat) = requirement.as_ref() {
|
||||
if !version_checker::is_minor_version_compatible(declared, semver_compat) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
@@ -36,6 +36,7 @@ use nym_api_requests::nym_nodes::NodeRoleQueryParam;
|
||||
use serde::Deserialize;
|
||||
|
||||
pub(crate) mod full_fat;
|
||||
mod helpers;
|
||||
pub(crate) mod semi_skimmed;
|
||||
pub(crate) mod skimmed;
|
||||
|
||||
|
||||
@@ -2,16 +2,15 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::node_status_api::models::{AxumErrorResponse, AxumResult};
|
||||
use crate::nym_nodes::handlers::unstable::helpers::{refreshed_at, semver};
|
||||
use crate::nym_nodes::handlers::unstable::{NodesParams, NodesParamsWithRole};
|
||||
use crate::support::http::helpers::PaginationRequest;
|
||||
use crate::support::http::state::AppState;
|
||||
use axum::extract::{Query, State};
|
||||
use axum::Json;
|
||||
use nym_api_requests::nym_nodes::{
|
||||
CachedNodesResponse, NodeRole, NodeRoleQueryParam, PaginatedCachedNodesResponse, SkimmedNode,
|
||||
};
|
||||
use nym_bin_common::version_checker;
|
||||
use std::collections::HashSet;
|
||||
use tracing::trace;
|
||||
|
||||
pub type PaginatedSkimmedNodes = AxumResult<Json<PaginatedCachedNodesResponse<SkimmedNode>>>;
|
||||
|
||||
@@ -31,116 +30,15 @@ pub(super) async fn deprecated_gateways_basic(
|
||||
state: State<AppState>,
|
||||
query_params: Query<NodesParams>,
|
||||
) -> AxumResult<Json<CachedNodesResponse<SkimmedNode>>> {
|
||||
// TODO:
|
||||
// 1. call '/v1/unstable/entry-gateways/mixnodes/skimmed/all'
|
||||
// 2. remove pagination
|
||||
// 1. call '/v1/unstable/skimmed/entry-gateways/all'
|
||||
let all_gateways = entry_gateways_basic_all(state, query_params).await?;
|
||||
|
||||
// 3. return result
|
||||
|
||||
todo!()
|
||||
|
||||
// let status_cache = state.node_status_cache();
|
||||
// let contract_cache = state.nym_contract_cache();
|
||||
// let describe_cache = state.described_nodes_cache();
|
||||
//
|
||||
// // 1. get the rewarded set
|
||||
// let rewarded_set = contract_cache
|
||||
// .rewarded_set()
|
||||
// .await
|
||||
// .ok_or_else(AxumErrorResponse::internal)?;
|
||||
//
|
||||
// // determine which gateways are active, i.e. which gateways the clients should be using for connecting and routing the traffic
|
||||
// let active_gateways = rewarded_set.gateways().into_iter().collect::<HashSet<_>>();
|
||||
//
|
||||
// // 2. grab all annotations so that we could attach scores to the [nym] nodes
|
||||
// let annotations = status_cache
|
||||
// .node_annotations()
|
||||
// .await
|
||||
// .ok_or_else(AxumErrorResponse::internal)?;
|
||||
//
|
||||
// // 3. grab all legacy gateways
|
||||
// // due to legacy endpoints we already have fully annotated data on them
|
||||
// let annotated_legacy_gateways = status_cache
|
||||
// .annotated_legacy_gateways()
|
||||
// .await
|
||||
// .ok_or_else(AxumErrorResponse::internal)?;
|
||||
//
|
||||
// // 4. grab all relevant described nym-nodes
|
||||
// let describe_cache = describe_cache.get().await?;
|
||||
// let gateway_capable_nym_nodes = describe_cache.gateway_capable_nym_nodes();
|
||||
//
|
||||
// // 5. only return nodes that are present in the active set
|
||||
// let mut active_skimmed_gateways = Vec::new();
|
||||
//
|
||||
// for (node_id, legacy) in annotated_legacy_gateways.iter() {
|
||||
// if !active_gateways.contains(node_id) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// if let Some(semver_compat) = semver_compatibility.as_ref() {
|
||||
// let version = legacy.version();
|
||||
// if !version_checker::is_minor_version_compatible(version, semver_compat) {
|
||||
// continue;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // if we have self-described info, prefer it over contract data
|
||||
// if let Some(described) = describe_cache.get_node(node_id) {
|
||||
// active_skimmed_gateways.push(
|
||||
// described.to_skimmed_node(NodeRole::EntryGateway, legacy.node_performance.last_24h),
|
||||
// )
|
||||
// } else {
|
||||
// active_skimmed_gateways.push(legacy.into());
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// for nym_node in gateway_capable_nym_nodes {
|
||||
// // if this node is not an active gateway, ignore it
|
||||
// if !active_gateways.contains(&nym_node.node_id) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// // if we have wrong version, ignore
|
||||
// if let Some(semver_compat) = semver_compatibility.as_ref() {
|
||||
// let version = nym_node.version();
|
||||
// if !version_checker::is_minor_version_compatible(version, semver_compat) {
|
||||
// continue;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // NOTE: if we determined our node IS an active gateway, it MUST be EITHER entry or exit
|
||||
// let role = if rewarded_set.is_exit(&nym_node.node_id) {
|
||||
// NodeRole::ExitGateway
|
||||
// } else {
|
||||
// NodeRole::EntryGateway
|
||||
// };
|
||||
//
|
||||
// // honestly, not sure under what exact circumstances this value could be missing,
|
||||
// // but in that case just use 0 performance
|
||||
// let annotation = annotations
|
||||
// .get(&nym_node.node_id)
|
||||
// .copied()
|
||||
// .unwrap_or_default();
|
||||
//
|
||||
// active_skimmed_gateways
|
||||
// .push(nym_node.to_skimmed_node(role, annotation.last_24h_performance));
|
||||
// }
|
||||
//
|
||||
// // min of all caches
|
||||
// let refreshed_at = [
|
||||
// rewarded_set.timestamp(),
|
||||
// annotations.timestamp(),
|
||||
// describe_cache.timestamp(),
|
||||
// annotated_legacy_gateways.timestamp(),
|
||||
// ]
|
||||
// .into_iter()
|
||||
// .min()
|
||||
// .unwrap()
|
||||
// .into();
|
||||
//
|
||||
// Ok(Json(CachedNodesResponse {
|
||||
// refreshed_at,
|
||||
// nodes: active_skimmed_gateways,
|
||||
// }))
|
||||
Ok(Json(CachedNodesResponse {
|
||||
refreshed_at: all_gateways.refreshed_at,
|
||||
// 2. remove pagination
|
||||
nodes: all_gateways.0.nodes.data,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Deprecated query that gets ACTIVE-ONLY mixnodes
|
||||
@@ -159,123 +57,15 @@ pub(super) async fn deprecated_mixnodes_basic(
|
||||
state: State<AppState>,
|
||||
query_params: Query<NodesParams>,
|
||||
) -> AxumResult<Json<CachedNodesResponse<SkimmedNode>>> {
|
||||
// TODO:
|
||||
// 1. call '/v1/unstable/nym-nodes/mixnodes/skimmed/active'
|
||||
// 2. remove pagination
|
||||
// 3. return result
|
||||
// 1. call '/v1/unstable/nym-nodes/skimmed/mixnodes/active'
|
||||
let active_mixnodes = mixnodes_basic_active(state, query_params).await?;
|
||||
|
||||
todo!()
|
||||
//
|
||||
// let status_cache = state.node_status_cache();
|
||||
// let contract_cache = state.nym_contract_cache();
|
||||
// let describe_cache = state.described_nodes_cache();
|
||||
//
|
||||
// // 1. get the rewarded set
|
||||
// let rewarded_set = contract_cache
|
||||
// .rewarded_set()
|
||||
// .await
|
||||
// .ok_or_else(AxumErrorResponse::internal)?;
|
||||
//
|
||||
// // determine which mixnodes are active, i.e. which mixnodes the clients should be using for routing the traffic
|
||||
// let active_mixnodes = rewarded_set
|
||||
// .active_mixnodes()
|
||||
// .into_iter()
|
||||
// .collect::<HashSet<_>>();
|
||||
//
|
||||
// // 2. grab all annotations so that we could attach scores to the [nym] nodes
|
||||
// let annotations = status_cache
|
||||
// .node_annotations()
|
||||
// .await
|
||||
// .ok_or_else(AxumErrorResponse::internal)?;
|
||||
//
|
||||
// // 3. grab all legacy mixnodes
|
||||
// // due to legacy endpoints we already have fully annotated data on them
|
||||
// let annotated_legacy_mixnodes = status_cache
|
||||
// .annotated_legacy_mixnodes()
|
||||
// .await
|
||||
// .ok_or_else(AxumErrorResponse::internal)?;
|
||||
//
|
||||
// // 4. grab all relevant described nym-nodes
|
||||
// let describe_cache = describe_cache.get().await?;
|
||||
// let mixing_nym_nodes = describe_cache.mixing_nym_nodes();
|
||||
//
|
||||
// // TODO: in the future, only use the self-described cache and simply reject mixnodes that did not expose it
|
||||
//
|
||||
// // 5. only return nodes that are present in the active set
|
||||
// let mut active_skimmed_mixnodes = Vec::new();
|
||||
//
|
||||
// for (mix_id, legacy) in annotated_legacy_mixnodes.iter() {
|
||||
// if !active_mixnodes.contains(mix_id) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// if let Some(semver_compat) = semver_compatibility.as_ref() {
|
||||
// let version = legacy.version();
|
||||
// if !version_checker::is_minor_version_compatible(version, semver_compat) {
|
||||
// continue;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // if we have self-described info, prefer it over contract data
|
||||
// if let Some(described) = describe_cache.get_node(mix_id) {
|
||||
// active_skimmed_mixnodes.push(described.to_skimmed_node(
|
||||
// NodeRole::Mixnode {
|
||||
// layer: legacy.mixnode_details.bond_information.layer.into(),
|
||||
// },
|
||||
// legacy.node_performance.last_24h,
|
||||
// ))
|
||||
// } else {
|
||||
// active_skimmed_mixnodes.push(legacy.into());
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// for nym_node in mixing_nym_nodes {
|
||||
// // if this node is not an active mixnode, ignore it
|
||||
// if !active_mixnodes.contains(&nym_node.node_id) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// // if we have wrong version, ignore
|
||||
// if let Some(semver_compat) = semver_compatibility.as_ref() {
|
||||
// let version = nym_node.version();
|
||||
// if !version_checker::is_minor_version_compatible(version, semver_compat) {
|
||||
// continue;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // SAFETY: if we determined our node IS active, it MUST have a layer
|
||||
// // no other thread could have updated the rewarded set as we're still holding the [read] lock on the data
|
||||
// #[allow(clippy::unwrap_used)]
|
||||
// let layer = rewarded_set.try_get_mix_layer(&nym_node.node_id).unwrap();
|
||||
//
|
||||
// // honestly, not sure under what exact circumstances this value could be missing,
|
||||
// // but in that case just use 0 performance
|
||||
// let annotation = annotations
|
||||
// .get(&nym_node.node_id)
|
||||
// .copied()
|
||||
// .unwrap_or_default();
|
||||
//
|
||||
// active_skimmed_mixnodes.push(
|
||||
// nym_node.to_skimmed_node(NodeRole::Mixnode { layer }, annotation.last_24h_performance),
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// // min of all caches
|
||||
// let refreshed_at = [
|
||||
// rewarded_set.timestamp(),
|
||||
// annotations.timestamp(),
|
||||
// describe_cache.timestamp(),
|
||||
// annotated_legacy_mixnodes.timestamp(),
|
||||
// ]
|
||||
// .into_iter()
|
||||
// .min()
|
||||
// .unwrap()
|
||||
// .into();
|
||||
//
|
||||
// Ok(Json(CachedNodesResponse {
|
||||
// refreshed_at,
|
||||
// nodes: active_skimmed_mixnodes,
|
||||
// }))
|
||||
// 3. return result
|
||||
Ok(Json(CachedNodesResponse {
|
||||
refreshed_at: active_mixnodes.refreshed_at,
|
||||
// 2. remove pagination
|
||||
nodes: active_mixnodes.0.nodes.data,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Return all Nym Nodes and optionally legacy mixnodes/gateways (if `no-legacy` flag is not used)
|
||||
@@ -292,24 +82,22 @@ pub(super) async fn deprecated_mixnodes_basic(
|
||||
)]
|
||||
pub(super) async fn nodes_basic(
|
||||
state: State<AppState>,
|
||||
query_params: Query<NodesParamsWithRole>,
|
||||
Query(query_params): Query<NodesParamsWithRole>,
|
||||
) -> PaginatedSkimmedNodes {
|
||||
if let Some(role) = query_params.0.role {
|
||||
if let Some(role) = query_params.role {
|
||||
return match role {
|
||||
NodeRoleQueryParam::ActiveMixnode => {
|
||||
mixnodes_basic_all(state, Query(query_params.0.into())).await
|
||||
mixnodes_basic_all(state, Query(query_params.into())).await
|
||||
}
|
||||
NodeRoleQueryParam::EntryGateway => {
|
||||
entry_gateways_basic_all(state, Query(query_params.0.into())).await
|
||||
entry_gateways_basic_all(state, Query(query_params.into())).await
|
||||
}
|
||||
NodeRoleQueryParam::ExitGateway => {
|
||||
exit_gateways_basic_all(state, Query(query_params.0.into())).await
|
||||
exit_gateways_basic_all(state, Query(query_params.into())).await
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let query_params = query_params.0;
|
||||
|
||||
// TODO: implement pagination
|
||||
|
||||
let semver = query_params.semver_compatibility;
|
||||
@@ -343,6 +131,9 @@ pub(super) async fn nodes_basic_active(
|
||||
state: State<AppState>,
|
||||
query_params: Query<NodesParamsWithRole>,
|
||||
) -> PaginatedSkimmedNodes {
|
||||
// TODO: implement it
|
||||
let _ = query_params.per_page;
|
||||
let _ = query_params.page;
|
||||
todo!()
|
||||
}
|
||||
|
||||
@@ -362,9 +153,123 @@ pub(super) async fn nodes_basic_standby(
|
||||
state: State<AppState>,
|
||||
query_params: Query<NodesParamsWithRole>,
|
||||
) -> PaginatedSkimmedNodes {
|
||||
// TODO: implement it
|
||||
let _ = query_params.per_page;
|
||||
let _ = query_params.page;
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn mixnodes_basic(
|
||||
state: State<AppState>,
|
||||
Query(query_params): Query<NodesParams>,
|
||||
active_only: bool,
|
||||
) -> PaginatedSkimmedNodes {
|
||||
// TODO: implement it
|
||||
let _ = query_params.per_page;
|
||||
let _ = query_params.page;
|
||||
let semver_compatibility = query_params.semver_compatibility;
|
||||
|
||||
// 1. get the rewarded set
|
||||
let rewarded_set = state.rewarded_set().await?;
|
||||
|
||||
// 2. grab all annotations so that we could attach scores to the [nym] nodes
|
||||
let annotations = state.node_annotations().await?;
|
||||
|
||||
// 3. grab all relevant described nym-nodes
|
||||
let describe_cache = state.describe_nodes_cache_data().await?;
|
||||
let mixing_nym_nodes = describe_cache.mixing_nym_nodes();
|
||||
|
||||
// 4. start building the response
|
||||
let mut nodes = Vec::new();
|
||||
|
||||
for nym_node in mixing_nym_nodes {
|
||||
let node_id = nym_node.node_id;
|
||||
|
||||
// if this node is not an active mixnode, ignore it
|
||||
if active_only && !rewarded_set.is_active_mixnode(&node_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if we have wrong version, ignore
|
||||
if !semver(&semver_compatibility, nym_node.version()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let role = match rewarded_set.try_get_mix_layer(&node_id) {
|
||||
Some(layer) => NodeRole::Mixnode { layer },
|
||||
None => NodeRole::Inactive,
|
||||
};
|
||||
|
||||
// honestly, not sure under what exact circumstances this value could be missing,
|
||||
// but in that case just use 0 performance
|
||||
let annotation = annotations.get(&node_id).copied().unwrap_or_default();
|
||||
|
||||
nodes.push(nym_node.to_skimmed_node(role, annotation.last_24h_performance));
|
||||
}
|
||||
|
||||
// 5. if we allow legacy mixnodes, repeat the procedure for mixnodes, otherwise return just nym-nodes
|
||||
if query_params.no_legacy {
|
||||
// min of all caches
|
||||
let refreshed_at = refreshed_at([
|
||||
rewarded_set.timestamp(),
|
||||
annotations.timestamp(),
|
||||
describe_cache.timestamp(),
|
||||
]);
|
||||
|
||||
return Ok(Json(PaginatedCachedNodesResponse::new_full(
|
||||
refreshed_at,
|
||||
nodes,
|
||||
)));
|
||||
}
|
||||
|
||||
// 6. grab all legacy mixnodes
|
||||
// due to legacy endpoints we already have fully annotated data on them
|
||||
let annotated_legacy_mixnodes = state.legacy_mixnode_annotations().await?;
|
||||
|
||||
for (mix_id, legacy) in annotated_legacy_mixnodes.iter() {
|
||||
// if this node is not an active mixnode, ignore it
|
||||
if active_only && !rewarded_set.is_active_mixnode(mix_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if we have wrong version, ignore
|
||||
if !semver(&semver_compatibility, legacy.version()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let role = match rewarded_set.try_get_mix_layer(mix_id) {
|
||||
Some(layer) => NodeRole::Mixnode { layer },
|
||||
None => NodeRole::Inactive,
|
||||
};
|
||||
|
||||
// if we have self-described info, prefer it over contract data
|
||||
if let Some(described) = describe_cache.get_node(mix_id) {
|
||||
nodes.push(described.to_skimmed_node(role, legacy.node_performance.last_24h))
|
||||
} else {
|
||||
match legacy.try_to_skimmed_node(role) {
|
||||
Ok(node) => nodes.push(node),
|
||||
Err(err) => {
|
||||
let id = legacy.identity_key();
|
||||
trace!("node {id} is malformed: {err}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// min of all caches
|
||||
let refreshed_at = refreshed_at([
|
||||
rewarded_set.timestamp(),
|
||||
annotations.timestamp(),
|
||||
describe_cache.timestamp(),
|
||||
annotated_legacy_mixnodes.timestamp(),
|
||||
]);
|
||||
|
||||
Ok(Json(PaginatedCachedNodesResponse::new_full(
|
||||
refreshed_at,
|
||||
nodes,
|
||||
)))
|
||||
}
|
||||
|
||||
/// Returns Nym Nodes and optionally legacy mixnodes (if `no-legacy` flag is not used)
|
||||
/// that are currently bonded and support mixing role.
|
||||
#[utoipa::path(
|
||||
@@ -381,7 +286,7 @@ pub(super) async fn mixnodes_basic_all(
|
||||
state: State<AppState>,
|
||||
query_params: Query<NodesParams>,
|
||||
) -> PaginatedSkimmedNodes {
|
||||
todo!()
|
||||
mixnodes_basic(state, query_params, false).await
|
||||
}
|
||||
|
||||
/// Returns Nym Nodes and optionally legacy mixnodes (if `no-legacy` flag is not used)
|
||||
@@ -400,7 +305,118 @@ pub(super) async fn mixnodes_basic_active(
|
||||
state: State<AppState>,
|
||||
query_params: Query<NodesParams>,
|
||||
) -> PaginatedSkimmedNodes {
|
||||
todo!()
|
||||
mixnodes_basic(state, query_params, true).await
|
||||
}
|
||||
|
||||
async fn entry_gateways_basic(
|
||||
state: State<AppState>,
|
||||
Query(query_params): Query<NodesParams>,
|
||||
active_only: bool,
|
||||
) -> PaginatedSkimmedNodes {
|
||||
// TODO: implement it
|
||||
let _ = query_params.per_page;
|
||||
let _ = query_params.page;
|
||||
let semver_compatibility = query_params.semver_compatibility;
|
||||
|
||||
// 1. get the rewarded set
|
||||
let rewarded_set = state.rewarded_set().await?;
|
||||
|
||||
// 2. grab all annotations so that we could attach scores to the [nym] nodes
|
||||
let annotations = state.node_annotations().await?;
|
||||
|
||||
// 3. grab all relevant described nym-nodes
|
||||
let describe_cache = state.describe_nodes_cache_data().await?;
|
||||
let gateway_capable_nym_nodes = describe_cache.entry_capable_nym_nodes();
|
||||
|
||||
// 4. start building the response
|
||||
let mut nodes = Vec::new();
|
||||
|
||||
for nym_node in gateway_capable_nym_nodes {
|
||||
let node_id = nym_node.node_id;
|
||||
|
||||
// if this node is not an active gateway, ignore it
|
||||
if active_only && !rewarded_set.is_entry(&node_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if we have wrong version, ignore
|
||||
if !semver(&semver_compatibility, nym_node.version()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let role = match rewarded_set.is_entry(&node_id) {
|
||||
true => NodeRole::EntryGateway,
|
||||
false => NodeRole::Inactive,
|
||||
};
|
||||
|
||||
// honestly, not sure under what exact circumstances this value could be missing,
|
||||
// but in that case just use 0 performance
|
||||
let annotation = annotations.get(&node_id).copied().unwrap_or_default();
|
||||
|
||||
nodes.push(nym_node.to_skimmed_node(role, annotation.last_24h_performance));
|
||||
}
|
||||
|
||||
// 5. if we allow legacy gateways, repeat the procedure for gateways, otherwise return just nym-nodes
|
||||
if query_params.no_legacy {
|
||||
// min of all caches
|
||||
let refreshed_at = refreshed_at([
|
||||
rewarded_set.timestamp(),
|
||||
annotations.timestamp(),
|
||||
describe_cache.timestamp(),
|
||||
]);
|
||||
|
||||
return Ok(Json(PaginatedCachedNodesResponse::new_full(
|
||||
refreshed_at,
|
||||
nodes,
|
||||
)));
|
||||
}
|
||||
|
||||
// 6. grab all legacy gateways
|
||||
// due to legacy endpoints we already have fully annotated data on them
|
||||
let annotated_legacy_gateways = state.legacy_gateways_annotations().await?;
|
||||
|
||||
for (node_id, legacy) in annotated_legacy_gateways.iter() {
|
||||
// if this node is not an active gateway, ignore it
|
||||
if active_only && !rewarded_set.is_entry(&node_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if we have wrong version, ignore
|
||||
if !semver(&semver_compatibility, legacy.version()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let role = match rewarded_set.is_entry(&node_id) {
|
||||
true => NodeRole::EntryGateway,
|
||||
false => NodeRole::Inactive,
|
||||
};
|
||||
|
||||
// if we have self-described info, prefer it over contract data
|
||||
if let Some(described) = describe_cache.get_node(node_id) {
|
||||
nodes.push(described.to_skimmed_node(role, legacy.node_performance.last_24h))
|
||||
} else {
|
||||
match legacy.try_to_skimmed_node(role) {
|
||||
Ok(node) => nodes.push(node),
|
||||
Err(err) => {
|
||||
let id = legacy.gateway_bond.identity();
|
||||
trace!("node {id} is malformed: {err}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// min of all caches
|
||||
let refreshed_at = refreshed_at([
|
||||
rewarded_set.timestamp(),
|
||||
annotations.timestamp(),
|
||||
describe_cache.timestamp(),
|
||||
annotated_legacy_gateways.timestamp(),
|
||||
]);
|
||||
|
||||
Ok(Json(PaginatedCachedNodesResponse::new_full(
|
||||
refreshed_at,
|
||||
nodes,
|
||||
)))
|
||||
}
|
||||
|
||||
/// Returns Nym Nodes and optionally legacy gateways (if `no-legacy` flag is not used)
|
||||
@@ -419,26 +435,7 @@ pub(super) async fn entry_gateways_basic_active(
|
||||
state: State<AppState>,
|
||||
query_params: Query<NodesParams>,
|
||||
) -> PaginatedSkimmedNodes {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Returns Nym Nodes and optionally legacy gateways (if `no-legacy` flag is not used)
|
||||
/// that are currently bonded and are in the active set with the exit role.
|
||||
#[utoipa::path(
|
||||
tag = "Unstable Nym Nodes",
|
||||
get,
|
||||
params(NodesParamsWithRole),
|
||||
path = "/exit-gateways/active",
|
||||
context_path = "/v1/unstable/nym-nodes/skimmed",
|
||||
responses(
|
||||
(status = 200, body = PaginatedCachedNodesResponse<SkimmedNode>)
|
||||
)
|
||||
)]
|
||||
pub(super) async fn exit_gateways_basic_active(
|
||||
state: State<AppState>,
|
||||
query_params: Query<NodesParams>,
|
||||
) -> PaginatedSkimmedNodes {
|
||||
todo!()
|
||||
entry_gateways_basic(state, query_params, true).await
|
||||
}
|
||||
|
||||
/// Returns Nym Nodes and optionally legacy gateways (if `no-legacy` flag is not used)
|
||||
@@ -457,7 +454,137 @@ pub(super) async fn entry_gateways_basic_all(
|
||||
state: State<AppState>,
|
||||
query_params: Query<NodesParams>,
|
||||
) -> PaginatedSkimmedNodes {
|
||||
todo!()
|
||||
entry_gateways_basic(state, query_params, false).await
|
||||
}
|
||||
|
||||
async fn exit_gateways_basic(
|
||||
state: State<AppState>,
|
||||
Query(query_params): Query<NodesParams>,
|
||||
active_only: bool,
|
||||
) -> PaginatedSkimmedNodes {
|
||||
// TODO: implement it
|
||||
let _ = query_params.per_page;
|
||||
let _ = query_params.page;
|
||||
let semver_compatibility = query_params.semver_compatibility;
|
||||
|
||||
// 1. get the rewarded set
|
||||
let rewarded_set = state.rewarded_set().await?;
|
||||
|
||||
// 2. grab all annotations so that we could attach scores to the [nym] nodes
|
||||
let annotations = state.node_annotations().await?;
|
||||
|
||||
// 3. grab all relevant described nym-nodes
|
||||
let describe_cache = state.describe_nodes_cache_data().await?;
|
||||
let gateway_capable_nym_nodes = describe_cache.exit_capable_nym_nodes();
|
||||
|
||||
// 4. start building the response
|
||||
let mut nodes = Vec::new();
|
||||
|
||||
for nym_node in gateway_capable_nym_nodes {
|
||||
let node_id = nym_node.node_id;
|
||||
|
||||
// if this node is not an active gateway, ignore it
|
||||
if active_only && !rewarded_set.is_exit(&node_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if we have wrong version, ignore
|
||||
if !semver(&semver_compatibility, nym_node.version()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let role = match rewarded_set.is_exit(&node_id) {
|
||||
true => NodeRole::ExitGateway,
|
||||
false => NodeRole::Inactive,
|
||||
};
|
||||
|
||||
// honestly, not sure under what exact circumstances this value could be missing,
|
||||
// but in that case just use 0 performance
|
||||
let annotation = annotations.get(&node_id).copied().unwrap_or_default();
|
||||
|
||||
nodes.push(nym_node.to_skimmed_node(role, annotation.last_24h_performance));
|
||||
}
|
||||
|
||||
// 5. if we allow legacy gateways, repeat the procedure for gateways, otherwise return just nym-nodes
|
||||
if query_params.no_legacy {
|
||||
// min of all caches
|
||||
let refreshed_at = refreshed_at([
|
||||
rewarded_set.timestamp(),
|
||||
annotations.timestamp(),
|
||||
describe_cache.timestamp(),
|
||||
]);
|
||||
|
||||
return Ok(Json(PaginatedCachedNodesResponse::new_full(
|
||||
refreshed_at,
|
||||
nodes,
|
||||
)));
|
||||
}
|
||||
|
||||
// 6. grab all legacy gateways
|
||||
// due to legacy endpoints we already have fully annotated data on them
|
||||
let annotated_legacy_gateways = state.legacy_gateways_annotations().await?;
|
||||
|
||||
for (node_id, legacy) in annotated_legacy_gateways.iter() {
|
||||
// if this node is not an active gateway, ignore it
|
||||
if active_only && !rewarded_set.is_exit(&node_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if we have wrong version, ignore
|
||||
if !semver(&semver_compatibility, legacy.version()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let role = match rewarded_set.is_exit(&node_id) {
|
||||
true => NodeRole::ExitGateway,
|
||||
false => NodeRole::Inactive,
|
||||
};
|
||||
|
||||
// if we have self-described info, prefer it over contract data
|
||||
if let Some(described) = describe_cache.get_node(node_id) {
|
||||
nodes.push(described.to_skimmed_node(role, legacy.node_performance.last_24h))
|
||||
} else {
|
||||
match legacy.try_to_skimmed_node(role) {
|
||||
Ok(node) => nodes.push(node),
|
||||
Err(err) => {
|
||||
let id = legacy.gateway_bond.identity();
|
||||
trace!("node {id} is malformed: {err}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// min of all caches
|
||||
let refreshed_at = refreshed_at([
|
||||
rewarded_set.timestamp(),
|
||||
annotations.timestamp(),
|
||||
describe_cache.timestamp(),
|
||||
annotated_legacy_gateways.timestamp(),
|
||||
]);
|
||||
|
||||
Ok(Json(PaginatedCachedNodesResponse::new_full(
|
||||
refreshed_at,
|
||||
nodes,
|
||||
)))
|
||||
}
|
||||
|
||||
/// Returns Nym Nodes and optionally legacy gateways (if `no-legacy` flag is not used)
|
||||
/// that are currently bonded and are in the active set with the exit role.
|
||||
#[utoipa::path(
|
||||
tag = "Unstable Nym Nodes",
|
||||
get,
|
||||
params(NodesParamsWithRole),
|
||||
path = "/exit-gateways/active",
|
||||
context_path = "/v1/unstable/nym-nodes/skimmed",
|
||||
responses(
|
||||
(status = 200, body = PaginatedCachedNodesResponse<SkimmedNode>)
|
||||
)
|
||||
)]
|
||||
pub(super) async fn exit_gateways_basic_active(
|
||||
state: State<AppState>,
|
||||
query_params: Query<NodesParams>,
|
||||
) -> PaginatedSkimmedNodes {
|
||||
exit_gateways_basic(state, query_params, true).await
|
||||
}
|
||||
|
||||
/// Returns Nym Nodes and optionally legacy gateways (if `no-legacy` flag is not used)
|
||||
@@ -476,5 +603,5 @@ pub(super) async fn exit_gateways_basic_all(
|
||||
state: State<AppState>,
|
||||
query_params: Query<NodesParams>,
|
||||
) -> PaginatedSkimmedNodes {
|
||||
todo!()
|
||||
exit_gateways_basic(state, query_params, false).await
|
||||
}
|
||||
|
||||
@@ -5,11 +5,17 @@ use crate::circulating_supply_api::cache::CirculatingSupplyCache;
|
||||
use crate::network::models::NetworkDetails;
|
||||
use crate::node_describe_cache::DescribedNodes;
|
||||
use crate::node_status_api::handlers::unstable;
|
||||
use crate::node_status_api::models::AxumErrorResponse;
|
||||
use crate::node_status_api::NodeStatusCache;
|
||||
use crate::nym_contract_cache::cache::NymContractCache;
|
||||
use crate::nym_contract_cache::cache::{CachedRewardedSet, NymContractCache};
|
||||
use crate::support::caching::cache::SharedCache;
|
||||
use crate::support::caching::Cache;
|
||||
use crate::support::storage;
|
||||
use nym_api_requests::models::{GatewayBondAnnotated, MixNodeBondAnnotated, NodeAnnotation};
|
||||
use nym_mixnet_contract_common::NodeId;
|
||||
use nym_task::TaskManager;
|
||||
use std::collections::HashMap;
|
||||
use tokio::sync::RwLockReadGuard;
|
||||
use tokio::task::JoinHandle;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
@@ -102,3 +108,50 @@ impl AppState {
|
||||
&self.node_info_cache
|
||||
}
|
||||
}
|
||||
|
||||
// handler helpers to easily get data or return error response
|
||||
impl AppState {
|
||||
pub(crate) async fn describe_nodes_cache_data(
|
||||
&self,
|
||||
) -> Result<RwLockReadGuard<Cache<DescribedNodes>>, AxumErrorResponse> {
|
||||
Ok(self.described_nodes_cache().get().await?)
|
||||
}
|
||||
|
||||
pub(crate) async fn rewarded_set(
|
||||
&self,
|
||||
) -> Result<RwLockReadGuard<Cache<CachedRewardedSet>>, AxumErrorResponse> {
|
||||
self.nym_contract_cache()
|
||||
.rewarded_set()
|
||||
.await
|
||||
.ok_or_else(AxumErrorResponse::internal)
|
||||
}
|
||||
|
||||
pub(crate) async fn node_annotations(
|
||||
&self,
|
||||
) -> Result<RwLockReadGuard<Cache<HashMap<NodeId, NodeAnnotation>>>, AxumErrorResponse> {
|
||||
self.node_status_cache()
|
||||
.node_annotations()
|
||||
.await
|
||||
.ok_or_else(AxumErrorResponse::internal)
|
||||
}
|
||||
|
||||
pub(crate) async fn legacy_mixnode_annotations(
|
||||
&self,
|
||||
) -> Result<RwLockReadGuard<Cache<HashMap<NodeId, MixNodeBondAnnotated>>>, AxumErrorResponse>
|
||||
{
|
||||
self.node_status_cache()
|
||||
.annotated_legacy_mixnodes()
|
||||
.await
|
||||
.ok_or_else(AxumErrorResponse::internal)
|
||||
}
|
||||
|
||||
pub(crate) async fn legacy_gateways_annotations(
|
||||
&self,
|
||||
) -> Result<RwLockReadGuard<Cache<HashMap<NodeId, GatewayBondAnnotated>>>, AxumErrorResponse>
|
||||
{
|
||||
self.node_status_cache()
|
||||
.annotated_legacy_gateways()
|
||||
.await
|
||||
.ok_or_else(AxumErrorResponse::internal)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user