diff --git a/Cargo.lock b/Cargo.lock index 4d30ae54a3..9452519df7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6238,6 +6238,7 @@ dependencies = [ "nym-bin-common", "nym-client-core-config-types", "nym-config", + "nym-contracts-common", "nym-crypto", "nym-gateway", "nym-gateway-stats-storage", @@ -6932,6 +6933,7 @@ version = "0.1.0" dependencies = [ "async-trait", "nym-api-requests", + "nym-client-core-config-types", "nym-config", "nym-crypto", "nym-mixnet-contract-common", @@ -6943,6 +6945,8 @@ dependencies = [ "serde", "serde_json", "thiserror 2.0.12", + "time", + "tokio", "tracing", "tsify", "wasm-bindgen", diff --git a/common/client-core/src/client/base_client/mod.rs b/common/client-core/src/client/base_client/mod.rs index 25d061c850..4edb130dd1 100644 --- a/common/client-core/src/client/base_client/mod.rs +++ b/common/client-core/src/client/base_client/mod.rs @@ -22,7 +22,7 @@ use crate::client::replies::reply_controller::{ReplyControllerReceiver, ReplyCon use crate::client::replies::reply_storage::{ CombinedReplyStorage, PersistentReplyStorage, ReplyStorageBackend, SentReplyKeys, }; -use crate::client::topology_control::nym_api_provider::NymApiTopologyProvider; +use crate::client::topology_control::smart_api_provider::NymApiTopologyProvider; use crate::client::topology_control::{ TopologyAccessor, TopologyRefresher, TopologyRefresherConfig, }; @@ -556,6 +556,7 @@ where config_topology, nym_api_urls, user_agent, + None, )), config::TopologyStructure::GeoAware(group_by) => { warn!("using deprecated 'GeoAware' topology provider - this option will be removed very soon"); diff --git a/common/client-core/src/client/topology_control/mod.rs b/common/client-core/src/client/topology_control/mod.rs index d75755d186..f15f8b59f9 100644 --- a/common/client-core/src/client/topology_control/mod.rs +++ b/common/client-core/src/client/topology_control/mod.rs @@ -23,8 +23,8 @@ pub mod smart_api_provider; #[allow(deprecated)] pub use geo_aware_provider::GeoAwareTopologyProvider; -pub use nym_api_provider::{Config as NymApiTopologyProviderConfig, NymApiTopologyProvider}; pub use nym_topology::providers::TopologyProvider; +pub use smart_api_provider::{Config as NymApiTopologyProviderConfig, NymApiTopologyProvider}; // TODO: move it to config later const MAX_FAILURE_COUNT: usize = 10; diff --git a/common/client-core/src/client/topology_control/smart_api_provider.rs b/common/client-core/src/client/topology_control/smart_api_provider.rs index 330a0e3a8f..a71161cfe7 100644 --- a/common/client-core/src/client/topology_control/smart_api_provider.rs +++ b/common/client-core/src/client/topology_control/smart_api_provider.rs @@ -8,9 +8,10 @@ use async_trait::async_trait; use log::{debug, error, warn}; +pub use nym_topology::providers::piecewise::Config; use nym_topology::{ - providers::piecewise::{Config, NymTopologyProvider, PiecewiseTopologyProvider}, - EpochRewardedSet, NymTopology, RoutingNode, + providers::piecewise::{NymTopologyProvider, PiecewiseTopologyProvider}, + EpochRewardedSet, NymTopology, RoutingNode, TopologyProvider, }; use nym_validator_client::UserAgent; use rand::{prelude::SliceRandom, thread_rng}; @@ -26,13 +27,13 @@ pub struct NymApiTopologyProvider { impl NymApiTopologyProvider { /// Construct a new thread safe Cached topology provider using the Nym API pub fn new( - user_agent: UserAgent, + config: impl Into, nym_api_urls: Vec, - config: Config, + user_agent: Option, initial_topology: Option, ) -> Self { - let manager = NymApiPiecewiseProvider::new(nym_api_urls, Some(user_agent)); - let inner = NymTopologyProvider::new(manager, config, initial_topology); + let manager = NymApiPiecewiseProvider::new(nym_api_urls, user_agent); + let inner = NymTopologyProvider::new(manager, config.into(), initial_topology); Self { inner } } @@ -44,6 +45,28 @@ impl AsRef> for NymApiTopologyProvi } } +impl AsMut> for NymApiTopologyProvider { + fn as_mut(&mut self) -> &mut NymTopologyProvider { + &mut self.inner + } +} + +#[cfg(not(target_arch = "wasm32"))] +#[async_trait] +impl TopologyProvider for NymApiTopologyProvider { + async fn get_new_topology(&mut self) -> Option { + self.as_mut().get_new_topology().await + } +} + +#[cfg(target_arch = "wasm32")] +#[async_trait(?Send)] +impl TopologyProvider for NymApiTopologyProvider { + async fn get_new_topology(&mut self) -> Option { + self.as_mut().get_new_topology().await + } +} + #[derive(Clone)] struct NymApiPiecewiseProvider { validator_client: nym_validator_client::client::NymApiClient, @@ -81,11 +104,8 @@ impl NymApiPiecewiseProvider { self.validator_client .change_nym_api(self.nym_api_urls[self.currently_used_api].clone()) } -} -#[async_trait] -impl PiecewiseTopologyProvider for NymApiPiecewiseProvider { - async fn get_full_topology(&mut self) -> Option { + async fn get_full_topology_inner(&mut self) -> Option { let layer_assignments = self.get_layer_assignments().await?; let mut topology = NymTopology::new_empty(layer_assignments); @@ -111,7 +131,7 @@ impl PiecewiseTopologyProvider for NymApiPiecewiseProvider { Some(topology) } - async fn get_descriptor_batch(&mut self, ids: &[u32]) -> Option> { + async fn get_descriptor_batch_inner(&mut self, ids: &[u32]) -> Option> { // Does this need to return a hashmap of RoutingNodes? that is moderately inconvenient // especially when the nodes themselves contain their node_id unless we expect to directly // use the result of this fn for lookups where we would otherwise for example, have to @@ -129,13 +149,13 @@ impl PiecewiseTopologyProvider for NymApiPiecewiseProvider { let mut out = Vec::new(); for node in descriptor_vec { if let Ok(routing_node) = RoutingNode::try_from(&node) { - let _ = out.push(routing_node); + out.push(routing_node); } } Some(out) } - async fn get_layer_assignments(&mut self) -> Option { + async fn get_layer_assignments_inner(&mut self) -> Option { self.validator_client .get_current_rewarded_set() .await @@ -146,3 +166,35 @@ impl PiecewiseTopologyProvider for NymApiPiecewiseProvider { .ok() } } + +#[cfg(not(target_arch = "wasm32"))] +#[async_trait] +impl PiecewiseTopologyProvider for NymApiPiecewiseProvider { + async fn get_full_topology(&mut self) -> Option { + self.get_full_topology_inner().await + } + + async fn get_descriptor_batch(&mut self, ids: &[u32]) -> Option> { + self.get_descriptor_batch_inner(ids).await + } + + async fn get_layer_assignments(&mut self) -> Option { + self.get_layer_assignments_inner().await + } +} + +#[cfg(target_arch = "wasm32")] +#[async_trait(?Send)] +impl PiecewiseTopologyProvider for NymApiPiecewiseProvider { + async fn get_full_topology(&mut self) -> Option { + self.get_full_topology_inner().await + } + + async fn get_descriptor_batch(&mut self, ids: &[u32]) -> Option> { + self.get_descriptor_batch_inner(ids).await + } + + async fn get_layer_assignments(&mut self) -> Option { + self.get_layer_assignments_inner().await + } +} diff --git a/common/topology/src/providers/piecewise.rs b/common/topology/src/providers/piecewise.rs index d5dce10466..0b46ead8d4 100644 --- a/common/topology/src/providers/piecewise.rs +++ b/common/topology/src/providers/piecewise.rs @@ -112,6 +112,7 @@ impl NymTopologyProvider { } } +#[cfg(not(target_arch = "wasm32"))] #[async_trait] impl TopologyProvider for NymTopologyProvider { async fn get_new_topology(&mut self) -> Option { @@ -127,6 +128,22 @@ impl TopologyProvider for NymTopologyProvider { } } +#[cfg(target_arch = "wasm32")] +#[async_trait(?Send)] +impl TopologyProvider for NymTopologyProvider { + async fn get_new_topology(&mut self) -> Option { + let mut guard = self.inner.lock().await; + // check the cache + if let Some(cached) = guard.get_current_compatible_topology().await { + return Some(cached); + } + + // not cached, or cache expired. try update. + guard.update_cache().await; + guard.get_current_compatible_topology().await + } +} + struct NymTopologyProviderInner { config: Config, @@ -242,6 +259,7 @@ impl NymTopologyProviderInner { } } +#[cfg(not(target_arch = "wasm32"))] #[async_trait] impl TopologyProvider for NymTopologyProviderInner

{ async fn get_new_topology(&mut self) -> Option { @@ -249,6 +267,15 @@ impl TopologyProvider for NymTopologyProviderInner } } +#[cfg(target_arch = "wasm32")] +#[async_trait(?Send)] +impl TopologyProvider for NymTopologyProviderInner

{ + async fn get_new_topology(&mut self) -> Option { + self.get_current_compatible_topology().await + } +} + +#[cfg(not(target_arch = "wasm32"))] /// Trait allowing construction and upkeep of a #[async_trait] pub trait PiecewiseTopologyProvider: Send { @@ -265,7 +292,25 @@ pub trait PiecewiseTopologyProvider: Send { async fn get_layer_assignments(&mut self) -> Option; } +#[cfg(target_arch = "wasm32")] +/// Trait allowing construction and upkeep of a +#[async_trait(?Send)] +pub trait PiecewiseTopologyProvider: Send { + /// Pull a copy of the full topology. + /// + /// This is intended to be used sparingly as repeated usage could result in fetching duplicate + /// information more often than necessary. + async fn get_full_topology(&mut self) -> Option; + + /// Fetch a node descriptors for the set of provided IDs if available. + async fn get_descriptor_batch(&mut self, ids: &[u32]) -> Option>; + + /// Fetch the latest mapping of node IDs to Nym Network layer. + async fn get_layer_assignments(&mut self) -> Option; +} + #[cfg(test)] +#[cfg(not(target_arch = "wasm32"))] mod test { use super::*; use crate::SupportedRoles;