Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b9a6ec2abc | |||
| f6ed17d949 |
Generated
+1
@@ -7020,6 +7020,7 @@ dependencies = [
|
||||
"tracing-subscriber",
|
||||
"url",
|
||||
"wasmtimer",
|
||||
"webpki-roots 0.26.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -38,7 +38,10 @@ itertools = { workspace = true }
|
||||
inventory = { workspace = true }
|
||||
fastrand = { workspace = true }
|
||||
tokio = { workspace = true, features = ["rt", "macros", "time"] }
|
||||
rustls = { workspace=true }
|
||||
rustls = { workspace = true, features = ["aws_lc_rs"] }
|
||||
# Android: preconfigured webpki roots replace the JNI-bound platform verifier
|
||||
# (see registry.rs); a standalone sidecar process can't init the platform store.
|
||||
webpki-roots = { workspace = true }
|
||||
# used for decoding text responses (they were already implicitly included)
|
||||
bytes = { workspace = true }
|
||||
encoding_rs = { workspace = true }
|
||||
|
||||
@@ -66,6 +66,28 @@ pub fn default_builder() -> ReqwestClientBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
// On Android the default rustls verifier (rustls-platform-verifier) reaches
|
||||
// the system trust store through JNI and must be initialized with the app's
|
||||
// Java context. A standalone client process (e.g. Goblin's bundled SOCKS5
|
||||
// sidecar) has no such context, so the verifier panics
|
||||
// ("Expect rustls-platform-verifier to be initialized") the moment it makes
|
||||
// its first HTTPS call to the nym-api. Per Nym's own troubleshooting docs,
|
||||
// pin preconfigured webpki roots instead so HTTPS verifies without the
|
||||
// platform store. Desktop/Windows keep the default verifier.
|
||||
#[cfg(target_os = "android")]
|
||||
{
|
||||
let mut roots = rustls::RootCertStore::empty();
|
||||
roots.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
|
||||
let tls = rustls::ClientConfig::builder_with_provider(std::sync::Arc::new(
|
||||
rustls::crypto::aws_lc_rs::default_provider(),
|
||||
))
|
||||
.with_safe_default_protocol_versions()
|
||||
.expect("aws-lc-rs provides the safe default protocol versions")
|
||||
.with_root_certificates(roots)
|
||||
.with_no_client_auth();
|
||||
b = b.use_preconfigured_tls(tls);
|
||||
}
|
||||
|
||||
b
|
||||
}
|
||||
|
||||
|
||||
@@ -53,22 +53,52 @@ impl IpMixStream {
|
||||
///
|
||||
/// Returns a ready-to-use tunnel with allocated IP addresses.
|
||||
pub async fn new() -> Result<Self, Error> {
|
||||
let ipr_address = Self::best_ipr().await?;
|
||||
Self::new_with_ipr(ipr_address).await
|
||||
}
|
||||
|
||||
/// Auto-discover the best available IPR exit for the mainnet.
|
||||
///
|
||||
/// Exposed so a caller that wants to back the tunnel with its own tuned
|
||||
/// [`MixnetClient`] (via [`IpMixStream::from_client`]) can still get the
|
||||
/// auto-selected exit that [`IpMixStream::new`] would have used.
|
||||
pub async fn best_ipr() -> Result<Recipient, Error> {
|
||||
let network_defaults = NymNetworkDetails::new_mainnet();
|
||||
let api_client =
|
||||
create_nym_api_client(network_defaults.nym_api_urls.ok_or(Error::NoNymAPIUrl)?)?;
|
||||
let ipr_address = get_best_ipr(api_client).await?;
|
||||
Self::new_with_ipr(ipr_address).await
|
||||
get_best_ipr(api_client).await
|
||||
}
|
||||
|
||||
/// Connect to a specific IPR address.
|
||||
///
|
||||
/// Use this when you already know the IPR `Recipient` address (e.g. for
|
||||
/// testing against a specific exit node). For automatic discovery, use
|
||||
/// [`IpMixStream::new`] instead.
|
||||
/// [`IpMixStream::new`] instead. Backs the tunnel with a default
|
||||
/// [`MixnetClient::connect_new`] client (full cover traffic, poisson per-hop
|
||||
/// delays); for a tuned client use [`IpMixStream::from_client`].
|
||||
pub async fn new_with_ipr(ipr_address: Recipient) -> Result<Self, Error> {
|
||||
nym_network_defaults::setup_env(None::<&str>);
|
||||
let mut client = MixnetClient::connect_new().await?;
|
||||
let mut stream = client.open_stream(ipr_address, Some(10)).await?;
|
||||
let client = MixnetClient::connect_new().await?;
|
||||
Self::from_client(client, ipr_address).await
|
||||
}
|
||||
|
||||
/// Establish the IP tunnel over a caller-provided, already-connected
|
||||
/// [`MixnetClient`].
|
||||
///
|
||||
/// This is the low-anonymity-tuning seam: the caller builds the client with
|
||||
/// whatever traffic/anonymity [`DebugConfig`](crate::mixnet::config) it wants
|
||||
/// (e.g. a higher-throughput / lower-cover-traffic preset for a public read
|
||||
/// tunnel) and passes it in. Everything after the client — the stream open,
|
||||
/// the IPR connect handshake and the allocated-IP bookkeeping — is identical
|
||||
/// to [`new_with_ipr`](Self::new_with_ipr).
|
||||
pub async fn from_client(
|
||||
mut client: MixnetClient,
|
||||
ipr_address: Recipient,
|
||||
) -> Result<Self, Error> {
|
||||
// 4 reply-SURBs per stream (was 10): the IPR replies steadily, so a
|
||||
// smaller SURB budget still keeps the return path fed while cutting the
|
||||
// per-write SURB overhead on this public read tunnel.
|
||||
let mut stream = client.open_stream(ipr_address, Some(4)).await?;
|
||||
|
||||
info!("Connecting to IP packet router at {ipr_address}");
|
||||
let allocated_ips = Self::connect_tunnel(&mut stream).await?;
|
||||
|
||||
@@ -141,6 +141,11 @@ pub use nym_network_defaults::NymContracts;
|
||||
/// println!("API: {:?}", network.endpoints);
|
||||
/// ```
|
||||
pub use nym_network_defaults::NymNetworkDetails;
|
||||
/// Export the network (mainnet by default) environment configuration. Callers
|
||||
/// that build a [`MixnetClient`](mixnet::MixnetClient) themselves (rather than
|
||||
/// via [`MixnetClient::connect_new`](mixnet::MixnetClient::connect_new)) should
|
||||
/// call this first, mirroring what the higher-level constructors do internally.
|
||||
pub use nym_network_defaults::setup_env;
|
||||
/// Validator/API endpoint configuration.
|
||||
pub use nym_network_defaults::ValidatorDetails;
|
||||
|
||||
|
||||
@@ -33,7 +33,11 @@ impl NymAsyncDevice {
|
||||
let mut capabilities = DeviceCapabilities::default();
|
||||
capabilities.medium = Medium::Ip;
|
||||
capabilities.max_transmission_unit = 1500;
|
||||
capabilities.max_burst_size = Some(1);
|
||||
// Let the reactor process up to 64 packets per poll loop instead of one.
|
||||
// max_burst_size = Some(1) serialized all I/O to a single packet per
|
||||
// reactor wake, which throttles throughput badly over the mixnet's long
|
||||
// RTT; 64 lets a burst drain in one pass.
|
||||
capabilities.max_burst_size = Some(64);
|
||||
|
||||
Self {
|
||||
rx,
|
||||
|
||||
@@ -217,11 +217,19 @@ impl Tunnel {
|
||||
// Configure smoltcp: raw IP mode (no Ethernet), /32 for the allocated IP,
|
||||
// default route via unspecified (the IPR does the actual routing).
|
||||
let iface_config = Config::new(HardwareAddress::Ip);
|
||||
let net_config = NetConfig::new(
|
||||
let mut net_config = NetConfig::new(
|
||||
iface_config,
|
||||
IpCidr::new(IpAddress::from(allocated_ips.ipv4), 32),
|
||||
vec![IpAddress::from(Ipv4Address::UNSPECIFIED)],
|
||||
);
|
||||
// Lift the per-socket TCP window from tokio-smoltcp's 8 KB default to
|
||||
// 256 KB. The mixnet RTT is large (5 hops + deliberate per-hop delay),
|
||||
// so an 8 KB window caps bulk throughput at ~8 KB per round trip; 256 KB
|
||||
// lets many segments stay in flight, which is what makes relay/NIP-11/
|
||||
// price reads fast. Only the two TCP buffers change — every other
|
||||
// `BufferSize` field keeps its default.
|
||||
net_config.buffer_size.tcp_rx_size = 262144;
|
||||
net_config.buffer_size.tcp_tx_size = 262144;
|
||||
|
||||
// Net::new spawns the smoltcp reactor as a background task. After this,
|
||||
// tcp_connect/udp_bind create sockets managed by that reactor.
|
||||
|
||||
Reference in New Issue
Block a user