diff --git a/common/client-core/src/client/topology_control/geo_aware_provider.rs b/common/client-core/src/client/topology_control/geo_aware_provider.rs index 9967e1a705..660acdf749 100644 --- a/common/client-core/src/client/topology_control/geo_aware_provider.rs +++ b/common/client-core/src/client/topology_control/geo_aware_provider.rs @@ -110,7 +110,11 @@ impl GeoAwareTopologyProvider { } async fn get_topology(&self) -> Option { - let mixnodes = match self.validator_client.get_basic_mixnodes(None).await { + let mixnodes = match self + .validator_client + .get_basic_active_mixing_assigned_nodes(Some(self.client_version.clone())) + .await + { Err(err) => { error!("failed to get network mixnodes - {err}"); return None; @@ -118,7 +122,11 @@ impl GeoAwareTopologyProvider { Ok(mixes) => mixes, }; - let gateways = match self.validator_client.get_basic_gateways(None).await { + let gateways = match self + .validator_client + .get_all_basic_entry_assigned_nodes(Some(self.client_version.clone())) + .await + { Err(err) => { error!("failed to get network gateways - {err}"); return None; @@ -185,8 +193,7 @@ impl GeoAwareTopologyProvider { .filter(|m| filtered_mixnode_ids.contains(&m.node_id)) .collect::>(); - let topology = nym_topology_from_basic_info(&mixnodes, &gateways) - .filter_system_version(&self.client_version); + let topology = nym_topology_from_basic_info(&mixnodes, &gateways); // TODO: return real error type check_layer_integrity(topology.clone()).ok()?; diff --git a/common/client-core/src/client/topology_control/nym_api_provider.rs b/common/client-core/src/client/topology_control/nym_api_provider.rs index 3a00f462bd..c2b2dd9003 100644 --- a/common/client-core/src/client/topology_control/nym_api_provider.rs +++ b/common/client-core/src/client/topology_control/nym_api_provider.rs @@ -98,7 +98,7 @@ impl NymApiTopologyProvider { async fn get_current_compatible_topology(&mut self) -> Option { let mixnodes = match self .validator_client - .get_basic_mixnodes(Some(self.client_version.clone())) + .get_basic_active_mixing_assigned_nodes(Some(self.client_version.clone())) .await { Err(err) => { @@ -110,7 +110,7 @@ impl NymApiTopologyProvider { let gateways = match self .validator_client - .get_basic_gateways(Some(self.client_version.clone())) + .get_all_basic_entry_assigned_nodes(Some(self.client_version.clone())) .await { Err(err) => { @@ -134,7 +134,6 @@ impl NymApiTopologyProvider { g.performance.round_to_integer() >= self.config.min_gateway_performance }), ); - if let Err(err) = self.check_layer_distribution(&topology) { warn!("The current filtered active topology has extremely skewed layer distribution. It cannot be used: {err}"); self.use_next_nym_api(); diff --git a/common/client-core/src/init/helpers.rs b/common/client-core/src/init/helpers.rs index ac2b01bf6f..8e90f30240 100644 --- a/common/client-core/src/init/helpers.rs +++ b/common/client-core/src/init/helpers.rs @@ -7,7 +7,7 @@ use futures::{SinkExt, StreamExt}; use log::{debug, info, trace, warn}; use nym_crypto::asymmetric::identity; use nym_gateway_client::GatewayClient; -use nym_topology::{filter::VersionFilterable, gateway, mix}; +use nym_topology::{gateway, mix}; use nym_validator_client::client::IdentityKeyRef; use nym_validator_client::UserAgent; use rand::{seq::SliceRandom, Rng}; @@ -94,7 +94,7 @@ pub async fn current_gateways( log::debug!("Fetching list of gateways from: {nym_api}"); - let gateways = client.get_basic_gateways(None).await?; + let gateways = client.get_all_basic_entry_assigned_nodes(None).await?; log::debug!("Found {} gateways", gateways.len()); log::trace!("Gateways: {:#?}", gateways); @@ -102,17 +102,12 @@ pub async fn current_gateways( .iter() .filter_map(|gateway| gateway.try_into().ok()) .collect::>(); - log::debug!("Ater checking validity: {}", valid_gateways.len()); + log::debug!("After checking validity: {}", valid_gateways.len()); log::trace!("Valid gateways: {:#?}", valid_gateways); - // we were always filtering by version so I'm not removing that 'feature' - let filtered_gateways = valid_gateways.filter_by_version(env!("CARGO_PKG_VERSION")); - log::debug!("After filtering for version: {}", filtered_gateways.len()); - log::trace!("Filtered gateways: {:#?}", filtered_gateways); + log::info!("nym-api reports {} valid gateways", valid_gateways.len()); - log::info!("nym-api reports {} valid gateways", filtered_gateways.len()); - - Ok(filtered_gateways) + Ok(valid_gateways) } pub async fn current_mixnodes( @@ -126,15 +121,13 @@ pub async fn current_mixnodes( log::trace!("Fetching list of mixnodes from: {nym_api}"); - let mixnodes = client.get_basic_mixnodes(None).await?; + let mixnodes = client.get_basic_active_mixing_assigned_nodes(None).await?; let valid_mixnodes = mixnodes .iter() .filter_map(|mixnode| mixnode.try_into().ok()) .collect::>(); - // we were always filtering by version so I'm not removing that 'feature' - let filtered_mixnodes = valid_mixnodes.filter_by_version(env!("CARGO_PKG_VERSION")); - Ok(filtered_mixnodes) + Ok(valid_mixnodes) } #[cfg(not(target_arch = "wasm32"))] diff --git a/common/client-libs/validator-client/src/client.rs b/common/client-libs/validator-client/src/client.rs index fc68ae748c..b8375abdbd 100644 --- a/common/client-libs/validator-client/src/client.rs +++ b/common/client-libs/validator-client/src/client.rs @@ -283,6 +283,7 @@ impl NymApiClient { self.nym_api.change_base_url(new_endpoint); } + #[deprecated(note = "use get_basic_active_mixing_assigned_nodes instead")] pub async fn get_basic_mixnodes( &self, semver_compatibility: Option, @@ -294,6 +295,7 @@ impl NymApiClient { .nodes) } + #[deprecated(note = "use get_all_basic_entry_assigned_nodes instead")] pub async fn get_basic_gateways( &self, semver_compatibility: Option, @@ -305,6 +307,70 @@ impl NymApiClient { .nodes) } + /// retrieve basic information for nodes are capable of operating as an entry gateway + /// this includes legacy gateways and nym-nodes + pub async fn get_all_basic_entry_assigned_nodes( + &self, + semver_compatibility: Option, + ) -> Result, 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(); + + loop { + let mut res = self + .nym_api + .get_all_basic_entry_assigned_nodes( + semver_compatibility.clone(), + false, + Some(page), + None, + ) + .await?; + + nodes.append(&mut res.nodes.data); + if nodes.len() < res.nodes.pagination.total { + page += 1 + } else { + break; + } + } + + Ok(nodes) + } + + /// retrieve basic information for nodes that got assigned 'mixing' node in this epoch + /// this includes legacy mixnodes and nym-nodes + pub async fn get_basic_active_mixing_assigned_nodes( + &self, + semver_compatibility: Option, + ) -> Result, 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(); + + loop { + let mut res = self + .nym_api + .get_basic_active_mixing_assigned_nodes( + semver_compatibility.clone(), + false, + Some(page), + None, + ) + .await?; + + nodes.append(&mut res.nodes.data); + if nodes.len() < res.nodes.pagination.total { + page += 1 + } else { + break; + } + } + + Ok(nodes) + } + pub async fn get_cached_active_mixnodes( &self, ) -> Result, ValidatorClientError> { diff --git a/common/client-libs/validator-client/src/nym_api/mod.rs b/common/client-libs/validator-client/src/nym_api/mod.rs index a664a503ab..9660e470c7 100644 --- a/common/client-libs/validator-client/src/nym_api/mod.rs +++ b/common/client-libs/validator-client/src/nym_api/mod.rs @@ -13,6 +13,7 @@ use nym_api_requests::ecash::VerificationKeyResponse; use nym_api_requests::models::{ AnnotationResponse, LegacyDescribedMixNode, NodePerformanceResponse, }; +use nym_api_requests::nym_nodes::PaginatedCachedNodesResponse; pub use nym_api_requests::{ ecash::{ models::{ @@ -164,6 +165,88 @@ pub trait NymApiClientExt: ApiClient { .await } + /// retrieve basic information for nodes are capable of operating as an entry gateway + /// this includes legacy gateways and nym-nodes + async fn get_all_basic_entry_assigned_nodes( + &self, + semver_compatibility: Option, + no_legacy: bool, + page: Option, + per_page: Option, + ) -> Result, NymAPIError> { + let mut params = Vec::new(); + + if let Some(arg) = &semver_compatibility { + params.push(("semver_compatibility", arg.clone())) + } + + if no_legacy { + params.push(("no_legacy", "true".to_string())) + } + + if let Some(page) = page { + params.push(("page", page.to_string())) + } + + if let Some(per_page) = per_page { + params.push(("per_page", per_page.to_string())) + } + + self.get_json( + &[ + routes::API_VERSION, + "unstable", + "nym-nodes", + "skimmed", + "entry-gateways", + "all", + ], + ¶ms, + ) + .await + } + + /// retrieve basic information for nodes that got assigned 'mixing' node in this epoch + /// this includes legacy mixnodes and nym-nodes + async fn get_basic_active_mixing_assigned_nodes( + &self, + semver_compatibility: Option, + no_legacy: bool, + page: Option, + per_page: Option, + ) -> Result, NymAPIError> { + let mut params = Vec::new(); + + if let Some(arg) = &semver_compatibility { + params.push(("semver_compatibility", arg.clone())) + } + + if no_legacy { + params.push(("no_legacy", "true".to_string())) + } + + if let Some(page) = page { + params.push(("page", page.to_string())) + } + + if let Some(per_page) = per_page { + params.push(("per_page", per_page.to_string())) + } + + self.get_json( + &[ + routes::API_VERSION, + "unstable", + "nym-nodes", + "skimmed", + "mixnodes", + "active", + ], + ¶ms, + ) + .await + } + async fn get_active_mixnodes(&self) -> Result, NymAPIError> { self.get_json( &[routes::API_VERSION, routes::MIXNODES, routes::ACTIVE], diff --git a/common/wasm/client-core/src/helpers.rs b/common/wasm/client-core/src/helpers.rs index 3eafcbd623..41b203d1b5 100644 --- a/common/wasm/client-core/src/helpers.rs +++ b/common/wasm/client-core/src/helpers.rs @@ -67,8 +67,10 @@ pub async fn current_network_topology_async( }; let api_client = NymApiClient::new(url); - let mixnodes = api_client.get_basic_mixnodes(None).await?; - let gateways = api_client.get_basic_gateways(None).await?; + let mixnodes = api_client + .get_basic_active_mixing_assigned_nodes(None) + .await?; + let gateways = api_client.get_all_basic_entry_assigned_nodes(None).await?; Ok(NymTopology::from_basic(&mixnodes, &gateways).into()) } diff --git a/sdk/rust/nym-sdk/examples/custom_topology_provider.rs b/sdk/rust/nym-sdk/examples/custom_topology_provider.rs index 6737c2dcae..7b22a4f584 100644 --- a/sdk/rust/nym-sdk/examples/custom_topology_provider.rs +++ b/sdk/rust/nym-sdk/examples/custom_topology_provider.rs @@ -21,7 +21,7 @@ impl MyTopologyProvider { async fn get_topology(&self) -> NymTopology { let mixnodes = self .validator_client - .get_basic_mixnodes(None) + .get_basic_active_mixing_assigned_nodes(None) .await .unwrap(); @@ -35,7 +35,7 @@ impl MyTopologyProvider { let gateways = self .validator_client - .get_basic_gateways(None) + .get_all_basic_entry_assigned_nodes(None) .await .unwrap();