Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1898853263 | |||
| 4e5ccf7926 | |||
| 0a8eb940bb | |||
| 34fb67602c | |||
| 766ae8dd8e | |||
| bd1fd73ba0 |
Generated
+15
@@ -4863,6 +4863,7 @@ dependencies = [
|
||||
"nym-ecash-signer-check",
|
||||
"nym-ecash-time",
|
||||
"nym-gateway-client",
|
||||
"nym-http-api-client",
|
||||
"nym-http-api-common",
|
||||
"nym-mixnet-contract-common",
|
||||
"nym-node-requests",
|
||||
@@ -5074,6 +5075,7 @@ dependencies = [
|
||||
"nym-crypto",
|
||||
"nym-ecash-contract-common",
|
||||
"nym-ecash-time",
|
||||
"nym-http-api-client",
|
||||
"nym-id",
|
||||
"nym-mixnet-contract-common",
|
||||
"nym-multisig-contract-common",
|
||||
@@ -5162,6 +5164,7 @@ dependencies = [
|
||||
"nym-http-api-client",
|
||||
"nym-id",
|
||||
"nym-mixnet-client",
|
||||
"nym-mixnet-contract-common",
|
||||
"nym-network-defaults",
|
||||
"nym-nonexhaustive-delayqueue",
|
||||
"nym-pemstore",
|
||||
@@ -5514,6 +5517,7 @@ dependencies = [
|
||||
"nym-crypto",
|
||||
"nym-ecash-contract-common",
|
||||
"nym-ecash-time",
|
||||
"nym-http-api-client",
|
||||
"nym-network-defaults",
|
||||
"nym-serde-helpers",
|
||||
"nym-validator-client",
|
||||
@@ -5613,6 +5617,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"nym-ecash-signer-check-types",
|
||||
"nym-http-api-client",
|
||||
"nym-network-defaults",
|
||||
"nym-validator-client",
|
||||
"semver 1.0.26",
|
||||
@@ -5885,6 +5890,7 @@ dependencies = [
|
||||
"mime",
|
||||
"nym-bin-common",
|
||||
"nym-http-api-common",
|
||||
"nym-network-defaults",
|
||||
"once_cell",
|
||||
"reqwest 0.12.22",
|
||||
"serde",
|
||||
@@ -6145,6 +6151,8 @@ dependencies = [
|
||||
"nym-client-core",
|
||||
"nym-crypto",
|
||||
"nym-gateway-requests",
|
||||
"nym-http-api-client",
|
||||
"nym-mixnet-contract-common",
|
||||
"nym-network-defaults",
|
||||
"nym-sdk",
|
||||
"nym-sphinx",
|
||||
@@ -6612,6 +6620,7 @@ dependencies = [
|
||||
"nym-credentials-interface",
|
||||
"nym-crypto",
|
||||
"nym-gateway-requests",
|
||||
"nym-http-api-client",
|
||||
"nym-network-defaults",
|
||||
"nym-ordered-buffer",
|
||||
"nym-service-providers-common",
|
||||
@@ -7185,6 +7194,7 @@ dependencies = [
|
||||
"nym-credentials-interface",
|
||||
"nym-crypto",
|
||||
"nym-ecash-time",
|
||||
"nym-http-api-client",
|
||||
"nym-network-defaults",
|
||||
"nym-pemstore",
|
||||
"nym-serde-helpers",
|
||||
@@ -7214,7 +7224,9 @@ dependencies = [
|
||||
"bytes",
|
||||
"futures",
|
||||
"humantime",
|
||||
"nym-api-requests",
|
||||
"nym-crypto",
|
||||
"nym-http-api-client",
|
||||
"nym-task",
|
||||
"nym-validator-client",
|
||||
"rand 0.8.5",
|
||||
@@ -10195,6 +10207,7 @@ dependencies = [
|
||||
"nym-crypto",
|
||||
"nym-ecash-contract-common",
|
||||
"nym-group-contract-common",
|
||||
"nym-http-api-client",
|
||||
"nym-mixnet-contract-common",
|
||||
"nym-multisig-contract-common",
|
||||
"nym-pemstore",
|
||||
@@ -11317,6 +11330,7 @@ dependencies = [
|
||||
"clap",
|
||||
"comfy-table",
|
||||
"nym-bin-common",
|
||||
"nym-http-api-client",
|
||||
"nym-network-defaults",
|
||||
"nym-validator-client",
|
||||
"serde",
|
||||
@@ -11547,6 +11561,7 @@ dependencies = [
|
||||
"nym-credential-storage",
|
||||
"nym-crypto",
|
||||
"nym-gateway-client",
|
||||
"nym-http-api-client",
|
||||
"nym-sphinx",
|
||||
"nym-sphinx-acknowledgements",
|
||||
"nym-statistics-common",
|
||||
|
||||
@@ -13,7 +13,7 @@ use nym_credentials_interface::{
|
||||
};
|
||||
use nym_ecash_time::Date;
|
||||
use nym_validator_client::coconut::all_ecash_api_clients;
|
||||
use nym_validator_client::nym_api::EpochId;
|
||||
use nym_validator_client::nym_api::{EpochId, NymApiClientExt};
|
||||
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
|
||||
use nym_validator_client::EcashApiClient;
|
||||
use rand::prelude::SliceRandom;
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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<Box<dyn TopologyProvider + Send + Sync>>,
|
||||
config_topology: config::Topology,
|
||||
nym_api_urls: Vec<Url>,
|
||||
nym_api_client: NymApiClient,
|
||||
nym_api_client: nym_http_api_client::Client,
|
||||
) -> Box<dyn TopologyProvider + Send + Sync> {
|
||||
// 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<UserAgent>) -> NymApiClient {
|
||||
fn construct_nym_api_client(
|
||||
config: &Config,
|
||||
user_agent: Option<UserAgent>,
|
||||
) -> Result<nym_http_api_client::Client, ClientCoreError> {
|
||||
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::<nym_validator_client::models::RequestError>()
|
||||
.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<KeyRotationConfig, ClientCoreError> {
|
||||
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<BaseClient, ClientCoreError>
|
||||
@@ -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(
|
||||
|
||||
@@ -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<Url>,
|
||||
currently_used_api: usize,
|
||||
use_bincode: bool,
|
||||
}
|
||||
|
||||
impl NymApiTopologyProvider {
|
||||
pub fn new(
|
||||
config: impl Into<Config>,
|
||||
mut nym_api_urls: Vec<Url>,
|
||||
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 all API URLs - the client will try them in order with automatic failover
|
||||
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) {
|
||||
@@ -72,8 +87,19 @@ 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())
|
||||
|
||||
// Provide all URLs starting from the next one in rotation order
|
||||
// This enables automatic failover to other endpoints
|
||||
let rotated_urls: Vec<_> = self
|
||||
.nym_api_urls
|
||||
.iter()
|
||||
.cycle()
|
||||
.skip(self.currently_used_api)
|
||||
.take(self.nym_api_urls.len())
|
||||
.map(|u| u.clone().into())
|
||||
.collect();
|
||||
|
||||
self.validator_client.change_base_urls(rotated_urls)
|
||||
}
|
||||
|
||||
async fn get_current_compatible_topology(&mut self) -> Option<NymTopology> {
|
||||
@@ -99,8 +125,13 @@ impl NymApiTopologyProvider {
|
||||
.filter(|n| n.performance.round_to_integer() >= self.config.min_node_performance())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
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 +179,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() {
|
||||
|
||||
@@ -7,7 +7,8 @@ use futures::{SinkExt, StreamExt};
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_gateway_client::GatewayClient;
|
||||
use nym_topology::node::RoutingNode;
|
||||
use nym_validator_client::client::IdentityKeyRef;
|
||||
use nym_validator_client::client::{IdentityKeyRef, NymApiClientExt};
|
||||
use nym_validator_client::nym_nodes::SkimmedNodesWithMetadata;
|
||||
use nym_validator_client::UserAgent;
|
||||
use rand::{seq::SliceRandom, Rng};
|
||||
#[cfg(unix)]
|
||||
@@ -83,6 +84,48 @@ struct GatewayWithLatency<'a, G: ConnectableGateway> {
|
||||
latency: Duration,
|
||||
}
|
||||
|
||||
// Helper to collect all pages of entry nodes - replicates NymApiClient's convenience method
|
||||
async fn get_all_basic_entry_nodes_with_metadata(
|
||||
client: &nym_http_api_client::Client,
|
||||
use_bincode: bool,
|
||||
) -> Result<SkimmedNodesWithMetadata, ClientCoreError> {
|
||||
// Get first page to obtain metadata
|
||||
let mut page = 0;
|
||||
let res = client
|
||||
.get_basic_entry_assigned_nodes_v2(false, Some(page), None, 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;
|
||||
|
||||
// Collect remaining pages
|
||||
loop {
|
||||
let mut res = client
|
||||
.get_basic_entry_assigned_nodes_v2(false, Some(page), None, use_bincode)
|
||||
.await?;
|
||||
|
||||
if !metadata.consistency_check(&res.metadata) {
|
||||
return Err(ClientCoreError::ValidatorClientError(
|
||||
nym_validator_client::ValidatorClientError::InconsistentPagedMetadata,
|
||||
));
|
||||
}
|
||||
|
||||
nodes.append(&mut res.nodes.data);
|
||||
if nodes.len() < res.nodes.pagination.total {
|
||||
page += 1
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(SkimmedNodesWithMetadata::new(nodes, metadata))
|
||||
}
|
||||
|
||||
impl<'a, G: ConnectableGateway> GatewayWithLatency<'a, G> {
|
||||
fn new(gateway: &'a G, latency: Duration) -> Self {
|
||||
GatewayWithLatency { gateway, latency }
|
||||
@@ -99,16 +142,32 @@ pub async fn gateways_for_init<R: Rng>(
|
||||
let nym_api = nym_apis
|
||||
.choose(rng)
|
||||
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
|
||||
let client = if let Some(user_agent) = user_agent {
|
||||
nym_validator_client::client::NymApiClient::new_with_user_agent(nym_api.clone(), user_agent)
|
||||
} else {
|
||||
nym_validator_client::client::NymApiClient::new(nym_api.clone())
|
||||
};
|
||||
|
||||
// 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
|
||||
|
||||
if let Some(user_agent) = user_agent {
|
||||
builder = builder.with_user_agent(user_agent);
|
||||
}
|
||||
|
||||
let client = builder
|
||||
.build::<nym_validator_client::models::RequestError>()
|
||||
.map_err(|e| {
|
||||
ClientCoreError::ValidatorClientError(
|
||||
nym_validator_client::ValidatorClientError::NymAPIError { source: e },
|
||||
)
|
||||
})?;
|
||||
|
||||
tracing::debug!("Fetching list of gateways from: {nym_api}");
|
||||
|
||||
let gateways = client
|
||||
.get_all_basic_entry_assigned_nodes_with_metadata()
|
||||
// Use our helper to handle pagination
|
||||
let gateways = get_all_basic_entry_nodes_with_metadata(&client, true)
|
||||
.await?
|
||||
.nodes;
|
||||
info!("nym api reports {} gateways", gateways.len());
|
||||
|
||||
@@ -5,8 +5,8 @@ use crate::nyxd::{self, NyxdClient};
|
||||
use crate::signing::direct_wallet::DirectSecp256k1HdWallet;
|
||||
use crate::signing::signer::{NoSigner, OfflineSigner};
|
||||
use crate::{
|
||||
nym_api, DirectSigningReqwestRpcValidatorClient, QueryReqwestRpcValidatorClient,
|
||||
ReqwestRpcClient, ValidatorClientError,
|
||||
DirectSigningReqwestRpcValidatorClient, QueryReqwestRpcValidatorClient, ReqwestRpcClient,
|
||||
ValidatorClientError,
|
||||
};
|
||||
use nym_api_requests::ecash::models::{
|
||||
AggregatedCoinIndicesSignatureResponse, AggregatedExpirationDateSignatureResponse,
|
||||
@@ -153,7 +153,7 @@ impl Config {
|
||||
pub struct Client<C, S = NoSigner> {
|
||||
// ideally they would have been read-only, but unfortunately rust doesn't have such features
|
||||
// #[deprecated(note = "please use `nym_api_client` instead")]
|
||||
pub nym_api: nym_api::Client,
|
||||
pub nym_api: nym_http_api_client::Client,
|
||||
// pub nym_api_client: NymApiClient,
|
||||
pub nyxd: NyxdClient<C, S>,
|
||||
}
|
||||
@@ -214,7 +214,7 @@ impl Client<ReqwestRpcClient> {
|
||||
|
||||
impl<C> Client<C> {
|
||||
pub fn new_with_rpc_client(config: Config, rpc_client: C) -> Self {
|
||||
let nym_api_client = nym_api::Client::new(config.api_url.clone(), None);
|
||||
let nym_api_client = nym_http_api_client::Client::new(config.api_url.clone(), None);
|
||||
|
||||
Client {
|
||||
nym_api: nym_api_client,
|
||||
@@ -228,7 +228,7 @@ impl<C, S> Client<C, S> {
|
||||
where
|
||||
S: OfflineSigner,
|
||||
{
|
||||
let nym_api_client = nym_api::Client::new(config.api_url.clone(), None);
|
||||
let nym_api_client = nym_http_api_client::Client::new(config.api_url.clone(), None);
|
||||
|
||||
Client {
|
||||
nym_api: nym_api_client,
|
||||
@@ -385,38 +385,25 @@ impl<C, S> Client<C, S> {
|
||||
}
|
||||
}
|
||||
|
||||
/// DEPRECATED: Use nym_http_api_client::Client with from_network() or with_bincode() instead
|
||||
#[deprecated(
|
||||
since = "1.2.0",
|
||||
note = "Use nym_http_api_client::Client::from_network() or ClientBuilder::with_bincode() instead"
|
||||
)]
|
||||
#[derive(Clone)]
|
||||
pub struct NymApiClient {
|
||||
pub use_bincode: bool,
|
||||
pub nym_api: nym_api::Client,
|
||||
pub nym_api: nym_http_api_client::Client,
|
||||
// TODO: perhaps if we really need it at some (currently I don't see any reasons for it)
|
||||
// we could re-implement the communication with the REST API on port 1317
|
||||
}
|
||||
|
||||
impl From<nym_api::Client> for NymApiClient {
|
||||
fn from(nym_api: nym_api::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_api::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_api::Client::new(api_url, Some(timeout));
|
||||
let nym_api = nym_http_api_client::Client::new(api_url, Some(timeout));
|
||||
|
||||
NymApiClient {
|
||||
use_bincode: true,
|
||||
@@ -431,7 +418,7 @@ impl NymApiClient {
|
||||
}
|
||||
|
||||
pub fn new_with_user_agent(api_url: Url, user_agent: impl Into<UserAgent>) -> Self {
|
||||
let nym_api = nym_api::Client::builder::<_, ValidatorClientError>(api_url)
|
||||
let nym_api = nym_http_api_client::Client::builder::<_, ValidatorClientError>(api_url)
|
||||
.expect("invalid api url")
|
||||
.with_user_agent(user_agent.into())
|
||||
.build::<ValidatorClientError>()
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
use crate::nyxd::contract_traits::{DkgQueryClient, PagedDkgQueryClient};
|
||||
use crate::nyxd::error::NyxdError;
|
||||
use crate::NymApiClient;
|
||||
use nym_coconut_dkg_common::types::{EpochId, NodeIndex};
|
||||
use nym_coconut_dkg_common::verification_key::ContractVKShare;
|
||||
use nym_compact_ecash::error::CompactEcashError;
|
||||
@@ -15,7 +14,7 @@ use url::Url;
|
||||
// TODO: it really doesn't feel like this should live in this crate.
|
||||
#[derive(Clone)]
|
||||
pub struct EcashApiClient {
|
||||
pub api_client: NymApiClient,
|
||||
pub api_client: nym_http_api_client::Client,
|
||||
pub verification_key: VerificationKeyAuth,
|
||||
pub node_id: NodeIndex,
|
||||
pub cosmos_address: cosmrs::AccountId,
|
||||
@@ -25,10 +24,10 @@ impl Display for EcashApiClient {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"[id: {}] {} @ {}",
|
||||
"[id: {}] {} @ {:?}",
|
||||
self.node_id,
|
||||
self.cosmos_address,
|
||||
self.api_client.api_url()
|
||||
self.api_client.base_urls()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -60,6 +59,9 @@ pub enum EcashApiError {
|
||||
source: CompactEcashError,
|
||||
},
|
||||
|
||||
#[error("failed to create API client: {0}")]
|
||||
ClientError(String),
|
||||
|
||||
#[error("the provided account address is malformed: {source}")]
|
||||
MalformedAccountAddress {
|
||||
#[from]
|
||||
@@ -89,8 +91,16 @@ impl TryFrom<ContractVKShare> 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::<nym_api_requests::models::RequestError>()
|
||||
.map_err(|e| EcashApiError::ClientError(e.to_string()))?;
|
||||
|
||||
Ok(EcashApiClient {
|
||||
api_client: NymApiClient::new(url_address),
|
||||
api_client,
|
||||
verification_key: VerificationKeyAuth::try_from_bs58(&share.share)?,
|
||||
node_id: share.node_index,
|
||||
cosmos_address: share.owner.as_str().parse()?,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use crate::nym_api::NymApiClientExt;
|
||||
use crate::nyxd::contract_traits::MixnetQueryClient;
|
||||
use crate::nyxd::error::NyxdError;
|
||||
use crate::nyxd::Config as ClientConfig;
|
||||
use crate::{NymApiClient, QueryHttpRpcNyxdClient, ValidatorClientError};
|
||||
use crate::{QueryHttpRpcNyxdClient, ValidatorClientError};
|
||||
use colored::Colorize;
|
||||
use core::fmt;
|
||||
use itertools::Itertools;
|
||||
@@ -87,8 +88,19 @@ fn setup_connection_tests<H: BuildHasher + 'static>(
|
||||
}
|
||||
});
|
||||
|
||||
let api_connection_test_clients = api_urls.map(|(network, url)| {
|
||||
ClientForConnectionTest::Api(network, url.clone(), NymApiClient::new(url))
|
||||
let api_connection_test_clients = api_urls.filter_map(|(network, url)| {
|
||||
match nym_http_api_client::Client::builder(url.clone())
|
||||
.and_then(|b| b.build::<nym_api_requests::models::RequestError>())
|
||||
{
|
||||
Ok(client) => Some(ClientForConnectionTest::Api(network, url, client)),
|
||||
Err(err) => {
|
||||
eprintln!(
|
||||
"Failed to create API client for {}: {err}",
|
||||
network.network_name
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
nyxd_connection_test_clients.chain(api_connection_test_clients)
|
||||
@@ -160,7 +172,7 @@ async fn test_nyxd_connection(
|
||||
async fn test_nym_api_connection(
|
||||
network: NymNetworkDetails,
|
||||
url: &Url,
|
||||
client: &NymApiClient,
|
||||
client: &nym_http_api_client::Client,
|
||||
) -> ConnectionResult {
|
||||
let result = match timeout(
|
||||
Duration::from_secs(CONNECTION_TEST_TIMEOUT_SEC),
|
||||
@@ -186,7 +198,7 @@ async fn test_nym_api_connection(
|
||||
|
||||
enum ClientForConnectionTest {
|
||||
Nyxd(NymNetworkDetails, Url, Box<QueryHttpRpcNyxdClient>),
|
||||
Api(NymNetworkDetails, Url, NymApiClient),
|
||||
Api(NymNetworkDetails, Url, nym_http_api_client::Client),
|
||||
}
|
||||
|
||||
impl ClientForConnectionTest {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
@@ -37,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;
|
||||
@@ -49,8 +50,8 @@ use time::format_description::BorrowedFormatItem;
|
||||
use time::Date;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::ValidatorClientError;
|
||||
pub use nym_coconut_dkg_common::types::EpochId;
|
||||
pub use nym_http_api_client::Client;
|
||||
|
||||
pub mod error;
|
||||
pub mod routes;
|
||||
@@ -62,6 +63,9 @@ pub fn rfc_3339_date() -> Vec<BorrowedFormatItem<'static>> {
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
pub trait NymApiClientExt: ApiClient {
|
||||
/// Get the current API URL being used by the client
|
||||
fn api_url(&self) -> &url::Url;
|
||||
|
||||
async fn health(&self) -> Result<ApiHealthResponse, NymAPIError> {
|
||||
self.get_json(
|
||||
&[
|
||||
@@ -241,6 +245,162 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_current_rewarded_set(&self) -> Result<RewardedSetResponse, NymAPIError> {
|
||||
self.get_rewarded_set().await
|
||||
}
|
||||
|
||||
async fn get_all_basic_nodes_with_metadata(
|
||||
&self,
|
||||
) -> Result<SkimmedNodesWithMetadata, NymAPIError> {
|
||||
// unroll first loop iteration in order to obtain the metadata
|
||||
let mut page = 0;
|
||||
let res = self
|
||||
.get_basic_nodes_v2(false, Some(page), None, true)
|
||||
.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
|
||||
.get_basic_nodes_v2(false, Some(page), None, true)
|
||||
.await?;
|
||||
|
||||
if !metadata.consistency_check(&res.metadata) {
|
||||
// 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",
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
nodes.append(&mut res.nodes.data);
|
||||
if nodes.len() >= res.nodes.pagination.total {
|
||||
break;
|
||||
} else {
|
||||
page += 1
|
||||
}
|
||||
}
|
||||
|
||||
Ok(SkimmedNodesWithMetadata::new(nodes, metadata))
|
||||
}
|
||||
|
||||
async fn get_all_basic_active_mixing_assigned_nodes_with_metadata(
|
||||
&self,
|
||||
) -> Result<SkimmedNodesWithMetadata, NymAPIError> {
|
||||
// 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<SkimmedNodesWithMetadata, NymAPIError> {
|
||||
// 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<Vec<NymNodeDescription>, NymAPIError> {
|
||||
// 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 descriptions = Vec::new();
|
||||
|
||||
loop {
|
||||
let mut res = self.get_nodes_described(Some(page), None).await?;
|
||||
|
||||
descriptions.append(&mut res.data);
|
||||
if descriptions.len() < res.pagination.total {
|
||||
page += 1
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(descriptions)
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all)]
|
||||
async fn get_nym_nodes(
|
||||
&self,
|
||||
@@ -268,6 +428,25 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_all_bonded_nym_nodes(&self) -> Result<Vec<NymNodeDetails>, 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 bonds = Vec::new();
|
||||
|
||||
loop {
|
||||
let mut res = self.get_nym_nodes(Some(page), None).await?;
|
||||
|
||||
bonds.append(&mut res.data);
|
||||
if bonds.len() < res.pagination.total {
|
||||
page += 1
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(bonds)
|
||||
}
|
||||
|
||||
#[deprecated]
|
||||
#[tracing::instrument(level = "debug", skip_all)]
|
||||
async fn get_basic_mixnodes(&self) -> Result<CachedNodesResponse<SkimmedNode>, NymAPIError> {
|
||||
@@ -1371,8 +1550,49 @@ 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<url::Url>);
|
||||
|
||||
/// Retrieve expanded information for all bonded nodes on the network
|
||||
async fn get_all_expanded_nodes(&self) -> Result<SemiSkimmedNodesWithMetadata, NymAPIError> {
|
||||
// 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
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl NymApiClientExt for Client {}
|
||||
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<url::Url>) {
|
||||
self.change_base_urls(urls.into_iter().map(|u| u.into()).collect());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ cosmrs = { workspace = true }
|
||||
cosmwasm-std = { workspace = true }
|
||||
|
||||
nym-validator-client = { path = "../client-libs/validator-client" }
|
||||
nym-http-api-client = { path = "../http-api-client" }
|
||||
nym-bin-common = { path = "../../common/bin-common", features = ["output_format"] }
|
||||
nym-crypto = { path = "../../common/crypto", features = ["asymmetric"] }
|
||||
nym-network-defaults = { path = "../network-defaults" }
|
||||
|
||||
@@ -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_validator_client::nym_api::Client as NymApiClient;
|
||||
use nym_validator_client::nyxd::{self, AccountId, NyxdClient};
|
||||
use nym_validator_client::{
|
||||
DirectSigningHttpRpcNyxdClient, DirectSigningHttpRpcValidatorClient, QueryHttpRpcNyxdClient,
|
||||
|
||||
@@ -14,7 +14,7 @@ use nym_api_requests::ecash::models::{BatchRedeemTicketsBody, VerifyEcashTicketB
|
||||
use nym_credentials_interface::Bandwidth;
|
||||
use nym_credentials_interface::{ClientTicket, TicketType};
|
||||
use nym_validator_client::coconut::EcashApiError;
|
||||
use nym_validator_client::nym_api::EpochId;
|
||||
use nym_validator_client::nym_api::{EpochId, NymApiClientExt};
|
||||
use nym_validator_client::nyxd::contract_traits::{
|
||||
EcashSigningClient, MultisigQueryClient, MultisigSigningClient, PagedMultisigQueryClient,
|
||||
};
|
||||
@@ -354,7 +354,7 @@ impl CredentialHandler {
|
||||
Err(err) => {
|
||||
error!("failed to send ticket {ticket_id} for verification to ecash signer '{client}': {err}. if we don't reach quorum, we'll retry later");
|
||||
Err(EcashTicketError::ApiFailure(EcashApiError::NymApi {
|
||||
source: err,
|
||||
source: nym_validator_client::ValidatorClientError::NymAPIError { source: err },
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ nym-ecash-time = { path = "../ecash-time", features = ["expiration"] }
|
||||
nym-credentials-interface = { path = "../credentials-interface" }
|
||||
nym-crypto = { path = "../crypto" }
|
||||
nym-api-requests = { path = "../../nym-api/nym-api-requests" }
|
||||
nym-http-api-client = { path = "../http-api-client" }
|
||||
nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
|
||||
nym-ecash-contract-common = { path = "../cosmwasm-smart-contracts/ecash-contract" }
|
||||
nym-network-defaults = { path = "../network-defaults" }
|
||||
|
||||
@@ -15,7 +15,7 @@ use nym_credentials_interface::{
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_ecash_contract_common::deposit::DepositId;
|
||||
use nym_ecash_time::{ecash_default_expiration_date, ecash_today, EcashTime};
|
||||
use nym_validator_client::nym_api::EpochId;
|
||||
use nym_validator_client::nym_api::{EpochId, NymApiClientExt};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use time::Date;
|
||||
|
||||
@@ -116,7 +116,7 @@ impl IssuanceTicketBook {
|
||||
|
||||
pub async fn obtain_blinded_credential(
|
||||
&self,
|
||||
client: &nym_validator_client::client::NymApiClient,
|
||||
client: &nym_http_api_client::Client,
|
||||
request_body: &BlindSignRequestBody,
|
||||
) -> Result<BlindedSignature, Error> {
|
||||
let server_response = client.blind_sign(request_body).await?;
|
||||
@@ -179,7 +179,7 @@ impl IssuanceTicketBook {
|
||||
// ideally this would have been generic over credential type, but we really don't need secp256k1 keys for bandwidth vouchers
|
||||
pub async fn obtain_partial_ticketbook_credential(
|
||||
&self,
|
||||
client: &nym_validator_client::client::NymApiClient,
|
||||
client: &nym_http_api_client::Client,
|
||||
signer_index: u64,
|
||||
validator_vk: &VerificationKeyAuth,
|
||||
signing_data: CredentialSigningData,
|
||||
|
||||
@@ -10,6 +10,7 @@ use nym_credentials_interface::{
|
||||
VerificationKeyAuth, WalletSignatures,
|
||||
};
|
||||
use nym_validator_client::client::EcashApiClient;
|
||||
use nym_validator_client::nym_api::NymApiClientExt;
|
||||
|
||||
// so we wouldn't break all the existing imports
|
||||
pub use nym_ecash_time::{cred_exp_date, ecash_date_offset, ecash_today, EcashTime};
|
||||
|
||||
@@ -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;
|
||||
use nym_validator_client::{nym_api::error::NymAPIError, ValidatorClientError};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
@@ -37,6 +37,9 @@ pub enum Error {
|
||||
#[error("Ran into a validator client error - {0}")]
|
||||
ValidatorClientError(#[from] ValidatorClientError),
|
||||
|
||||
#[error("Nym API request failed - {0}")]
|
||||
NymAPIError(#[from] NymAPIError),
|
||||
|
||||
#[error("Bandwidth operation overflowed. {0}")]
|
||||
BandwidthOverflow(String),
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<BinaryBuildInformationOwned>,
|
||||
}
|
||||
|
||||
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<Self, SignerCheckError> {
|
||||
// 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 {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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 {
|
||||
|
||||
@@ -13,6 +13,7 @@ license.workspace = true
|
||||
[features]
|
||||
default=["tunneling"]
|
||||
tunneling=[]
|
||||
network-defaults = ["dep:nym-network-defaults"]
|
||||
|
||||
[dependencies]
|
||||
async-trait = { workspace = true }
|
||||
@@ -34,6 +35,7 @@ mime = { workspace = true }
|
||||
|
||||
nym-http-api-common = { path = "../http-api-common", default-features = false }
|
||||
nym-bin-common = { path = "../bin-common" }
|
||||
nym-network-defaults = { path = "../network-defaults", optional = true }
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies]
|
||||
hickory-resolver = { workspace = true, features = ["https-ring", "tls-ring", "webpki-roots"] }
|
||||
|
||||
@@ -54,10 +54,14 @@ impl Front {
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Clone)]
|
||||
#[cfg(feature = "tunneling")]
|
||||
/// Policy for when to use domain fronting for HTTP requests.
|
||||
pub enum FrontPolicy {
|
||||
/// Always use domain fronting for all requests.
|
||||
Always,
|
||||
/// Only use domain fronting when retrying failed requests.
|
||||
OnRetry,
|
||||
#[default]
|
||||
/// Never use domain fronting.
|
||||
Off,
|
||||
}
|
||||
|
||||
|
||||
@@ -162,6 +162,8 @@ use std::sync::Arc;
|
||||
|
||||
#[cfg(feature = "tunneling")]
|
||||
mod fronted;
|
||||
#[cfg(feature = "tunneling")]
|
||||
pub use fronted::FrontPolicy;
|
||||
mod url;
|
||||
pub use url::{IntoUrl, Url};
|
||||
mod user_agent;
|
||||
@@ -192,6 +194,15 @@ pub type Params<'a, K, V> = &'a [(K, V)];
|
||||
/// Empty collection of HTTP Request Parameters.
|
||||
pub const NO_PARAMS: Params<'_, &'_ str, &'_ str> = &[];
|
||||
|
||||
/// Serialization format for API requests and responses
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum SerializationFormat {
|
||||
/// Use JSON serialization (default, always works)
|
||||
Json,
|
||||
/// Use bincode serialization (must be explicitly opted into)
|
||||
Bincode,
|
||||
}
|
||||
|
||||
/// The Errors that may occur when creating or using an HTTP client.
|
||||
#[derive(Debug, Error)]
|
||||
#[allow(missing_docs)]
|
||||
@@ -371,6 +382,7 @@ pub struct ClientBuilder {
|
||||
front: Option<fronted::Front>,
|
||||
|
||||
retry_limit: usize,
|
||||
serialization: SerializationFormat,
|
||||
}
|
||||
|
||||
impl ClientBuilder {
|
||||
@@ -396,6 +408,50 @@ 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<Self, HttpClientError> {
|
||||
let urls = network
|
||||
.nym_api_urls
|
||||
.as_ref()
|
||||
.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 {
|
||||
let fronts: Vec<String> = front_hosts
|
||||
.iter()
|
||||
.map(|host| format!("https://{}", host))
|
||||
.collect();
|
||||
url = Url::new(api_url.url.clone(), Some(fronts))
|
||||
.map_err(|e| HttpClientError::GenericRequestFailure(e.to_string()))?;
|
||||
}
|
||||
|
||||
Ok(url)
|
||||
})
|
||||
.collect::<Result<Vec<_>, 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)
|
||||
}
|
||||
|
||||
/// Constructs a new http `ClientBuilder` from a valid url.
|
||||
pub fn new_with_urls(urls: Vec<Url>) -> Self {
|
||||
let urls = Self::check_urls(urls);
|
||||
@@ -429,6 +485,7 @@ impl ClientBuilder {
|
||||
front: None,
|
||||
|
||||
retry_limit: 0,
|
||||
serialization: SerializationFormat::Json,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -501,6 +558,17 @@ impl ClientBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the serialization format for API requests and responses
|
||||
pub fn with_serialization(mut self, format: SerializationFormat) -> Self {
|
||||
self.serialization = format;
|
||||
self
|
||||
}
|
||||
|
||||
/// Configure the client to use bincode serialization
|
||||
pub fn with_bincode(self) -> Self {
|
||||
self.with_serialization(SerializationFormat::Bincode)
|
||||
}
|
||||
|
||||
/// Returns a Client that uses this ClientBuilder configuration.
|
||||
pub fn build<E>(self) -> Result<Client, HttpClientError<E>>
|
||||
where
|
||||
@@ -542,6 +610,7 @@ impl ClientBuilder {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
request_timeout: self.timeout.unwrap_or(DEFAULT_TIMEOUT),
|
||||
retry_limit: self.retry_limit,
|
||||
serialization: self.serialization,
|
||||
};
|
||||
|
||||
Ok(client)
|
||||
@@ -562,6 +631,7 @@ pub struct Client {
|
||||
request_timeout: Duration,
|
||||
|
||||
retry_limit: usize,
|
||||
serialization: SerializationFormat,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
@@ -619,6 +689,7 @@ impl Client {
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
request_timeout: self.request_timeout,
|
||||
serialization: self.serialization,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -694,26 +765,37 @@ 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();
|
||||
|
||||
#[cfg(feature = "tunneling")]
|
||||
if let Some(ref front) = self.front {
|
||||
if front.is_enabled() {
|
||||
// this should never fail as we are transplanting the host from one url to another
|
||||
r.url_mut().set_host(url.front_str()).unwrap();
|
||||
let front_host = url.front_str().unwrap_or("");
|
||||
let actual_host = url.host_str().unwrap_or("");
|
||||
|
||||
let actual_host: HeaderValue = url
|
||||
.host_str()
|
||||
.unwrap_or("")
|
||||
.parse()
|
||||
.unwrap_or(HeaderValue::from_static(""));
|
||||
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(""));
|
||||
// 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);
|
||||
_ = r
|
||||
.headers_mut()
|
||||
.insert(reqwest::header::HOST, actual_host_header);
|
||||
|
||||
return (url.as_str(), url.front_str());
|
||||
}
|
||||
}
|
||||
(url.as_str(), None)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -743,6 +825,13 @@ impl ApiClientCore for Client {
|
||||
|
||||
let mut rb = RequestBuilder::from_parts(self.reqwest_client.clone(), req);
|
||||
|
||||
// Set Accept header based on serialization preference
|
||||
let accept_header = match self.serialization {
|
||||
SerializationFormat::Json => "application/json",
|
||||
SerializationFormat::Bincode => "application/bincode",
|
||||
};
|
||||
rb = rb.header(reqwest::header::ACCEPT, accept_header);
|
||||
|
||||
if let Some(body) = json_body {
|
||||
rb = rb.json(body);
|
||||
}
|
||||
@@ -791,7 +880,14 @@ impl ApiClientCore for Client {
|
||||
if let Some(ref front) = self.front {
|
||||
// If fronting is set to be enabled on error, enable domain fronting as we
|
||||
// have encountered an error.
|
||||
let was_enabled = front.is_enabled();
|
||||
front.retry_enable();
|
||||
if !was_enabled && front.is_enabled() {
|
||||
tracing::info!(
|
||||
"Domain fronting activated after connection failure: {}",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if attempts < self.retry_limit {
|
||||
|
||||
@@ -55,6 +55,7 @@ pub struct ApiUrl {
|
||||
pub front_hosts: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ApiUrlConst<'a> {
|
||||
pub url: &'a str,
|
||||
pub front_hosts: Option<&'a [&'a str]>,
|
||||
@@ -188,8 +189,14 @@ impl NymNetworkDetails {
|
||||
),
|
||||
},
|
||||
nym_vpn_api_url: parse_optional_str(mainnet::NYM_VPN_API),
|
||||
nym_api_urls: None,
|
||||
nym_vpn_api_urls: None,
|
||||
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(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,3 +25,5 @@ url = { workspace = true }
|
||||
nym-crypto = { path = "../crypto", features = ["asymmetric"] }
|
||||
nym-task = { path = "../task" }
|
||||
nym-validator-client = { path = "../client-libs/validator-client" }
|
||||
nym-http-api-client = { path = "../http-api-client" }
|
||||
nym-api-requests = { path = "../../nym-api/nym-api-requests" }
|
||||
|
||||
@@ -10,7 +10,7 @@ use futures::StreamExt;
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_task::ShutdownToken;
|
||||
use nym_validator_client::models::NymNodeDescription;
|
||||
use nym_validator_client::NymApiClient;
|
||||
use nym_validator_client::nym_api::NymApiClientExt;
|
||||
use rand::prelude::SliceRandom;
|
||||
use rand::thread_rng;
|
||||
use std::net::SocketAddr;
|
||||
@@ -135,10 +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 = NymApiClient::new_with_user_agent(
|
||||
api_endpoint.clone(),
|
||||
self.config.user_agent.clone(),
|
||||
);
|
||||
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::<nym_api_requests::models::RequestError>()
|
||||
}) {
|
||||
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) => {
|
||||
|
||||
@@ -34,6 +34,7 @@ nym-statistics-common = { path = "../../statistics" }
|
||||
nym-task = { path = "../../task" }
|
||||
nym-topology = { path = "../../topology", features = ["wasm-serde-types"] }
|
||||
nym-validator-client = { path = "../../client-libs/validator-client", default-features = false }
|
||||
nym-http-api-client = { path = "../../http-api-client" }
|
||||
wasm-utils = { path = "../utils" }
|
||||
wasm-storage = { path = "../storage" }
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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::{EpochRewardedSet, NymTopology, RoutingNode};
|
||||
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,19 @@ 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::<nym_validator_client::models::RequestError>()
|
||||
.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,9 +102,14 @@ pub async fn current_network_topology_async(
|
||||
|
||||
let gateways = gateways_res.nodes;
|
||||
|
||||
let topology = NymTopology::new(metadata.to_topology_metadata(), rewarded_set, Vec::new())
|
||||
.with_skimmed_nodes(&mixnodes)
|
||||
.with_skimmed_nodes(&gateways);
|
||||
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);
|
||||
|
||||
Ok(topology.into())
|
||||
}
|
||||
|
||||
@@ -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_validator_client::nym_api::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;
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
|
||||
+8
-2
@@ -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::<nym_validator_client::models::RequestError>()
|
||||
.expect("Failed to build API client");
|
||||
|
||||
MyTopologyProvider {
|
||||
validator_client: nym_validator_client::client::NymApiClient::new(nym_api_url),
|
||||
validator_client,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -93,6 +93,7 @@ nym-task = { path = "../common/task" }
|
||||
nym-topology = { path = "../common/topology" }
|
||||
nym-api-requests = { path = "nym-api-requests" }
|
||||
nym-validator-client = { path = "../common/client-libs/validator-client" }
|
||||
nym-http-api-client = { path = "../common/http-api-client" }
|
||||
nym-bin-common = { path = "../common/bin-common", features = ["output_format", "openapi", "basic_tracing"] }
|
||||
nym-node-tester-utils = { path = "../common/node-tester-utils" }
|
||||
nym-node-requests = { path = "../nym-node/nym-node-requests" }
|
||||
|
||||
@@ -8,7 +8,9 @@ use nym_crypto::asymmetric::x25519::serde_helpers::bs58_x25519_pubkey;
|
||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
use nym_mixnet_contract_common::reward_params::Performance;
|
||||
use nym_mixnet_contract_common::NodeId;
|
||||
use nym_network_defaults::{DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT};
|
||||
use nym_network_defaults::{
|
||||
DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT, WG_METADATA_PORT, WG_TUNNEL_PORT,
|
||||
};
|
||||
use nym_node_requests::api::v1::authenticator::models::Authenticator;
|
||||
use nym_node_requests::api::v1::gateway::models::Wireguard;
|
||||
use nym_node_requests::api::v1::ip_packet_router::models::IpPacketRouter;
|
||||
@@ -313,11 +315,20 @@ impl From<Authenticator> for AuthenticatorDetails {
|
||||
pub struct WireguardDetails {
|
||||
// NOTE: the port field is deprecated in favour of tunnel_port
|
||||
pub port: u16,
|
||||
#[serde(default = "default_tunnel_port")]
|
||||
pub tunnel_port: u16,
|
||||
#[serde(default = "default_metadata_port")]
|
||||
pub metadata_port: u16,
|
||||
pub public_key: String,
|
||||
}
|
||||
|
||||
fn default_tunnel_port() -> u16 {
|
||||
WG_TUNNEL_PORT
|
||||
}
|
||||
fn default_metadata_port() -> u16 {
|
||||
WG_METADATA_PORT
|
||||
}
|
||||
|
||||
// works for current simple case.
|
||||
impl From<Wireguard> for WireguardDetails {
|
||||
fn from(value: Wireguard) -> Self {
|
||||
|
||||
@@ -13,6 +13,7 @@ use nym_dkg::Threshold;
|
||||
use nym_ecash_contract_common::deposit::DepositId;
|
||||
use nym_ecash_contract_common::redeem_credential::BATCH_REDEMPTION_PROPOSAL_TITLE;
|
||||
use nym_validator_client::coconut::EcashApiError;
|
||||
use nym_validator_client::nym_api::error::NymAPIError;
|
||||
use nym_validator_client::nyxd::error::NyxdError;
|
||||
use nym_validator_client::nyxd::AccountId;
|
||||
use thiserror::Error;
|
||||
@@ -46,6 +47,9 @@ pub enum EcashError {
|
||||
#[error("coconut api query failure: {0}")]
|
||||
CoconutApiError(#[from] EcashApiError),
|
||||
|
||||
#[error("nym api query failure: {0}")]
|
||||
NymApiError(#[from] NymAPIError),
|
||||
|
||||
#[error(transparent)]
|
||||
SerdeJsonError(#[from] serde_json::Error),
|
||||
|
||||
|
||||
@@ -46,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};
|
||||
|
||||
@@ -67,7 +67,7 @@ use nym_validator_client::nym_api::routes::{
|
||||
use nym_validator_client::nyxd::cosmwasm_client::logs::Log;
|
||||
use nym_validator_client::nyxd::cosmwasm_client::types::ExecuteResult;
|
||||
use nym_validator_client::nyxd::{AccountId, ExecTxResult, Fee, Hash, TxResponse};
|
||||
use nym_validator_client::{EcashApiClient, NymApiClient};
|
||||
use nym_validator_client::EcashApiClient;
|
||||
use rand::rngs::OsRng;
|
||||
use rand::RngCore;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
@@ -1148,7 +1148,10 @@ impl DummyCommunicationChannel {
|
||||
cosmos_address: AccountId,
|
||||
) -> Self {
|
||||
let client = EcashApiClient {
|
||||
api_client: NymApiClient::new("http://localhost:1234".parse().unwrap()),
|
||||
api_client: nym_http_api_client::Client::new(
|
||||
"http://localhost:1234".parse().unwrap(),
|
||||
None,
|
||||
),
|
||||
verification_key: aggregated_verification_key,
|
||||
node_id: 1,
|
||||
cosmos_address,
|
||||
|
||||
@@ -12,6 +12,9 @@ use nym_credential_proxy_requests::api::v1::ticketbook::models::{
|
||||
};
|
||||
use nym_credentials_interface::Base58;
|
||||
use nym_validator_client::ecash::BlindSignRequestBody;
|
||||
use nym_validator_client::nyxd::Coin;
|
||||
use nym_validator_client::nym_api::NymApiClientExt;
|
||||
use rand::rngs::OsRng;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use nym_ecash_signer_check::SignerCheckError;
|
||||
use nym_validator_client::coconut::EcashApiError;
|
||||
use nym_validator_client::nym_api::EpochId;
|
||||
use nym_validator_client::nym_api::{error::NymAPIError, EpochId};
|
||||
use nym_validator_client::nyxd::error::NyxdError;
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
@@ -71,6 +71,12 @@ pub enum CredentialProxyError {
|
||||
source: EcashApiError,
|
||||
},
|
||||
|
||||
#[error("Nym API request failed: {source}")]
|
||||
NymApiFailure {
|
||||
#[from]
|
||||
source: NymAPIError,
|
||||
},
|
||||
|
||||
#[error("Compact ecash internal error: {0}")]
|
||||
CompactEcashInternalError(#[from] nym_compact_ecash::error::CompactEcashError),
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ use nym_ecash_contract_common::deposit::DepositId;
|
||||
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, NymContractsProvider, PagedDkgQueryClient,
|
||||
|
||||
@@ -40,3 +40,5 @@ nym-sphinx = { path = "../common/nymsphinx" }
|
||||
nym-topology = { path = "../common/topology" }
|
||||
nym-types = { path = "../common/types" }
|
||||
nym-validator-client = { path = "../common/client-libs/validator-client" }
|
||||
nym-http-api-client = { path = "../common/http-api-client" }
|
||||
nym-mixnet-contract-common = { path = "../common/cosmwasm-smart-contracts/mixnet-contract" }
|
||||
|
||||
@@ -6,12 +6,15 @@ 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};
|
||||
use nym_sphinx::chunking::monitoring;
|
||||
use nym_topology::provider_trait::ToTopologyMetadata;
|
||||
use nym_topology::{HardcodedTopologyProvider, NymTopology};
|
||||
use nym_validator_client::nym_api::NymApiClientExt;
|
||||
use nym_validator_client::UserAgent;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::sync::LazyLock;
|
||||
@@ -160,10 +163,12 @@ async fn nym_topology_from_env() -> anyhow::Result<NymTopology> {
|
||||
let api_url = std::env::var(NYM_API)?;
|
||||
|
||||
info!("Generating topology from {api_url}");
|
||||
let client = nym_validator_client::client::NymApiClient::new_with_user_agent(
|
||||
api_url.parse()?,
|
||||
bin_info!(),
|
||||
);
|
||||
let client = nym_http_api_client::Client::builder::<
|
||||
_,
|
||||
nym_validator_client::models::RequestError,
|
||||
>(api_url)?
|
||||
.with_user_agent(UserAgent::from(bin_info!()))
|
||||
.build::<nym_validator_client::models::RequestError>()?;
|
||||
|
||||
let rewarded_set = client.get_current_rewarded_set().await?;
|
||||
|
||||
@@ -172,10 +177,15 @@ async fn nym_topology_from_env() -> anyhow::Result<NymTopology> {
|
||||
let nodes = nodes_response.nodes;
|
||||
let metadata = nodes_response.metadata;
|
||||
|
||||
Ok(
|
||||
NymTopology::new(metadata.to_topology_metadata(), rewarded_set, Vec::new())
|
||||
.with_skimmed_nodes(&nodes),
|
||||
// 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))
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
|
||||
@@ -5,11 +5,11 @@ use nym_node_requests::api::{client::NymNodeApiClientExt, v1::metrics::models::S
|
||||
use nym_validator_client::{
|
||||
client::{NodeId, NymNodeDetails},
|
||||
models::{DescribedNodeType, NymNodeDescription},
|
||||
NymApiClient,
|
||||
};
|
||||
use time::OffsetDateTime;
|
||||
|
||||
use nym_statistics_common::types::SessionType;
|
||||
use nym_validator_client::client::NymApiClientExt;
|
||||
use std::collections::HashMap;
|
||||
use tokio::time::Duration;
|
||||
use tracing::instrument;
|
||||
@@ -62,11 +62,9 @@ async fn run(
|
||||
.with_timeout(nym_api_client_timeout)
|
||||
.build::<&str>()?;
|
||||
|
||||
let api_client = NymApiClient::from(nym_api);
|
||||
|
||||
//SW TBC what nodes exactly need to be scraped, the skimmed node endpoint seems to return more nodes
|
||||
let bonded_nodes = api_client.get_all_bonded_nym_nodes().await?;
|
||||
let all_nodes = api_client.get_all_described_nodes().await?; //legacy node that did not upgrade the contract bond yet
|
||||
let bonded_nodes = nym_api.get_all_bonded_nym_nodes().await?;
|
||||
let all_nodes = nym_api.get_all_described_nodes().await?; //legacy node that did not upgrade the contract bond yet
|
||||
tracing::debug!("Fetched {} total nodes", all_nodes.len());
|
||||
|
||||
let mut nodes_to_scrape: HashMap<NodeId, MetricsScrapingData> = bonded_nodes
|
||||
|
||||
@@ -19,7 +19,7 @@ use nym_validator_client::{
|
||||
use nym_validator_client::{
|
||||
nym_nodes::{NodeRole, SkimmedNode},
|
||||
nyxd::{contract_traits::PagedMixnetQueryClient, AccountId},
|
||||
NymApiClient, QueryHttpRpcNyxdClient,
|
||||
QueryHttpRpcNyxdClient,
|
||||
};
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
@@ -111,9 +111,7 @@ impl Monitor {
|
||||
.with_timeout(self.nym_api_client_timeout)
|
||||
.build::<&str>()?;
|
||||
|
||||
let api_client = NymApiClient::from(nym_api);
|
||||
|
||||
let described_nodes = api_client
|
||||
let described_nodes = nym_api
|
||||
.get_all_described_nodes()
|
||||
.await
|
||||
.log_error("get_all_described_nodes")?
|
||||
@@ -135,7 +133,7 @@ impl Monitor {
|
||||
|
||||
tracing::info!("🟣 🚪 gateway nodes: {}", gateways.len());
|
||||
|
||||
let bonded_nym_nodes = api_client
|
||||
let bonded_nym_nodes = nym_api
|
||||
.get_all_bonded_nym_nodes()
|
||||
.await?
|
||||
.into_iter()
|
||||
@@ -146,10 +144,11 @@ impl Monitor {
|
||||
tracing::info!("🟣 bonded_nodes: {}", bonded_nym_nodes.len());
|
||||
|
||||
// returns only bonded nodes
|
||||
let nym_nodes = api_client
|
||||
.get_all_basic_nodes()
|
||||
let nym_nodes = nym_api
|
||||
.get_all_basic_nodes_with_metadata()
|
||||
.await
|
||||
.log_error("get_all_basic_nodes")?;
|
||||
.log_error("get_all_basic_nodes")?
|
||||
.nodes;
|
||||
|
||||
let nym_node_count = nym_nodes.len();
|
||||
tracing::info!("🟣 get_all_basic_nodes: {}", nym_node_count);
|
||||
@@ -167,8 +166,7 @@ impl Monitor {
|
||||
self.location_cached(node_description).await;
|
||||
}
|
||||
|
||||
let mixnodes_detailed = api_client
|
||||
.nym_api
|
||||
let mixnodes_detailed = nym_api
|
||||
.get_mixnodes_detailed_unfiltered()
|
||||
.await
|
||||
.log_error("get_mixnodes_detailed_unfiltered")?;
|
||||
@@ -190,15 +188,13 @@ impl Monitor {
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mixnodes_described = api_client
|
||||
.nym_api
|
||||
let mixnodes_described = nym_api
|
||||
.get_mixnodes_described()
|
||||
.await
|
||||
.log_error("get_mixnodes_described")?;
|
||||
|
||||
tracing::info!("🟣 mixnodes_described: {}", mixnodes_described.len());
|
||||
let mixing_assigned_nodes = api_client
|
||||
.nym_api
|
||||
let mixing_assigned_nodes = nym_api
|
||||
.get_basic_active_mixing_assigned_nodes(false, None, None, false)
|
||||
.await
|
||||
.log_error("get_basic_active_mixing_assigned_nodes")?
|
||||
|
||||
@@ -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<Url>,
|
||||
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,
|
||||
@@ -88,9 +87,19 @@ impl NymApisClient {
|
||||
if guard.currently_used_api != last_working_endpoint {
|
||||
drop(guard);
|
||||
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);
|
||||
|
||||
// Provide all URLs starting from the working endpoint for automatic failover
|
||||
let rotated_urls: Vec<_> = guard
|
||||
.available_urls
|
||||
.iter()
|
||||
.cycle()
|
||||
.skip(last_working_endpoint)
|
||||
.take(guard.available_urls.len())
|
||||
.map(|u| u.clone().into())
|
||||
.collect();
|
||||
|
||||
guard.active_client.change_base_urls(rotated_urls);
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
@@ -123,10 +132,10 @@ 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();
|
||||
// For broadcast, we intentionally set a single URL per client
|
||||
// to ensure each endpoint receives the request
|
||||
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 +181,10 @@ 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();
|
||||
// For exhaustive query, we test each endpoint individually in sequence
|
||||
// to find a working one - so single URL is correct here
|
||||
nym_api.change_base_urls(vec![url.clone().into()]);
|
||||
|
||||
let timeout_fut = sleep(timeout_duration);
|
||||
let query_fut = req(nym_api);
|
||||
@@ -216,8 +225,8 @@ impl InnerClient {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<NymApiClient> for InnerClient {
|
||||
fn as_ref(&self) -> &NymApiClient {
|
||||
impl AsRef<Client> for InnerClient {
|
||||
fn as_ref(&self) -> &Client {
|
||||
&self.active_client
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Url>,
|
||||
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<EpochRewardedSet, ValidatorClientError> {
|
||||
@@ -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<NodesByAddressesResponse, ValidatorClientError> {
|
||||
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,
|
||||
},
|
||||
|
||||
@@ -6,7 +6,6 @@ use nym_task::ShutdownToken;
|
||||
|
||||
use celes::Country;
|
||||
use nym_validator_client::models::NymNodeDescription;
|
||||
use nym_validator_client::NymApiClient;
|
||||
use std::collections::HashMap;
|
||||
use std::time::Duration;
|
||||
use std::{net::IpAddr, sync::Arc};
|
||||
@@ -14,6 +13,8 @@ use tokio::sync::RwLock;
|
||||
use tokio::time::interval;
|
||||
use url::Url;
|
||||
|
||||
use nym_http_api_client::Client;
|
||||
use nym_validator_client::client::NymApiClientExt;
|
||||
use tracing::{error, info, trace, warn};
|
||||
|
||||
const NETWORK_CACHE_TTL: Duration = Duration::from_secs(600);
|
||||
@@ -22,7 +23,7 @@ type IpToCountryMap = HashMap<IpAddr, Option<Country>>;
|
||||
|
||||
// SW this should use a proper NS API client once it exists
|
||||
struct NodesQuerier {
|
||||
client: NymApiClient,
|
||||
client: Client,
|
||||
}
|
||||
|
||||
impl NodesQuerier {
|
||||
@@ -99,14 +100,11 @@ impl NetworkRefresher {
|
||||
this
|
||||
}
|
||||
|
||||
fn build_http_api_client(url: Url) -> Result<NymApiClient> {
|
||||
Ok(
|
||||
nym_http_api_client::Client::builder::<_, anyhow::Error>(url)?
|
||||
.no_hickory_dns()
|
||||
.with_user_agent("node-statistics-api")
|
||||
.build::<anyhow::Error>()?
|
||||
.into(),
|
||||
)
|
||||
fn build_http_api_client(url: Url) -> Result<Client> {
|
||||
Ok(Client::builder::<_, anyhow::Error>(url)?
|
||||
.no_hickory_dns()
|
||||
.with_user_agent("node-statistics-api")
|
||||
.build::<anyhow::Error>()?)
|
||||
}
|
||||
|
||||
async fn refresh_network_nodes(&mut self) -> Result<()> {
|
||||
|
||||
@@ -42,6 +42,7 @@ nym-credentials = { path = "../common/credentials" }
|
||||
nym-network-defaults = { path = "../common/network-defaults" }
|
||||
nym-task = { path = "../common/task" }
|
||||
nym-validator-client = { path = "../common/client-libs/validator-client" }
|
||||
nym-http-api-client = { path = "../common/http-api-client" }
|
||||
nym-coconut-dkg-common = { path = "../common/cosmwasm-smart-contracts/coconut-dkg" }
|
||||
nyxd-scraper = { path = "../common/nyxd-scraper" }
|
||||
nym-ticketbooks-merkle = { path = "../common/ticketbooks-merkle" }
|
||||
|
||||
@@ -9,6 +9,7 @@ use crate::rewarder::ticketbook_issuance::verifier::TicketbookIssuanceVerifier;
|
||||
use crate::rewarder::Rewarder;
|
||||
use anyhow::bail;
|
||||
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;
|
||||
|
||||
@@ -15,7 +15,7 @@ use nym_validator_client::nyxd::module_traits::staking::{
|
||||
use nym_validator_client::nyxd::{
|
||||
AccountId, Coin, CosmWasmClient, Hash, PageRequest, StakingQueryClient,
|
||||
};
|
||||
use nym_validator_client::{nyxd, DirectSigningHttpRpcNyxdClient, NymApiClient};
|
||||
use nym_validator_client::{nyxd, DirectSigningHttpRpcNyxdClient};
|
||||
use std::collections::HashMap;
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
@@ -139,10 +139,23 @@ impl NyxdClient {
|
||||
continue;
|
||||
};
|
||||
|
||||
let api_client = match nym_http_api_client::Client::builder(api_address)
|
||||
.and_then(|b| b.build::<nym_validator_client::models::RequestError>())
|
||||
{
|
||||
Ok(client) => client,
|
||||
Err(err) => {
|
||||
error!(
|
||||
"Failed to create API client for issuer {}: {}",
|
||||
info.assigned_index, err
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
issuers.push(CredentialIssuer {
|
||||
public_key,
|
||||
operator_account: addr_to_account_id(share.owner),
|
||||
api_client: NymApiClient::new(api_address),
|
||||
api_client,
|
||||
verification_key,
|
||||
node_id: info.assigned_index,
|
||||
})
|
||||
|
||||
@@ -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::nym_api::NymApiClientExt;
|
||||
use nym_validator_client::nyxd::{AccountId, Coin};
|
||||
use nym_validator_client::NymApiClient;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use tracing::info;
|
||||
|
||||
@@ -68,7 +68,7 @@ impl TicketbookIssuanceResults {
|
||||
pub struct CredentialIssuer {
|
||||
pub public_key: ed25519::PublicKey,
|
||||
pub operator_account: AccountId,
|
||||
pub api_client: NymApiClient,
|
||||
pub api_client: nym_http_api_client::Client,
|
||||
pub verification_key: VerificationKeyAuth,
|
||||
pub node_id: NodeIndex,
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ 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 rand::distributions::{Distribution, WeightedIndex};
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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::<nym_validator_client::models::RequestError>()
|
||||
.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
|
||||
|
||||
@@ -37,6 +37,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:
|
||||
|
||||
@@ -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::<nym_validator_client::models::RequestError>()
|
||||
.expect("Failed to build API client");
|
||||
|
||||
let wait_fut = async {
|
||||
let inner_fut = async {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<NymApiClient> {
|
||||
let api_endpoint = match self.api_endpoint.as_str().parse() {
|
||||
fn build_api_client(&self) -> Option<nym_http_api_client::Client> {
|
||||
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::<nym_validator_client::models::RequestError>()
|
||||
.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 =
|
||||
|
||||
Reference in New Issue
Block a user