keep details provided by topology instead of just a url
This commit is contained in:
Generated
+3
@@ -5254,7 +5254,9 @@ dependencies = [
|
||||
"cosmrs",
|
||||
"nym-crypto",
|
||||
"nym-gateway-requests",
|
||||
"nym-topology",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"thiserror 2.0.12",
|
||||
"time",
|
||||
@@ -5872,6 +5874,7 @@ dependencies = [
|
||||
"nym-sphinx",
|
||||
"nym-statistics-common",
|
||||
"nym-task",
|
||||
"nym-topology",
|
||||
"nym-validator-client",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
|
||||
@@ -11,6 +11,7 @@ rust-version.workspace = true
|
||||
async-trait.workspace = true
|
||||
cosmrs.workspace = true
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json.workspace = true
|
||||
thiserror.workspace = true
|
||||
time.workspace = true
|
||||
tokio = { workspace = true, features = ["sync"] }
|
||||
@@ -20,6 +21,7 @@ zeroize = { workspace = true, features = ["zeroize_derive"] }
|
||||
|
||||
nym-crypto = { path = "../../crypto", features = ["asymmetric"] }
|
||||
nym-gateway-requests = { path = "../../gateway-requests" }
|
||||
nym-topology = { path = "../../topology", features = ["persistence"] }
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
|
||||
workspace = true
|
||||
|
||||
@@ -48,6 +48,6 @@ pub enum BadGateway {
|
||||
raw_listener: String,
|
||||
|
||||
#[source]
|
||||
source: url::ParseError,
|
||||
source: serde_json::Error,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -5,13 +5,13 @@ use crate::BadGateway;
|
||||
use cosmrs::AccountId;
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_gateway_requests::shared_key::{LegacySharedKeys, SharedGatewayKey, SharedSymmetricKey};
|
||||
use nym_topology::EntryDetails;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use time::OffsetDateTime;
|
||||
use url::Url;
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
|
||||
pub const REMOTE_GATEWAY_TYPE: &str = "remote";
|
||||
@@ -67,7 +67,7 @@ impl GatewayDetails {
|
||||
gateway_id: ed25519::PublicKey,
|
||||
shared_key: Arc<SharedGatewayKey>,
|
||||
gateway_owner_address: Option<AccountId>,
|
||||
gateway_listener: Url,
|
||||
gateway_listener: EntryDetails,
|
||||
) -> Self {
|
||||
GatewayDetails::Remote(RemoteGatewayDetails {
|
||||
gateway_id,
|
||||
@@ -164,8 +164,7 @@ pub struct RegisteredGateway {
|
||||
pub gateway_type: GatewayType,
|
||||
}
|
||||
|
||||
#[derive(Debug, Zeroize, ZeroizeOnDrop, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct RawRemoteGatewayDetails {
|
||||
pub gateway_id_bs58: String,
|
||||
pub derived_aes128_ctr_blake3_hmac_keys_bs58: Option<String>,
|
||||
@@ -174,6 +173,16 @@ pub struct RawRemoteGatewayDetails {
|
||||
pub gateway_listener: String,
|
||||
}
|
||||
|
||||
impl Zeroize for RawRemoteGatewayDetails {
|
||||
fn zeroize(&mut self) {
|
||||
self.gateway_id_bs58.zeroize();
|
||||
self.derived_aes128_ctr_blake3_hmac_keys_bs58.zeroize();
|
||||
self.derived_aes256_gcm_siv_key.zeroize();
|
||||
self.gateway_owner_address.zeroize();
|
||||
}
|
||||
}
|
||||
impl ZeroizeOnDrop for RawRemoteGatewayDetails {}
|
||||
|
||||
impl TryFrom<RawRemoteGatewayDetails> for RemoteGatewayDetails {
|
||||
type Error = BadGateway;
|
||||
|
||||
@@ -230,7 +239,7 @@ impl TryFrom<RawRemoteGatewayDetails> for RemoteGatewayDetails {
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let gateway_listener = Url::parse(&value.gateway_listener).map_err(|source| {
|
||||
let gateway_listener = serde_json::from_str(&value.gateway_listener).map_err(|source| {
|
||||
BadGateway::MalformedListener {
|
||||
gateway_id: value.gateway_id_bs58.clone(),
|
||||
raw_listener: value.gateway_listener.clone(),
|
||||
@@ -255,12 +264,16 @@ impl<'a> From<&'a RemoteGatewayDetails> for RawRemoteGatewayDetails {
|
||||
SharedGatewayKey::Legacy(key) => (Some(key.to_base58_string()), None),
|
||||
};
|
||||
|
||||
#[allow(clippy::expect_used)]
|
||||
let gateway_listener = serde_json::to_string(&value.gateway_listener)
|
||||
.expect("serialize for gateway details should never fail");
|
||||
|
||||
RawRemoteGatewayDetails {
|
||||
gateway_id_bs58: value.gateway_id.to_base58_string(),
|
||||
derived_aes128_ctr_blake3_hmac_keys_bs58,
|
||||
derived_aes256_gcm_siv_key,
|
||||
gateway_owner_address: value.gateway_owner_address.as_ref().map(|o| o.to_string()),
|
||||
gateway_listener: value.gateway_listener.to_string(),
|
||||
gateway_listener,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -273,7 +286,7 @@ pub struct RemoteGatewayDetails {
|
||||
|
||||
pub gateway_owner_address: Option<AccountId>,
|
||||
|
||||
pub gateway_listener: Url,
|
||||
pub gateway_listener: EntryDetails,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
||||
@@ -561,7 +561,7 @@ where
|
||||
.gateway_owner_address
|
||||
.as_ref()
|
||||
.map(|o| o.to_string()),
|
||||
details.gateway_listener.to_string(),
|
||||
details.gateway_listener,
|
||||
);
|
||||
GatewayClient::new(
|
||||
GatewayClientConfig::new_default()
|
||||
|
||||
@@ -436,7 +436,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref mut next_delay) = &mut self.next_delay {
|
||||
if let Some(next_delay) = &mut self.next_delay {
|
||||
// it is not yet time to return a message
|
||||
if next_delay.as_mut().poll(cx).is_pending() {
|
||||
return Poll::Pending;
|
||||
|
||||
@@ -40,8 +40,8 @@ pub enum ClientCoreError {
|
||||
#[error("no gateway with id: {0}")]
|
||||
NoGatewayWithId(String),
|
||||
|
||||
#[error("Invalid URL: {0}")]
|
||||
InvalidUrl(String),
|
||||
#[error("Invalid Endpoint: {0}")]
|
||||
InvalidEndpoint(String),
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[error("resolution failed: {0}")]
|
||||
|
||||
@@ -21,7 +21,7 @@ use url::Url;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use crate::init::websockets::connect_async;
|
||||
|
||||
use nym_topology::NodeId;
|
||||
use nym_topology::{EntryDetails, NodeId};
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio::net::TcpStream;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
@@ -55,7 +55,7 @@ const PING_TIMEOUT: Duration = Duration::from_millis(1000);
|
||||
pub trait ConnectableGateway {
|
||||
fn node_id(&self) -> NodeId;
|
||||
fn identity(&self) -> ed25519::PublicKey;
|
||||
fn clients_address(&self, prefer_ipv6: bool) -> Option<String>;
|
||||
fn endpoint_details(&self) -> Option<EntryDetails>;
|
||||
fn is_wss(&self) -> bool;
|
||||
}
|
||||
|
||||
@@ -68,8 +68,8 @@ impl ConnectableGateway for RoutingNode {
|
||||
self.identity_key
|
||||
}
|
||||
|
||||
fn clients_address(&self, prefer_ipv6: bool) -> Option<String> {
|
||||
self.ws_entry_address(prefer_ipv6)
|
||||
fn endpoint_details(&self) -> Option<EntryDetails> {
|
||||
self.entry.clone()
|
||||
}
|
||||
|
||||
fn is_wss(&self) -> bool {
|
||||
@@ -191,7 +191,7 @@ pub async fn gateways_for_init(
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
async fn connect(endpoint: &str) -> Result<WsConn, ClientCoreError> {
|
||||
async fn connect(endpoint: &EntryDetails) -> Result<WsConn, ClientCoreError> {
|
||||
match tokio::time::timeout(CONN_TIMEOUT, connect_async(endpoint)).await {
|
||||
Err(_elapsed) => Err(ClientCoreError::GatewayConnectionTimeout),
|
||||
Ok(Err(conn_failure)) => Err(conn_failure),
|
||||
@@ -208,17 +208,17 @@ async fn measure_latency<G>(gateway: &G) -> Result<GatewayWithLatency<'_, G>, Cl
|
||||
where
|
||||
G: ConnectableGateway,
|
||||
{
|
||||
let Some(addr) = gateway.clients_address(false) else {
|
||||
let Some(endpoint) = gateway.endpoint_details() else {
|
||||
return Err(ClientCoreError::UnsupportedEntry {
|
||||
id: gateway.node_id(),
|
||||
identity: gateway.identity().to_string(),
|
||||
});
|
||||
};
|
||||
trace!(
|
||||
"establishing connection to {} ({addr})...",
|
||||
"establishing connection to {} ({endpoint})...",
|
||||
gateway.identity(),
|
||||
);
|
||||
let mut stream = connect(&addr).await?;
|
||||
let mut stream = connect(&endpoint).await?;
|
||||
|
||||
let mut results = Vec::new();
|
||||
for _ in 0..MEASUREMENTS {
|
||||
@@ -379,7 +379,7 @@ pub(super) fn get_specified_gateway(
|
||||
|
||||
pub(super) async fn register_with_gateway(
|
||||
gateway_id: ed25519::PublicKey,
|
||||
gateway_listener: Url,
|
||||
gateway_listener: EntryDetails,
|
||||
our_identity: Arc<ed25519::KeyPair>,
|
||||
#[cfg(unix)] connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
|
||||
) -> Result<RegistrationResult, ClientCoreError> {
|
||||
|
||||
@@ -14,6 +14,7 @@ use nym_gateway_client::client::InitGatewayClient;
|
||||
use nym_gateway_requests::shared_key::SharedGatewayKey;
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_topology::node::RoutingNode;
|
||||
use nym_topology::EntryDetails;
|
||||
use nym_validator_client::client::IdentityKey;
|
||||
use nym_validator_client::nyxd::AccountId;
|
||||
use serde::Serialize;
|
||||
@@ -22,7 +23,6 @@ use std::fmt::{Debug, Display};
|
||||
use std::os::fd::RawFd;
|
||||
use std::sync::Arc;
|
||||
use time::OffsetDateTime;
|
||||
use url::Url;
|
||||
|
||||
pub enum SelectedGateway {
|
||||
Remote {
|
||||
@@ -30,7 +30,7 @@ pub enum SelectedGateway {
|
||||
|
||||
gateway_owner_address: Option<AccountId>,
|
||||
|
||||
gateway_listener: Url,
|
||||
gateway_listener: EntryDetails,
|
||||
},
|
||||
Custom {
|
||||
gateway_id: ed25519::PublicKey,
|
||||
@@ -46,25 +46,25 @@ impl SelectedGateway {
|
||||
// for now, let's use 'old' behaviour, if you want to change it, you can pass it up the enum stack yourself : )
|
||||
let prefer_ipv6 = false;
|
||||
|
||||
let gateway_listener = if must_use_tls {
|
||||
node.ws_entry_address_tls()
|
||||
.ok_or(ClientCoreError::UnsupportedWssProtocol {
|
||||
gateway: node.identity_key.to_base58_string(),
|
||||
})?
|
||||
} else {
|
||||
node.ws_entry_address(prefer_ipv6)
|
||||
.ok_or(ClientCoreError::UnsupportedEntry {
|
||||
id: node.node_id,
|
||||
identity: node.identity_key.to_base58_string(),
|
||||
})?
|
||||
};
|
||||
let gateway_listener = node.entry.ok_or(ClientCoreError::UnsupportedEntry {
|
||||
id: node.node_id,
|
||||
identity: node.identity_key.to_base58_string(),
|
||||
})?;
|
||||
|
||||
let gateway_listener =
|
||||
Url::parse(&gateway_listener).map_err(|source| ClientCoreError::MalformedListener {
|
||||
gateway_id: node.identity_key.to_base58_string(),
|
||||
raw_listener: gateway_listener,
|
||||
source,
|
||||
})?;
|
||||
if must_use_tls
|
||||
&& (gateway_listener.hostname.is_none() || gateway_listener.clients_wss_port.is_none())
|
||||
{
|
||||
return Err(ClientCoreError::UnsupportedWssProtocol {
|
||||
gateway: node.identity_key.to_base58_string(),
|
||||
});
|
||||
}
|
||||
|
||||
if prefer_ipv6 && gateway_listener.ip_addresses.iter().all(|i| i.is_ipv4()) {
|
||||
return Err(ClientCoreError::UnsupportedEntry {
|
||||
id: node.node_id,
|
||||
identity: node.identity_key.to_base58_string(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(SelectedGateway::Remote {
|
||||
gateway_id: node.identity_key,
|
||||
|
||||
@@ -1,43 +1,60 @@
|
||||
use crate::error::ClientCoreError;
|
||||
|
||||
use nym_http_api_client::HickoryDnsResolver;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use nym_topology::EntryDetails;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
|
||||
use tungstenite::handshake::client::Response;
|
||||
use url::{Host, Url};
|
||||
use url::Url;
|
||||
|
||||
use std::net::SocketAddr;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(crate) async fn connect_async(
|
||||
endpoint: &str,
|
||||
endpoint: &EntryDetails,
|
||||
) -> Result<(WebSocketStream<MaybeTlsStream<TcpStream>>, Response), ClientCoreError> {
|
||||
let resolver = HickoryDnsResolver::default();
|
||||
let uri = Url::parse(endpoint).map_err(|_| ClientCoreError::InvalidUrl(endpoint.to_owned()))?;
|
||||
let uri = ws_entry_address(endpoint, false)
|
||||
.ok_or(ClientCoreError::InvalidEndpoint(endpoint.to_string()))?;
|
||||
let port: u16 = uri.port_or_known_default().unwrap_or(443);
|
||||
|
||||
let host = uri
|
||||
.host()
|
||||
.ok_or(ClientCoreError::InvalidUrl(endpoint.to_owned()))?;
|
||||
|
||||
// Get address for tcp connection, if a domain is provided use our preferred resolver rather than
|
||||
// the default std resolve
|
||||
let sock_addrs: Vec<SocketAddr> = match host {
|
||||
Host::Ipv4(addr) => vec![SocketAddr::new(addr.into(), port)],
|
||||
Host::Ipv6(addr) => vec![SocketAddr::new(addr.into(), port)],
|
||||
Host::Domain(domain) => {
|
||||
// Do a DNS lookup for the domain using our custom DNS resolver
|
||||
resolver
|
||||
.resolve_str(domain)
|
||||
.await?
|
||||
.map(|a| SocketAddr::new(a, port))
|
||||
.collect()
|
||||
}
|
||||
};
|
||||
let sock_addrs: Vec<SocketAddr> = endpoint
|
||||
.ip_addresses
|
||||
.iter()
|
||||
.map(|addr| SocketAddr::new(addr.clone(), port))
|
||||
.collect();
|
||||
|
||||
let stream = TcpStream::connect(&sock_addrs[..]).await?;
|
||||
|
||||
tokio_tungstenite::client_async_tls(endpoint, stream)
|
||||
tokio_tungstenite::client_async_tls(uri.to_string(), stream)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn ws_entry_address_tls(entry: &EntryDetails) -> Option<Url> {
|
||||
let hostname = entry.hostname.as_ref()?;
|
||||
let wss_port = entry.clients_wss_port?;
|
||||
|
||||
Url::parse(&format!("wss://{hostname}:{wss_port}")).ok()
|
||||
}
|
||||
|
||||
pub fn ws_entry_address_no_tls(entry: &EntryDetails, prefer_ipv6: bool) -> Option<Url> {
|
||||
if let Some(hostname) = entry.hostname.as_ref() {
|
||||
return Url::parse(&format!("ws://{hostname}:{}", entry.clients_ws_port)).ok();
|
||||
}
|
||||
|
||||
if prefer_ipv6 {
|
||||
if let Some(ipv6) = entry.ip_addresses.iter().find(|ip| ip.is_ipv6()) {
|
||||
return Url::parse(&format!("ws://{ipv6}:{}", entry.clients_ws_port)).ok();
|
||||
}
|
||||
}
|
||||
|
||||
let any_ip = entry.ip_addresses.first()?;
|
||||
Url::parse(&format!("ws://{any_ip}:{}", entry.clients_ws_port)).ok()
|
||||
}
|
||||
|
||||
pub fn ws_entry_address(entry: &EntryDetails, prefer_ipv6: bool) -> Option<Url> {
|
||||
if let Some(tls) = ws_entry_address_tls(entry) {
|
||||
return Some(tls);
|
||||
}
|
||||
ws_entry_address_no_tls(entry, prefer_ipv6)
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ nym-network-defaults = { path = "../../network-defaults" }
|
||||
nym-sphinx = { path = "../../nymsphinx" }
|
||||
nym-statistics-common = { path = "../../statistics" }
|
||||
nym-pemstore = { path = "../../pemstore" }
|
||||
nym-topology = { path = "../../topology", features = ["persistence"] }
|
||||
nym-validator-client = { path = "../validator-client", default-features = false }
|
||||
nym-task = { path = "../../task" }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
|
||||
@@ -28,13 +28,13 @@ use nym_sphinx::forwarding::packet::MixPacket;
|
||||
use nym_statistics_common::clients::connection::ConnectionStatsEvent;
|
||||
use nym_statistics_common::clients::ClientStatsSender;
|
||||
use nym_task::ShutdownToken;
|
||||
use nym_topology::EntryDetails;
|
||||
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
|
||||
use rand::rngs::OsRng;
|
||||
use std::sync::Arc;
|
||||
use tracing::instrument;
|
||||
use tracing::*;
|
||||
use tungstenite::protocol::Message;
|
||||
use url::Url;
|
||||
|
||||
#[cfg(unix)]
|
||||
use std::os::fd::RawFd;
|
||||
@@ -62,14 +62,14 @@ pub struct GatewayConfig {
|
||||
// currently a dead field
|
||||
pub gateway_owner: Option<String>,
|
||||
|
||||
pub gateway_listener: String,
|
||||
pub gateway_listener: EntryDetails,
|
||||
}
|
||||
|
||||
impl GatewayConfig {
|
||||
pub fn new(
|
||||
gateway_identity: ed25519::PublicKey,
|
||||
gateway_owner: Option<String>,
|
||||
gateway_listener: String,
|
||||
gateway_listener: EntryDetails,
|
||||
) -> Self {
|
||||
GatewayConfig {
|
||||
gateway_identity,
|
||||
@@ -92,7 +92,7 @@ pub struct GatewayClient<C, St = EphemeralCredentialStorage> {
|
||||
|
||||
authenticated: bool,
|
||||
bandwidth: ClientBandwidth,
|
||||
gateway_address: String,
|
||||
gateway_address: EntryDetails,
|
||||
gateway_identity: ed25519::PublicKey,
|
||||
local_identity: Arc<ed25519::KeyPair>,
|
||||
shared_key: Option<Arc<SharedGatewayKey>>,
|
||||
@@ -201,7 +201,7 @@ impl<C, St> GatewayClient<C, St> {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub async fn establish_connection(&mut self) -> Result<(), GatewayClientError> {
|
||||
debug!(
|
||||
"Attempting to establish connection to gateway at: {}",
|
||||
"Attempting to establish connection to gateway at: {:?}",
|
||||
self.gateway_address
|
||||
);
|
||||
let (ws_stream, _) = connect_async(
|
||||
@@ -467,7 +467,9 @@ impl<C, St> GatewayClient<C, St> {
|
||||
// right now there are no failure cases here, but this might change in the future
|
||||
match gateway_protocol {
|
||||
None => {
|
||||
warn!("the gateway we're connected to has not specified its protocol version. It's probably running version < 1.1.X, but that's still fine for now. It will become a hard error in 1.2.0");
|
||||
warn!(
|
||||
"the gateway we're connected to has not specified its protocol version. It's probably running version < 1.1.X, but that's still fine for now. It will become a hard error in 1.2.0"
|
||||
);
|
||||
// note: in +1.2.0 we will have to return a hard error here
|
||||
Ok(())
|
||||
}
|
||||
@@ -481,7 +483,9 @@ impl<C, St> GatewayClient<C, St> {
|
||||
}
|
||||
|
||||
Some(_) => {
|
||||
debug!("the gateway is using exactly the same (or older) protocol version as we are. We're good to continue!");
|
||||
debug!(
|
||||
"the gateway is using exactly the same (or older) protocol version as we are. We're good to continue!"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -527,7 +531,7 @@ impl<C, St> GatewayClient<C, St> {
|
||||
status,
|
||||
} => (status, protocol_version),
|
||||
ServerResponse::Error { message } => {
|
||||
return Err(GatewayClientError::GatewayError(message))
|
||||
return Err(GatewayClientError::GatewayError(message));
|
||||
}
|
||||
other => return Err(GatewayClientError::UnexpectedResponse { name: other.name() }),
|
||||
};
|
||||
@@ -589,7 +593,7 @@ impl<C, St> GatewayClient<C, St> {
|
||||
{
|
||||
ServerResponse::EncryptedResponse { ciphertext, nonce } => (ciphertext, nonce),
|
||||
ServerResponse::Error { message } => {
|
||||
return Err(GatewayClientError::GatewayError(message))
|
||||
return Err(GatewayClientError::GatewayError(message));
|
||||
}
|
||||
other => return Err(GatewayClientError::UnexpectedResponse { name: other.name() }),
|
||||
};
|
||||
@@ -858,7 +862,9 @@ impl<C, St> GatewayClient<C, St> {
|
||||
|
||||
warn!("Not enough bandwidth. Trying to get more bandwidth, this might take a while");
|
||||
if !self.cfg.bandwidth.require_tickets {
|
||||
info!("The client is running in disabled credentials mode - attempting to claim bandwidth without a credential");
|
||||
info!(
|
||||
"The client is running in disabled credentials mode - attempting to claim bandwidth without a credential"
|
||||
);
|
||||
return self.try_claim_testnet_bandwidth().await;
|
||||
}
|
||||
|
||||
@@ -896,7 +902,9 @@ impl<C, St> GatewayClient<C, St> {
|
||||
Err(err) => {
|
||||
error!("failed to claim ecash bandwidth with the gateway...: {err}");
|
||||
if err.is_ticket_replay() {
|
||||
warn!("this was due to our ticket being replayed! have you messed with the database file?")
|
||||
warn!(
|
||||
"this was due to our ticket being replayed! have you messed with the database file?"
|
||||
)
|
||||
} else {
|
||||
// TODO: tracing span
|
||||
info!("attempting to revert ticket withdrawal...");
|
||||
@@ -1112,7 +1120,9 @@ impl<C, St> GatewayClient<C, St> {
|
||||
self.cfg
|
||||
.bandwidth
|
||||
.ensure_above_cutoff(bandwidth_remaining)?;
|
||||
info!("Claiming more bandwidth with existing credentials. Stop the process now if you don't want that to happen.");
|
||||
info!(
|
||||
"Claiming more bandwidth with existing credentials. Stop the process now if you don't want that to happen."
|
||||
);
|
||||
self.claim_bandwidth().await?;
|
||||
}
|
||||
Ok(())
|
||||
@@ -1128,7 +1138,7 @@ pub struct InitOnly;
|
||||
impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
|
||||
// for initialisation we do not need credential storage. Though it's still a bit weird we have to set the generic...
|
||||
pub fn new_init(
|
||||
gateway_listener: Url,
|
||||
gateway_listener: EntryDetails,
|
||||
gateway_identity: ed25519::PublicKey,
|
||||
local_identity: Arc<ed25519::KeyPair>,
|
||||
#[cfg(unix)] connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
|
||||
@@ -1147,7 +1157,7 @@ impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
|
||||
cfg: GatewayClientConfig::default().with_disabled_credentials_mode(true),
|
||||
authenticated: false,
|
||||
bandwidth: ClientBandwidth::new_empty(),
|
||||
gateway_address: gateway_listener.to_string(),
|
||||
gateway_address: gateway_listener.clone(),
|
||||
gateway_identity,
|
||||
local_identity,
|
||||
shared_key: None,
|
||||
|
||||
@@ -1,51 +1,37 @@
|
||||
use crate::error::GatewayClientError;
|
||||
|
||||
use nym_http_api_client::HickoryDnsResolver;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use nym_topology::EntryDetails;
|
||||
#[cfg(unix)]
|
||||
use std::{
|
||||
os::fd::{AsRawFd, RawFd},
|
||||
sync::Arc,
|
||||
};
|
||||
use tokio::net::TcpSocket;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
|
||||
use tungstenite::handshake::client::Response;
|
||||
use url::{Host, Url};
|
||||
use url::Url;
|
||||
|
||||
use std::net::SocketAddr;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(crate) async fn connect_async(
|
||||
endpoint: &str,
|
||||
endpoint: &EntryDetails,
|
||||
#[cfg(unix)] connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
|
||||
) -> Result<(WebSocketStream<MaybeTlsStream<TcpStream>>, Response), GatewayClientError> {
|
||||
use tokio::net::TcpSocket;
|
||||
|
||||
let resolver = HickoryDnsResolver::default();
|
||||
let uri =
|
||||
Url::parse(endpoint).map_err(|_| GatewayClientError::InvalidUrl(endpoint.to_owned()))?;
|
||||
let uri = ws_entry_address(endpoint, false)
|
||||
.ok_or(GatewayClientError::InvalidEndpoint(endpoint.to_string()))?;
|
||||
let port: u16 = uri.port_or_known_default().unwrap_or(443);
|
||||
|
||||
let host = uri
|
||||
.host()
|
||||
.ok_or(GatewayClientError::InvalidUrl(endpoint.to_owned()))?;
|
||||
|
||||
// Get address for tcp connection, if a domain is provided use our preferred resolver rather than
|
||||
// the default std resolve
|
||||
let sock_addrs: Vec<SocketAddr> = match host {
|
||||
Host::Ipv4(addr) => vec![SocketAddr::new(addr.into(), port)],
|
||||
Host::Ipv6(addr) => vec![SocketAddr::new(addr.into(), port)],
|
||||
Host::Domain(domain) => {
|
||||
// Do a DNS lookup for the domain using our custom DNS resolver
|
||||
resolver
|
||||
.resolve_str(domain)
|
||||
.await?
|
||||
.map(|a| SocketAddr::new(a, port))
|
||||
.collect()
|
||||
}
|
||||
};
|
||||
let sock_addrs = endpoint
|
||||
.ip_addresses
|
||||
.iter()
|
||||
.map(|addr| SocketAddr::new(*addr, port));
|
||||
let uri_str = uri.to_string();
|
||||
|
||||
let mut stream = Err(GatewayClientError::NoEndpointForConnection {
|
||||
address: endpoint.to_owned(),
|
||||
address: uri_str.clone(),
|
||||
});
|
||||
for sock_addr in sock_addrs {
|
||||
let socket = if sock_addr.is_ipv4() {
|
||||
@@ -54,7 +40,7 @@ pub(crate) async fn connect_async(
|
||||
TcpSocket::new_v6()
|
||||
}
|
||||
.map_err(|err| GatewayClientError::NetworkConnectionFailed {
|
||||
address: endpoint.to_owned(),
|
||||
address: uri_str.clone(),
|
||||
source: Box::new(tungstenite::Error::from(err)),
|
||||
})?;
|
||||
|
||||
@@ -70,7 +56,7 @@ pub(crate) async fn connect_async(
|
||||
}
|
||||
Err(err) => {
|
||||
stream = Err(GatewayClientError::NetworkConnectionFailed {
|
||||
address: endpoint.to_owned(),
|
||||
address: uri_str.clone(),
|
||||
source: Box::new(tungstenite::Error::from(err)),
|
||||
});
|
||||
continue;
|
||||
@@ -78,10 +64,39 @@ pub(crate) async fn connect_async(
|
||||
}
|
||||
}
|
||||
|
||||
tokio_tungstenite::client_async_tls(endpoint, stream?)
|
||||
tokio_tungstenite::client_async_tls(uri.clone(), stream?)
|
||||
.await
|
||||
.map_err(|error| GatewayClientError::NetworkConnectionFailed {
|
||||
address: endpoint.to_owned(),
|
||||
address: uri_str.clone(),
|
||||
source: Box::new(error),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn ws_entry_address_tls(entry: &EntryDetails) -> Option<Url> {
|
||||
let hostname = entry.hostname.as_ref()?;
|
||||
let wss_port = entry.clients_wss_port?;
|
||||
|
||||
Url::parse(&format!("wss://{hostname}:{wss_port}")).ok()
|
||||
}
|
||||
|
||||
pub fn ws_entry_address_no_tls(entry: &EntryDetails, prefer_ipv6: bool) -> Option<Url> {
|
||||
if let Some(hostname) = entry.hostname.as_ref() {
|
||||
return Url::parse(&format!("ws://{hostname}:{}", entry.clients_ws_port)).ok();
|
||||
}
|
||||
|
||||
if prefer_ipv6 {
|
||||
if let Some(ipv6) = entry.ip_addresses.iter().find(|ip| ip.is_ipv6()) {
|
||||
return Url::parse(&format!("ws://{ipv6}:{}", entry.clients_ws_port)).ok();
|
||||
}
|
||||
}
|
||||
|
||||
let any_ip = entry.ip_addresses.first()?;
|
||||
Url::parse(&format!("ws://{any_ip}:{}", entry.clients_ws_port)).ok()
|
||||
}
|
||||
|
||||
pub fn ws_entry_address(entry: &EntryDetails, prefer_ipv6: bool) -> Option<Url> {
|
||||
if let Some(tls) = ws_entry_address_tls(entry) {
|
||||
return Some(tls);
|
||||
}
|
||||
ws_entry_address_no_tls(entry, prefer_ipv6)
|
||||
}
|
||||
|
||||
@@ -49,8 +49,8 @@ pub enum GatewayClientError {
|
||||
#[error("no socket address for endpoint: {address}")]
|
||||
NoEndpointForConnection { address: String },
|
||||
|
||||
#[error("Invalid URL: {0}")]
|
||||
InvalidUrl(String),
|
||||
#[error("Invalid Endpoint: {0}")]
|
||||
InvalidEndpoint(String),
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[error("resolution failed: {0}")]
|
||||
|
||||
@@ -28,6 +28,12 @@ pub struct EntryDetails {
|
||||
pub clients_wss_port: Option<u16>,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for EntryDetails {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
pub struct SupportedRoles {
|
||||
pub mixnode: bool,
|
||||
@@ -59,36 +65,6 @@ pub struct RoutingNode {
|
||||
}
|
||||
|
||||
impl RoutingNode {
|
||||
pub fn ws_entry_address_tls(&self) -> Option<String> {
|
||||
let entry = self.entry.as_ref()?;
|
||||
let hostname = entry.hostname.as_ref()?;
|
||||
let wss_port = entry.clients_wss_port?;
|
||||
|
||||
Some(format!("wss://{hostname}:{wss_port}"))
|
||||
}
|
||||
|
||||
pub fn ws_entry_address_no_tls(&self, prefer_ipv6: bool) -> Option<String> {
|
||||
let entry = self.entry.as_ref()?;
|
||||
|
||||
if let Some(hostname) = entry.hostname.as_ref() {
|
||||
return Some(format!("ws://{hostname}:{}", entry.clients_ws_port));
|
||||
}
|
||||
|
||||
if prefer_ipv6 && let Some(ipv6) = entry.ip_addresses.iter().find(|ip| ip.is_ipv6()) {
|
||||
return Some(format!("ws://{ipv6}:{}", entry.clients_ws_port));
|
||||
}
|
||||
|
||||
let any_ip = entry.ip_addresses.first()?;
|
||||
Some(format!("ws://{any_ip}:{}", entry.clients_ws_port))
|
||||
}
|
||||
|
||||
pub fn ws_entry_address(&self, prefer_ipv6: bool) -> Option<String> {
|
||||
if let Some(tls) = self.ws_entry_address_tls() {
|
||||
return Some(tls);
|
||||
}
|
||||
self.ws_entry_address_no_tls(prefer_ipv6)
|
||||
}
|
||||
|
||||
pub fn identity(&self) -> ed25519::PublicKey {
|
||||
self.identity_key
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user