diff --git a/common/client-core/Cargo.toml b/common/client-core/Cargo.toml index 74663fe807..d3e2409eec 100644 --- a/common/client-core/Cargo.toml +++ b/common/client-core/Cargo.toml @@ -53,6 +53,7 @@ nym-client-core-config-types = { path = "./config-types", features = [ nym-client-core-surb-storage = { path = "./surb-storage" } nym-client-core-gateways-storage = { path = "./gateways-storage" } nym-ecash-time = { path = "../ecash-time" } +nym-mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" } [target."cfg(not(target_arch = \"wasm32\"))".dependencies] nym-mixnet-client = { path = "../client-libs/mixnet-client", default-features = false } diff --git a/common/client-core/src/client/base_client/mod.rs b/common/client-core/src/client/base_client/mod.rs index 36cb427780..70f041d796 100644 --- a/common/client-core/src/client/base_client/mod.rs +++ b/common/client-core/src/client/base_client/mod.rs @@ -57,7 +57,7 @@ use nym_task::{TaskClient, TaskHandle}; use nym_topology::provider_trait::TopologyProvider; use nym_topology::HardcodedTopologyProvider; use nym_validator_client::nym_api::NymApiClientExt; -use nym_validator_client::{nyxd::contract_traits::DkgQueryClient, NymApiClient, UserAgent}; +use nym_validator_client::{nyxd::contract_traits::DkgQueryClient, UserAgent}; use rand::prelude::SliceRandom; use rand::rngs::OsRng; use rand::thread_rng; @@ -566,7 +566,7 @@ where custom_provider: Option>, config_topology: config::Topology, nym_api_urls: Vec, - nym_api_client: NymApiClient, + nym_api_client: nym_http_api_client::Client, ) -> Box { // if no custom provider was ... provided ..., create one using nym-api custom_provider.unwrap_or_else(|| { @@ -749,21 +749,42 @@ where setup_gateway(setup_method, key_store, details_store).await } - fn construct_nym_api_client(config: &Config, user_agent: Option) -> NymApiClient { + fn construct_nym_api_client( + config: &Config, + user_agent: Option, + ) -> Result { let mut nym_api_urls = config.get_nym_api_endpoints(); nym_api_urls.shuffle(&mut thread_rng()); + let mut builder = nym_http_api_client::Client::builder::< + _, + nym_validator_client::models::RequestError, + >(nym_api_urls[0].clone()) + .map_err(|e| ClientCoreError::NymApiQueryFailure { + source: nym_validator_client::nym_api::error::NymAPIError::GenericRequestFailure( + e.to_string(), + ), + })?; + if let Some(user_agent) = user_agent { - NymApiClient::new_with_user_agent(nym_api_urls[0].clone(), user_agent) - } else { - NymApiClient::new(nym_api_urls[0].clone()) + builder = builder.with_user_agent(user_agent); } + + builder = builder.with_bincode(); + + builder + .build::() + .map_err(|e| ClientCoreError::NymApiQueryFailure { + source: nym_validator_client::nym_api::error::NymAPIError::GenericRequestFailure( + e.to_string(), + ), + }) } async fn determine_key_rotation_state( - client: &NymApiClient, + client: &nym_http_api_client::Client, ) -> Result { - Ok(client.nym_api.get_key_rotation_info().await?.into()) + Ok(client.get_key_rotation_info().await?.into()) } pub async fn start_base(mut self) -> Result @@ -830,7 +851,7 @@ where .dkg_query_client .map(|client| BandwidthController::new(credential_store, client)); - let nym_api_client = Self::construct_nym_api_client(&self.config, self.user_agent.clone()); + let nym_api_client = Self::construct_nym_api_client(&self.config, self.user_agent.clone())?; let key_rotation_config = Self::determine_key_rotation_state(&nym_api_client).await?; let topology_provider = Self::setup_topology_provider( 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 339cd12653..15ffc8d482 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 @@ -2,8 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 use async_trait::async_trait; +use nym_mixnet_contract_common::EpochRewardedSet; use nym_topology::provider_trait::{ToTopologyMetadata, TopologyProvider}; use nym_topology::NymTopology; +use nym_validator_client::nym_api::NymApiClientExt; use rand::prelude::SliceRandom; use rand::thread_rng; use std::cmp::min; @@ -39,30 +41,43 @@ impl Config { pub struct NymApiTopologyProvider { config: Config, - validator_client: nym_validator_client::client::NymApiClient, + validator_client: nym_http_api_client::Client, nym_api_urls: Vec, currently_used_api: usize, + use_bincode: bool, } impl NymApiTopologyProvider { pub fn new( config: impl Into, mut nym_api_urls: Vec, - mut validator_client: nym_validator_client::client::NymApiClient, + validator_client: nym_http_api_client::Client, ) -> Self { nym_api_urls.shuffle(&mut thread_rng()); - validator_client.change_nym_api(nym_api_urls[0].clone()); - - NymApiTopologyProvider { + let mut provider = NymApiTopologyProvider { config: config.into(), validator_client, nym_api_urls, currently_used_api: 0, - } + use_bincode: true, + }; + // Set the initial API URL + provider.validator_client.change_base_urls( + provider + .nym_api_urls + .iter() + .map(|u| u.clone().into()) + .collect(), + ); + provider } pub fn disable_bincode(&mut self) { - self.validator_client.use_bincode = false; + self.use_bincode = false; + // Note: The unified client doesn't support toggling bincode after creation. + // This would require recreating the client without bincode. + // For now, we'll track the preference but it won't take effect. + warn!("Disabling bincode on existing client is not currently supported"); } fn use_next_nym_api(&mut self) { @@ -73,7 +88,9 @@ impl NymApiTopologyProvider { self.currently_used_api = (self.currently_used_api + 1) % self.nym_api_urls.len(); self.validator_client - .change_nym_api(self.nym_api_urls[self.currently_used_api].clone()) + .change_base_urls(vec![self.nym_api_urls[self.currently_used_api] + .clone() + .into()]) } async fn get_current_compatible_topology(&mut self) -> Option { @@ -99,8 +116,13 @@ impl NymApiTopologyProvider { .filter(|n| n.performance.round_to_integer() >= self.config.min_node_performance()) .collect::>(); - NymTopology::new(metadata.to_topology_metadata(), rewarded_set, Vec::new()) - .with_skimmed_nodes(&nodes_filtered) + let epoch_rewarded_set: EpochRewardedSet = rewarded_set.into(); + NymTopology::new( + metadata.to_topology_metadata(), + epoch_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 @@ -148,8 +170,13 @@ impl NymApiTopologyProvider { } } - NymTopology::new(metadata.to_topology_metadata(), rewarded_set, Vec::new()) - .with_skimmed_nodes(&nodes) + let epoch_rewarded_set: EpochRewardedSet = rewarded_set.into(); + NymTopology::new( + metadata.to_topology_metadata(), + epoch_rewarded_set, + Vec::new(), + ) + .with_skimmed_nodes(&nodes) }; if !topology.is_minimally_routable() { diff --git a/common/client-core/src/init/helpers.rs b/common/client-core/src/init/helpers.rs index c8fa3f5722..88755c8629 100644 --- a/common/client-core/src/init/helpers.rs +++ b/common/client-core/src/init/helpers.rs @@ -142,23 +142,27 @@ pub async fn gateways_for_init( let nym_api = nym_apis .choose(rng) .ok_or(ClientCoreError::ListOfNymApisIsEmpty)?; - + // Use the unified HTTP client directly with optional user agent let mut builder = nym_http_api_client::Client::builder(nym_api.clone()) - .map_err(|e| ClientCoreError::ValidatorClientError( - nym_validator_client::ValidatorClientError::NymAPIError { source: e }, - ))? - .with_bincode(); // Use bincode for better performance - + .map_err(|e| { + ClientCoreError::ValidatorClientError( + nym_validator_client::ValidatorClientError::NymAPIError { source: e }, + ) + })? + .with_bincode(); // Use bincode for better performance + if let Some(user_agent) = user_agent { builder = builder.with_user_agent(user_agent); } - + let client = builder .build::() - .map_err(|e| ClientCoreError::ValidatorClientError( - nym_validator_client::ValidatorClientError::NymAPIError { source: e }, - ))?; + .map_err(|e| { + ClientCoreError::ValidatorClientError( + nym_validator_client::ValidatorClientError::NymAPIError { source: e }, + ) + })?; tracing::debug!("Fetching list of gateways from: {nym_api}"); diff --git a/common/client-libs/validator-client/src/client.rs b/common/client-libs/validator-client/src/client.rs index e3e9cd7fe8..9bb066c880 100644 --- a/common/client-libs/validator-client/src/client.rs +++ b/common/client-libs/validator-client/src/client.rs @@ -5,8 +5,8 @@ use crate::nyxd::{self, NyxdClient}; use crate::signing::direct_wallet::DirectSecp256k1HdWallet; use crate::signing::signer::{NoSigner, OfflineSigner}; use crate::{ - DirectSigningReqwestRpcValidatorClient, QueryReqwestRpcValidatorClient, - ReqwestRpcClient, ValidatorClientError, + DirectSigningReqwestRpcValidatorClient, QueryReqwestRpcValidatorClient, ReqwestRpcClient, + ValidatorClientError, }; use nym_api_requests::ecash::models::{ AggregatedCoinIndicesSignatureResponse, AggregatedExpirationDateSignatureResponse, @@ -398,27 +398,9 @@ pub struct NymApiClient { // we could re-implement the communication with the REST API on port 1317 } -impl From for NymApiClient { - fn from(nym_api: nym_http_api_client::Client) -> Self { - NymApiClient { - use_bincode: false, - nym_api, - } - } -} - // we have to allow the use of deprecated method here as they're calling the deprecated trait methods #[allow(deprecated)] impl NymApiClient { - pub fn new(api_url: Url) -> Self { - let nym_api = nym_http_api_client::Client::new(api_url, None); - - NymApiClient { - use_bincode: true, - nym_api, - } - } - #[cfg(not(target_arch = "wasm32"))] pub fn new_with_timeout(api_url: Url, timeout: std::time::Duration) -> Self { let nym_api = nym_http_api_client::Client::new(api_url, Some(timeout)); diff --git a/common/client-libs/validator-client/src/coconut/mod.rs b/common/client-libs/validator-client/src/coconut/mod.rs index b07132b489..8eb78548f5 100644 --- a/common/client-libs/validator-client/src/coconut/mod.rs +++ b/common/client-libs/validator-client/src/coconut/mod.rs @@ -91,11 +91,14 @@ impl TryFrom for EcashApiClient { // In non-client applications this resolver can cause warning logs about H2 connection // failure. This indicates that the long lived https connection was closed by the remote // peer and the resolver will have to reconnect. It should not impact actual functionality - let api_client = nym_http_api_client::Client::builder::<_, nym_api_requests::models::RequestError>(url_address) - .map_err(|e| EcashApiError::ClientError(e.to_string()))? - .build::() - .map_err(|e| EcashApiError::ClientError(e.to_string()))?; - + let api_client = nym_http_api_client::Client::builder::< + _, + nym_api_requests::models::RequestError, + >(url_address) + .map_err(|e| EcashApiError::ClientError(e.to_string()))? + .build::() + .map_err(|e| EcashApiError::ClientError(e.to_string()))?; + Ok(EcashApiClient { api_client, verification_key: VerificationKeyAuth::try_from_bs58(&share.share)?, diff --git a/common/client-libs/validator-client/src/connection_tester.rs b/common/client-libs/validator-client/src/connection_tester.rs index b5c7617d9e..745788732e 100644 --- a/common/client-libs/validator-client/src/connection_tester.rs +++ b/common/client-libs/validator-client/src/connection_tester.rs @@ -1,7 +1,7 @@ +use crate::nym_api::NymApiClientExt; use crate::nyxd::contract_traits::MixnetQueryClient; use crate::nyxd::error::NyxdError; use crate::nyxd::Config as ClientConfig; -use crate::nym_api::NymApiClientExt; use crate::{QueryHttpRpcNyxdClient, ValidatorClientError}; use colored::Colorize; use core::fmt; @@ -94,7 +94,10 @@ fn setup_connection_tests( { Ok(client) => Some(ClientForConnectionTest::Api(network, url, client)), Err(err) => { - eprintln!("Failed to create API client for {}: {err}", network.network_name); + eprintln!( + "Failed to create API client for {}: {err}", + network.network_name + ); None } } diff --git a/common/client-libs/validator-client/src/lib.rs b/common/client-libs/validator-client/src/lib.rs index 725b86450b..db86658047 100644 --- a/common/client-libs/validator-client/src/lib.rs +++ b/common/client-libs/validator-client/src/lib.rs @@ -14,7 +14,6 @@ pub mod signing; pub use crate::error::ValidatorClientError; pub use crate::rpc::reqwest::ReqwestRpcClient; pub use crate::signing::direct_wallet::DirectSecp256k1HdWallet; -pub use client::NymApiClient; pub use client::{Client, Config, EcashApiClient}; pub use nym_api_requests::*; pub use nym_http_api_client::UserAgent; 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 09af47a1aa..115cfcf5df 100644 --- a/common/client-libs/validator-client/src/nym_api/mod.rs +++ b/common/client-libs/validator-client/src/nym_api/mod.rs @@ -3,6 +3,7 @@ use crate::nym_api::error::NymAPIError; use crate::nym_api::routes::{ecash, CORE_STATUS_COUNT, SINCE_ARG}; +use crate::nym_nodes::SkimmedNodesWithMetadata; use async_trait::async_trait; use nym_api_requests::ecash::models::{ AggregatedCoinIndicesSignatureResponse, AggregatedExpirationDateSignatureResponse, @@ -23,7 +24,6 @@ use nym_api_requests::nym_nodes::{ NodesByAddressesRequestBody, NodesByAddressesResponse, PaginatedCachedNodesResponseV1, PaginatedCachedNodesResponseV2, }; -use crate::nym_nodes::SkimmedNodesWithMetadata; use nym_api_requests::pagination::PaginatedResponse; pub use nym_api_requests::{ ecash::{ @@ -38,7 +38,7 @@ pub use nym_api_requests::{ MixnodeStatusResponse, MixnodeUptimeHistoryResponse, RewardEstimationResponse, StakeSaturationResponse, UptimeResponse, }, - nym_nodes::{CachedNodesResponse, SemiSkimmedNode, SkimmedNode}, + nym_nodes::{CachedNodesResponse, SemiSkimmedNode, SemiSkimmedNodesWithMetadata, SkimmedNode}, NymNetworkDetailsResponse, }; use nym_contracts_common::IdentityKey; @@ -247,7 +247,9 @@ pub trait NymApiClientExt: ApiClient { self.get_rewarded_set().await } - async fn get_all_basic_nodes_with_metadata(&self) -> Result { + async fn get_all_basic_nodes_with_metadata( + &self, + ) -> Result { // unroll first loop iteration in order to obtain the metadata let mut page = 0; let res = self @@ -271,7 +273,9 @@ pub trait NymApiClientExt: ApiClient { // Create a custom error for inconsistent metadata return Err(NymAPIError::EndpointFailure { status: reqwest::StatusCode::INTERNAL_SERVER_ERROR, - error: nym_api_requests::models::RequestError::new("Inconsistent paged metadata"), + error: nym_api_requests::models::RequestError::new( + "Inconsistent paged metadata", + ), }); } @@ -286,6 +290,96 @@ pub trait NymApiClientExt: ApiClient { Ok(SkimmedNodesWithMetadata::new(nodes, metadata)) } + async fn get_all_basic_active_mixing_assigned_nodes_with_metadata( + &self, + ) -> Result { + // Get all mixing nodes that are in the active/rewarded set + let mut page = 0; + let res = self + .get_basic_active_mixing_assigned_nodes_v2(false, Some(page), None, false) + .await?; + + let metadata = res.metadata; + let mut nodes = res.nodes.data; + + if res.nodes.pagination.total == nodes.len() { + return Ok(SkimmedNodesWithMetadata::new(nodes, metadata)); + } + + page += 1; + + loop { + let res = self + .get_basic_active_mixing_assigned_nodes_v2(false, Some(page), None, false) + .await?; + + if !metadata.consistency_check(&res.metadata) { + return Err(NymAPIError::EndpointFailure { + status: reqwest::StatusCode::INTERNAL_SERVER_ERROR, + error: nym_api_requests::models::RequestError::new( + "Inconsistent paged metadata", + ), + }); + } + + nodes.append(&mut res.nodes.data.clone()); + + // Check if we've got all nodes + if nodes.len() >= res.nodes.pagination.total { + break; + } else { + page += 1; + } + } + + Ok(SkimmedNodesWithMetadata::new(nodes, metadata)) + } + + async fn get_all_basic_entry_assigned_nodes_with_metadata( + &self, + ) -> Result { + // Get all nodes that can act as entry gateways + let mut page = 0; + let res = self + .get_basic_entry_assigned_nodes_v2(false, Some(page), None, false) + .await?; + + let metadata = res.metadata; + let mut nodes = res.nodes.data; + + if res.nodes.pagination.total == nodes.len() { + return Ok(SkimmedNodesWithMetadata::new(nodes, metadata)); + } + + page += 1; + + loop { + let res = self + .get_basic_entry_assigned_nodes_v2(false, Some(page), None, false) + .await?; + + if !metadata.consistency_check(&res.metadata) { + return Err(NymAPIError::EndpointFailure { + status: reqwest::StatusCode::INTERNAL_SERVER_ERROR, + error: nym_api_requests::models::RequestError::new( + "Inconsistent paged metadata", + ), + }); + } + + nodes.append(&mut res.nodes.data.clone()); + + // Check if we've got all nodes + if nodes.len() >= res.nodes.pagination.total { + break; + } else { + page += 1; + } + } + + Ok(SkimmedNodesWithMetadata::new(nodes, metadata)) + } + async fn get_all_described_nodes(&self) -> Result, NymAPIError> { // TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere let mut page = 0; @@ -1435,6 +1529,38 @@ pub trait NymApiClientExt: ApiClient { ) .await } + + /// Method to change the base API URLs being used by the client + fn change_base_urls(&mut self, urls: Vec); + + /// Retrieve expanded information for all bonded nodes on the network + async fn get_all_expanded_nodes(&self) -> Result { + // Unroll the first iteration to get the metadata + let mut page = 0; + + let res = self.get_expanded_nodes(false, Some(page), None).await?; + let mut nodes = res.nodes.data; + let metadata = res.metadata; + + if res.nodes.pagination.total == nodes.len() { + return Ok(SemiSkimmedNodesWithMetadata::new(nodes, metadata)); + } + + page += 1; + + loop { + let mut res = self.get_expanded_nodes(false, Some(page), None).await?; + + nodes.append(&mut res.nodes.data); + if nodes.len() < res.nodes.pagination.total { + page += 1 + } else { + break; + } + } + + Ok(SemiSkimmedNodesWithMetadata::new(nodes, metadata)) + } } // Client is already nym_http_api_client::Client (re-exported above), so just one impl needed @@ -1444,4 +1570,8 @@ impl NymApiClientExt for nym_http_api_client::Client { fn api_url(&self) -> &url::Url { self.current_url().as_ref() } + + fn change_base_urls(&mut self, urls: Vec) { + self.change_base_urls(urls.into_iter().map(|u| u.into()).collect()); + } } diff --git a/common/commands/src/context/mod.rs b/common/commands/src/context/mod.rs index 4a8553b695..daca52df77 100644 --- a/common/commands/src/context/mod.rs +++ b/common/commands/src/context/mod.rs @@ -2,12 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 use crate::context::errors::ContextError; +pub use nym_http_api_client::Client as NymApiClient; use nym_network_defaults::{ setup_env, var_names::{MIXNET_CONTRACT_ADDRESS, NYM_API, NYXD, VESTING_CONTRACT_ADDRESS}, NymNetworkDetails, }; -pub use nym_http_api_client::Client as NymApiClient; use nym_validator_client::nyxd::{self, AccountId, NyxdClient}; use nym_validator_client::{ DirectSigningHttpRpcNyxdClient, DirectSigningHttpRpcValidatorClient, QueryHttpRpcNyxdClient, diff --git a/common/credentials/src/error.rs b/common/credentials/src/error.rs index 9a585b9195..3c342e942b 100644 --- a/common/credentials/src/error.rs +++ b/common/credentials/src/error.rs @@ -4,7 +4,7 @@ use crate::ecash::bandwidth::issued::CURRENT_SERIALIZATION_REVISION; use nym_credentials_interface::CompactEcashError; use nym_crypto::asymmetric::x25519::KeyRecoveryError; -use nym_validator_client::{ValidatorClientError, nym_api::error::NymAPIError}; +use nym_validator_client::{nym_api::error::NymAPIError, ValidatorClientError}; use thiserror::Error; #[derive(Debug, Error)] diff --git a/common/ecash-signer-check/Cargo.toml b/common/ecash-signer-check/Cargo.toml index be22872ad8..cc8fb6ec29 100644 --- a/common/ecash-signer-check/Cargo.toml +++ b/common/ecash-signer-check/Cargo.toml @@ -22,6 +22,7 @@ url = { workspace = true } nym-validator-client = { path = "../client-libs/validator-client" } nym-network-defaults = { path = "../network-defaults" } nym-ecash-signer-check-types = { path = "../ecash-signer-check-types" } +nym-http-api-client = { path = "../http-api-client" } [lints] workspace = true diff --git a/common/ecash-signer-check/src/client_check.rs b/common/ecash-signer-check/src/client_check.rs index b5c367794d..aff2db6725 100644 --- a/common/ecash-signer-check/src/client_check.rs +++ b/common/ecash-signer-check/src/client_check.rs @@ -1,15 +1,14 @@ // Copyright 2025 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::{LocalChainStatus, SigningStatus, TypedSignerResult}; +use crate::{LocalChainStatus, SignerCheckError, SigningStatus, TypedSignerResult}; use nym_ecash_signer_check_types::dealer_information::RawDealerInformation; use nym_ecash_signer_check_types::status::{SignerStatus, SignerTestResult}; -use nym_validator_client::client::NymApiClientExt; use nym_validator_client::models::BinaryBuildInformationOwned; +use nym_validator_client::nym_api::NymApiClientExt; use nym_validator_client::nyxd::contract_traits::dkg_query_client::{ ContractVKShare, DealerDetails, }; -use nym_validator_client::NymApiClient; use std::time::Duration; use tracing::{error, warn}; use url::Url; @@ -32,37 +31,38 @@ pub(crate) mod signing_status { } struct ClientUnderTest { - api_client: NymApiClient, + api_client: nym_http_api_client::Client, build_info: Option, } impl ClientUnderTest { - pub(crate) fn new(api_url: &Url) -> Self { - ClientUnderTest { - api_client: NymApiClient::new(api_url.clone()), + pub(crate) fn new(api_url: &Url) -> Result { + // The builder should not fail with a valid URL that's already parsed + // If it does fail, it's an internal error that we can't recover from + let api_client = nym_http_api_client::Client::builder(api_url.clone())?.build()?; + + Ok(ClientUnderTest { + api_client, build_info: None, - } + }) } pub(crate) async fn try_retrieve_build_information(&mut self) -> bool { - match tokio::time::timeout( - Duration::from_secs(5), - self.api_client.nym_api.build_information(), - ) - .await + match tokio::time::timeout(Duration::from_secs(5), self.api_client.build_information()) + .await { Ok(Ok(build_information)) => { self.build_info = Some(build_information); true } Ok(Err(err)) => { - warn!("{}: failed to retrieve build information: {err}. the signer is most likely down", self.api_client.api_url()); + warn!("{}: failed to retrieve build information: {err}. the signer is most likely down", self.api_client.current_url()); false } Err(_timeout) => { warn!( "{}: timed out while attempting to retrieve build information", - self.api_client.api_url() + self.api_client.current_url() ); false } @@ -77,7 +77,7 @@ impl ClientUnderTest { .inspect_err(|err| { error!( "ecash signer '{}' reports invalid version {}: {err}", - self.api_client.api_url(), + self.api_client.current_url(), build_info.build_version ) }) @@ -121,14 +121,14 @@ impl ClientUnderTest { // check if it supports the current query if self.supports_chain_status_query() { - return match self.api_client.nym_api.get_chain_blocks_status().await { + return match self.api_client.get_chain_blocks_status().await { Ok(status) => LocalChainStatus::Reachable { response: Box::new(status), }, Err(err) => { warn!( "{}: failed to retrieve local chain status: {err}", - self.api_client.api_url() + self.api_client.current_url() ); LocalChainStatus::Unreachable } @@ -136,14 +136,14 @@ impl ClientUnderTest { } // fallback to the legacy query - match self.api_client.nym_api.get_chain_status().await { + match self.api_client.get_chain_status().await { Ok(status) => LocalChainStatus::ReachableLegacy { response: Box::new(status), }, Err(err) => { warn!( "{}: failed to retrieve [legacy] local chain status: {err}", - self.api_client.api_url() + self.api_client.current_url() ); LocalChainStatus::Unreachable } @@ -158,14 +158,14 @@ impl ClientUnderTest { // check if it supports the current query if self.supports_signing_status_query() { - return match self.api_client.nym_api.get_signer_status().await { + return match self.api_client.get_signer_status().await { Ok(response) => SigningStatus::Reachable { response: Box::new(response), }, Err(err) => { warn!( "{}: failed to retrieve signer chain status: {err}", - self.api_client.api_url() + self.api_client.current_url() ); SigningStatus::Unreachable } @@ -173,14 +173,14 @@ impl ClientUnderTest { } // fallback to the legacy query - match self.api_client.nym_api.get_signer_information().await { + match self.api_client.get_signer_information().await { Ok(status) => SigningStatus::ReachableLegacy { response: Box::new(status), }, Err(err) => { warn!( "{}: failed to retrieve [legacy] signer chain status: {err}", - self.api_client.api_url() + self.api_client.current_url() ); // NOTE: this might equally mean the signing is disabled SigningStatus::Unreachable @@ -201,7 +201,13 @@ pub(crate) async fn check_client( return SignerStatus::ProvidedInvalidDetails.with_details(dealer_information, dkg_epoch); }; - let mut client = ClientUnderTest::new(&parsed_information.announce_address); + let mut client = match ClientUnderTest::new(&parsed_information.announce_address) { + Ok(client) => client, + Err(err) => { + error!("failed to create client instance: {err}"); + return SignerStatus::Unreachable.with_details(dealer_information, dkg_epoch); + } + }; // 8. check basic connection status - can you retrieve build information? if !client.try_retrieve_build_information().await { diff --git a/common/ecash-signer-check/src/error.rs b/common/ecash-signer-check/src/error.rs index bb7c3c719b..14b498c0b7 100644 --- a/common/ecash-signer-check/src/error.rs +++ b/common/ecash-signer-check/src/error.rs @@ -1,6 +1,7 @@ // Copyright 2025 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use nym_http_api_client::HttpClientError; use nym_validator_client::nyxd::error::NyxdError; use thiserror::Error; @@ -11,6 +12,12 @@ pub enum SignerCheckError { #[error("failed to query the DKG contract: {source}")] DKGContractQueryFailure { source: NyxdError }, + + #[error("failed to build client: {source}")] + HttpClient { + #[from] + source: HttpClientError, + }, } impl SignerCheckError { diff --git a/common/http-api-client/src/lib.rs b/common/http-api-client/src/lib.rs index e9dc809b87..5dc20e7534 100644 --- a/common/http-api-client/src/lib.rs +++ b/common/http-api-client/src/lib.rs @@ -409,15 +409,22 @@ impl ClientBuilder { /// Create a client builder from network details with sensible defaults #[cfg(feature = "network-defaults")] - pub fn from_network(network: &nym_network_defaults::NymNetworkDetails) -> Result { - let urls = network.nym_api_urls + pub fn from_network( + network: &nym_network_defaults::NymNetworkDetails, + ) -> Result { + let urls = network + .nym_api_urls .as_ref() - .ok_or_else(|| HttpClientError::GenericRequestFailure("No API URLs configured in network details".to_string()))? + .ok_or_else(|| { + HttpClientError::GenericRequestFailure( + "No API URLs configured in network details".to_string(), + ) + })? .iter() .map(|api_url| { // Convert ApiUrl to our Url type with fronting support let mut url = Url::parse(&api_url.url)?; - + // Add fronting domains if available #[cfg(feature = "tunneling")] if let Some(ref front_hosts) = api_url.front_hosts { @@ -428,19 +435,19 @@ impl ClientBuilder { url = Url::new(api_url.url.clone(), Some(fronts)) .map_err(|e| HttpClientError::GenericRequestFailure(e.to_string()))?; } - + Ok(url) }) .collect::, HttpClientError>>()?; let mut builder = Self::new_with_urls(urls); - + // Enable domain fronting by default (on retry) #[cfg(feature = "tunneling")] { builder = builder.with_fronting(FrontPolicy::OnRetry); } - + Ok(builder) } @@ -757,7 +764,7 @@ impl Client { /// this method. For example, if the client is configured to rotate hosts after each error, this /// method should be called after the host has been updated -- i.e. as part of the subsequent /// send. - fn apply_hosts_to_req(&self, r: &mut reqwest::Request) { + fn apply_hosts_to_req(&self, r: &mut reqwest::Request) -> (&str, Option<&str>) { let url = self.current_url(); r.url_mut().set_host(url.host_str()).unwrap(); @@ -766,24 +773,28 @@ impl Client { if front.is_enabled() { let front_host = url.front_str().unwrap_or(""); let actual_host = url.host_str().unwrap_or(""); - + tracing::debug!( "Domain fronting enabled: routing via CDN {} to actual host {}", front_host, actual_host ); - + // this should never fail as we are transplanting the host from one url to another r.url_mut().set_host(Some(front_host)).unwrap(); - let actual_host_header: HeaderValue = actual_host - .parse() - .unwrap_or(HeaderValue::from_static("")); + let actual_host_header: HeaderValue = + actual_host.parse().unwrap_or(HeaderValue::from_static("")); // If the map did have this key present, the new value is associated with the key // and all previous values are removed. (reqwest HeaderMap docs) - _ = r.headers_mut().insert(reqwest::header::HOST, actual_host_header); + _ = r + .headers_mut() + .insert(reqwest::header::HOST, actual_host_header); + + return (url.as_str(), url.front_str()); } } + (url.as_str(), None) } } diff --git a/common/network-defaults/src/network.rs b/common/network-defaults/src/network.rs index f75b5b7286..3fdf03671e 100644 --- a/common/network-defaults/src/network.rs +++ b/common/network-defaults/src/network.rs @@ -190,7 +190,13 @@ impl NymNetworkDetails { }, nym_vpn_api_url: parse_optional_str(mainnet::NYM_VPN_API), nym_api_urls: Some(mainnet::NYM_APIS.iter().copied().map(Into::into).collect()), - nym_vpn_api_urls: Some(mainnet::NYM_VPN_APIS.iter().copied().map(Into::into).collect()), + nym_vpn_api_urls: Some( + mainnet::NYM_VPN_APIS + .iter() + .copied() + .map(Into::into) + .collect(), + ), } } diff --git a/common/verloc/src/measurements/measurer.rs b/common/verloc/src/measurements/measurer.rs index d9a27254f0..60939e359e 100644 --- a/common/verloc/src/measurements/measurer.rs +++ b/common/verloc/src/measurements/measurer.rs @@ -135,16 +135,17 @@ impl VerlocMeasurer { let mut api_endpoints = self.config.nym_api_urls.clone(); api_endpoints.shuffle(&mut thread_rng()); for api_endpoint in api_endpoints { - let client = match nym_http_api_client::Client::builder(api_endpoint.clone()) - .and_then(|b| b.with_user_agent(self.config.user_agent.clone()) - .build::()) - { - Ok(c) => c, - Err(err) => { - warn!("failed to create client for {api_endpoint}: {err}"); - continue; - } - }; + let client = + match nym_http_api_client::Client::builder(api_endpoint.clone()).and_then(|b| { + b.with_user_agent(self.config.user_agent.clone()) + .build::() + }) { + Ok(c) => c, + Err(err) => { + warn!("failed to create client for {api_endpoint}: {err}"); + continue; + } + }; match client.get_all_described_nodes().await { Ok(res) => return Some(res), Err(err) => { diff --git a/common/wasm/client-core/src/error.rs b/common/wasm/client-core/src/error.rs index 8066a9f2bb..ab41b190dd 100644 --- a/common/wasm/client-core/src/error.rs +++ b/common/wasm/client-core/src/error.rs @@ -37,6 +37,12 @@ pub enum WasmCoreError { source: ValidatorClientError, }, + #[error("failed to query nym api: {source}")] + NymApiQueryError { + #[from] + source: nym_validator_client::nym_api::error::NymAPIError, + }, + #[error("The provided wasm topology was invalid: {source}")] WasmTopologyError { #[from] diff --git a/common/wasm/client-core/src/helpers.rs b/common/wasm/client-core/src/helpers.rs index 4cb5f06c55..e90c2c6718 100644 --- a/common/wasm/client-core/src/helpers.rs +++ b/common/wasm/client-core/src/helpers.rs @@ -19,9 +19,9 @@ use nym_client_core::init::{ use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag; use nym_topology::wasm_helpers::WasmFriendlyNymTopology; -use nym_topology::{NymTopology, RoutingNode}; +use nym_topology::{NymTopology, RoutingNode, EpochRewardedSet}; use nym_validator_client::client::IdentityKey; -use nym_validator_client::{NymApiClient, UserAgent}; +use nym_validator_client::{nym_api::NymApiClientExt, UserAgent}; use rand::thread_rng; use url::Url; use wasm_bindgen::prelude::wasm_bindgen; @@ -72,7 +72,17 @@ pub async fn current_network_topology_async( } }; - let api_client = NymApiClient::new(url); + let api_client = + nym_http_api_client::Client::builder::<_, nym_validator_client::models::RequestError>(url.clone()) + .map_err(|_err| WasmCoreError::MalformedUrl { + raw: nym_api_url.to_string(), + source: url::ParseError::EmptyHost, + })? + .build::() + .map_err(|_err| WasmCoreError::MalformedUrl { + raw: nym_api_url.to_string(), + source: url::ParseError::EmptyHost, + })?; let rewarded_set = api_client.get_current_rewarded_set().await?; let mixnodes_res = api_client .get_all_basic_active_mixing_assigned_nodes_with_metadata() @@ -90,7 +100,8 @@ pub async fn current_network_topology_async( let gateways = gateways_res.nodes; - let topology = NymTopology::new(metadata.to_topology_metadata(), rewarded_set, Vec::new()) + let epoch_rewarded_set: EpochRewardedSet = rewarded_set.into(); + let topology = NymTopology::new(metadata.to_topology_metadata(), epoch_rewarded_set, Vec::new()) .with_skimmed_nodes(&mixnodes) .with_skimmed_nodes(&gateways); diff --git a/common/wasm/client-core/src/lib.rs b/common/wasm/client-core/src/lib.rs index ae1ba45ad9..761df16ae4 100644 --- a/common/wasm/client-core/src/lib.rs +++ b/common/wasm/client-core/src/lib.rs @@ -21,6 +21,7 @@ pub use nym_client_core::{ pub use nym_gateway_client::{ error::GatewayClientError, GatewayClient, GatewayClientConfig, GatewayConfig, }; +pub use nym_http_api_client::Client as ApiClient; pub use nym_sphinx::{ addressing::{clients::Recipient, nodes::NodeIdentity}, params::PacketType, @@ -29,7 +30,6 @@ pub use nym_sphinx::{ pub use nym_statistics_common::clients::ClientStatsSender; pub use nym_task; pub use nym_topology::{HardcodedTopologyProvider, MixLayer, NymTopology, TopologyProvider}; -pub use nym_http_api_client::Client as ApiClient; pub use nym_validator_client::{DirectSigningReqwestRpcNyxdClient, QueryReqwestRpcNyxdClient}; // TODO: that's a very nasty import path. it should come from contracts instead! pub use nym_validator_client::client::IdentityKey; diff --git a/contracts/mixnet/src/delegations/queries.rs b/contracts/mixnet/src/delegations/queries.rs index df2c9befa7..cf970e653e 100644 --- a/contracts/mixnet/src/delegations/queries.rs +++ b/contracts/mixnet/src/delegations/queries.rs @@ -139,9 +139,9 @@ mod tests { fn add_dummy_mixes_with_delegations(test: &mut TestSetup, delegators: usize, mixes: usize) { for i in 0..mixes { - let mix_id = test.add_legacy_mixnode(&test.make_addr(format!("mix-owner{}", i)), None); + let mix_id = test.add_legacy_mixnode(&test.make_addr(format!("mix-owner{i}")), None); for delegator in 0..delegators { - let name = &test.make_addr(format!("delegator{}", delegator)); + let name = &test.make_addr(format!("delegator{delegator}")); test.add_immediate_delegation(name, 100_000_000u32, mix_id) } } diff --git a/contracts/mixnet/src/mixnodes/queries.rs b/contracts/mixnet/src/mixnodes/queries.rs index bac59ab591..7591b6cba4 100644 --- a/contracts/mixnet/src/mixnodes/queries.rs +++ b/contracts/mixnet/src/mixnodes/queries.rs @@ -503,8 +503,8 @@ pub(crate) mod tests { storage, id, &UnbondedMixnode { - identity_key: format!("dummy{}", id), - owner: Addr::unchecked(format!("dummy{}", id)), + identity_key: format!("dummy{id}"), + owner: Addr::unchecked(format!("dummy{id}")), proxy: None, unbonding_height: 123, }, @@ -570,7 +570,7 @@ pub(crate) mod tests { storage, id, &UnbondedMixnode { - identity_key: format!("dummy{}", id), + identity_key: format!("dummy{id}"), owner: owner.clone(), proxy: None, unbonding_height: 123, @@ -817,7 +817,7 @@ pub(crate) mod tests { id, &UnbondedMixnode { identity_key: identity.to_string(), - owner: Addr::unchecked(format!("dummy{}", id)), + owner: Addr::unchecked(format!("dummy{id}")), proxy: None, unbonding_height: 123, }, diff --git a/contracts/mixnet/src/support/tests/mod.rs b/contracts/mixnet/src/support/tests/mod.rs index 29aafaa92e..8ba4a4d3db 100644 --- a/contracts/mixnet/src/support/tests/mod.rs +++ b/contracts/mixnet/src/support/tests/mod.rs @@ -165,9 +165,9 @@ pub mod test_helpers { #[track_caller] pub fn assert_eq_with_leeway(a: Uint128, b: Uint128, leeway: Uint128) { if a > b { - assert!(a - b <= leeway, "{} != {}", a, b) + assert!(a - b <= leeway, "{a} != {b}") } else { - assert!(b - a <= leeway, "{} != {}", a, b) + assert!(b - a <= leeway, "{a} != {b}") } } @@ -175,9 +175,9 @@ pub mod test_helpers { pub fn assert_decimals(a: Decimal, b: Decimal) { let epsilon = Decimal::from_ratio(1u128, 100_000_000u128); if a > b { - assert!(a - b < epsilon, "{} != {}", a, b) + assert!(a - b < epsilon, "{a} != {b}") } else { - assert!(b - a < epsilon, "{} != {}", a, b) + assert!(b - a < epsilon, "{a} != {b}") } } @@ -1699,7 +1699,7 @@ pub mod test_helpers { deps.branch(), &env, env.block.height, - Addr::unchecked(format!("owner{}", i)), + Addr::unchecked(format!("owner{i}")), mix_id, tests::fixtures::good_mixnode_pledge().pop().unwrap(), ) @@ -1713,7 +1713,7 @@ pub mod test_helpers { n: usize, ) { for i in 0..n { - add_unbonded_mixnode(&mut rng, deps.branch(), None, &addr(format!("owner{}", i))); + add_unbonded_mixnode(&mut rng, deps.branch(), None, &addr(format!("owner{i}"))); } } @@ -1765,7 +1765,7 @@ pub mod test_helpers { id, &UnbondedMixnode { identity_key: identity_key - .unwrap_or(&*format!("identity{}", id)) + .unwrap_or(&*format!("identity{id}")) .to_string(), owner: Addr::unchecked(owner), proxy: None, diff --git a/documentation/docs/pages/developers/rust/mixnet/examples/custom-topology/custom-provider.mdx b/documentation/docs/pages/developers/rust/mixnet/examples/custom-topology/custom-provider.mdx index 3f833616be..be32aecb2d 100644 --- a/documentation/docs/pages/developers/rust/mixnet/examples/custom-topology/custom-provider.mdx +++ b/documentation/docs/pages/developers/rust/mixnet/examples/custom-topology/custom-provider.mdx @@ -20,16 +20,22 @@ use nym_sdk::mixnet; use nym_sdk::mixnet::MixnetMessageSender; use nym_topology::provider_trait::{async_trait, TopologyProvider}; use nym_topology::{nym_topology_from_detailed, NymTopology}; +use nym_validator_client::nym_api::NymApiClientExt; use url::Url; struct MyTopologyProvider { - validator_client: nym_validator_client::client::NymApiClient, + validator_client: nym_http_api_client::Client, } impl MyTopologyProvider { fn new(nym_api_url: Url) -> MyTopologyProvider { + let validator_client = nym_http_api_client::Client::builder::<_, nym_validator_client::models::RequestError>(nym_api_url) + .expect("Failed to create API client builder") + .build::() + .expect("Failed to build API client"); + MyTopologyProvider { - validator_client: nym_validator_client::client::NymApiClient::new(nym_api_url), + validator_client, } } diff --git a/nym-api/src/ecash/state/mod.rs b/nym-api/src/ecash/state/mod.rs index c1aad45a64..3f4a8b908c 100644 --- a/nym-api/src/ecash/state/mod.rs +++ b/nym-api/src/ecash/state/mod.rs @@ -27,7 +27,6 @@ use nym_api_requests::ecash::models::{ }; use nym_api_requests::ecash::BlindSignRequestBody; use nym_coconut_dkg_common::types::EpochId; -use nym_validator_client::nym_api::NymApiClientExt; use nym_compact_ecash::scheme::coin_indices_signatures::{ aggregate_annotated_indices_signatures, sign_coin_indices, CoinIndexSignatureShare, }; @@ -47,6 +46,7 @@ use nym_ecash_contract_common::redeem_credential::BATCH_REDEMPTION_PROPOSAL_TITL use nym_ecash_time::{ecash_default_expiration_date, ecash_today_date}; use nym_task::TaskClient; use nym_ticketbooks_merkle::{IssuedTicketbook, IssuedTicketbooksFullMerkleProof, MerkleLeaf}; +use nym_validator_client::nym_api::NymApiClientExt; use nym_validator_client::nyxd::AccountId; use nym_validator_client::EcashApiClient; use rand::{thread_rng, RngCore}; diff --git a/nym-api/src/ecash/tests/mod.rs b/nym-api/src/ecash/tests/mod.rs index 071e57a1a6..69bf5cb56e 100644 --- a/nym-api/src/ecash/tests/mod.rs +++ b/nym-api/src/ecash/tests/mod.rs @@ -1148,7 +1148,10 @@ impl DummyCommunicationChannel { cosmos_address: AccountId, ) -> Self { let client = EcashApiClient { - api_client: nym_http_api_client::Client::new("http://localhost:1234".parse().unwrap(), None), + api_client: nym_http_api_client::Client::new( + "http://localhost:1234".parse().unwrap(), + None, + ), verification_key: aggregated_verification_key, node_id: 1, cosmos_address, diff --git a/nym-credential-proxy/nym-credential-proxy/src/credentials/ticketbook/mod.rs b/nym-credential-proxy/nym-credential-proxy/src/credentials/ticketbook/mod.rs index 396d2f1b6a..3d82032878 100644 --- a/nym-credential-proxy/nym-credential-proxy/src/credentials/ticketbook/mod.rs +++ b/nym-credential-proxy/nym-credential-proxy/src/credentials/ticketbook/mod.rs @@ -15,8 +15,8 @@ use nym_credentials::IssuanceTicketBook; use nym_credentials_interface::Base58; use nym_crypto::asymmetric::ed25519; use nym_validator_client::ecash::BlindSignRequestBody; -use nym_validator_client::nyxd::Coin; use nym_validator_client::nym_api::NymApiClientExt; +use nym_validator_client::nyxd::Coin; use rand::rngs::OsRng; use std::collections::HashMap; use std::sync::Arc; diff --git a/nym-credential-proxy/nym-credential-proxy/src/error.rs b/nym-credential-proxy/nym-credential-proxy/src/error.rs index 24daa2b76f..b9f5441584 100644 --- a/nym-credential-proxy/nym-credential-proxy/src/error.rs +++ b/nym-credential-proxy/nym-credential-proxy/src/error.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-3.0-only use nym_validator_client::coconut::EcashApiError; -use nym_validator_client::nym_api::{EpochId, error::NymAPIError}; +use nym_validator_client::nym_api::{error::NymAPIError, EpochId}; use nym_validator_client::nyxd::error::NyxdError; use std::io; use std::net::SocketAddr; diff --git a/nym-credential-proxy/nym-credential-proxy/src/http/state/mod.rs b/nym-credential-proxy/nym-credential-proxy/src/http/state/mod.rs index 42b4da406b..7bd48dbb4f 100644 --- a/nym-credential-proxy/nym-credential-proxy/src/http/state/mod.rs +++ b/nym-credential-proxy/nym-credential-proxy/src/http/state/mod.rs @@ -28,11 +28,11 @@ use nym_credentials::ecash::utils::{ecash_today, EcashTime}; use nym_credentials::{ AggregatedCoinIndicesSignatures, AggregatedExpirationDateSignatures, EpochVerificationKey, }; -use nym_validator_client::nym_api::NymApiClientExt; use nym_credentials_interface::VerificationKeyAuth; use nym_ecash_contract_common::msg::ExecuteMsg; use nym_validator_client::coconut::EcashApiError; use nym_validator_client::nym_api::EpochId; +use nym_validator_client::nym_api::NymApiClientExt; use nym_validator_client::nyxd::contract_traits::dkg_query_client::Epoch; use nym_validator_client::nyxd::contract_traits::{ DkgQueryClient, EcashQueryClient, NymContractsProvider, PagedDkgQueryClient, diff --git a/nym-network-monitor/src/main.rs b/nym-network-monitor/src/main.rs index dff0b7854c..545a37bbbe 100644 --- a/nym-network-monitor/src/main.rs +++ b/nym-network-monitor/src/main.rs @@ -6,6 +6,7 @@ use log::{info, warn}; use nym_bin_common::bin_info; use nym_client_core::config::ForgetMe; use nym_crypto::asymmetric::ed25519::PrivateKey; +use nym_mixnet_contract_common::EpochRewardedSet; use nym_network_defaults::setup_env; use nym_network_defaults::var_names::NYM_API; use nym_sdk::mixnet::{self, MixnetClient}; @@ -14,7 +15,6 @@ use nym_topology::provider_trait::ToTopologyMetadata; use nym_topology::{HardcodedTopologyProvider, NymTopology}; use nym_validator_client::nym_api::NymApiClientExt; use nym_validator_client::UserAgent; -use nym_mixnet_contract_common::EpochRewardedSet; use std::fs::File; use std::io::Write; use std::sync::LazyLock; @@ -163,9 +163,12 @@ async fn nym_topology_from_env() -> anyhow::Result { let api_url = std::env::var(NYM_API)?; info!("Generating topology from {api_url}"); - let client = nym_http_api_client::Client::builder::<_, nym_validator_client::models::RequestError>(api_url)? - .with_user_agent(UserAgent::from(bin_info!())) - .build::()?; + let client = nym_http_api_client::Client::builder::< + _, + nym_validator_client::models::RequestError, + >(api_url)? + .with_user_agent(UserAgent::from(bin_info!())) + .build::()?; let rewarded_set = client.get_current_rewarded_set().await?; @@ -177,10 +180,12 @@ async fn nym_topology_from_env() -> anyhow::Result { // Convert RewardedSetResponse to EpochRewardedSet which can then be converted to CachedEpochRewardedSet let epoch_rewarded_set: EpochRewardedSet = rewarded_set.into(); - Ok( - NymTopology::new(metadata.to_topology_metadata(), epoch_rewarded_set, Vec::new()) - .with_skimmed_nodes(&nodes), + Ok(NymTopology::new( + metadata.to_topology_metadata(), + epoch_rewarded_set, + Vec::new(), ) + .with_skimmed_nodes(&nodes)) } #[tokio::main] diff --git a/nym-node/src/node/nym_apis_client.rs b/nym-node/src/node/nym_apis_client.rs index 08ef68aff7..7e6b490f56 100644 --- a/nym-node/src/node/nym_apis_client.rs +++ b/nym-node/src/node/nym_apis_client.rs @@ -10,7 +10,6 @@ use nym_task::ShutdownToken; use nym_validator_client::client::NymApiClientExt; use nym_validator_client::models::{KeyRotationInfoResponse, NodeRefreshBody}; use nym_validator_client::nym_api::error::NymAPIError; -use nym_validator_client::NymApiClient; use rand::prelude::SliceRandom; use rand::thread_rng; use std::sync::Arc; @@ -27,7 +26,7 @@ pub struct NymApisClient { struct InnerClient { // NOTE: this was implemented before the internal http client supported multiple URLs - active_client: NymApiClient, + active_client: Client, available_urls: Vec, shutdown_token: ShutdownToken, currently_used_api: usize, @@ -45,7 +44,7 @@ impl NymApisClient { let mut urls = nym_apis.to_vec(); urls.shuffle(&mut thread_rng()); - let active_client = nym_http_api_client::Client::builder(urls[0].clone())? + let active_client = Client::builder(urls[0].clone())? .no_hickory_dns() .with_user_agent(NymNode::user_agent()) .with_timeout(Duration::from_secs(5)) @@ -53,7 +52,7 @@ impl NymApisClient { Ok(NymApisClient { inner: Arc::new(RwLock::new(InnerClient { - active_client: NymApiClient::from(active_client), + active_client: active_client.clone(), available_urls: urls, shutdown_token, currently_used_api: 0, @@ -90,7 +89,7 @@ impl NymApisClient { let mut guard = self.inner.write().await; let next_url = guard.available_urls[last_working_endpoint].clone(); guard.currently_used_api = last_working_endpoint; - guard.active_client.change_nym_api(next_url); + guard.active_client.change_base_urls(vec![next_url.into()]); } Ok(res) @@ -123,10 +122,8 @@ impl InnerClient { { let broadcast_fut = stream::iter(self.available_urls.clone()).for_each_concurrent(None, |url| { - let nym_api = self - .active_client - .nym_api - .clone_with_new_url(url.clone().into()); + let mut nym_api = self.active_client.clone(); + nym_api.change_base_urls(vec![url.clone().into()]); let req_fut = req(nym_api, request_body); async move { if let Err(err) = req_fut.await { @@ -172,10 +169,8 @@ impl InnerClient { .skip(last_working) .chain(self.available_urls.iter().enumerate().take(last_working)) { - let nym_api = self - .active_client - .nym_api - .clone_with_new_url(url.clone().into()); + let mut nym_api = self.active_client.clone(); + nym_api.change_base_urls(vec![url.clone().into()]); let timeout_fut = sleep(timeout_duration); let query_fut = req(nym_api); @@ -216,8 +211,8 @@ impl InnerClient { } } -impl AsRef for InnerClient { - fn as_ref(&self) -> &NymApiClient { +impl AsRef for InnerClient { + fn as_ref(&self) -> &Client { &self.active_client } } diff --git a/nym-node/src/node/shared_network.rs b/nym-node/src/node/shared_network.rs index 7fb88b31b0..8ca8077c8e 100644 --- a/nym-node/src/node/shared_network.rs +++ b/nym-node/src/node/shared_network.rs @@ -7,6 +7,7 @@ use crate::node::routing_filter::network_filter::NetworkRoutingFilter; use async_trait::async_trait; use nym_crypto::asymmetric::ed25519; use nym_gateway::node::UserAgent; +use nym_http_api_client::Client; use nym_node_metrics::prometheus_wrapper::{PrometheusMetric, PROMETHEUS_METRICS}; use nym_noise::config::NoiseNetworkView; use nym_task::ShutdownToken; @@ -20,7 +21,7 @@ use nym_validator_client::nym_api::NymApiClientExt; use nym_validator_client::nym_nodes::{ NodesByAddressesResponse, SemiSkimmedNode, SemiSkimmedNodesWithMetadata, }; -use nym_validator_client::{NymApiClient, ValidatorClientError}; +use nym_validator_client::ValidatorClientError; use std::collections::{HashMap, HashSet}; use std::net::{IpAddr, SocketAddr}; use std::ops::Deref; @@ -35,7 +36,7 @@ use url::Url; const LOCAL_NODE_ID: NodeId = 1234567890; struct NodesQuerier { - client: NymApiClient, + client: Client, nym_api_urls: Vec, currently_used_api: usize, } @@ -49,7 +50,7 @@ impl NodesQuerier { self.currently_used_api = (self.currently_used_api + 1) % self.nym_api_urls.len(); self.client - .change_nym_api(self.nym_api_urls[self.currently_used_api].clone()) + .change_base_urls(self.nym_api_urls.iter().map(|u| u.clone().into()).collect()) } async fn rewarded_set(&mut self) -> Result { @@ -62,7 +63,7 @@ impl NodesQuerier { if res.is_err() { self.use_next_nym_api() } - res + Ok(res?.into()) } async fn current_nymnodes( @@ -77,7 +78,7 @@ impl NodesQuerier { if res.is_err() { self.use_next_nym_api() } - res + Ok(res?) } async fn query_for_info( @@ -86,7 +87,6 @@ impl NodesQuerier { ) -> Result { let res = self .client - .nym_api .nodes_by_addresses(ips) .await .inspect_err(|err| error!("failed to obtain node information: {err}")); @@ -228,7 +228,7 @@ impl NetworkRefresher { let mut this = NetworkRefresher { querier: NodesQuerier { - client: NymApiClient::from(nym_api), + client: nym_api, nym_api_urls, currently_used_api: 0, }, diff --git a/nym-validator-rewarder/src/cli/dry_run_check_issuer.rs b/nym-validator-rewarder/src/cli/dry_run_check_issuer.rs index f2f7e1010b..f8974bd601 100644 --- a/nym-validator-rewarder/src/cli/dry_run_check_issuer.rs +++ b/nym-validator-rewarder/src/cli/dry_run_check_issuer.rs @@ -8,8 +8,8 @@ use crate::rewarder::ticketbook_issuance::types::CredentialIssuer; use crate::rewarder::ticketbook_issuance::verifier::TicketbookIssuanceVerifier; use crate::rewarder::Rewarder; use anyhow::bail; -use nym_validator_client::nym_api::NymApiClientExt; use nym_ecash_time::ecash_default_expiration_date; +use nym_validator_client::nym_api::NymApiClientExt; use std::collections::HashSet; use std::path::PathBuf; use time::macros::format_description; diff --git a/nym-validator-rewarder/src/rewarder/nyxd_client.rs b/nym-validator-rewarder/src/rewarder/nyxd_client.rs index b7090d6945..f621422740 100644 --- a/nym-validator-rewarder/src/rewarder/nyxd_client.rs +++ b/nym-validator-rewarder/src/rewarder/nyxd_client.rs @@ -16,7 +16,6 @@ use nym_validator_client::nyxd::{ AccountId, Coin, CosmWasmClient, Hash, PageRequest, StakingQueryClient, }; use nym_validator_client::{nyxd, DirectSigningHttpRpcNyxdClient}; -use nym_validator_client::nym_api::NymApiClientExt; use std::collections::HashMap; use std::ops::Deref; use std::sync::Arc; @@ -145,7 +144,10 @@ impl NyxdClient { { Ok(client) => client, Err(err) => { - error!("Failed to create API client for issuer {}: {}", info.assigned_index, err); + error!( + "Failed to create API client for issuer {}: {}", + info.assigned_index, err + ); continue; } }; diff --git a/nym-validator-rewarder/src/rewarder/ticketbook_issuance/types.rs b/nym-validator-rewarder/src/rewarder/ticketbook_issuance/types.rs index aa065b3ffc..f677c0e9bc 100644 --- a/nym-validator-rewarder/src/rewarder/ticketbook_issuance/types.rs +++ b/nym-validator-rewarder/src/rewarder/ticketbook_issuance/types.rs @@ -6,8 +6,8 @@ use cosmwasm_std::{Addr, Decimal, Uint128}; use nym_coconut_dkg_common::types::NodeIndex; use nym_compact_ecash::VerificationKeyAuth; use nym_crypto::asymmetric::ed25519; -use nym_validator_client::nyxd::{AccountId, Coin}; use nym_validator_client::nym_api::NymApiClientExt; +use nym_validator_client::nyxd::{AccountId, Coin}; use std::fmt::{Display, Formatter}; use tracing::info; diff --git a/nym-validator-rewarder/src/rewarder/ticketbook_issuance/verifier.rs b/nym-validator-rewarder/src/rewarder/ticketbook_issuance/verifier.rs index ebed2f8c27..dcd7525476 100644 --- a/nym-validator-rewarder/src/rewarder/ticketbook_issuance/verifier.rs +++ b/nym-validator-rewarder/src/rewarder/ticketbook_issuance/verifier.rs @@ -16,9 +16,9 @@ use nym_validator_client::ecash::models::{ IssuedTicketbooksChallengeCommitmentResponse, IssuedTicketbooksDataRequestBody, IssuedTicketbooksDataResponse, IssuedTicketbooksDataResponseBody, IssuedTicketbooksForResponse, }; +use nym_validator_client::nym_api::NymApiClientExt; use nym_validator_client::nyxd::AccountId; use nym_validator_client::signable::{SignableMessageBody, SignedMessage}; -use nym_validator_client::nym_api::NymApiClientExt; use rand::distributions::{Distribution, WeightedIndex}; use rand::prelude::SliceRandom; use rand::thread_rng; diff --git a/sdk/rust/nym-sdk/Cargo.toml b/sdk/rust/nym-sdk/Cargo.toml index 9c67bbf898..523e5801c5 100644 --- a/sdk/rust/nym-sdk/Cargo.toml +++ b/sdk/rust/nym-sdk/Cargo.toml @@ -38,6 +38,7 @@ nym-socks5-client-core = { path = "../../../common/socks5-client-core" } nym-validator-client = { path = "../../../common/client-libs/validator-client", features = [ "http-client", ] } +nym-http-api-client = { path = "../../../common/http-api-client" } nym-socks5-requests = { path = "../../../common/socks5/requests" } nym-ordered-buffer = { path = "../../../common/socks5/ordered-buffer" } nym-service-providers-common = { path = "../../../service-providers/common" } diff --git a/sdk/rust/nym-sdk/examples/custom_topology_provider.rs b/sdk/rust/nym-sdk/examples/custom_topology_provider.rs index 7b08e73f24..c3434aa117 100644 --- a/sdk/rust/nym-sdk/examples/custom_topology_provider.rs +++ b/sdk/rust/nym-sdk/examples/custom_topology_provider.rs @@ -4,18 +4,25 @@ use nym_sdk::mixnet; use nym_sdk::mixnet::MixnetMessageSender; use nym_topology::provider_trait::{async_trait, ToTopologyMetadata, TopologyProvider}; -use nym_topology::NymTopology; +use nym_topology::{EpochRewardedSet, NymTopology}; +use nym_validator_client::nym_api::NymApiClientExt; use url::Url; struct MyTopologyProvider { - validator_client: nym_validator_client::client::NymApiClient, + validator_client: nym_http_api_client::Client, } impl MyTopologyProvider { fn new(nym_api_url: Url) -> MyTopologyProvider { - MyTopologyProvider { - validator_client: nym_validator_client::client::NymApiClient::new(nym_api_url), - } + let validator_client = nym_http_api_client::Client::builder::< + _, + nym_validator_client::models::RequestError, + >(nym_api_url) + .expect("Failed to create API client builder") + .build::() + .expect("Failed to build API client"); + + MyTopologyProvider { validator_client } } async fn get_topology(&self) -> NymTopology { @@ -33,7 +40,8 @@ impl MyTopologyProvider { let metadata = mixnodes_response.metadata.to_topology_metadata(); - let mut base_topology = NymTopology::new(metadata, rewarded_set, Vec::new()); + let epoch_rewarded_set: EpochRewardedSet = rewarded_set.into(); + let mut base_topology = NymTopology::new(metadata, epoch_rewarded_set, Vec::new()); // in our topology provider only use mixnodes that have node_id divisible by 3 // and has exactly 100 performance score diff --git a/tools/internal/testnet-manager/Cargo.toml b/tools/internal/testnet-manager/Cargo.toml index e1a32ec685..652b2bf5fd 100644 --- a/tools/internal/testnet-manager/Cargo.toml +++ b/tools/internal/testnet-manager/Cargo.toml @@ -36,6 +36,7 @@ nym-crypto = { path = "../../../common/crypto", features = ["asymmetric", "rand" nym-config = { path = "../../../common/config" } nym-validator-client = { path = "../../../common/client-libs/validator-client" } nym-compact-ecash = { path = "../../../common/nym_offline_compact_ecash" } +nym-http-api-client = { path = "../../../common/http-api-client" } dkg-bypass-contract = { path = "dkg-bypass-contract", default-features = false } # contracts: diff --git a/tools/internal/testnet-manager/src/manager/local_client.rs b/tools/internal/testnet-manager/src/manager/local_client.rs index 9e1d2025e2..1d7084d35c 100644 --- a/tools/internal/testnet-manager/src/manager/local_client.rs +++ b/tools/internal/testnet-manager/src/manager/local_client.rs @@ -7,7 +7,7 @@ use crate::manager::network::LoadedNetwork; use crate::manager::NetworkManager; use console::style; use nym_config::{must_get_home, DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, NYM_DIR}; -use nym_validator_client::NymApiClient; +use nym_validator_client::nym_api::NymApiClientExt; use rand::{thread_rng, RngCore}; use std::fs; use std::fs::OpenOptions; @@ -91,7 +91,13 @@ impl NetworkManager { "⌛waiting for any gateway to appear in the directory ({api_url})..." )); - let api_client = NymApiClient::new(api_url); + let api_client = nym_http_api_client::Client::builder::< + _, + nym_validator_client::models::RequestError, + >(api_url.clone()) + .expect("Failed to create API client builder") + .build::() + .expect("Failed to build API client"); let wait_fut = async { let inner_fut = async { diff --git a/tools/internal/validator-status-check/Cargo.toml b/tools/internal/validator-status-check/Cargo.toml index 99eb182621..a0d09e336f 100644 --- a/tools/internal/validator-status-check/Cargo.toml +++ b/tools/internal/validator-status-check/Cargo.toml @@ -25,6 +25,7 @@ time = { workspace = true } nym-validator-client = { path = "../../../common/client-libs/validator-client" } nym-bin-common = { path = "../../../common/bin-common", features = ["output_format", "basic_tracing"] } nym-network-defaults = { path = "../../../common/network-defaults" } +nym-http-api-client = { path = "../../../common/http-api-client" } [lints] workspace = true diff --git a/tools/internal/validator-status-check/src/models.rs b/tools/internal/validator-status-check/src/models.rs index 12b8337f5d..3c164e93eb 100644 --- a/tools/internal/validator-status-check/src/models.rs +++ b/tools/internal/validator-status-check/src/models.rs @@ -1,8 +1,7 @@ // Copyright 2025 - Nym Technologies SA // SPDX-License-Identifier: GPL-3.0-only -use nym_validator_client::client::NymApiClientExt; -use nym_validator_client::NymApiClient; +use nym_validator_client::nym_api::NymApiClientExt; use serde::{Deserialize, Serialize}; use std::fmt::Display; use strum::{Display, EnumProperty}; @@ -48,8 +47,8 @@ impl SignerStatus { matches!(self.rpc_status, RpcStatus::Up) } - fn build_api_client(&self) -> Option { - let api_endpoint = match self.api_endpoint.as_str().parse() { + fn build_api_client(&self) -> Option { + let api_endpoint: nym_http_api_client::Url = match self.api_endpoint.as_str().parse() { Ok(endpoint) => endpoint, Err(err) => { error!("{} is not a valid api endpoint: {err}", self.api_endpoint); @@ -57,14 +56,19 @@ impl SignerStatus { } }; - Some(NymApiClient::new(api_endpoint)) + nym_http_api_client::Client::builder::<_, nym_validator_client::models::RequestError>( + api_endpoint, + ) + .ok()? + .build::() + .ok() } pub(crate) async fn try_update_api_version(&mut self) { let Some(client) = self.build_api_client() else { return; }; - match client.nym_api.build_information().await { + match client.build_information().await { Ok(build_info) => { self.api_version = ApiVersion::Available { version: build_info.build_version, @@ -84,7 +88,7 @@ impl SignerStatus { return; }; - match client.nym_api.get_chain_status().await { + match client.get_chain_status().await { Ok(chain_status) => { self.used_rpc_endpoint = RpcEndpoint(chain_status.connected_nyxd); let last_block =