retrieving rotation id when pulling topology
This commit is contained in:
@@ -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() {
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)))
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user