Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 867acfef26 | |||
| e4996dc0ce | |||
| a6e23a210b |
Generated
+1
-22
@@ -2262,7 +2262,7 @@ dependencies = [
|
||||
"libc",
|
||||
"option-ext",
|
||||
"redox_users",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6798,7 +6798,6 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
"typed-builder",
|
||||
"url",
|
||||
]
|
||||
|
||||
@@ -11392,26 +11391,6 @@ dependencies = [
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typed-builder"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d0dd654273fc253fde1df4172c31fb6615cf8b041d3a4008a028ef8b1119e66"
|
||||
dependencies = [
|
||||
"typed-builder-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typed-builder-macro"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "016c26257f448222014296978b2c8456e2cad4de308c35bdb1e383acd569ef5b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.18.0"
|
||||
|
||||
+12
-1
@@ -215,6 +215,7 @@ base64 = "0.22.1"
|
||||
base85rs = "0.1.3"
|
||||
bincode = "1.3.3"
|
||||
bip39 = { version = "2.0.0", features = ["zeroize"] }
|
||||
bit-vec = "0.7.0" # can we unify those?
|
||||
bitvec = "1.0.0"
|
||||
blake3 = "1.7.0"
|
||||
bloomfilter = "3.0.1"
|
||||
@@ -242,11 +243,13 @@ criterion = "0.5"
|
||||
csv = "1.3.1"
|
||||
ctr = "0.9.1"
|
||||
cupid = "0.6.1"
|
||||
curve25519-dalek = "4.1"
|
||||
dashmap = "5.5.3"
|
||||
# We want https://github.com/DefGuard/wireguard-rs/pull/64 , but there's no crates.io release being pushed out anymore
|
||||
defguard_wireguard_rs = { git = "https://github.com/DefGuard/wireguard-rs.git", rev = "v0.4.7" }
|
||||
digest = "0.10.7"
|
||||
dirs = "6.0"
|
||||
doc-comment = "0.3"
|
||||
dotenvy = "0.15.6"
|
||||
dyn-clone = "1.0.19"
|
||||
ecdsa = "0.16"
|
||||
@@ -262,8 +265,11 @@ futures = "0.3.31"
|
||||
futures-util = "0.3"
|
||||
generic-array = "0.14.7"
|
||||
getrandom = "0.2.10"
|
||||
getset = "0.1.5"
|
||||
handlebars = "3.5.5"
|
||||
headers = "0.4.0"
|
||||
hex = "0.4.3"
|
||||
hex-literal = "0.3.3"
|
||||
hickory-resolver = "0.25"
|
||||
hkdf = "0.12.3"
|
||||
hmac = "0.12.1"
|
||||
@@ -287,10 +293,12 @@ lazy_static = "1.5.0"
|
||||
ledger-transport = "0.10.0"
|
||||
ledger-transport-hid = "0.10.0"
|
||||
log = "0.4"
|
||||
maxminddb = "0.23.0"
|
||||
mime = "0.3.17"
|
||||
moka = { version = "0.12", features = ["future"] }
|
||||
nix = "0.27.1"
|
||||
notify = "5.1.0"
|
||||
okapi = "0.7.0"
|
||||
once_cell = "1.21.3"
|
||||
opentelemetry = "0.19.0"
|
||||
opentelemetry-jaeger = "0.18.0"
|
||||
@@ -299,6 +307,7 @@ pem = "0.8"
|
||||
petgraph = "0.6.5"
|
||||
pin-project = "1.1"
|
||||
pnet_packet = "0.35.0"
|
||||
pin-project-lite = "0.2.16"
|
||||
publicsuffix = "2.3.0"
|
||||
proc_pidinfo = "0.1.3"
|
||||
quote = "1"
|
||||
@@ -306,10 +315,13 @@ rand = "0.8.5"
|
||||
rand_chacha = "0.3"
|
||||
rand_core = "0.6.3"
|
||||
rand_distr = "0.4"
|
||||
rand_pcg = "0.3.1"
|
||||
rand_seeder = "0.2.3"
|
||||
rayon = "1.5.1"
|
||||
regex = "1.10.6"
|
||||
reqwest = { version = "0.12.15", default-features = false }
|
||||
rs_merkle = "1.5.0"
|
||||
safer-ffi = "0.1.13"
|
||||
schemars = "0.8.22"
|
||||
semver = "1.0.26"
|
||||
serde = "1.0.219"
|
||||
@@ -356,7 +368,6 @@ tracing-indicatif = "0.3.9"
|
||||
tracing-test = "0.2.5"
|
||||
ts-rs = "10.1.0"
|
||||
tungstenite = { version = "0.20.1", default-features = false }
|
||||
typed-builder = "0.23.0"
|
||||
uniffi = "0.29.2"
|
||||
uniffi_build = "0.29.0"
|
||||
url = "2.5"
|
||||
|
||||
@@ -36,7 +36,7 @@ nym-bandwidth-controller = { path = "../bandwidth-controller" }
|
||||
nym-crypto = { path = "../crypto" }
|
||||
nym-gateway-client = { path = "../client-libs/gateway-client" }
|
||||
nym-gateway-requests = { path = "../gateway-requests" }
|
||||
nym-http-api-client = { path = "../http-api-client", features = ["network-defaults"] }
|
||||
nym-http-api-client = { path = "../http-api-client" }
|
||||
nym-nonexhaustive-delayqueue = { path = "../nonexhaustive-delayqueue" }
|
||||
nym-sphinx = { path = "../nymsphinx" }
|
||||
nym-statistics-common = { path = "../statistics" }
|
||||
|
||||
@@ -73,10 +73,6 @@ use url::Url;
|
||||
#[cfg(debug_assertions)]
|
||||
use wasm_utils::console_log;
|
||||
|
||||
/// Default number of retries for Nym API requests when using network details with domain fronting.
|
||||
/// This allows the client to try alternative URLs if the primary endpoint is unavailable.
|
||||
const DEFAULT_NYM_API_RETRIES: usize = 3;
|
||||
|
||||
#[cfg(all(
|
||||
not(target_arch = "wasm32"),
|
||||
feature = "fs-surb-storage",
|
||||
@@ -216,9 +212,6 @@ pub struct BaseClientBuilder<C, S: MixnetClientStorage> {
|
||||
client_store: S,
|
||||
dkg_query_client: Option<C>,
|
||||
|
||||
// Optional API URLs for domain fronting support
|
||||
nym_api_urls: Option<Vec<nym_network_defaults::ApiUrl>>,
|
||||
|
||||
wait_for_gateway: bool,
|
||||
custom_topology_provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
|
||||
custom_gateway_transceiver: Option<Box<dyn GatewayTransceiver + Send>>,
|
||||
@@ -248,7 +241,6 @@ where
|
||||
config: base_config,
|
||||
client_store,
|
||||
dkg_query_client,
|
||||
nym_api_urls: None,
|
||||
wait_for_gateway: false,
|
||||
custom_topology_provider: None,
|
||||
custom_gateway_transceiver: None,
|
||||
@@ -271,16 +263,6 @@ where
|
||||
self
|
||||
}
|
||||
|
||||
/// Set Nym API URLs for domain fronting support.
|
||||
///
|
||||
/// When provided, the client will use these API URLs (which include front_hosts)
|
||||
/// to construct HTTP clients with domain fronting enabled.
|
||||
#[must_use]
|
||||
pub fn with_nym_api_urls(mut self, nym_api_urls: Vec<nym_network_defaults::ApiUrl>) -> Self {
|
||||
self.nym_api_urls = Some(nym_api_urls);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_forget_me(mut self, forget_me: &ForgetMe) -> Self {
|
||||
self.config.debug.forget_me = *forget_me;
|
||||
@@ -801,7 +783,7 @@ where
|
||||
event_tx,
|
||||
);
|
||||
|
||||
let mix_tx = mix_traffic_controller.mix_tx();
|
||||
let mix_tx = mix_traffic_controller.mix_rx();
|
||||
let client_tx = mix_traffic_controller.client_tx();
|
||||
|
||||
shutdown_tracker.try_spawn_named(
|
||||
@@ -881,67 +863,21 @@ where
|
||||
}
|
||||
|
||||
fn construct_nym_api_client(
|
||||
nym_api_urls: Option<&Vec<nym_network_defaults::ApiUrl>>,
|
||||
config: &Config,
|
||||
user_agent: Option<UserAgent>,
|
||||
) -> Result<nym_http_api_client::Client, ClientCoreError> {
|
||||
tracing::debug!(
|
||||
"construct_nym_api_client called with nym_api_urls: {}",
|
||||
nym_api_urls.is_some()
|
||||
);
|
||||
|
||||
// If API URLs are provided, use new_with_fronted_urls() which handles domain fronting
|
||||
if let Some(nym_api_urls) = nym_api_urls {
|
||||
if nym_api_urls.is_empty() {
|
||||
tracing::warn!("Provided nym_api_urls is empty, falling back to config endpoints");
|
||||
} else {
|
||||
tracing::info!(
|
||||
"Building nym-api client from provided URLs (with domain fronting support): {} URLs",
|
||||
nym_api_urls.len()
|
||||
);
|
||||
|
||||
let mut builder =
|
||||
nym_http_api_client::ClientBuilder::new_with_fronted_urls(nym_api_urls.clone())
|
||||
.map_err(ClientCoreError::from)?
|
||||
.with_retries(DEFAULT_NYM_API_RETRIES);
|
||||
|
||||
if let Some(user_agent) = user_agent {
|
||||
builder = builder.with_user_agent(user_agent);
|
||||
}
|
||||
|
||||
return builder.build().map_err(ClientCoreError::from);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to basic client for backwards compatibility
|
||||
tracing::debug!("Building basic nym-api HTTP client from config endpoints");
|
||||
|
||||
let mut nym_api_urls = config.get_nym_api_endpoints();
|
||||
if nym_api_urls.is_empty() {
|
||||
tracing::warn!("No API endpoints configured in config, this may cause issues");
|
||||
}
|
||||
nym_api_urls.shuffle(&mut thread_rng());
|
||||
|
||||
// Convert config URLs to ApiUrl format for consistency
|
||||
let api_urls: Vec<nym_network_defaults::ApiUrl> = nym_api_urls
|
||||
.into_iter()
|
||||
.map(|url| nym_network_defaults::ApiUrl {
|
||||
url: url.to_string(),
|
||||
front_hosts: None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
tracing::debug!("Using {} config API endpoints", api_urls.len());
|
||||
|
||||
let mut builder = nym_http_api_client::ClientBuilder::new_with_fronted_urls(api_urls)
|
||||
.map_err(ClientCoreError::from)?
|
||||
.with_retries(DEFAULT_NYM_API_RETRIES)
|
||||
.with_bincode();
|
||||
let mut builder = nym_http_api_client::Client::builder(nym_api_urls[0].clone())
|
||||
.map_err(ClientCoreError::from)?;
|
||||
|
||||
if let Some(user_agent) = user_agent {
|
||||
builder = builder.with_user_agent(user_agent);
|
||||
}
|
||||
|
||||
builder = builder.with_bincode();
|
||||
|
||||
builder.build().map_err(ClientCoreError::from)
|
||||
}
|
||||
|
||||
@@ -1004,8 +940,8 @@ where
|
||||
// Create a shutdown tracker for this client - either as a child of provided tracker
|
||||
// or get one from the registry
|
||||
let shutdown_tracker = match self.shutdown {
|
||||
Some(parent_tracker) => parent_tracker.clone(),
|
||||
None => nym_task::create_sdk_shutdown_tracker()?,
|
||||
Some(parent_tracker) => parent_tracker.child_tracker(),
|
||||
None => nym_task::get_sdk_shutdown_tracker()?,
|
||||
};
|
||||
|
||||
Self::start_event_control(self.event_tx, event_receiver, &shutdown_tracker);
|
||||
@@ -1025,11 +961,7 @@ where
|
||||
.dkg_query_client
|
||||
.map(|client| BandwidthController::new(credential_store, client));
|
||||
|
||||
let nym_api_client = Self::construct_nym_api_client(
|
||||
self.nym_api_urls.as_ref(),
|
||||
&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(
|
||||
@@ -1044,7 +976,7 @@ where
|
||||
self.user_agent.clone(),
|
||||
generate_client_stats_id(*self_address.identity()),
|
||||
input_sender.clone(),
|
||||
&shutdown_tracker.clone(),
|
||||
&shutdown_tracker.child_tracker(),
|
||||
);
|
||||
|
||||
// needs to be started as the first thing to block if required waiting for the gateway
|
||||
@@ -1054,7 +986,7 @@ where
|
||||
shared_topology_accessor.clone(),
|
||||
self_address.gateway(),
|
||||
self.wait_for_gateway,
|
||||
&shutdown_tracker.clone(),
|
||||
&shutdown_tracker.child_tracker(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -1074,7 +1006,7 @@ where
|
||||
stats_reporter.clone(),
|
||||
#[cfg(unix)]
|
||||
self.connection_fd_callback,
|
||||
&shutdown_tracker.clone(),
|
||||
&shutdown_tracker.child_tracker(),
|
||||
)
|
||||
.await?;
|
||||
let gateway_ws_fd = gateway_transceiver.ws_fd();
|
||||
@@ -1082,7 +1014,7 @@ where
|
||||
let reply_storage = Self::setup_persistent_reply_storage(
|
||||
reply_storage_backend,
|
||||
key_rotation_config,
|
||||
&shutdown_tracker.clone(),
|
||||
&shutdown_tracker.child_tracker(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -1093,7 +1025,7 @@ where
|
||||
reply_storage.key_storage(),
|
||||
reply_controller_sender.clone(),
|
||||
stats_reporter.clone(),
|
||||
&shutdown_tracker.clone(),
|
||||
&shutdown_tracker.child_tracker(),
|
||||
);
|
||||
|
||||
// The message_sender is the transmitter for any component generating sphinx packets
|
||||
@@ -1103,7 +1035,7 @@ where
|
||||
|
||||
let (message_sender, client_request_sender) = Self::start_mix_traffic_controller(
|
||||
gateway_transceiver,
|
||||
&shutdown_tracker.clone(),
|
||||
&shutdown_tracker.child_tracker(),
|
||||
EventSender(event_sender),
|
||||
);
|
||||
|
||||
@@ -1134,7 +1066,7 @@ where
|
||||
shared_lane_queue_lengths.clone(),
|
||||
client_connection_rx,
|
||||
stats_reporter.clone(),
|
||||
&shutdown_tracker.clone(),
|
||||
&shutdown_tracker.child_tracker(),
|
||||
);
|
||||
|
||||
if !self
|
||||
@@ -1150,7 +1082,7 @@ where
|
||||
shared_topology_accessor.clone(),
|
||||
message_sender,
|
||||
stats_reporter.clone(),
|
||||
&shutdown_tracker.clone(),
|
||||
&shutdown_tracker.child_tracker(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1204,53 +1136,3 @@ pub struct BaseClient {
|
||||
pub forget_me: ForgetMe,
|
||||
pub remember_me: RememberMe,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use nym_network_defaults::{ApiUrl, NymNetworkDetails};
|
||||
|
||||
#[test]
|
||||
fn test_network_details_with_multiple_urls() {
|
||||
// Verify that network details can be configured with multiple API URLs
|
||||
let mut network_details = NymNetworkDetails::new_empty();
|
||||
network_details.nym_api_urls = Some(vec![
|
||||
ApiUrl {
|
||||
url: "https://validator.nymtech.net/api/".to_string(),
|
||||
front_hosts: None,
|
||||
},
|
||||
ApiUrl {
|
||||
url: "https://nym-frontdoor.vercel.app/api/".to_string(),
|
||||
front_hosts: Some(vec!["vercel.app".to_string(), "vercel.com".to_string()]),
|
||||
},
|
||||
]);
|
||||
|
||||
assert_eq!(network_details.nym_api_urls.as_ref().unwrap().len(), 2);
|
||||
assert!(network_details.nym_api_urls.as_ref().unwrap()[1]
|
||||
.front_hosts
|
||||
.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_network_details_with_front_hosts() {
|
||||
// Verify that ApiUrl can store domain fronting configuration
|
||||
let api_url = ApiUrl {
|
||||
url: "https://nym-frontdoor.vercel.app/api/".to_string(),
|
||||
front_hosts: Some(vec!["vercel.app".to_string(), "vercel.com".to_string()]),
|
||||
};
|
||||
|
||||
assert_eq!(api_url.url, "https://nym-frontdoor.vercel.app/api/");
|
||||
assert_eq!(api_url.front_hosts.as_ref().unwrap().len(), 2);
|
||||
assert!(api_url
|
||||
.front_hosts
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.contains(&"vercel.app".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_nym_api_retries_constant() {
|
||||
// Verify the retry constant is set correctly
|
||||
assert_eq!(DEFAULT_NYM_API_RETRIES, 3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,7 +205,7 @@ impl LoopCoverTrafficStream<OsRng> {
|
||||
TrySendError::Full(_) => {
|
||||
// This isn't a problem, if the channel is full means we're already sending the
|
||||
// max amount of messages downstream can handle.
|
||||
tracing::trace!("Failed to send cover message - channel full");
|
||||
tracing::debug!("Failed to send cover message - channel full");
|
||||
}
|
||||
TrySendError::Closed(_) => {
|
||||
tracing::warn!("Failed to send cover message - channel closed");
|
||||
|
||||
@@ -20,10 +20,7 @@ pub mod transceiver;
|
||||
|
||||
// We remind ourselves that 32 x 32kb = 1024kb, a reasonable size for a network buffer.
|
||||
pub const MIX_MESSAGE_RECEIVER_BUFFER_SIZE: usize = 32;
|
||||
|
||||
/// Reduced from 100 to 20 to fail fast (~1-2 seconds instead of ~6 seconds).
|
||||
/// If we can't send 20 packets in a row, the gateway is unreachable.
|
||||
const MAX_FAILURE_COUNT: usize = 20;
|
||||
const MAX_FAILURE_COUNT: usize = 100;
|
||||
|
||||
// that's also disgusting.
|
||||
pub struct Empty;
|
||||
@@ -87,7 +84,7 @@ impl MixTrafficController {
|
||||
self.client_tx.clone()
|
||||
}
|
||||
|
||||
pub fn mix_tx(&self) -> BatchMixMessageSender {
|
||||
pub fn mix_rx(&self) -> BatchMixMessageSender {
|
||||
self.mix_tx.clone()
|
||||
}
|
||||
|
||||
@@ -159,11 +156,6 @@ impl MixTrafficController {
|
||||
// Do we need to handle the embedded mixnet client case
|
||||
// separately?
|
||||
self.event_tx.send(MixnetClientEvent::Traffic(MixTrafficEvent::FailedSendingSphinx));
|
||||
// IMO it shouldn't be signalled from there but it is how it is
|
||||
// TODO : report the failure upwards and shutdown from upwards
|
||||
// Gateway is dead, we have to shut down currently
|
||||
error!("Signalling shutdown from the MixTrafficController");
|
||||
self.shutdown_token.cancel();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,8 +298,6 @@ where
|
||||
"failed to send mixnet packet due to closed channel (outside of shutdown!)"
|
||||
);
|
||||
}
|
||||
// Early return to avoid further processing when channel is closed
|
||||
return;
|
||||
}
|
||||
Ok(_) => {
|
||||
let event = if fragment_id.is_some() {
|
||||
|
||||
@@ -151,7 +151,7 @@ pub async fn gateways_for_init(
|
||||
}
|
||||
|
||||
let retry_count = retry_count.unwrap_or(DEFAULT_NYM_API_RETRIES);
|
||||
let mut builder = nym_http_api_client::ClientBuilder::new_with_urls(nym_api_urls.clone())?
|
||||
let mut builder = nym_http_api_client::ClientBuilder::new_with_urls(nym_api_urls.clone())
|
||||
.with_retries(retry_count)
|
||||
.with_bincode();
|
||||
|
||||
|
||||
@@ -296,9 +296,6 @@ impl std::error::Error for ReqwestErrorWrapper {}
|
||||
#[derive(Debug, Error)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum HttpClientError {
|
||||
#[error("did not provide any valid client URLs")]
|
||||
NoUrlsProvided,
|
||||
|
||||
#[error("failed to construct inner reqwest client: {source}")]
|
||||
ReqwestBuildError {
|
||||
#[source]
|
||||
@@ -585,29 +582,24 @@ impl ClientBuilder {
|
||||
Self::new(alt)
|
||||
} else {
|
||||
let url = url.to_url()?;
|
||||
Self::new_with_urls(vec![url])
|
||||
Ok(Self::new_with_urls(vec![url]))
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a client builder from network details with sensible defaults
|
||||
#[cfg(feature = "network-defaults")]
|
||||
// deprecating function since it's not clear from its signature whether the client
|
||||
// would be constructed using `nym_api_urls` or `nym_vpn_api_urls`
|
||||
#[deprecated(note = "use explicit Self::new_with_fronted_urls instead")]
|
||||
pub fn from_network(
|
||||
network: &nym_network_defaults::NymNetworkDetails,
|
||||
) -> Result<Self, HttpClientError> {
|
||||
let urls = network.nym_api_urls.as_ref().cloned().unwrap_or_default();
|
||||
Self::new_with_fronted_urls(urls.clone())
|
||||
}
|
||||
|
||||
/// Create a client builder using the provided set of domain-fronted URLs
|
||||
#[cfg(feature = "network-defaults")]
|
||||
pub fn new_with_fronted_urls(
|
||||
urls: Vec<nym_network_defaults::ApiUrl>,
|
||||
) -> Result<Self, HttpClientError> {
|
||||
let urls = urls
|
||||
.into_iter()
|
||||
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)?;
|
||||
@@ -619,19 +611,15 @@ impl ClientBuilder {
|
||||
.iter()
|
||||
.map(|host| format!("https://{}", host))
|
||||
.collect();
|
||||
url = Url::new(api_url.url.clone(), Some(fronts)).map_err(|source| {
|
||||
HttpClientError::MalformedUrl {
|
||||
raw: api_url.url.clone(),
|
||||
source,
|
||||
}
|
||||
})?;
|
||||
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)?;
|
||||
let mut builder = Self::new_with_urls(urls);
|
||||
|
||||
// Enable domain fronting by default (on retry)
|
||||
#[cfg(feature = "tunneling")]
|
||||
@@ -643,11 +631,7 @@ impl ClientBuilder {
|
||||
}
|
||||
|
||||
/// Constructs a new http `ClientBuilder` from a valid url.
|
||||
pub fn new_with_urls(urls: Vec<Url>) -> Result<Self, HttpClientError> {
|
||||
if urls.is_empty() {
|
||||
return Err(HttpClientError::NoUrlsProvided);
|
||||
}
|
||||
|
||||
pub fn new_with_urls(urls: Vec<Url>) -> Self {
|
||||
let urls = Self::check_urls(urls);
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
@@ -656,7 +640,7 @@ impl ClientBuilder {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let reqwest_client_builder = default_builder();
|
||||
|
||||
Ok(ClientBuilder {
|
||||
ClientBuilder {
|
||||
urls,
|
||||
timeout: None,
|
||||
custom_user_agent: false,
|
||||
@@ -667,7 +651,7 @@ impl ClientBuilder {
|
||||
|
||||
retry_limit: 0,
|
||||
serialization: SerializationFormat::Json,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Add an additional URL to the set usable by this constructed `Client`
|
||||
@@ -964,13 +948,13 @@ impl Client {
|
||||
|
||||
return (url.as_str(), url.front_str());
|
||||
} else {
|
||||
tracing::debug!(
|
||||
"Domain fronting is enabled, but no host_url is defined for current URL"
|
||||
warn!(
|
||||
"Domain fronting is enabled, but no host_url is defined! Domain fronting WILL NOT WORK"
|
||||
)
|
||||
}
|
||||
} else {
|
||||
tracing::debug!(
|
||||
"Domain fronting is enabled, but current URL has no front_hosts configured"
|
||||
warn!(
|
||||
"Domain fronting is enabled, but no front_url is defined! Domain fronting WILL NOT WORK"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,10 +21,6 @@ inventory::collect!(ConfigRecord);
|
||||
/// Returns the default builder with all registered configurations applied.
|
||||
pub fn default_builder() -> ReqwestClientBuilder {
|
||||
let mut b = ReqwestClientBuilder::new();
|
||||
|
||||
#[cfg(feature = "debug-inventory")]
|
||||
let mut test_client = ReqwestClientBuilder::new();
|
||||
|
||||
let mut records: Vec<&'static ConfigRecord> =
|
||||
inventory::iter::<ConfigRecord>.into_iter().collect();
|
||||
records.sort_by_key(|r| r.priority); // lower runs first
|
||||
@@ -39,10 +35,6 @@ pub fn default_builder() -> ReqwestClientBuilder {
|
||||
|
||||
for r in records {
|
||||
b = (r.apply)(b);
|
||||
#[cfg(feature = "debug-inventory")]
|
||||
{
|
||||
test_client = (r.apply)(test_client);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "debug-inventory")]
|
||||
@@ -55,7 +47,7 @@ pub fn default_builder() -> ReqwestClientBuilder {
|
||||
eprintln!("[HTTP-INVENTORY] Building test client to verify configuration...");
|
||||
|
||||
// Try to build a client to see if it works
|
||||
match test_client.build() {
|
||||
match b.try_clone().unwrap().build() {
|
||||
Ok(client) => {
|
||||
eprintln!("[HTTP-INVENTORY] ✓ Client built successfully");
|
||||
eprintln!("[HTTP-INVENTORY] Client debug info: {:#?}", client);
|
||||
|
||||
@@ -2,77 +2,77 @@ use super::*;
|
||||
|
||||
#[test]
|
||||
fn sanitizing_urls() {
|
||||
let base_url: Url = "http://api.test".parse().unwrap();
|
||||
let base_url: Url = "http://foomp.com".parse().unwrap();
|
||||
|
||||
// works with a full string
|
||||
assert_eq!(
|
||||
"http://api.test/foo/bar",
|
||||
"http://foomp.com/foo/bar",
|
||||
sanitize_url(&base_url, "/foo//bar/", NO_PARAMS).as_str()
|
||||
);
|
||||
|
||||
// (and leading slash doesn't matter)
|
||||
assert_eq!(
|
||||
"http://api.test/foo/bar",
|
||||
"http://foomp.com/foo/bar",
|
||||
sanitize_url(&base_url, "foo//bar/", NO_PARAMS).as_str()
|
||||
);
|
||||
|
||||
// works with 1 segment
|
||||
assert_eq!(
|
||||
"http://api.test/foo",
|
||||
"http://foomp.com/foo",
|
||||
sanitize_url(&base_url, &["foo"], NO_PARAMS).as_str()
|
||||
);
|
||||
|
||||
// works with 2 segments
|
||||
assert_eq!(
|
||||
"http://api.test/foo/bar",
|
||||
"http://foomp.com/foo/bar",
|
||||
sanitize_url(&base_url, &["foo", "bar"], NO_PARAMS).as_str()
|
||||
);
|
||||
|
||||
// works with leading slash
|
||||
assert_eq!(
|
||||
"http://api.test/foo",
|
||||
"http://foomp.com/foo",
|
||||
sanitize_url(&base_url, &["/foo"], NO_PARAMS).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://api.test/foo/bar",
|
||||
"http://foomp.com/foo/bar",
|
||||
sanitize_url(&base_url, &["/foo", "bar"], NO_PARAMS).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://api.test/foo/bar",
|
||||
"http://foomp.com/foo/bar",
|
||||
sanitize_url(&base_url, &["foo", "/bar"], NO_PARAMS).as_str()
|
||||
);
|
||||
|
||||
// works with trailing slash
|
||||
assert_eq!(
|
||||
"http://api.test/foo",
|
||||
"http://foomp.com/foo",
|
||||
sanitize_url(&base_url, &["foo/"], NO_PARAMS).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://api.test/foo/bar",
|
||||
"http://foomp.com/foo/bar",
|
||||
sanitize_url(&base_url, &["foo/", "bar"], NO_PARAMS).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://api.test/foo/bar",
|
||||
"http://foomp.com/foo/bar",
|
||||
sanitize_url(&base_url, &["foo", "bar/"], NO_PARAMS).as_str()
|
||||
);
|
||||
|
||||
// works with both leading and trailing slash
|
||||
assert_eq!(
|
||||
"http://api.test/foo",
|
||||
"http://foomp.com/foo",
|
||||
sanitize_url(&base_url, &["/foo/"], NO_PARAMS).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://api.test/foo/bar",
|
||||
"http://foomp.com/foo/bar",
|
||||
sanitize_url(&base_url, &["/foo/", "/bar/"], NO_PARAMS).as_str()
|
||||
);
|
||||
|
||||
// adds params
|
||||
assert_eq!(
|
||||
"http://api.test/foo/bar?foomp=baz",
|
||||
"http://foomp.com/foo/bar?foomp=baz",
|
||||
sanitize_url(&base_url, &["foo", "bar"], &[("foomp", "baz")]).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://api.test/foo/bar?arg1=val1&arg2=val2",
|
||||
"http://foomp.com/foo/bar?arg1=val1&arg2=val2",
|
||||
sanitize_url(
|
||||
&base_url,
|
||||
&["/foo/", "/bar/"],
|
||||
@@ -91,87 +91,83 @@ fn sanitizing_urls() {
|
||||
#[tokio::test]
|
||||
async fn api_client_retry() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let client = ClientBuilder::new_with_urls(vec![
|
||||
"http://broken.nym.test".parse()?, // This will fail
|
||||
"https://httpbin.org/status/200".parse()?, // This will succeed
|
||||
])?
|
||||
"http://broken.nym.badurl".parse()?,
|
||||
"http://example.com/".parse()?,
|
||||
])
|
||||
.with_retries(3)
|
||||
.build()?;
|
||||
|
||||
let req = client.create_get_request(&[], NO_PARAMS).unwrap();
|
||||
let req = client.create_get_request(&["/"], NO_PARAMS).unwrap();
|
||||
let resp = client.send(req).await?;
|
||||
|
||||
// The main test is that we successfully retried and switched to the working URL
|
||||
// We accept any response from the working endpoint since external services can be unreliable
|
||||
assert_eq!(
|
||||
client.current_url().as_str(),
|
||||
"https://httpbin.org/status/200"
|
||||
);
|
||||
assert_eq!(resp.status(), 200);
|
||||
|
||||
println!("Response status: {}", resp.status());
|
||||
// check that the url was updated
|
||||
assert_eq!(client.current_url().as_str(), "http://example.com/");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn host_updating() {
|
||||
let url = Url::new("http://nym-api1.test", None).unwrap();
|
||||
let url = Url::new("http://example.com", None).unwrap();
|
||||
let mut client = ClientBuilder::new(url).unwrap().build().unwrap();
|
||||
|
||||
// check that the url is set correctly
|
||||
let current_url = client.current_url();
|
||||
assert_eq!(current_url.as_str(), "http://nym-api1.test/");
|
||||
assert_eq!(current_url.as_str(), "http://example.com/");
|
||||
assert_eq!(current_url.front_str(), None);
|
||||
|
||||
// update the url
|
||||
client.update_host();
|
||||
|
||||
// check that the url is still the same since there is one URL
|
||||
assert_eq!(client.current_url().as_str(), "http://nym-api1.test/");
|
||||
assert_eq!(client.current_url().as_str(), "http://example.com/");
|
||||
|
||||
// =======================================
|
||||
// we rotate through urls when available
|
||||
|
||||
let new_urls = vec![
|
||||
Url::new("http://nym-api1.test", None).unwrap(),
|
||||
Url::new("http://nym-api2.test", None).unwrap(),
|
||||
Url::new("http://example.com", None).unwrap(),
|
||||
Url::new("http://example.org", None).unwrap(),
|
||||
];
|
||||
client.change_base_urls(new_urls);
|
||||
assert_eq!(client.current_url().as_str(), "http://nym-api1.test/");
|
||||
assert_eq!(client.current_url().as_str(), "http://example.com/");
|
||||
|
||||
client.update_host();
|
||||
|
||||
// check that the url got updated now that there are multiple URLs
|
||||
assert_eq!(client.current_url().as_str(), "http://nym-api2.test/");
|
||||
assert_eq!(client.current_url().as_str(), "http://example.org/");
|
||||
assert_eq!(client.current_url().front_str(), None);
|
||||
|
||||
client.update_host();
|
||||
assert_eq!(client.current_url().as_str(), "http://nym-api1.test/");
|
||||
assert_eq!(client.current_url().as_str(), "http://example.com/");
|
||||
|
||||
// =======================================
|
||||
// we rotate through urls when available if fronting is disabled
|
||||
|
||||
let new_urls = vec![
|
||||
Url::new(
|
||||
"http://nym-api1.test",
|
||||
Some(vec!["http://cdn1.test", "http://cdn2.test"]),
|
||||
"http://example.com",
|
||||
Some(vec!["http://front1.com", "http://front2.com"]),
|
||||
)
|
||||
.unwrap(),
|
||||
Url::new("http://nym-api2.test", None).unwrap(),
|
||||
Url::new("http://example.org", None).unwrap(),
|
||||
];
|
||||
client.change_base_urls(new_urls);
|
||||
|
||||
assert_eq!(client.current_url().as_str(), "http://nym-api1.test/");
|
||||
assert_eq!(client.current_url().as_str(), "http://example.com/");
|
||||
|
||||
client.update_host();
|
||||
|
||||
// check that the url got updated now that there are multiple URLs
|
||||
assert_eq!(client.current_url().as_str(), "http://nym-api2.test/");
|
||||
assert_eq!(client.current_url().as_str(), "http://example.org/");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "tunneling")]
|
||||
fn fronted_host_updating() {
|
||||
let url = Url::new("http://nym-api.test", Some(vec!["http://cdn1.test"])).unwrap();
|
||||
let url = Url::new("http://example.com", Some(vec!["http://front1.com"])).unwrap();
|
||||
let mut client = ClientBuilder::new(url)
|
||||
.unwrap()
|
||||
.with_fronting(crate::fronted::FrontPolicy::Always)
|
||||
@@ -180,103 +176,46 @@ fn fronted_host_updating() {
|
||||
|
||||
// check that the url is set correctly
|
||||
let current_url = client.current_url();
|
||||
assert_eq!(current_url.as_str(), "http://nym-api.test/");
|
||||
assert_eq!(current_url.front_str(), Some("cdn1.test"));
|
||||
assert_eq!(current_url.as_str(), "http://example.com/");
|
||||
assert_eq!(current_url.front_str(), Some("front1.com"));
|
||||
|
||||
// update the url
|
||||
client.update_host();
|
||||
|
||||
// check that the url is still the same since there is one URL and one front
|
||||
let current_url = client.current_url();
|
||||
assert_eq!(current_url.as_str(), "http://nym-api.test/");
|
||||
assert_eq!(current_url.front_str(), Some("cdn1.test"));
|
||||
assert_eq!(current_url.as_str(), "http://example.com/");
|
||||
assert_eq!(current_url.front_str(), Some("front1.com"));
|
||||
|
||||
// =======================================
|
||||
// we rotate through front urls when available if fronting is enabled
|
||||
|
||||
let new_urls = vec![
|
||||
Url::new(
|
||||
"http://nym-api.test",
|
||||
Some(vec!["http://cdn1.test", "http://cdn2.test"]),
|
||||
"http://example.com",
|
||||
Some(vec!["http://front1.com", "http://front2.com"]),
|
||||
)
|
||||
.unwrap(),
|
||||
Url::new("http://nym-api2.test", None).unwrap(),
|
||||
Url::new("http://example.org", None).unwrap(),
|
||||
];
|
||||
client.change_base_urls(new_urls);
|
||||
|
||||
let current_url = client.current_url();
|
||||
assert_eq!(current_url.as_str(), "http://nym-api.test/");
|
||||
assert_eq!(current_url.front_str(), Some("cdn1.test"));
|
||||
assert_eq!(current_url.as_str(), "http://example.com/");
|
||||
assert_eq!(current_url.front_str(), Some("front1.com"));
|
||||
|
||||
// update the url - this should keep the same host but change the front
|
||||
client.update_host();
|
||||
|
||||
let current_url = client.current_url();
|
||||
// check that the url is still the same since there is one URL
|
||||
assert_eq!(current_url.as_str(), "http://nym-api.test/");
|
||||
assert_eq!(current_url.front_str(), Some("cdn2.test"));
|
||||
assert_eq!(current_url.as_str(), "http://example.com/");
|
||||
assert_eq!(current_url.front_str(), Some("front2.com"));
|
||||
|
||||
// update the url - this should wrap around to the first front as the second url is not fronted
|
||||
client.update_host();
|
||||
|
||||
let current_url = client.current_url();
|
||||
assert_eq!(current_url.as_str(), "http://nym-api.test/");
|
||||
assert_eq!(current_url.front_str(), Some("cdn1.test"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "network-defaults")]
|
||||
fn from_network_configures_multiple_urls_and_retries() {
|
||||
use nym_network_defaults::{ApiUrl, NymNetworkDetails};
|
||||
|
||||
// Create network details with multiple URLs and fronting
|
||||
let mut network_details = NymNetworkDetails::new_empty();
|
||||
network_details.nym_api_urls = Some(vec![
|
||||
ApiUrl {
|
||||
url: "https://validator.nymtech.net/api/".to_string(),
|
||||
front_hosts: None,
|
||||
},
|
||||
ApiUrl {
|
||||
url: "https://nym-frontdoor.vercel.app/api/".to_string(),
|
||||
front_hosts: Some(vec!["vercel.app".to_string(), "vercel.com".to_string()]),
|
||||
},
|
||||
ApiUrl {
|
||||
url: "https://nym-frontdoor.global.ssl.fastly.net/api/".to_string(),
|
||||
front_hosts: Some(vec!["yelp.global.ssl.fastly.net".to_string()]),
|
||||
},
|
||||
]);
|
||||
|
||||
// Build client from network details
|
||||
let client = ClientBuilder::new_with_fronted_urls(
|
||||
network_details.nym_api_urls.clone().unwrap_or_default(),
|
||||
)
|
||||
.expect("Failed to create client from network")
|
||||
.build()
|
||||
.expect("Failed to build client");
|
||||
|
||||
// Verify all URLs were configured
|
||||
assert_eq!(
|
||||
client.base_urls().len(),
|
||||
3,
|
||||
"Expected 3 URLs to be configured from network details"
|
||||
);
|
||||
|
||||
// Verify the URLs have fronting configured where appropriate
|
||||
assert_eq!(
|
||||
client.base_urls()[0].as_str(),
|
||||
"https://validator.nymtech.net/api/"
|
||||
);
|
||||
assert!(client.base_urls()[0].front_str().is_none());
|
||||
|
||||
assert_eq!(
|
||||
client.base_urls()[1].as_str(),
|
||||
"https://nym-frontdoor.vercel.app/api/"
|
||||
);
|
||||
assert!(client.base_urls()[1].front_str().is_some());
|
||||
|
||||
assert_eq!(
|
||||
client.base_urls()[2].as_str(),
|
||||
"https://nym-frontdoor.global.ssl.fastly.net/api/"
|
||||
);
|
||||
assert!(client.base_urls()[2].front_str().is_some());
|
||||
assert_eq!(current_url.as_str(), "http://example.com/");
|
||||
assert_eq!(current_url.front_str(), Some("front1.com"));
|
||||
}
|
||||
|
||||
@@ -124,8 +124,6 @@ impl NymNetworkDetails {
|
||||
}
|
||||
}
|
||||
|
||||
let nym_api = var(var_names::NYM_API).expect("nym api not set");
|
||||
|
||||
NymNetworkDetails::new_empty()
|
||||
.with_network_name(var(var_names::NETWORK_NAME).expect("network name not set"))
|
||||
.with_bech32_account_prefix(
|
||||
@@ -151,7 +149,7 @@ impl NymNetworkDetails {
|
||||
})
|
||||
.with_additional_validator_endpoint(ValidatorDetails::new(
|
||||
var(var_names::NYXD).expect("nyxd validator not set"),
|
||||
Some(nym_api.clone()),
|
||||
Some(var(var_names::NYM_API).expect("nym api not set")),
|
||||
get_optional_env(var_names::NYXD_WEBSOCKET),
|
||||
))
|
||||
.with_mixnet_contract(get_optional_env(var_names::MIXNET_CONTRACT_ADDRESS))
|
||||
@@ -161,10 +159,6 @@ impl NymNetworkDetails {
|
||||
.with_multisig_contract(get_optional_env(var_names::MULTISIG_CONTRACT_ADDRESS))
|
||||
.with_coconut_dkg_contract(get_optional_env(var_names::COCONUT_DKG_CONTRACT_ADDRESS))
|
||||
.with_nym_vpn_api_url(get_optional_env(var_names::NYM_VPN_API))
|
||||
.with_nym_api_urls(Some(vec![ApiUrl {
|
||||
url: nym_api,
|
||||
front_hosts: None,
|
||||
}]))
|
||||
}
|
||||
|
||||
pub fn new_mainnet() -> Self {
|
||||
@@ -354,12 +348,6 @@ impl NymNetworkDetails {
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_nym_api_urls(mut self, urls: Option<Vec<ApiUrl>>) -> Self {
|
||||
self.nym_api_urls = urls;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn nym_vpn_api_url(&self) -> Option<Url> {
|
||||
self.nym_vpn_api_url.as_ref().map(|url| {
|
||||
url.parse()
|
||||
|
||||
@@ -24,6 +24,6 @@ pub use crate::runtime_registry::RegistryAccessError;
|
||||
|
||||
/// Get or create a ShutdownTracker for SDK use.
|
||||
/// This provides automatic task management without requiring manual setup.
|
||||
pub fn create_sdk_shutdown_tracker() -> Result<ShutdownTracker, RegistryAccessError> {
|
||||
Ok(runtime_registry::RuntimeRegistry::create_sdk()?.shutdown_tracker_owned())
|
||||
pub fn get_sdk_shutdown_tracker() -> Result<ShutdownTracker, RegistryAccessError> {
|
||||
Ok(runtime_registry::RuntimeRegistry::get_or_create_sdk()?.shutdown_tracker_owned())
|
||||
}
|
||||
|
||||
@@ -19,45 +19,30 @@ pub(crate) struct RuntimeRegistry {
|
||||
pub enum RegistryAccessError {
|
||||
#[error("the runtime registry is poisoned")]
|
||||
Poisoned,
|
||||
|
||||
#[error("The SDK ShutdownManager already exists")]
|
||||
ExistingShutdownManager,
|
||||
|
||||
#[error("No existing SDK ShutdownManager")]
|
||||
MissingShutdownManager,
|
||||
}
|
||||
|
||||
impl RuntimeRegistry {
|
||||
/// Create a ShutdownManager for SDK use.
|
||||
/// Get or create a ShutdownManager for SDK use.
|
||||
/// This manager doesn't listen to OS signals, making it suitable for library use.
|
||||
/// This function overwrite any existing manager!
|
||||
pub(crate) fn create_sdk() -> Result<Arc<ShutdownManager>, RegistryAccessError> {
|
||||
let mut guard = REGISTRY
|
||||
.sdk_manager
|
||||
.write()
|
||||
.map_err(|_| RegistryAccessError::Poisoned)?;
|
||||
|
||||
Ok(guard
|
||||
.insert(Arc::new(
|
||||
ShutdownManager::new_without_signals().with_cancel_on_panic(),
|
||||
))
|
||||
.clone())
|
||||
}
|
||||
|
||||
/// Get the ShutdownManager for SDK use.
|
||||
/// This manager doesn't listen to OS signals, making it suitable for library use.
|
||||
/// Not yet used, but maybe in the future
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn get_sdk() -> Result<Arc<ShutdownManager>, RegistryAccessError> {
|
||||
pub(crate) fn get_or_create_sdk() -> Result<Arc<ShutdownManager>, RegistryAccessError> {
|
||||
let guard = REGISTRY
|
||||
.sdk_manager
|
||||
.read()
|
||||
.map_err(|_| RegistryAccessError::Poisoned)?;
|
||||
if let Some(manager) = guard.as_ref() {
|
||||
Ok(manager.clone())
|
||||
} else {
|
||||
Err(RegistryAccessError::MissingShutdownManager)
|
||||
return Ok(manager.clone());
|
||||
}
|
||||
drop(guard);
|
||||
|
||||
let mut guard = REGISTRY
|
||||
.sdk_manager
|
||||
.write()
|
||||
.map_err(|_| RegistryAccessError::Poisoned)?;
|
||||
Ok(guard
|
||||
.get_or_insert_with(|| {
|
||||
Arc::new(ShutdownManager::new_without_signals().with_cancel_on_panic())
|
||||
})
|
||||
.clone())
|
||||
}
|
||||
|
||||
/// Check if an SDK manager has been created.
|
||||
@@ -100,13 +85,10 @@ mod tests {
|
||||
|
||||
assert!(!RuntimeRegistry::has_sdk_manager().unwrap());
|
||||
|
||||
// Error if nothing was created
|
||||
assert!(RuntimeRegistry::get_sdk().is_err());
|
||||
|
||||
let manager1 = RuntimeRegistry::create_sdk().unwrap();
|
||||
let manager1 = RuntimeRegistry::get_or_create_sdk().unwrap();
|
||||
assert!(RuntimeRegistry::has_sdk_manager().unwrap());
|
||||
|
||||
let manager2 = RuntimeRegistry::get_sdk().unwrap();
|
||||
let manager2 = RuntimeRegistry::get_or_create_sdk().unwrap();
|
||||
// Should return the same instance
|
||||
assert!(Arc::ptr_eq(&manager1, &manager2));
|
||||
|
||||
|
||||
@@ -259,7 +259,7 @@ pub fn migrate(deps: DepsMut<'_>, env: Env, _msg: MigrateMsg) -> Result<Response
|
||||
set_build_information!(deps.storage)?;
|
||||
cw2::ensure_from_older_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
|
||||
|
||||
crate::queued_migrations::introduce_historical_epochs(deps, env)?;
|
||||
// crate::queued_migrations::introduce_historical_epochs(deps, env)?;
|
||||
|
||||
Ok(Response::new())
|
||||
}
|
||||
|
||||
+1
-2
@@ -15,7 +15,6 @@ Operators can use [Nym Bridge Configuration Tool](https://github.com/nymtech/nym
|
||||
<Steps>
|
||||
###### 1. Download [`quic_bridge_deployment.sh`](https://github.com/nymtech/nym/blob/develop/scripts/nym-node-setup/quic_bridge_deployment.sh) script
|
||||
- SSH to your server
|
||||
- **Run as root**
|
||||
- Download the script and make executable
|
||||
```sh
|
||||
wget https://raw.githubusercontent.com/nymtech/nym/refs/heads/develop/scripts/nym-node-setup/quic_bridge_deployment.sh && \
|
||||
@@ -27,7 +26,7 @@ chmod +x quic_bridge_deployment.sh
|
||||
- Optional: open `tmux` in case you will need to run another commands on the VPS
|
||||
- Run the script with a command `full_bridge_setup`
|
||||
```sh
|
||||
./quic_bridge_deployment.sh full_bridge_setup
|
||||
./nym-node-setup/quic_bridge_deployment.sh full_bridge_setup
|
||||
```
|
||||
|
||||
###### 3. Follow the interactive prompts
|
||||
|
||||
@@ -50,7 +50,7 @@ impl AuthClientMixnetListener {
|
||||
}
|
||||
}
|
||||
|
||||
async fn run(mut self) {
|
||||
async fn run(mut self) -> Self {
|
||||
let mixnet_cancel_token = self.mixnet_client.cancellation_token();
|
||||
self.shutdown_token.run_until_cancelled(async {
|
||||
loop {
|
||||
@@ -95,8 +95,12 @@ impl AuthClientMixnetListener {
|
||||
tracing::debug!("AuthClientMixnetListener is shutting down");
|
||||
}).await;
|
||||
|
||||
tracing::debug!("AuthClientMixnetListener: Disconnect mixnet client");
|
||||
if !mixnet_cancel_token.is_cancelled() {
|
||||
self
|
||||
}
|
||||
|
||||
// Disconnects the mixnet client and effectively drop itself, since it doesn't work without one, and reconnecting isn't supported
|
||||
pub async fn disconnect_mixnet_client(self) {
|
||||
if !self.mixnet_client.cancellation_token().is_cancelled() {
|
||||
self.mixnet_client.disconnect().await;
|
||||
}
|
||||
}
|
||||
@@ -124,7 +128,7 @@ pub struct AuthClientMixnetListenerHandle {
|
||||
message_sender: MixnetMessageInputSender,
|
||||
cancellation_token: CancellationToken,
|
||||
mixnet_cancellation_token: CancellationToken,
|
||||
handle: JoinHandle<()>,
|
||||
handle: JoinHandle<AuthClientMixnetListener>,
|
||||
}
|
||||
|
||||
impl AuthClientMixnetListenerHandle {
|
||||
@@ -144,8 +148,13 @@ impl AuthClientMixnetListenerHandle {
|
||||
// If shutdown was externally called, that call is a no-op
|
||||
// If we're only stopping this, it is very much needed
|
||||
self.cancellation_token.cancel();
|
||||
if let Err(e) = self.handle.await {
|
||||
tracing::error!("Error waiting for auth clients mixnet listener to stop: {e}")
|
||||
match self.handle.await {
|
||||
Ok(auth_client_mixnet_listener) => {
|
||||
auth_client_mixnet_listener.disconnect_mixnet_client().await;
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("Error waiting for auth clients mixnet listener to stop: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -765,7 +765,7 @@ async fn connect_exit(
|
||||
);
|
||||
// The IPR supports cancellation, but it's unused in the gateway probe
|
||||
let cancel_token = CancellationToken::new();
|
||||
let mut ipr_client = IprClientConnect::new(mixnet_client, cancel_token);
|
||||
let mut ipr_client = IprClientConnect::new(mixnet_client, cancel_token).await;
|
||||
|
||||
let maybe_ip_pair = ipr_client.connect(exit_router_address).await;
|
||||
let mixnet_client = ipr_client.into_mixnet_client();
|
||||
|
||||
@@ -43,7 +43,7 @@ pub struct IprClientConnect {
|
||||
}
|
||||
|
||||
impl IprClientConnect {
|
||||
pub fn new(mixnet_client: MixnetClient, cancel_token: CancellationToken) -> Self {
|
||||
pub async fn new(mixnet_client: MixnetClient, cancel_token: CancellationToken) -> Self {
|
||||
Self {
|
||||
mixnet_client,
|
||||
connected: ConnectionState::Disconnected,
|
||||
|
||||
@@ -57,7 +57,7 @@ async fn run(
|
||||
.clone()
|
||||
.expect("rust sdk mainnet default missing api_url");
|
||||
|
||||
let nym_api = nym_http_api_client::ClientBuilder::new_with_urls(vec![default_api_url.into()])?
|
||||
let nym_api = nym_http_api_client::ClientBuilder::new_with_urls(vec![default_api_url.into()])
|
||||
.no_hickory_dns()
|
||||
.with_timeout(nym_api_client_timeout)
|
||||
.build()?;
|
||||
|
||||
@@ -98,7 +98,7 @@ impl Monitor {
|
||||
.expect("rust sdk mainnet default missing api_url");
|
||||
|
||||
let nym_api =
|
||||
nym_http_api_client::ClientBuilder::new_with_urls(vec![default_api_url.into()])?
|
||||
nym_http_api_client::ClientBuilder::new_with_urls(vec![default_api_url.into()])
|
||||
.no_hickory_dns()
|
||||
.with_timeout(self.nym_api_client_timeout)
|
||||
.build()?;
|
||||
|
||||
@@ -17,7 +17,6 @@ thiserror.workspace = true
|
||||
tokio.workspace = true
|
||||
tokio-util.workspace = true
|
||||
tracing.workspace = true
|
||||
typed-builder.workspace = true
|
||||
url.workspace = true
|
||||
|
||||
nym-authenticator-client = { path = "../nym-authenticator-client" }
|
||||
|
||||
@@ -15,12 +15,10 @@ use nym_sdk::{
|
||||
use std::os::fd::RawFd;
|
||||
use std::{path::PathBuf, sync::Arc, time::Duration};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use typed_builder::TypedBuilder;
|
||||
|
||||
use crate::error::RegistrationClientError;
|
||||
|
||||
const VPN_AVERAGE_PACKET_DELAY: Duration = Duration::from_millis(15);
|
||||
const MIXNET_CLIENT_STARTUP_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct NymNodeWithKeys {
|
||||
@@ -28,14 +26,11 @@ pub struct NymNodeWithKeys {
|
||||
pub keys: Arc<KeyPair>,
|
||||
}
|
||||
|
||||
#[derive(TypedBuilder)]
|
||||
pub struct BuilderConfig {
|
||||
pub entry_node: NymNodeWithKeys,
|
||||
pub exit_node: NymNodeWithKeys,
|
||||
pub data_path: Option<PathBuf>,
|
||||
pub mixnet_client_config: MixnetClientConfig,
|
||||
#[builder(default = MIXNET_CLIENT_STARTUP_TIMEOUT)]
|
||||
pub mixnet_client_startup_timeout: Duration,
|
||||
pub two_hops: bool,
|
||||
pub user_agent: UserAgent,
|
||||
pub custom_topology_provider: Box<dyn TopologyProvider + Send + Sync>,
|
||||
@@ -61,6 +56,53 @@ pub struct MixnetClientConfig {
|
||||
}
|
||||
|
||||
impl BuilderConfig {
|
||||
/// Creates a new BuilderConfig with all required parameters.
|
||||
///
|
||||
/// However, consider using `BuilderConfig::builder()` instead.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
entry_node: NymNodeWithKeys,
|
||||
exit_node: NymNodeWithKeys,
|
||||
data_path: Option<PathBuf>,
|
||||
mixnet_client_config: MixnetClientConfig,
|
||||
two_hops: bool,
|
||||
user_agent: UserAgent,
|
||||
custom_topology_provider: Box<dyn TopologyProvider + Send + Sync>,
|
||||
network_env: NymNetworkDetails,
|
||||
cancel_token: CancellationToken,
|
||||
#[cfg(unix)] connection_fd_callback: Arc<dyn Fn(RawFd) + Send + Sync>,
|
||||
) -> Self {
|
||||
Self {
|
||||
entry_node,
|
||||
exit_node,
|
||||
data_path,
|
||||
mixnet_client_config,
|
||||
two_hops,
|
||||
user_agent,
|
||||
custom_topology_provider,
|
||||
network_env,
|
||||
cancel_token,
|
||||
#[cfg(unix)]
|
||||
connection_fd_callback,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a builder for BuilderConfig
|
||||
///
|
||||
/// This is the preferred way to construct a BuilderConfig.
|
||||
///
|
||||
/// # Example
|
||||
/// ```ignore
|
||||
/// let config = BuilderConfig::builder()
|
||||
/// .entry_node(entry)
|
||||
/// .exit_node(exit)
|
||||
/// .user_agent(agent)
|
||||
/// .build()?;
|
||||
/// ```
|
||||
pub fn builder() -> BuilderConfigBuilder {
|
||||
BuilderConfigBuilder::default()
|
||||
}
|
||||
|
||||
pub fn mixnet_client_debug_config(&self) -> DebugConfig {
|
||||
if self.two_hops {
|
||||
two_hop_debug_config(&self.mixnet_client_config)
|
||||
@@ -212,6 +254,144 @@ fn true_to_disabled(val: bool) -> &'static str {
|
||||
if val { "disabled" } else { "enabled" }
|
||||
}
|
||||
|
||||
/// Error type for BuilderConfig validation
|
||||
#[derive(Debug, Clone, thiserror::Error)]
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
pub enum BuilderConfigError {
|
||||
#[error("entry_node is required")]
|
||||
MissingEntryNode,
|
||||
#[error("exit_node is required")]
|
||||
MissingExitNode,
|
||||
#[error("mixnet_client_config is required")]
|
||||
MissingMixnetClientConfig,
|
||||
#[error("user_agent is required")]
|
||||
MissingUserAgent,
|
||||
#[error("custom_topology_provider is required")]
|
||||
MissingTopologyProvider,
|
||||
#[error("network_env is required")]
|
||||
MissingNetworkEnv,
|
||||
#[error("cancel_token is required")]
|
||||
MissingCancelToken,
|
||||
#[cfg(unix)]
|
||||
#[error("connection_fd_callback is required")]
|
||||
MissingConnectionFdCallback,
|
||||
}
|
||||
|
||||
/// Builder for `BuilderConfig`
|
||||
///
|
||||
/// This provides a more convenient way to construct a `BuilderConfig` compared to the
|
||||
/// `new()` constructor with many arguments.
|
||||
#[derive(Default)]
|
||||
pub struct BuilderConfigBuilder {
|
||||
entry_node: Option<NymNodeWithKeys>,
|
||||
exit_node: Option<NymNodeWithKeys>,
|
||||
data_path: Option<PathBuf>,
|
||||
mixnet_client_config: Option<MixnetClientConfig>,
|
||||
two_hops: bool,
|
||||
user_agent: Option<UserAgent>,
|
||||
custom_topology_provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
|
||||
network_env: Option<NymNetworkDetails>,
|
||||
cancel_token: Option<CancellationToken>,
|
||||
#[cfg(unix)]
|
||||
connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
|
||||
}
|
||||
|
||||
impl BuilderConfigBuilder {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn entry_node(mut self, entry_node: NymNodeWithKeys) -> Self {
|
||||
self.entry_node = Some(entry_node);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn exit_node(mut self, exit_node: NymNodeWithKeys) -> Self {
|
||||
self.exit_node = Some(exit_node);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn data_path(mut self, data_path: Option<PathBuf>) -> Self {
|
||||
self.data_path = data_path;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn mixnet_client_config(mut self, mixnet_client_config: MixnetClientConfig) -> Self {
|
||||
self.mixnet_client_config = Some(mixnet_client_config);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn two_hops(mut self, two_hops: bool) -> Self {
|
||||
self.two_hops = two_hops;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn user_agent(mut self, user_agent: UserAgent) -> Self {
|
||||
self.user_agent = Some(user_agent);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn custom_topology_provider(
|
||||
mut self,
|
||||
custom_topology_provider: Box<dyn TopologyProvider + Send + Sync>,
|
||||
) -> Self {
|
||||
self.custom_topology_provider = Some(custom_topology_provider);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn network_env(mut self, network_env: NymNetworkDetails) -> Self {
|
||||
self.network_env = Some(network_env);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn cancel_token(mut self, cancel_token: CancellationToken) -> Self {
|
||||
self.cancel_token = Some(cancel_token);
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn connection_fd_callback(
|
||||
mut self,
|
||||
connection_fd_callback: Arc<dyn Fn(RawFd) + Send + Sync>,
|
||||
) -> Self {
|
||||
self.connection_fd_callback = Some(connection_fd_callback);
|
||||
self
|
||||
}
|
||||
|
||||
/// Builds the `BuilderConfig`.
|
||||
///
|
||||
/// Returns an error if any required field is missing.
|
||||
pub fn build(self) -> Result<BuilderConfig, BuilderConfigError> {
|
||||
Ok(BuilderConfig {
|
||||
entry_node: self
|
||||
.entry_node
|
||||
.ok_or(BuilderConfigError::MissingEntryNode)?,
|
||||
exit_node: self.exit_node.ok_or(BuilderConfigError::MissingExitNode)?,
|
||||
data_path: self.data_path,
|
||||
mixnet_client_config: self
|
||||
.mixnet_client_config
|
||||
.ok_or(BuilderConfigError::MissingMixnetClientConfig)?,
|
||||
two_hops: self.two_hops,
|
||||
user_agent: self
|
||||
.user_agent
|
||||
.ok_or(BuilderConfigError::MissingUserAgent)?,
|
||||
custom_topology_provider: self
|
||||
.custom_topology_provider
|
||||
.ok_or(BuilderConfigError::MissingTopologyProvider)?,
|
||||
network_env: self
|
||||
.network_env
|
||||
.ok_or(BuilderConfigError::MissingNetworkEnv)?,
|
||||
cancel_token: self
|
||||
.cancel_token
|
||||
.ok_or(BuilderConfigError::MissingCancelToken)?,
|
||||
#[cfg(unix)]
|
||||
connection_fd_callback: self
|
||||
.connection_fd_callback
|
||||
.ok_or(BuilderConfigError::MissingConnectionFdCallback)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -224,4 +404,54 @@ mod tests {
|
||||
assert_eq!(config.min_mixnode_performance, None);
|
||||
assert_eq!(config.min_gateway_performance, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_builder_config_builder_fails_without_required_fields() {
|
||||
// Building without any fields should fail with specific error
|
||||
let result = BuilderConfig::builder().build();
|
||||
assert!(result.is_err());
|
||||
match result {
|
||||
Err(BuilderConfigError::MissingEntryNode) => (), // Expected
|
||||
Err(e) => panic!("Expected MissingEntryNode, got: {}", e),
|
||||
Ok(_) => panic!("Expected error, got Ok"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_builder_config_builder_validates_all_required_fields() {
|
||||
// Test that each required field is validated
|
||||
let result = BuilderConfig::builder().build();
|
||||
assert!(result.is_err());
|
||||
|
||||
// Short-circuits at first missing field, so we just verify it's one of the expected errors
|
||||
#[allow(unreachable_patterns)] // All variants are covered, but keeping catch-all for safety
|
||||
match result {
|
||||
Err(BuilderConfigError::MissingEntryNode)
|
||||
| Err(BuilderConfigError::MissingExitNode)
|
||||
| Err(BuilderConfigError::MissingMixnetClientConfig)
|
||||
| Err(BuilderConfigError::MissingUserAgent)
|
||||
| Err(BuilderConfigError::MissingTopologyProvider)
|
||||
| Err(BuilderConfigError::MissingNetworkEnv)
|
||||
| Err(BuilderConfigError::MissingCancelToken) => (),
|
||||
#[cfg(unix)]
|
||||
Err(BuilderConfigError::MissingConnectionFdCallback) => (),
|
||||
Err(e) => panic!("Unexpected error: {}", e),
|
||||
Ok(_) => panic!("Expected validation error, got Ok"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_builder_config_builder_method_chaining() {
|
||||
// Test that builder methods chain properly and return Self
|
||||
let builder = BuilderConfig::builder();
|
||||
|
||||
// Verify the builder returns itself for chaining
|
||||
let builder = builder.two_hops(true);
|
||||
let builder = builder.two_hops(false);
|
||||
let builder = builder.data_path(None);
|
||||
|
||||
// Builder should still fail because required fields are missing
|
||||
let result = builder.build();
|
||||
assert!(result.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,12 +12,15 @@ use nym_validator_client::{
|
||||
QueryHttpRpcNyxdClient,
|
||||
nyxd::{Config as NyxdClientConfig, NyxdClient},
|
||||
};
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::{RegistrationClient, config::RegistrationClientConfig, error::RegistrationClientError};
|
||||
use config::BuilderConfig;
|
||||
|
||||
pub(crate) mod config;
|
||||
|
||||
pub(crate) const MIXNET_CLIENT_STARTUP_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
|
||||
pub struct RegistrationClientBuilder {
|
||||
pub config: BuilderConfig,
|
||||
}
|
||||
@@ -46,7 +49,7 @@ impl RegistrationClientBuilder {
|
||||
let builder = MixnetClientBuilder::new_with_storage(mixnet_client_storage)
|
||||
.event_tx(EventSender(event_tx));
|
||||
let mixnet_client = tokio::time::timeout(
|
||||
self.config.mixnet_client_startup_timeout,
|
||||
MIXNET_CLIENT_STARTUP_TIMEOUT,
|
||||
self.config.build_and_connect_mixnet_client(builder),
|
||||
)
|
||||
.await??;
|
||||
@@ -56,7 +59,7 @@ impl RegistrationClientBuilder {
|
||||
} else {
|
||||
let builder = MixnetClientBuilder::new_ephemeral().event_tx(EventSender(event_tx));
|
||||
let mixnet_client = tokio::time::timeout(
|
||||
self.config.mixnet_client_startup_timeout,
|
||||
MIXNET_CLIENT_STARTUP_TIMEOUT,
|
||||
self.config.build_and_connect_mixnet_client(builder),
|
||||
)
|
||||
.await??;
|
||||
|
||||
@@ -9,7 +9,6 @@ use nym_credentials_interface::TicketType;
|
||||
use nym_ip_packet_client::IprClientConnect;
|
||||
use nym_registration_common::AssignedAddresses;
|
||||
use nym_sdk::mixnet::{EventReceiver, MixnetClient, Recipient};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::config::RegistrationClientConfig;
|
||||
|
||||
@@ -35,49 +34,23 @@ pub struct RegistrationClient {
|
||||
event_rx: EventReceiver,
|
||||
}
|
||||
|
||||
// Bundle of an actual error and the underlying mixnet client so it can be shutdown correctly if needed
|
||||
struct RegistrationError {
|
||||
mixnet_client: Option<MixnetClient>,
|
||||
source: crate::RegistrationClientError,
|
||||
}
|
||||
|
||||
impl RegistrationClient {
|
||||
async fn register_mix_exit(self) -> Result<RegistrationResult, RegistrationError> {
|
||||
async fn register_mix_exit(self) -> Result<RegistrationResult, RegistrationClientError> {
|
||||
let entry_mixnet_gateway_ip = self.config.entry.node.ip_address;
|
||||
|
||||
let exit_mixnet_gateway_ip = self.config.exit.node.ip_address;
|
||||
|
||||
let Some(ipr_address) = self.config.exit.node.ipr_address else {
|
||||
return Err(RegistrationError {
|
||||
mixnet_client: Some(self.mixnet_client),
|
||||
source: RegistrationClientError::NoIpPacketRouterAddress {
|
||||
node_id: self.config.exit.node.identity.to_base58_string(),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
let ipr_address = self.config.exit.node.ipr_address.ok_or(
|
||||
RegistrationClientError::NoIpPacketRouterAddress {
|
||||
node_id: self.config.exit.node.identity.to_base58_string(),
|
||||
},
|
||||
)?;
|
||||
let mut ipr_client =
|
||||
IprClientConnect::new(self.mixnet_client, self.cancel_token.child_token());
|
||||
|
||||
let interface_addresses = match self
|
||||
.cancel_token
|
||||
.run_until_cancelled(ipr_client.connect(ipr_address))
|
||||
IprClientConnect::new(self.mixnet_client, self.cancel_token.clone()).await;
|
||||
let interface_addresses = ipr_client
|
||||
.connect(ipr_address)
|
||||
.await
|
||||
{
|
||||
Some(Ok(addr)) => addr,
|
||||
Some(Err(e)) => {
|
||||
return Err(RegistrationError {
|
||||
mixnet_client: Some(ipr_client.into_mixnet_client()),
|
||||
source: RegistrationClientError::ConnectToIpPacketRouter(e),
|
||||
});
|
||||
}
|
||||
None => {
|
||||
return Err(RegistrationError {
|
||||
mixnet_client: Some(ipr_client.into_mixnet_client()),
|
||||
source: RegistrationClientError::Cancelled,
|
||||
});
|
||||
}
|
||||
};
|
||||
.map_err(RegistrationClientError::ConnectToIpPacketRouter)?;
|
||||
|
||||
Ok(RegistrationResult::Mixnet(Box::new(
|
||||
MixnetRegistrationResult {
|
||||
@@ -94,24 +67,18 @@ impl RegistrationClient {
|
||||
)))
|
||||
}
|
||||
|
||||
async fn register_wg(self) -> Result<RegistrationResult, RegistrationError> {
|
||||
let Some(entry_auth_address) = self.config.entry.node.authenticator_address else {
|
||||
return Err(RegistrationError {
|
||||
mixnet_client: Some(self.mixnet_client),
|
||||
source: RegistrationClientError::AuthenticationNotPossible {
|
||||
node_id: self.config.entry.node.identity.to_base58_string(),
|
||||
},
|
||||
});
|
||||
};
|
||||
async fn register_wg(self) -> Result<RegistrationResult, RegistrationClientError> {
|
||||
let entry_auth_address = self.config.entry.node.authenticator_address.ok_or(
|
||||
RegistrationClientError::AuthenticationNotPossible {
|
||||
node_id: self.config.entry.node.identity.to_base58_string(),
|
||||
},
|
||||
)?;
|
||||
|
||||
let Some(exit_auth_address) = self.config.exit.node.authenticator_address else {
|
||||
return Err(RegistrationError {
|
||||
mixnet_client: Some(self.mixnet_client),
|
||||
source: RegistrationClientError::AuthenticationNotPossible {
|
||||
node_id: self.config.exit.node.identity.to_base58_string(),
|
||||
},
|
||||
});
|
||||
};
|
||||
let exit_auth_address = self.config.exit.node.authenticator_address.ok_or(
|
||||
RegistrationClientError::AuthenticationNotPossible {
|
||||
node_id: self.config.exit.node.identity.to_base58_string(),
|
||||
},
|
||||
)?;
|
||||
|
||||
let entry_version = self.config.entry.node.version;
|
||||
tracing::debug!("Entry gateway version: {entry_version}");
|
||||
@@ -120,10 +87,8 @@ impl RegistrationClient {
|
||||
|
||||
// Start the auth client mixnet listener, which will listen for incoming messages from the
|
||||
// mixnet and rebroadcast them to the auth clients.
|
||||
// From this point on, we don't need to care about the mixnet client anymore
|
||||
let mixnet_listener =
|
||||
AuthClientMixnetListener::new(self.mixnet_client, self.cancel_token.child_token())
|
||||
.start();
|
||||
AuthClientMixnetListener::new(self.mixnet_client, self.cancel_token.clone()).start();
|
||||
|
||||
let mut entry_auth_client = AuthenticatorClient::new(
|
||||
mixnet_listener.subscribe(),
|
||||
@@ -150,33 +115,24 @@ impl RegistrationClient {
|
||||
let exit_fut = exit_auth_client
|
||||
.register_wireguard(&*self.bandwidth_controller, TicketType::V1WireguardExit);
|
||||
|
||||
let (entry, exit) = Box::pin(
|
||||
self.cancel_token
|
||||
.run_until_cancelled(async { tokio::join!(entry_fut, exit_fut) }),
|
||||
)
|
||||
.await
|
||||
.ok_or(RegistrationError {
|
||||
mixnet_client: None,
|
||||
source: RegistrationClientError::Cancelled,
|
||||
})?;
|
||||
let (entry, exit) = Box::pin(async { tokio::join!(entry_fut, exit_fut) }).await;
|
||||
|
||||
let entry = entry.map_err(|source| RegistrationError {
|
||||
mixnet_client: None,
|
||||
source: RegistrationClientError::EntryGatewayRegisterWireguard {
|
||||
gateway_id: self.config.entry.node.identity.to_base58_string(),
|
||||
authenticator_address: Box::new(entry_auth_address),
|
||||
source: Box::new(source),
|
||||
},
|
||||
})?;
|
||||
|
||||
let exit = exit.map_err(|source| RegistrationError {
|
||||
mixnet_client: None,
|
||||
source: RegistrationClientError::EntryGatewayRegisterWireguard {
|
||||
gateway_id: self.config.exit.node.identity.to_base58_string(),
|
||||
authenticator_address: Box::new(exit_auth_address),
|
||||
source: Box::new(source),
|
||||
},
|
||||
})?;
|
||||
let entry =
|
||||
entry.map_err(
|
||||
|source| RegistrationClientError::EntryGatewayRegisterWireguard {
|
||||
gateway_id: self.config.entry.node.identity.to_base58_string(),
|
||||
authenticator_address: Box::new(entry_auth_address),
|
||||
source: Box::new(source),
|
||||
},
|
||||
)?;
|
||||
let exit =
|
||||
exit.map_err(
|
||||
|source| RegistrationClientError::ExitGatewayRegisterWireguard {
|
||||
gateway_id: self.config.exit.node.identity.to_base58_string(),
|
||||
authenticator_address: Box::new(exit_auth_address),
|
||||
source: Box::new(source),
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(RegistrationResult::Wireguard(Box::new(
|
||||
WireguardRegistrationResult {
|
||||
@@ -191,23 +147,16 @@ impl RegistrationClient {
|
||||
}
|
||||
|
||||
pub async fn register(self) -> Result<RegistrationResult, RegistrationClientError> {
|
||||
let registration_result = if self.config.two_hops {
|
||||
self.register_wg().await
|
||||
} else {
|
||||
self.register_mix_exit().await
|
||||
};
|
||||
|
||||
// If we failed to register, and we were the owner of the mixnet client, shut it down
|
||||
match registration_result {
|
||||
Ok(result) => Ok(result),
|
||||
Err(error) => {
|
||||
debug!("Registration failed");
|
||||
if let Some(mixnet_client) = error.mixnet_client {
|
||||
debug!("Shutting down mixnet client");
|
||||
mixnet_client.disconnect().await;
|
||||
self.cancel_token
|
||||
.clone()
|
||||
.run_until_cancelled(async {
|
||||
if self.config.two_hops {
|
||||
self.register_wg().await
|
||||
} else {
|
||||
self.register_mix_exit().await
|
||||
}
|
||||
Err(error.source)
|
||||
}
|
||||
}
|
||||
})
|
||||
.await
|
||||
.ok_or(RegistrationClientError::Cancelled)?
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -706,16 +706,6 @@ where
|
||||
.config
|
||||
.as_base_client_config(nyxd_endpoints, nym_api_endpoints.clone());
|
||||
|
||||
tracing::debug!(
|
||||
"SDK: Passing nym_api_urls to BaseClientBuilder (has {} nym_api_urls)",
|
||||
self.config
|
||||
.network_details
|
||||
.nym_api_urls
|
||||
.as_ref()
|
||||
.map(|urls| urls.len())
|
||||
.unwrap_or(0)
|
||||
);
|
||||
|
||||
let mut base_builder: BaseClientBuilder<_, _> =
|
||||
BaseClientBuilder::new(base_config, self.storage, self.dkg_query_client)
|
||||
.with_wait_for_gateway(self.wait_for_gateway)
|
||||
@@ -723,11 +713,6 @@ where
|
||||
.with_remember_me(&self.remember_me)
|
||||
.with_derivation_material(self.derivation_material);
|
||||
|
||||
// Add nym_api_urls if available in network_details
|
||||
if let Some(nym_api_urls) = self.config.network_details.nym_api_urls.clone() {
|
||||
base_builder = base_builder.with_nym_api_urls(nym_api_urls);
|
||||
}
|
||||
|
||||
if let Some(user_agent) = self.user_agent {
|
||||
base_builder = base_builder.with_user_agent(user_agent);
|
||||
}
|
||||
@@ -736,11 +721,15 @@ where
|
||||
base_builder = base_builder.with_topology_provider(topology_provider);
|
||||
}
|
||||
|
||||
// Use custom shutdown if provided, otherwise the sdk one will be used later down the line
|
||||
if let Some(shutdown_tracker) = self.custom_shutdown {
|
||||
base_builder = base_builder.with_shutdown(shutdown_tracker);
|
||||
}
|
||||
|
||||
// Use custom shutdown if provided, otherwise get from registry
|
||||
let shutdown_tracker = match self.custom_shutdown {
|
||||
Some(custom) => custom,
|
||||
None => {
|
||||
// Auto-create from registry for SDK use
|
||||
nym_task::get_sdk_shutdown_tracker()?
|
||||
}
|
||||
};
|
||||
base_builder = base_builder.with_shutdown(shutdown_tracker);
|
||||
if let Some(event_tx) = self.event_tx {
|
||||
base_builder = base_builder.with_event_tx(event_tx);
|
||||
}
|
||||
@@ -805,7 +794,7 @@ where
|
||||
client_output,
|
||||
client_state.clone(),
|
||||
nym_address,
|
||||
started_client.shutdown_handle.clone(),
|
||||
started_client.shutdown_handle.child_tracker(),
|
||||
packet_type,
|
||||
);
|
||||
|
||||
|
||||
@@ -226,8 +226,7 @@ mod tests {
|
||||
error!("{err}");
|
||||
// this is not an ideal way of checking it, but if test fails due to networking failures
|
||||
// it should be fine to progress
|
||||
let err_str = err.to_string();
|
||||
if err_str.contains("nym api") || err_str.contains("failed to connect") {
|
||||
if err.to_string().contains("nym api request failed") {
|
||||
return Ok(());
|
||||
}
|
||||
return Err(err);
|
||||
@@ -292,8 +291,7 @@ mod tests {
|
||||
error!("{err}");
|
||||
// this is not an ideal way of checking it, but if test fails due to networking failures
|
||||
// it should be fine to progress
|
||||
let err_str = err.to_string();
|
||||
if err_str.contains("nym api") || err_str.contains("failed to connect") {
|
||||
if err.to_string().contains("nym api request failed") {
|
||||
return Ok(());
|
||||
}
|
||||
return Err(err);
|
||||
|
||||
Reference in New Issue
Block a user