retrieving rotation id when pulling topology

This commit is contained in:
Jędrzej Stuczyński
2025-05-14 17:05:31 +01:00
parent 58087a029b
commit 0c653cef51
13 changed files with 354 additions and 69 deletions
@@ -4,7 +4,7 @@
use async_trait::async_trait;
use log::{debug, error, warn};
use nym_topology::provider_trait::TopologyProvider;
use nym_topology::NymTopology;
use nym_topology::{NymTopology, NymTopologyMetadata};
use nym_validator_client::UserAgent;
use rand::prelude::SliceRandom;
use rand::thread_rng;
@@ -89,55 +89,84 @@ impl NymApiTopologyProvider {
let rewarded_set_fut = self.validator_client.get_current_rewarded_set();
let topology = if self.config.use_extended_topology {
let all_nodes_fut = self.validator_client.get_all_basic_nodes();
let all_nodes_fut = self.validator_client.get_all_basic_nodes_with_metadata();
// Join rewarded_set_fut and all_nodes_fut concurrently
let (rewarded_set, all_nodes) = futures::try_join!(rewarded_set_fut, all_nodes_fut)
let (rewarded_set, all_nodes_res) = futures::try_join!(rewarded_set_fut, all_nodes_fut)
.inspect_err(|err| error!("failed to get network nodes: {err}"))
.ok()?;
let metadata = all_nodes_res.metadata;
let all_nodes = all_nodes_res.nodes;
debug!(
"there are {} nodes on the network (before filtering)",
all_nodes.len()
);
let mut topology = NymTopology::new_empty(rewarded_set);
topology.add_additional_nodes(all_nodes.iter().filter(|n| {
n.performance.round_to_integer() >= self.config.min_node_performance()
}));
let nodes_filtered = all_nodes
.into_iter()
.filter(|n| n.performance.round_to_integer() >= self.config.min_node_performance())
.collect::<Vec<_>>();
topology
NymTopology::new(
NymTopologyMetadata::new(metadata.rotation_id, metadata.absolute_epoch_id),
rewarded_set,
Vec::new(),
)
.with_skimmed_nodes(&nodes_filtered)
} else {
// if we're not using extended topology, we're only getting active set mixnodes and gateways
let mixnodes_fut = self
.validator_client
.get_all_basic_active_mixing_assigned_nodes();
.get_all_basic_active_mixing_assigned_nodes_with_metadata();
// TODO: we really should be getting ACTIVE gateways only
let gateways_fut = self.validator_client.get_all_basic_entry_assigned_nodes();
let gateways_fut = self
.validator_client
.get_all_basic_entry_assigned_nodes_with_metadata();
let (rewarded_set, mixnodes, gateways) =
let (rewarded_set, mixnodes_res, gateways_res) =
futures::try_join!(rewarded_set_fut, mixnodes_fut, gateways_fut)
.inspect_err(|err| {
error!("failed to get network nodes: {err}");
})
.ok()?;
let metadata = mixnodes_res.metadata;
let mixnodes = mixnodes_res.nodes;
if gateways_res.metadata != metadata {
warn!("inconsistent nodes metadata between mixnodes and gateways calls! {metadata:?} and {:?}", gateways_res.metadata);
return None;
}
let gateways = gateways_res.nodes;
debug!(
"there are {} mixnodes and {} gateways in total (before performance filtering)",
mixnodes.len(),
gateways.len()
);
let mut topology = NymTopology::new_empty(rewarded_set);
topology.add_additional_nodes(mixnodes.iter().filter(|m| {
m.performance.round_to_integer() >= self.config.min_mixnode_performance
}));
topology.add_additional_nodes(gateways.iter().filter(|m| {
m.performance.round_to_integer() >= self.config.min_gateway_performance
}));
let mut nodes = Vec::new();
for mix in mixnodes {
if mix.performance.round_to_integer() >= self.config.min_mixnode_performance {
nodes.push(mix)
}
}
for gateway in gateways {
if gateway.performance.round_to_integer() >= self.config.min_gateway_performance {
nodes.push(gateway)
}
}
topology
NymTopology::new(
NymTopologyMetadata::new(metadata.rotation_id, metadata.absolute_epoch_id),
rewarded_set,
Vec::new(),
)
.with_skimmed_nodes(&nodes)
};
if !topology.is_minimally_routable() {
+4 -1
View File
@@ -107,7 +107,10 @@ pub async fn gateways_for_init<R: Rng>(
log::debug!("Fetching list of gateways from: {nym_api}");
let gateways = client.get_all_basic_entry_assigned_nodes().await?;
let gateways = client
.get_all_basic_entry_assigned_nodes_with_metadata()
.await?
.nodes;
info!("nym api reports {} gateways", gateways.len());
log::trace!("Gateways: {:#?}", gateways);
@@ -25,7 +25,9 @@ use nym_api_requests::models::{
NymNodeDescription, RewardEstimationResponse, StakeSaturationResponse,
};
use nym_api_requests::models::{LegacyDescribedGateway, MixNodeBondAnnotated};
use nym_api_requests::nym_nodes::{NodesByAddressesResponse, SkimmedNode};
use nym_api_requests::nym_nodes::{
NodesByAddressesResponse, SkimmedNode, SkimmedNodesWithMetadata,
};
use nym_coconut_dkg_common::types::EpochId;
use nym_http_api_client::UserAgent;
use nym_mixnet_contract_common::EpochRewardedSet;
@@ -425,12 +427,34 @@ impl NymApiClient {
/// retrieve basic information for nodes are capable of operating as an entry gateway
/// this includes legacy gateways and nym-nodes
#[deprecated(note = "use get_all_basic_entry_assigned_nodes_with_metadata instead")]
pub async fn get_all_basic_entry_assigned_nodes(
&self,
) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
self.get_all_basic_entry_assigned_nodes_with_metadata()
.await
.map(|res| res.nodes)
}
pub async fn get_all_basic_entry_assigned_nodes_with_metadata(
&self,
) -> Result<SkimmedNodesWithMetadata, ValidatorClientError> {
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
let mut page = 0;
let mut nodes = Vec::new();
// unroll first loop iteration in order to obtain the metadata
let res = self
.nym_api
.get_basic_entry_assigned_nodes(false, Some(page), None, self.use_bincode)
.await?;
let mut nodes = res.nodes.data;
let metadata = res.metadata;
if res.nodes.pagination.total == nodes.len() {
return Ok(SkimmedNodesWithMetadata::new(nodes, metadata));
}
page += 1;
loop {
let mut res = self
@@ -438,6 +462,10 @@ impl NymApiClient {
.get_basic_entry_assigned_nodes(false, Some(page), None, self.use_bincode)
.await?;
if metadata != res.metadata {
return Err(ValidatorClientError::InconsistentPagedMetadata);
}
nodes.append(&mut res.nodes.data);
if nodes.len() < res.nodes.pagination.total {
page += 1
@@ -446,17 +474,39 @@ impl NymApiClient {
}
}
Ok(nodes)
Ok(SkimmedNodesWithMetadata::new(nodes, metadata))
}
/// retrieve basic information for nodes that got assigned 'mixing' node in this epoch
/// this includes legacy mixnodes and nym-nodes
#[deprecated(note = "use get_all_basic_active_mixing_assigned_nodes_with_metadata instead")]
pub async fn get_all_basic_active_mixing_assigned_nodes(
&self,
) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
self.get_all_basic_active_mixing_assigned_nodes_with_metadata()
.await
.map(|res| res.nodes)
}
pub async fn get_all_basic_active_mixing_assigned_nodes_with_metadata(
&self,
) -> Result<SkimmedNodesWithMetadata, ValidatorClientError> {
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
let mut page = 0;
let mut nodes = Vec::new();
// unroll first loop iteration in order to obtain the metadata
let res = self
.nym_api
.get_basic_active_mixing_assigned_nodes(false, Some(page), None, self.use_bincode)
.await?;
let mut nodes = res.nodes.data;
let metadata = res.metadata;
if res.nodes.pagination.total == nodes.len() {
return Ok(SkimmedNodesWithMetadata::new(nodes, metadata));
}
page += 1;
loop {
let mut res = self
@@ -464,6 +514,10 @@ impl NymApiClient {
.get_basic_active_mixing_assigned_nodes(false, Some(page), None, self.use_bincode)
.await?;
if metadata != res.metadata {
return Err(ValidatorClientError::InconsistentPagedMetadata);
}
nodes.append(&mut res.nodes.data);
if nodes.len() < res.nodes.pagination.total {
page += 1
@@ -472,17 +526,39 @@ impl NymApiClient {
}
}
Ok(nodes)
Ok(SkimmedNodesWithMetadata::new(nodes, metadata))
}
/// retrieve basic information for nodes are capable of operating as a mixnode
/// this includes legacy mixnodes and nym-nodes
#[deprecated(note = "use get_all_basic_mixing_capable_nodes_with_metadata instead")]
pub async fn get_all_basic_mixing_capable_nodes(
&self,
) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
self.get_all_basic_mixing_capable_nodes_with_metadata()
.await
.map(|res| res.nodes)
}
pub async fn get_all_basic_mixing_capable_nodes_with_metadata(
&self,
) -> Result<SkimmedNodesWithMetadata, ValidatorClientError> {
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
let mut page = 0;
let mut nodes = Vec::new();
// unroll first loop iteration in order to obtain the metadata
let res = self
.nym_api
.get_basic_mixing_capable_nodes(false, Some(page), None, self.use_bincode)
.await?;
let mut nodes = res.nodes.data;
let metadata = res.metadata;
if res.nodes.pagination.total == nodes.len() {
return Ok(SkimmedNodesWithMetadata::new(nodes, metadata));
}
page += 1;
loop {
let mut res = self
@@ -490,6 +566,10 @@ impl NymApiClient {
.get_basic_mixing_capable_nodes(false, Some(page), None, self.use_bincode)
.await?;
if metadata != res.metadata {
return Err(ValidatorClientError::InconsistentPagedMetadata);
}
nodes.append(&mut res.nodes.data);
if nodes.len() < res.nodes.pagination.total {
page += 1
@@ -498,14 +578,36 @@ impl NymApiClient {
}
}
Ok(nodes)
Ok(SkimmedNodesWithMetadata::new(nodes, metadata))
}
/// retrieve basic information for all bonded nodes on the network
#[deprecated(note = "use get_all_basic_nodes_with_metadata instead")]
pub async fn get_all_basic_nodes(&self) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
self.get_all_basic_nodes_with_metadata()
.await
.map(|res| res.nodes)
}
pub async fn get_all_basic_nodes_with_metadata(
&self,
) -> Result<SkimmedNodesWithMetadata, ValidatorClientError> {
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
let mut page = 0;
let mut nodes = Vec::new();
// unroll first loop iteration in order to obtain the metadata
let res = self
.nym_api
.get_basic_nodes(false, Some(page), None, self.use_bincode)
.await?;
let mut nodes = res.nodes.data;
let metadata = res.metadata;
if res.nodes.pagination.total == nodes.len() {
return Ok(SkimmedNodesWithMetadata::new(nodes, metadata));
}
page += 1;
loop {
let mut res = self
@@ -513,6 +615,10 @@ impl NymApiClient {
.get_basic_nodes(false, Some(page), None, self.use_bincode)
.await?;
if metadata != res.metadata {
return Err(ValidatorClientError::InconsistentPagedMetadata);
}
nodes.append(&mut res.nodes.data);
if nodes.len() < res.nodes.pagination.total {
page += 1
@@ -521,7 +627,7 @@ impl NymApiClient {
}
}
Ok(nodes)
Ok(SkimmedNodesWithMetadata::new(nodes, metadata))
}
pub async fn health(&self) -> Result<ApiHealthResponse, ValidatorClientError> {
@@ -22,6 +22,9 @@ pub enum ValidatorClientError {
#[error("nyxd request failed: {0}")]
NyxdError(#[from] crate::nyxd::error::NyxdError),
#[error("the response metadata has changed between pages")]
InconsistentPagedMetadata,
#[error("No validator API url has been provided")]
NoAPIUrlAvailable,
}
+5 -1
View File
@@ -21,9 +21,12 @@ pub struct InvalidSphinxKeyRotation {
received: u8,
}
// convert from particular rotation id into SphinxKeyRotation variant
impl From<u32> for SphinxKeyRotation {
fn from(value: u32) -> Self {
if value % 2 == 0 {
if value == 0 || value == u32::MAX {
SphinxKeyRotation::Unknown
} else if value % 2 == 0 {
SphinxKeyRotation::EvenRotation
} else {
SphinxKeyRotation::OddRotation
@@ -31,6 +34,7 @@ impl From<u32> for SphinxKeyRotation {
}
}
// convert from an encoded SphinxKeyRotation into particular variant
// if value is actually provided, it MUST be one of the two. otherwise is invalid
impl TryFrom<u8> for SphinxKeyRotation {
type Error = InvalidSphinxKeyRotation;
+47 -3
View File
@@ -3,6 +3,8 @@
use ::serde::{Deserialize, Serialize};
use nym_api_requests::nym_nodes::SkimmedNode;
use nym_crypto::asymmetric::ed25519;
use nym_mixnet_contract_common::EpochId;
use nym_sphinx_addressing::nodes::NodeIdentity;
use nym_sphinx_types::Node as SphinxNode;
use rand::prelude::IteratorRandom;
@@ -15,7 +17,6 @@ use tracing::{debug, trace, warn};
pub use crate::node::{EntryDetails, RoutingNode, SupportedRoles};
pub use error::NymTopologyError;
use nym_crypto::asymmetric::ed25519;
pub use nym_mixnet_contract_common::nym_node::Role;
pub use nym_mixnet_contract_common::{EpochRewardedSet, NodeId};
pub use rewarded_set::CachedEpochRewardedSet;
@@ -92,8 +93,39 @@ mod deprecated_network_address_impls {
pub type MixLayer = u8;
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct NymTopologyMetadata {
key_rotation_id: u32,
// we have to keep track of key rotation id anyway, so we might as well also include the epoch id
// to keep track of the data staleness
absolute_epoch_id: EpochId,
}
impl NymTopologyMetadata {
pub fn new(key_rotation_id: u32, absolute_epoch_id: EpochId) -> Self {
NymTopologyMetadata {
key_rotation_id,
absolute_epoch_id,
}
}
}
impl Default for NymTopologyMetadata {
fn default() -> Self {
// that's not ideal, but we don't want to break backwards compatibility : /
NymTopologyMetadata {
key_rotation_id: u32::MAX,
absolute_epoch_id: 0,
}
}
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct NymTopology {
// while this is not ideal, use empty values as default to not break backwards compatibility
#[serde(default)]
metadata: NymTopologyMetadata,
// for the purposes of future VRF, everyone will need the same view of the network, regardless of performance filtering
// so we use the same 'master' rewarded set information for that
//
@@ -130,8 +162,11 @@ impl NymRouteProvider {
}
pub fn current_key_rotation(&self) -> u32 {
todo!()
// self.topology.rewarded_set.epoch_id
self.topology.metadata.key_rotation_id
}
pub fn absolute_epoch_id(&self) -> EpochId {
self.topology.metadata.absolute_epoch_id
}
pub fn new_empty(ignore_egress_epoch_roles: bool) -> NymRouteProvider {
@@ -207,18 +242,22 @@ impl NymRouteProvider {
}
impl NymTopology {
#[deprecated]
pub fn new_empty(rewarded_set: impl Into<CachedEpochRewardedSet>) -> Self {
NymTopology {
metadata: NymTopologyMetadata::default(),
rewarded_set: rewarded_set.into(),
node_details: Default::default(),
}
}
pub fn new(
metadata: NymTopologyMetadata,
rewarded_set: impl Into<CachedEpochRewardedSet>,
node_details: Vec<RoutingNode>,
) -> Self {
NymTopology {
metadata,
rewarded_set: rewarded_set.into(),
node_details: node_details.into_iter().map(|n| (n.node_id, n)).collect(),
}
@@ -234,6 +273,11 @@ impl NymTopology {
self.add_additional_nodes(nodes.iter())
}
pub fn with_skimmed_nodes(mut self, nodes: &[SkimmedNode]) -> Self {
self.add_skimmed_nodes(nodes);
self
}
pub fn add_routing_nodes<B: Borrow<RoutingNode>>(
&mut self,
nodes: impl IntoIterator<Item = B>,
+5
View File
@@ -84,6 +84,8 @@ pub enum TypesError {
NotADelegationEvent,
#[error("Unknown network - {0}")]
UnknownNetwork(String),
#[error("the response metadata has changed between pages")]
InconsistentPagedMetadata,
}
impl Serialize for TypesError {
@@ -103,6 +105,9 @@ impl From<ValidatorClientError> for TypesError {
ValidatorClientError::NyxdError(e) => e.into(),
ValidatorClientError::NoAPIUrlAvailable => TypesError::NoNymApiUrlConfigured,
ValidatorClientError::TendermintErrorRpc(err) => err.into(),
ValidatorClientError::InconsistentPagedMetadata => {
TypesError::InconsistentPagedMetadata
}
}
}
}
+63 -12
View File
@@ -8,14 +8,28 @@ use nym_crypto::asymmetric::x25519::serde_helpers::bs58_x25519_pubkey;
use nym_crypto::asymmetric::{ed25519, x25519};
use nym_mixnet_contract_common::nym_node::Role;
use nym_mixnet_contract_common::reward_params::Performance;
use nym_mixnet_contract_common::{Interval, NodeId};
use nym_mixnet_contract_common::{EpochId, Interval, NodeId};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::net::IpAddr;
use time::OffsetDateTime;
use utoipa::ToSchema;
#[derive(Clone, Copy, Debug, Serialize, Deserialize, schemars::JsonSchema, utoipa::ToSchema)]
#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, utoipa::ToSchema)]
pub struct SkimmedNodesWithMetadata {
pub nodes: Vec<SkimmedNode>,
pub metadata: NodesResponseMetadata,
}
impl SkimmedNodesWithMetadata {
pub fn new(nodes: Vec<SkimmedNode>, metadata: NodesResponseMetadata) -> Self {
SkimmedNodesWithMetadata { nodes, metadata }
}
}
#[derive(
Clone, Copy, Debug, Serialize, Deserialize, schemars::JsonSchema, utoipa::ToSchema, PartialEq,
)]
#[serde(rename_all = "kebab-case")]
pub enum TopologyRequestStatus {
NoUpdates,
@@ -43,22 +57,52 @@ impl<T: ToSchema> CachedNodesResponse<T> {
}
}
#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema)]
pub struct PaginatedCachedNodesResponse<T> {
#[derive(
Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, utoipa::ToSchema, PartialEq,
)]
pub struct NodesResponseMetadata {
pub status: Option<TopologyRequestStatus>,
#[schema(value_type = u32)]
pub absolute_epoch_id: EpochId,
pub rotation_id: u32,
pub refreshed_at: OffsetDateTimeJsonSchemaWrapper,
}
impl NodesResponseMetadata {
pub fn refreshed_at(&self) -> OffsetDateTime {
self.refreshed_at.into()
}
}
const TODO: &str =
"create new endpoints with metadata after poc works for backwards bincode compat...";
#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema)]
// can't add any new fields here, even with #[serde(default)] and whatnot,
// because it will break all clients using bincode : (
pub struct LegacyPaginatedCachedNodesResponse<T> {
pub status: Option<TopologyRequestStatus>,
pub refreshed_at: OffsetDateTimeJsonSchemaWrapper,
pub nodes: PaginatedResponse<T>,
}
#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema)]
pub struct PaginatedCachedNodesResponse<T> {
// make sure to flatten it to preserve backwards compatibility!!
#[serde(flatten)]
pub metadata: NodesResponseMetadata,
pub nodes: PaginatedResponse<T>,
}
impl<T> PaginatedCachedNodesResponse<T> {
pub fn new_full(
absolute_epoch_id: EpochId,
rotation_id: u32,
refreshed_at: impl Into<OffsetDateTimeJsonSchemaWrapper>,
nodes: Vec<T>,
) -> Self {
PaginatedCachedNodesResponse {
refreshed_at: refreshed_at.into(),
nodes: PaginatedResponse {
pagination: Pagination {
total: nodes.len(),
@@ -67,19 +111,22 @@ impl<T> PaginatedCachedNodesResponse<T> {
},
data: nodes,
},
status: None,
rotation_id,
metadata: NodesResponseMetadata {
refreshed_at: refreshed_at.into(),
status: None,
absolute_epoch_id,
rotation_id,
},
}
}
pub fn fresh(mut self, interval: Interval) -> Self {
self.status = Some(TopologyRequestStatus::Fresh(interval));
self.metadata.status = Some(TopologyRequestStatus::Fresh(interval));
self
}
pub fn no_updates(rotation_id: u32) -> Self {
pub fn no_updates(absolute_epoch_id: EpochId, rotation_id: u32) -> Self {
PaginatedCachedNodesResponse {
refreshed_at: OffsetDateTime::now_utc().into(),
nodes: PaginatedResponse {
pagination: Pagination {
total: 0,
@@ -88,8 +135,12 @@ impl<T> PaginatedCachedNodesResponse<T> {
},
data: Vec::new(),
},
status: Some(TopologyRequestStatus::NoUpdates),
rotation_id,
metadata: NodesResponseMetadata {
refreshed_at: OffsetDateTime::now_utc().into(),
status: Some(TopologyRequestStatus::NoUpdates),
absolute_epoch_id,
rotation_id,
},
}
}
}
@@ -416,7 +416,14 @@ impl PacketPreparer {
let node_3 = rand_l3[i].clone();
let gateway = rand_gateways[i].clone();
routes.push(TestRoute::new(rng.gen(), node_1, node_2, node_3, gateway))
routes.push(TestRoute::new(
rng.gen(),
current_rotation_id,
node_1,
node_2,
node_3,
gateway,
))
}
info!("The following routes will be used for testing: {routes:#?}");
Some(routes)
@@ -7,7 +7,7 @@ use nym_crypto::asymmetric::ed25519;
use nym_mixnet_contract_common::nym_node::Role;
use nym_mixnet_contract_common::{EpochId, EpochRewardedSet, RewardedSet};
use nym_topology::node::RoutingNode;
use nym_topology::{NymRouteProvider, NymTopology};
use nym_topology::{NymRouteProvider, NymTopology, NymTopologyMetadata};
use std::fmt::{Debug, Formatter};
#[derive(Clone)]
@@ -19,6 +19,7 @@ pub(crate) struct TestRoute {
impl TestRoute {
pub(crate) fn new(
id: u64,
key_rotation_id: u32,
l1_mix: RoutingNode,
l2_mix: RoutingNode,
l3_mix: RoutingNode,
@@ -40,7 +41,11 @@ impl TestRoute {
TestRoute {
id,
nodes: NymTopology::new(fake_rewarded_set, nodes),
nodes: NymTopology::new(
NymTopologyMetadata::new(key_rotation_id, 0),
fake_rewarded_set,
nodes,
),
}
}
@@ -143,6 +143,7 @@ where
if let Some(client_known_epoch) = query_params.epoch_id {
if client_known_epoch == interval.current_epoch_id() {
return Ok(output.to_response(PaginatedCachedNodesResponse::no_updates(
interval.current_epoch_absolute_id(),
current_key_rotation,
)));
}
@@ -167,8 +168,13 @@ where
]);
return Ok(output.to_response(
PaginatedCachedNodesResponse::new_full(current_key_rotation, refreshed_at, nodes)
.fresh(interval),
PaginatedCachedNodesResponse::new_full(
interval.current_epoch_absolute_id(),
current_key_rotation,
refreshed_at,
nodes,
)
.fresh(interval),
));
}
@@ -193,8 +199,13 @@ where
]);
let base_response = output.to_response(
PaginatedCachedNodesResponse::new_full(current_key_rotation, refreshed_at, nodes)
.fresh(interval),
PaginatedCachedNodesResponse::new_full(
interval.current_epoch_absolute_id(),
current_key_rotation,
refreshed_at,
nodes,
)
.fresh(interval),
);
if !active_only {
@@ -237,7 +248,7 @@ pub(super) async fn deprecated_gateways_basic(
// 3. return result
Ok(output.to_response(CachedNodesResponse {
refreshed_at: all_gateways.refreshed_at,
refreshed_at: all_gateways.metadata.refreshed_at,
// 2. remove pagination
nodes: all_gateways.nodes.data,
}))
@@ -272,7 +283,7 @@ pub(super) async fn deprecated_mixnodes_basic(
// 3. return result
Ok(output.to_response(CachedNodesResponse {
refreshed_at: active_mixnodes.refreshed_at,
refreshed_at: active_mixnodes.metadata.refreshed_at,
// 2. remove pagination
nodes: active_mixnodes.nodes.data,
}))
@@ -296,6 +307,7 @@ async fn nodes_basic(
let legacy_mixnodes = state.legacy_mixnode_annotations().await?;
let legacy_gateways = state.legacy_gateways_annotations().await?;
let interval = state.nym_contract_cache().current_interval().await?;
let current_key_rotation = state.nym_contract_cache().current_key_rotation_id().await?;
let mut nodes = build_nym_nodes_response(
@@ -337,6 +349,7 @@ async fn nodes_basic(
Ok(output.to_response(PaginatedCachedNodesResponse::new_full(
current_key_rotation,
interval.current_epoch_absolute_id(),
refreshed_at,
nodes,
)))
+2 -2
View File
@@ -30,8 +30,8 @@ use nym_mixnet_contract_common::nym_node::Role;
use nym_mixnet_contract_common::reward_params::RewardingParams;
use nym_mixnet_contract_common::{
ConfigScoreParams, CurrentIntervalResponse, Delegation, EpochRewardedSet, EpochStatus,
ExecuteMsg, GatewayBond, HistoricalNymNodeVersionEntry, IdentityKey, KeyRotationState, NymNodeDetails,
RewardedSet, RoleAssignment,
ExecuteMsg, GatewayBond, HistoricalNymNodeVersionEntry, IdentityKey, KeyRotationState,
NymNodeDetails, RewardedSet, RoleAssignment,
};
use nym_validator_client::coconut::EcashApiError;
use nym_validator_client::nyxd::contract_traits::mixnet_query_client::MixnetQueryClientExt;
+28 -13
View File
@@ -10,9 +10,14 @@ use nym_gateway::node::UserAgent;
use nym_node_metrics::prometheus_wrapper::{PrometheusMetric, PROMETHEUS_METRICS};
use nym_task::ShutdownToken;
use nym_topology::node::RoutingNode;
use nym_topology::{EntryDetails, EpochRewardedSet, NodeId, NymTopology, Role, TopologyProvider};
use nym_topology::{
EntryDetails, EpochRewardedSet, NodeId, NymTopology, NymTopologyMetadata, Role,
TopologyProvider,
};
use nym_validator_client::nym_api::NymApiClientExt;
use nym_validator_client::nym_nodes::{NodesByAddressesResponse, SkimmedNode};
use nym_validator_client::nym_nodes::{
NodesByAddressesResponse, SkimmedNode, SkimmedNodesWithMetadata,
};
use nym_validator_client::{NymApiClient, ValidatorClientError};
use std::collections::HashSet;
use std::net::{IpAddr, SocketAddr};
@@ -58,10 +63,10 @@ impl NodesQuerier {
res
}
async fn current_nymnodes(&mut self) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
async fn current_nymnodes(&mut self) -> Result<SkimmedNodesWithMetadata, ValidatorClientError> {
let res = self
.client
.get_all_basic_nodes()
.get_all_basic_nodes_with_metadata()
.await
.inspect_err(|err| error!("failed to get network nodes: {err}"));
@@ -140,14 +145,18 @@ impl TopologyProvider for CachedTopologyProvider {
let network_guard = self.cached_network.inner.read().await;
let self_node = self.gateway_node.identity_key;
let mut topology = NymTopology::new_empty(network_guard.rewarded_set.clone())
.with_additional_nodes(network_guard.network_nodes.iter().filter(|node| {
if node.supported_roles.mixnode {
node.performance.round_to_integer() >= self.min_mix_performance
} else {
true
}
}));
let mut topology = NymTopology::new(
network_guard.topology_metadata,
network_guard.rewarded_set.clone(),
Vec::new(),
)
.with_additional_nodes(network_guard.network_nodes.iter().filter(|node| {
if node.supported_roles.mixnode {
node.performance.round_to_integer() >= self.min_mix_performance
} else {
true
}
}));
if !topology.has_node(self.gateway_node.identity_key) {
debug!("{self_node} didn't exist in topology. inserting it.",);
@@ -169,6 +178,7 @@ impl CachedNetwork {
CachedNetwork {
inner: Arc::new(RwLock::new(CachedNetworkInner {
rewarded_set: Default::default(),
topology_metadata: Default::default(),
network_nodes: vec![],
})),
}
@@ -177,6 +187,7 @@ impl CachedNetwork {
struct CachedNetworkInner {
rewarded_set: EpochRewardedSet,
topology_metadata: NymTopologyMetadata,
network_nodes: Vec<SkimmedNode>,
}
@@ -264,7 +275,9 @@ impl NetworkRefresher {
async fn refresh_network_nodes_inner(&mut self) -> Result<(), ValidatorClientError> {
let rewarded_set = self.querier.rewarded_set().await?;
let nodes = self.querier.current_nymnodes().await?;
let res = self.querier.current_nymnodes().await?;
let nodes = res.nodes;
let metadata = res.metadata;
// collect all known/allowed nodes information
let known_nodes = nodes
@@ -293,6 +306,8 @@ impl NetworkRefresher {
self.routing_filter.pending.clear().await;
let mut network_guard = self.network.inner.write().await;
network_guard.topology_metadata =
NymTopologyMetadata::new(metadata.rotation_id, metadata.absolute_epoch_id);
network_guard.network_nodes = nodes;
network_guard.rewarded_set = rewarded_set;