90e9e3cff8
* feat: unify HTTP client creation and enable domain fronting Enhanced the base nym_http_api_client to reduce fragmentation and enable domain fronting: - Added SerializationFormat enum for explicit JSON/bincode choice (no auto-detection) - Added from_network() method to create clients from NymNetworkDetails with domain fronting - Added with_bincode() builder method for explicit serialization configuration - Set Accept header based on serialization preference - Added deprecation paths for NymApiClient wrapper and nym_api::Client re-export - Enabled domain fronting support via network defaults feature This is part of a broader effort to consolidate HTTP client implementations across the codebase, reducing ~500 lines of wrapper code and providing automatic domain fronting for censorship resistance. * feat: migrate NymApiClient usage to unified HTTP client - Wire up domain fronting configuration in NymNetworkDetails - Implement NymApiClientExt trait for base nym_http_api_client::Client - Migrate direct NymApiClient usage in multiple components: - nym-network-monitor - verloc measurements - connection tester - coconut/ecash client - validator rewarder - Add Copy derive to ApiUrlConst to enable iteration - Update error handling and Display implementations This enables automatic domain fronting for all Nym API calls via the configured CDN front hosts. * fix: resolve all compilation errors after NymApiClient migration - Add missing nym-http-api-client dependencies to multiple crates - Add NymApiClientExt trait imports where needed - Fix type mismatches from NymApiClient to unified Client - Add error conversions for NymAPIError in various error enums - Implement missing trait methods (get_current_rewarded_set, get_all_basic_nodes_with_metadata, get_all_described_nodes) - Fix type conversions for RewardedSetResponse in network monitor - Update all API client instantiation to use new unified HTTP client * feat: complete migration to unified HTTP client and fix all compilation errors - Added missing NymApiClientExt trait methods (get_all_expanded_nodes, change_base_urls) - Fixed all compilation errors across the workspace - Updated nym-node to use unified client instead of deprecated NymApiClient - Fixed type conversions for RewardedSetResponse → EpochRewardedSet - Added nym-http-api-client dependency where needed - Updated all examples and documentation to use new client API * fix: provide all API URLs for automatic failover in endpoint rotation Previously, when rotating API endpoints, only a single URL was provided to the HTTP client, defeating the purpose of having multiple URLs for resilience. Changes: - NymApiTopologyProvider now provides all URLs in rotated order when switching endpoints - NymApisClient similarly provides all URLs starting from the working endpoint - Added clarifying comments for broadcast/exhaustive query methods where single URLs are intentionally used - This enables the HTTP client's built-in failover mechanism while maintaining endpoint rotation behavior The fix ensures that if the primary endpoint fails, the client can automatically failover to alternative endpoints without manual intervention, improving overall network resilience. * Update common/client-core/src/client/base_client/mod.rs Co-authored-by: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com> * Remove error generics, address PR comments * Explicit warning on missing fronting configuration * Assorted CI fixes * Registry proc-macro * Rename macro * Syn workspace version * Where do we need to put inventory * Ergonomics and call sites, incept the builder * fix: Address critical issues in client configuration registry implementation - Fixed HeaderMapInit parsing bug that would cause compilation errors - Added comprehensive documentation with usage examples and DSL reference - Improved error handling with better error messages for invalid headers - Added test coverage for both macro and registry functionality - Added debug inspection capabilities for registered configurations - Fixed module name conflicts in tests by using separate modules All tests now passing: - 7 macro tests validating DSL parsing and code generation - 4 registry tests verifying configuration collection and application * Use default value for the ports until api is deployed * Feature/improved http error (#6025) * use display impl for urls * feat: attempt to add more details to reqwest errors * temporarily restored GenericRequestFailure variant * another restoration * cleanup * Some debug tooling, and default timeout fix * Fix user-agent override * Fix various wasm things --------- Co-authored-by: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com> Co-authored-by: Bogdan-Ștefan Neacşu <bogdan@nymtech.net>
290 lines
9.1 KiB
Rust
290 lines
9.1 KiB
Rust
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
use crate::client::mix_traffic::transceiver::ErasedGatewayError;
|
|
use nym_crypto::asymmetric::ed25519::Ed25519RecoveryError;
|
|
use nym_gateway_client::error::GatewayClientError;
|
|
use nym_topology::node::RoutingNodeError;
|
|
use nym_topology::{NodeId, NymTopologyError};
|
|
use nym_validator_client::nym_api::error::NymAPIError;
|
|
use nym_validator_client::nyxd::error::NyxdError;
|
|
use nym_validator_client::ValidatorClientError;
|
|
use rand::distributions::WeightedError;
|
|
use std::error::Error;
|
|
use std::path::PathBuf;
|
|
|
|
#[derive(thiserror::Error, Debug)]
|
|
pub enum ClientCoreError {
|
|
#[error("I/O error: {0}")]
|
|
IoError(#[from] std::io::Error),
|
|
|
|
#[error("gateway client error ({gateway_id}): {source}")]
|
|
GatewayClientError {
|
|
gateway_id: String,
|
|
source: Box<GatewayClientError>,
|
|
},
|
|
|
|
#[error("custom gateway client error: {source}")]
|
|
ErasedGatewayClientError {
|
|
#[from]
|
|
source: ErasedGatewayError,
|
|
},
|
|
|
|
#[error("ed25519 error: {0}")]
|
|
Ed25519RecoveryError(#[from] Ed25519RecoveryError),
|
|
|
|
#[error("validator client error: {0}")]
|
|
ValidatorClientError(#[from] ValidatorClientError),
|
|
|
|
#[error("no gateway with id: {0}")]
|
|
NoGatewayWithId(String),
|
|
|
|
#[error("Invalid URL: {0}")]
|
|
InvalidUrl(String),
|
|
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
#[error("resolution failed: {0}")]
|
|
ResolutionFailed(#[from] nym_http_api_client::HickoryDnsError),
|
|
|
|
#[error("no gateways on network")]
|
|
NoGatewaysOnNetwork,
|
|
|
|
#[error("there are no more new gateways on the network - it seems this client has already registered with all nodes it could have")]
|
|
NoNewGatewaysAvailable,
|
|
|
|
#[error("list of nym apis is empty")]
|
|
ListOfNymApisIsEmpty,
|
|
|
|
#[error("failed to resolve a query to nym API: {source}")]
|
|
NymApiQueryFailure { source: Box<NymAPIError> },
|
|
|
|
#[error(
|
|
"the current network topology seem to be insufficient to route any packets through:\n\t{0}"
|
|
)]
|
|
InsufficientNetworkTopology(#[from] NymTopologyError),
|
|
|
|
#[error("experienced a failure with our reply surb persistent storage: {source}")]
|
|
SurbStorageError {
|
|
source: Box<dyn Error + Send + Sync>,
|
|
},
|
|
|
|
#[error("experienced a failure with our cryptographic keys persistent storage: {source}")]
|
|
KeyStoreError {
|
|
source: Box<dyn Error + Send + Sync>,
|
|
},
|
|
|
|
#[error("experienced a failure with our gateways details storage: {source}")]
|
|
GatewaysDetailsStoreError {
|
|
source: Box<dyn Error + Send + Sync>,
|
|
},
|
|
|
|
#[error("experienced a failure with our credentials storage: {source}")]
|
|
CredentialStoreError {
|
|
source: Box<dyn Error + Send + Sync>,
|
|
},
|
|
|
|
#[error("the provided ticket type is invalid")]
|
|
UnknownTicketType,
|
|
|
|
#[error("the gateway id is invalid - {0}")]
|
|
UnableToCreatePublicKeyFromGatewayId(Ed25519RecoveryError),
|
|
|
|
#[error("the node is malformed: {source}")]
|
|
MalformedGateway {
|
|
#[from]
|
|
source: Box<RoutingNodeError>,
|
|
},
|
|
|
|
#[error("failed to establish connection to gateway: {source}")]
|
|
GatewayConnectionFailure { source: Box<tungstenite::Error> },
|
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
#[error("failed to establish gateway connection (wasm)")]
|
|
GatewayJsConnectionFailure,
|
|
|
|
#[error("gateway connection was abruptly closed")]
|
|
GatewayConnectionAbruptlyClosed,
|
|
|
|
#[error("timed out while trying to establish gateway connection")]
|
|
GatewayConnectionTimeout,
|
|
|
|
#[error("failed to forward mix messages to gateway")]
|
|
GatewayFailedToForwardMessages,
|
|
|
|
#[error("no ping measurements for the gateway ({identity}) performed")]
|
|
NoGatewayMeasurements { identity: String },
|
|
|
|
#[error("failed to register receiver for reconstructed mixnet messages")]
|
|
FailedToRegisterReceiver,
|
|
|
|
#[error("unexpected exit")]
|
|
UnexpectedExit,
|
|
|
|
#[error("this operation would have resulted in the gateway {gateway_id:?} key being overwritten without permission")]
|
|
ForbiddenGatewayKeyOverwrite { gateway_id: String },
|
|
|
|
#[error(
|
|
"this operation would have resulted in clients keys being overwritten without permission"
|
|
)]
|
|
ForbiddenKeyOverwrite,
|
|
|
|
#[error("the client doesn't have any gateway set as active")]
|
|
NoActiveGatewaySet,
|
|
|
|
#[error("gateway details for gateway {gateway_id:?} are unavailable")]
|
|
UnavailableGatewayDetails {
|
|
gateway_id: String,
|
|
#[source]
|
|
source: Box<dyn Error + Send + Sync>,
|
|
},
|
|
|
|
#[error("gateway shared key is unavailable whilst we have full node information")]
|
|
UnavailableSharedKey,
|
|
|
|
#[error("attempted to obtain fresh gateway details whilst already knowing about one")]
|
|
UnexpectedGatewayDetails,
|
|
|
|
#[error("the provided gateway details (for gateway {gateway_id}) do not correspond to the shared keys")]
|
|
MismatchedGatewayDetails { gateway_id: String },
|
|
|
|
#[error("unable to upgrade config file from `{current_version}`")]
|
|
ConfigFileUpgradeFailure { current_version: String },
|
|
|
|
#[error("unable to upgrade config file to `{new_version}`")]
|
|
UnableToUpgradeConfigFile { new_version: String },
|
|
|
|
#[error("failed to upgrade config file: {message}")]
|
|
UpgradeFailure { message: String },
|
|
|
|
#[error("the provided gateway details don't much the stored data")]
|
|
MismatchedStoredGatewayDetails,
|
|
|
|
#[error("custom selection of gateway was expected")]
|
|
CustomGatewaySelectionExpected,
|
|
|
|
#[error("the persisted gateway details were set for a custom setup")]
|
|
UnexpectedPersistedCustomGatewayDetails,
|
|
|
|
#[error("this client has performed gateway initialisation in another session")]
|
|
NoInitClientPresent,
|
|
|
|
#[error("there are no gateways supporting the wss protocol available")]
|
|
NoWssGateways,
|
|
|
|
#[error("the specified gateway '{gateway}' does not support the wss protocol")]
|
|
UnsupportedWssProtocol { gateway: String },
|
|
|
|
#[error("node {id} ({identity}) does not support mixnet entry mode")]
|
|
UnsupportedEntry { id: NodeId, identity: String },
|
|
|
|
#[error(
|
|
"failed to load custom topology using path '{}'. detailed message: {source}", file_path.display()
|
|
)]
|
|
CustomTopologyLoadFailure {
|
|
file_path: PathBuf,
|
|
#[source]
|
|
source: std::io::Error,
|
|
},
|
|
|
|
#[error(
|
|
"failed to save config file for client-{typ} id {id} using path '{}'. detailed message: {source}", path.display()
|
|
)]
|
|
ConfigSaveFailure {
|
|
typ: String,
|
|
id: String,
|
|
path: PathBuf,
|
|
#[source]
|
|
source: std::io::Error,
|
|
},
|
|
|
|
#[error("the provided gateway identity {gateway_id} is malformed: {source}")]
|
|
MalformedGatewayIdentity {
|
|
gateway_id: String,
|
|
|
|
#[source]
|
|
source: Ed25519RecoveryError,
|
|
},
|
|
|
|
#[error(
|
|
"the listening address of gateway {gateway_id} ({raw_listener}) is malformed: {source}"
|
|
)]
|
|
MalformedListener {
|
|
gateway_id: String,
|
|
|
|
raw_listener: String,
|
|
|
|
#[source]
|
|
source: url::ParseError,
|
|
},
|
|
|
|
#[error("this client (id: '{client_id}') has already been initialised before. If you want to add additional gateway, use `add-gateway` command")]
|
|
AlreadyInitialised { client_id: String },
|
|
|
|
#[error("this client has already registered with gateway {gateway_id}")]
|
|
AlreadyRegistered { gateway_id: String },
|
|
|
|
#[error(
|
|
"fresh registration with gateway {gateway_id} somehow requires an additional key upgrade!"
|
|
)]
|
|
UnexpectedKeyUpgrade { gateway_id: String },
|
|
|
|
#[error("failed to derive keys from master key")]
|
|
HkdfDerivationError,
|
|
|
|
#[error("missing url for constructing RPC client")]
|
|
RpcClientMissingUrl,
|
|
|
|
#[error("provided nym network details were malformed: {source}")]
|
|
InvalidNetworkDetails { source: NyxdError },
|
|
|
|
#[error("failed to construct RPC client: {source}")]
|
|
RpcClientCreationFailure { source: NyxdError },
|
|
|
|
#[error("failed to select valid gateway due to incomputable latency")]
|
|
GatewaySelectionFailure { source: WeightedError },
|
|
}
|
|
|
|
impl From<tungstenite::Error> for ClientCoreError {
|
|
fn from(err: tungstenite::Error) -> ClientCoreError {
|
|
ClientCoreError::GatewayConnectionFailure {
|
|
source: Box::new(err),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<NymAPIError> for ClientCoreError {
|
|
fn from(err: NymAPIError) -> ClientCoreError {
|
|
ClientCoreError::NymApiQueryFailure {
|
|
source: Box::new(err),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Set of messages that the client can send to listeners via the task manager
|
|
#[derive(Debug)]
|
|
pub enum ClientCoreStatusMessage {
|
|
GatewayIsSlow,
|
|
GatewayIsVerySlow,
|
|
}
|
|
|
|
impl std::fmt::Display for ClientCoreStatusMessage {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
ClientCoreStatusMessage::GatewayIsSlow => write!(
|
|
f,
|
|
"The connected gateway is slow, or the connection to it is slow"
|
|
),
|
|
ClientCoreStatusMessage::GatewayIsVerySlow => write!(
|
|
f,
|
|
"The connected gateway is very slow, or the connection to it is very slow"
|
|
),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl nym_task::TaskStatusEvent for ClientCoreStatusMessage {
|
|
fn as_any(&self) -> &dyn std::any::Any {
|
|
self
|
|
}
|
|
}
|