chore: refresh wasm sdk (#5353)

* make packet statistics wasm-compatible

* fixed possible overflow issue in delay controller

* updated wasm-client to be compatible with the current network

* applied same logic to mixfetch client

* removed dead imports

* updated versions
This commit is contained in:
Jędrzej Stuczyński
2025-01-15 17:11:17 +00:00
committed by GitHub
parent fffec65cab
commit adb248dbcc
37 changed files with 9218 additions and 949 deletions
Generated
+5 -4
View File
@@ -2843,7 +2843,7 @@ dependencies = [
"futures-core",
"futures-sink",
"gloo-utils 0.2.0",
"http 1.1.0",
"http 1.2.0",
"js-sys",
"pin-project",
"serde",
@@ -4223,7 +4223,7 @@ dependencies = [
[[package]]
name = "mix-fetch-wasm"
version = "1.3.0-rc.0"
version = "1.4.0-rc.0"
dependencies = [
"async-trait",
"futures",
@@ -5048,7 +5048,7 @@ dependencies = [
[[package]]
name = "nym-client-wasm"
version = "1.3.0-rc.0"
version = "1.4.0-rc.0"
dependencies = [
"anyhow",
"futures",
@@ -6014,7 +6014,7 @@ dependencies = [
[[package]]
name = "nym-node"
version = "1.3.1"
version = "1.3.0"
dependencies = [
"anyhow",
"async-trait",
@@ -6662,6 +6662,7 @@ dependencies = [
"thiserror 1.0.69",
"time",
"tokio",
"wasmtimer",
]
[[package]]
@@ -472,6 +472,7 @@ where
.claim_initial_bandwidth()
.await
.map_err(gateway_failure)?;
gateway_client
.start_listening_for_mixnet_messages()
.map_err(gateway_failure)?;
@@ -9,10 +9,12 @@ use self::{
acknowledgement_control::AcknowledgementController, real_traffic_stream::OutQueueControl,
};
use crate::client::real_messages_control::message_handler::MessageHandler;
use crate::client::replies::reply_controller;
use crate::client::replies::reply_controller::{
ReplyController, ReplyControllerReceiver, ReplyControllerSender,
};
use crate::client::replies::reply_storage::CombinedReplyStorage;
use crate::config;
use crate::{
client::{
inbound_messages::InputMessageReceiver, mix_traffic::BatchMixMessageSender,
@@ -27,16 +29,13 @@ use nym_gateway_client::AcknowledgementReceiver;
use nym_sphinx::acknowledgements::AckKey;
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::params::PacketType;
use nym_statistics_common::clients::ClientStatsSender;
use nym_task::connections::{ConnectionCommandReceiver, LaneQueueLengths};
use rand::{rngs::OsRng, CryptoRng, Rng};
use std::sync::Arc;
use crate::client::replies::reply_controller;
use crate::config;
pub(crate) use acknowledgement_control::{AckActionSender, Action};
use nym_statistics_common::clients::ClientStatsSender;
pub(crate) mod acknowledgement_control;
pub(crate) mod message_handler;
pub(crate) mod real_traffic_stream;
@@ -70,7 +70,10 @@ impl SendingDelayController {
lower_bound,
multiplier_elevated_counter: 0,
time_when_logged_about_elevated_multiplier: now
- Duration::from_secs(INTERVAL_BETWEEN_WARNING_ABOUT_ELEVATED_MULTIPLIER_SECS),
.checked_sub(Duration::from_secs(
INTERVAL_BETWEEN_WARNING_ABOUT_ELEVATED_MULTIPLIER_SECS,
))
.unwrap_or(now),
time_when_changed: now,
time_when_backpressure_detected: now,
}
@@ -16,14 +16,14 @@
#![warn(clippy::todo)]
#![warn(clippy::dbg_macro)]
use std::time::Duration;
use futures::StreamExt;
use nym_client_core_config_types::StatsReporting;
use nym_sphinx::addressing::Recipient;
use nym_statistics_common::clients::{
ClientStatsController, ClientStatsReceiver, ClientStatsSender,
};
use nym_task::connections::TransmissionLane;
use std::time::Duration;
use crate::{
client::inbound_messages::{InputMessage, InputMessageSender},
@@ -94,10 +94,32 @@ impl StatisticsControl {
async fn run_with_shutdown(&mut self, mut task_client: nym_task::TaskClient) {
log::debug!("Started StatisticsControl with graceful shutdown support");
let mut stats_report_interval =
tokio::time::interval(self.reporting_config.reporting_interval);
let mut local_report_interval = tokio::time::interval(LOCAL_REPORT_INTERVAL);
let mut snapshot_interval = tokio::time::interval(SNAPSHOT_INTERVAL);
#[cfg(not(target_arch = "wasm32"))]
let mut stats_report_interval = tokio_stream::wrappers::IntervalStream::new(
tokio::time::interval(self.reporting_config.reporting_interval),
);
#[cfg(not(target_arch = "wasm32"))]
let mut local_report_interval = tokio_stream::wrappers::IntervalStream::new(
tokio::time::interval(LOCAL_REPORT_INTERVAL),
);
#[cfg(not(target_arch = "wasm32"))]
let mut snapshot_interval =
tokio_stream::wrappers::IntervalStream::new(tokio::time::interval(SNAPSHOT_INTERVAL));
#[cfg(target_arch = "wasm32")]
let mut stats_report_interval = gloo_timers::future::IntervalStream::new(
self.reporting_config.reporting_interval.as_millis() as u32,
);
#[cfg(target_arch = "wasm32")]
let mut local_report_interval =
gloo_timers::future::IntervalStream::new(LOCAL_REPORT_INTERVAL.as_millis() as u32);
#[cfg(target_arch = "wasm32")]
let mut snapshot_interval =
gloo_timers::future::IntervalStream::new(SNAPSHOT_INTERVAL.as_millis() as u32);
loop {
tokio::select! {
@@ -108,16 +130,20 @@ impl StatisticsControl {
break;
}
},
_ = snapshot_interval.tick() => {
_ = snapshot_interval.next() => {
self.stats.snapshot();
}
_ = stats_report_interval.tick(), if self.reporting_config.enabled && self.reporting_config.provider_address.is_some() => {
// SAFTEY : this branch executes only if reporting is not none, so unwrapp is fine
#[allow(clippy::unwrap_used)]
self.report_stats(self.reporting_config.provider_address.unwrap()).await;
_ = stats_report_interval.next() => {
let Some(recipient) = self.reporting_config.provider_address else {
continue
};
if self.reporting_config.enabled {
self.report_stats(recipient).await;
}
}
_ = local_report_interval.tick() => {
_ = local_report_interval.next() => {
self.stats.local_report(&mut task_client);
}
_ = task_client.recv_with_delay() => {
+29 -1
View File
@@ -17,7 +17,7 @@ use nym_topology::node::RoutingNode;
use nym_validator_client::client::IdentityKey;
use nym_validator_client::nyxd::AccountId;
use serde::Serialize;
use std::fmt::Display;
use std::fmt::{Debug, Display};
use std::sync::Arc;
use time::OffsetDateTime;
use url::Url;
@@ -221,6 +221,34 @@ pub enum GatewaySetup {
},
}
impl Debug for GatewaySetup {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GatewaySetup::MustLoad { gateway_id } => f
.debug_struct("GatewaySetup::MustLoad")
.field("gateway_id", gateway_id)
.finish(),
GatewaySetup::New {
specification,
available_gateways,
} => f
.debug_struct("GatewaySetup::New")
.field("specification", specification)
.field("available_gateways", available_gateways)
.field("gateways", specification)
.finish(),
GatewaySetup::ReuseConnection {
gateway_details, ..
} => f
.debug_struct("GatewaySetup::ReuseConnection")
.field("authenticated_ephemeral_client", &"***")
.field("gateway_details", gateway_details)
.field("client_keys", &"***")
.finish(),
}
}
}
impl GatewaySetup {
pub fn try_reuse_connection(init_res: InitialisationResult) -> Result<Self, ClientCoreError> {
if let Some(authenticated_ephemeral_client) = init_res.authenticated_ephemeral_client {
+2
View File
@@ -27,3 +27,5 @@ nym-credentials-interface = { path = "../credentials-interface" }
nym-metrics = { path = "../nym-metrics" }
nym-task = { path = "../task" }
[target."cfg(target_arch = \"wasm32\")".dependencies.wasmtimer]
workspace = true
@@ -3,10 +3,7 @@
use super::ClientStatsEvents;
use core::fmt;
use std::{
collections::VecDeque,
time::{Duration, Instant},
};
use std::{collections::VecDeque, time::Duration};
use nym_metrics::{inc, inc_by};
use serde::{Deserialize, Serialize};
@@ -18,6 +15,51 @@ use si_scale::helpers::bibytes2;
// Also, set it larger than the packet report interval so that we don't miss notable singular events
const RECORDING_WINDOW_MS: u64 = 2300;
#[derive(PartialOrd, PartialEq, Clone, Copy, Debug)]
struct Instant {
#[cfg(not(target_arch = "wasm32"))]
inner: std::time::Instant,
#[cfg(target_arch = "wasm32")]
inner: wasmtimer::std::Instant,
}
impl Instant {
#[cfg(not(target_arch = "wasm32"))]
fn inner(&self) -> &std::time::Instant {
&self.inner
}
#[cfg(target_arch = "wasm32")]
fn inner(&self) -> &wasmtimer::std::Instant {
&self.inner
}
#[cfg(not(target_arch = "wasm32"))]
fn now() -> Self {
Instant {
inner: std::time::Instant::now(),
}
}
#[cfg(target_arch = "wasm32")]
fn now() -> Self {
Instant {
inner: wasmtimer::std::Instant::now(),
}
}
fn checked_sub(&self, duration: Duration) -> Option<Instant> {
self.inner()
.checked_sub(duration)
.map(|inner| Instant { inner })
}
fn duration_since(&self, earlier: &Instant) -> Duration {
self.inner.duration_since(*earlier.inner())
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub(crate) struct PacketStatistics {
// Sent
@@ -424,7 +466,11 @@ impl PacketStatisticsControl {
self.history.push_back((Instant::now(), self.stats.clone()));
// Filter out old ones
let recording_window = Instant::now() - Duration::from_millis(RECORDING_WINDOW_MS);
let now = Instant::now();
let recording_window = Instant::now()
.checked_sub(Duration::from_millis(RECORDING_WINDOW_MS))
.unwrap_or(now);
while self
.history
.front()
@@ -442,7 +488,7 @@ impl PacketStatisticsControl {
// Do basic averaging over the entire history, which just uses the first and last
if let Some((start, start_stats)) = self.history.front() {
let duration_secs = Instant::now().duration_since(*start).as_secs_f64();
let duration_secs = Instant::now().duration_since(start).as_secs_f64();
let delta = self.stats.clone() - start_stats.clone();
let rates = PacketRates::from(delta) / duration_secs;
Some(rates)
@@ -458,7 +504,10 @@ impl PacketStatisticsControl {
}
// Filter out old ones
let recording_window = Instant::now() - Duration::from_millis(RECORDING_WINDOW_MS);
let now = Instant::now();
let recording_window = now
.checked_sub(Duration::from_millis(RECORDING_WINDOW_MS))
.unwrap_or(now);
while self
.rates
.front()
@@ -0,0 +1,3 @@
[build]
target = "wasm32-unknown-unknown"
target_arch = "wasm32"
+88 -2
View File
@@ -5,12 +5,15 @@ use crate::error::WasmCoreError;
use crate::storage::wasm_client_traits::WasmClientStorage;
use crate::storage::ClientStorage;
use js_sys::Promise;
use nym_client_core::client::base_client::storage::helpers::set_active_gateway;
use nym_client_core::client::base_client::storage::GatewaysDetailsStore;
use nym_client_core::client::replies::reply_storage::browser_backend;
use nym_client_core::config;
use nym_client_core::error::ClientCoreError;
use nym_client_core::init::helpers::current_gateways;
use nym_client_core::init::types::GatewaySelectionSpecification;
use nym_client_core::init::{
self,
self, setup_gateway,
types::{GatewaySetup, InitialisationResult},
};
use nym_sphinx::addressing::clients::Recipient;
@@ -18,7 +21,7 @@ use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
use nym_topology::wasm_helpers::WasmFriendlyNymTopology;
use nym_topology::{NymTopology, RoutingNode};
use nym_validator_client::client::IdentityKey;
use nym_validator_client::NymApiClient;
use nym_validator_client::{NymApiClient, UserAgent};
use rand::thread_rng;
use url::Url;
use wasm_bindgen::prelude::wasm_bindgen;
@@ -26,6 +29,7 @@ use wasm_bindgen_futures::future_to_promise;
use wasm_utils::error::PromisableResult;
pub use nym_credential_storage::ephemeral_storage::EphemeralStorage as EphemeralCredentialStorage;
use wasm_utils::console_log;
// don't get too excited about the name, under the hood it's just a big fat placeholder
// with no disk_persistence
@@ -134,6 +138,15 @@ pub async fn setup_gateway_from_api(
setup_gateway_wasm(client_store, force_tls, chosen_gateway, gateways).await
}
pub async fn current_gateways_wasm(
nym_apis: &[Url],
user_agent: Option<UserAgent>,
minimum_performance: u8,
) -> Result<Vec<RoutingNode>, ClientCoreError> {
let mut rng = thread_rng();
current_gateways(&mut rng, nym_apis, user_agent, minimum_performance).await
}
pub async fn setup_from_topology(
explicit_gateway: Option<IdentityKey>,
force_tls: bool,
@@ -143,3 +156,76 @@ pub async fn setup_from_topology(
let gateways = topology.entry_capable_nodes().cloned().collect::<Vec<_>>();
setup_gateway_wasm(client_store, force_tls, explicit_gateway, gateways).await
}
pub async fn generate_new_client_keys(store: &ClientStorage) -> Result<(), WasmCoreError> {
let mut rng = thread_rng();
init::generate_new_client_keys(&mut rng, store).await?;
Ok(())
}
pub async fn add_gateway(
preferred_gateway: Option<IdentityKey>,
latency_based_selection: Option<bool>,
force_tls: bool,
nym_apis: &[Url],
user_agent: UserAgent,
min_performance: u8,
storage: &ClientStorage,
) -> Result<(), WasmCoreError> {
let selection_spec = GatewaySelectionSpecification::new(
preferred_gateway.clone(),
latency_based_selection,
force_tls,
);
let preferred_gateway = preferred_gateway
.as_ref()
.map(|g| g.parse())
.transpose()
.map_err(|source| WasmCoreError::InvalidGatewayIdentity { source })?;
let registered_gateways = storage.all_gateways_identities().await.map_err(|source| {
ClientCoreError::GatewaysDetailsStoreError {
source: Box::new(source),
}
})?;
// if user provided gateway id (and we can't overwrite data), make sure we're not trying to register
// with a known gateway
if let Some(user_chosen) = preferred_gateway {
if registered_gateways.contains(&user_chosen) {
return Err(ClientCoreError::AlreadyRegistered {
gateway_id: user_chosen.to_base58_string(),
}
.into());
}
}
// Setup gateway by either registering a new one, or creating a new config from the selected
// one but with keys kept, or reusing the gateway configuration.
let available_gateways =
current_gateways_wasm(nym_apis, Some(user_agent), min_performance).await?;
// since we're registering with a brand new gateway,
// make sure the list of available gateways doesn't overlap the list of known gateways
let available_gateways = available_gateways
.into_iter()
.filter(|g| !registered_gateways.contains(&g.identity()))
.collect::<Vec<_>>();
if available_gateways.is_empty() {
return Err(ClientCoreError::NoNewGatewaysAvailable.into());
}
let gateway_setup = GatewaySetup::New {
specification: selection_spec,
available_gateways,
};
let init_details = setup_gateway(gateway_setup, storage, storage).await?;
let gateway = init_details.gateway_id().to_base58_string();
set_active_gateway(storage, &gateway).await?;
console_log!("finished registration with gateway {gateway}");
Ok(())
}
@@ -128,8 +128,12 @@ impl GatewaysDetailsStore for ClientStorage {
}
async fn all_gateways(&self) -> Result<Vec<GatewayRegistration>, Self::StorageError> {
todo!()
// let identities = self.all
let identities = self.registered_gateways().await?;
let mut registered = Vec::with_capacity(identities.len());
for gateway_id in identities {
registered.push(self.load_gateway_details(&gateway_id).await?);
}
Ok(registered)
}
async fn has_gateway_details(&self, gateway_id: &str) -> Result<bool, Self::StorageError> {
@@ -59,6 +59,8 @@ impl ClientStorage {
db.create_object_store(v2::GATEWAY_REGISTRATIONS_ACTIVE_GATEWAY_STORE)?;
db.create_object_store(v2::GATEWAY_REGISTRATIONS_REGISTERED_GATEWAYS_STORE)?;
return Ok(());
}
// version 1 -> unimplemented migration
@@ -119,6 +119,15 @@ pub trait WasmClientStorage: BaseWasmStorage {
.map_err(Into::into)
}
async fn has_identity_key(&self) -> Result<bool, <Self as WasmClientStorage>::StorageError> {
self.has_value(
v1::KEYS_STORE,
JsValue::from_str(v1::ED25519_IDENTITY_KEYPAIR),
)
.await
.map_err(Into::into)
}
async fn store_identity_keypair(
&self,
keypair: &identity::KeyPair,
+3
View File
@@ -0,0 +1,3 @@
[build]
target = "wasm32-unknown-unknown"
target_arch = "wasm32"
+3
View File
@@ -0,0 +1,3 @@
[build]
target = "wasm32-unknown-unknown"
target_arch = "wasm32"
@@ -1,6 +1,6 @@
{
"name": "@nymproject/mix-fetch-node",
"version": "1.3.0-rc.0",
"version": "1.4.0-rc.0",
"description": "This package is a drop-in replacement for `fetch` in NodeJS to send HTTP requests over the Nym Mixnet.",
"license": "Apache-2.0",
"author": "Nym Technologies SA",
@@ -1,6 +1,6 @@
{
"name": "@nymproject/mix-fetch",
"version": "1.3.0-rc.0",
"version": "1.4.0-rc.0",
"description": "This package is a drop-in replacement for `fetch` to send HTTP requests over the Nym Mixnet.",
"license": "Apache-2.0",
"author": "Nym Technologies SA",
@@ -1,6 +1,6 @@
{
"name": "@nymproject/nodejs-client",
"version": "1.3.0-rc.0",
"version": "1.4.0-rc.0",
"license": "Apache-2.0",
"author": "Nym Technologies SA",
"files": [
@@ -1,6 +1,6 @@
{
"name": "@nymproject/sdk-react",
"version": "1.3.0-rc.0",
"version": "1.4.0-rc.0",
"license": "Apache-2.0",
"author": "Nym Technologies SA",
"files": [
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@nymproject/sdk",
"version": "1.3.0-rc.0",
"version": "1.4.0-rc.0",
"license": "Apache-2.0",
"author": "Nym Technologies SA",
"files": [
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "nym-client-wasm"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jedrzej Stuczynski <andrew@nymtech.net>"]
version = "1.3.0-rc.0"
version = "1.4.0-rc.0"
edition = "2021"
keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy"]
license = "Apache-2.0"
+1 -1
View File
@@ -6,7 +6,7 @@ build:
wasm-opt -Oz -o ../../dist/wasm/client/nym_client_wasm_bg.wasm ../../dist/wasm/client/nym_client_wasm_bg.wasm
build-debug-dev:
wasm-pack build --scope nymproject --target no-modules
wasm-pack build --debug --scope nymproject --target no-modules
build-rust-node:
wasm-pack build --scope nymproject --target nodejs --out-dir ../../dist/node/wasm/client
File diff suppressed because it is too large Load Diff
+14 -72
View File
@@ -100,69 +100,6 @@ function printAndDisplayTestResult(result) {
});
}
async function testWithNymClient() {
const preferredGateway = "6pXQcG1Jt9hxBzMgTbQL5Y58z6mu4KXVRbA1idmibwsw";
const topology = dummyTopology()
let received = 0
const onMessageHandler = (message) => {
received += 1;
self.postMessage({
kind: 'ReceiveMessage',
args: {
message,
senderTag: undefined,
isMagicPayload: true,
},
});
// it's really up to the user to create proper callback here...
console.log(`received ${received} packets so far`)
};
console.log('Instantiating WASM client...');
let clientBuilder = NymClientBuilder.new_tester(topology, onMessageHandler, preferredGateway)
console.log('Web worker creating WASM client...');
let local_client = await clientBuilder.start_client();
console.log('WASM client running!');
const selfAddress = local_client.self_address();
// set the global (I guess we don't have to anymore?)
client = local_client;
console.log(`Client address is ${selfAddress}`);
self.postMessage({
kind: 'Ready',
args: {
selfAddress,
},
});
// Set callback to handle messages passed to the worker.
self.onmessage = async event => {
console.log(event)
if (event.data && event.data.kind) {
switch (event.data.kind) {
case 'SendMessage': {
const {message, recipient} = event.data.args;
let uint8Array = new TextEncoder().encode(message);
await client.send_regular_message(uint8Array, recipient);
break;
}
case 'MagicPayload': {
const {mixnodeIdentity} = event.data.args;
const req = await client.try_construct_test_packet_request(mixnodeIdentity);
await client.change_hardcoded_topology(req.injectable_topology());
await client.try_send_test_packets(req);
break;
}
}
}
};
}
async function wasm_bindgenSetup(onMessageHandler) {
const preferredGateway = "6qQYb4ArXANU6HJDxzH4PFCUqYb39Dae2Gem2KpxescM";
@@ -196,7 +133,7 @@ async function wasm_bindgenSetup(onMessageHandler) {
updatedTraffic.average_packet_delay_ms = 666;
differentDebug.traffic = updatedTraffic;
const config = new ClientConfig( { debug: differentDebug } );
const config = new ClientConfig({debug: differentDebug});
//
// // STEP 2. setup the client
// // note, the extra optional argument is of the following type:
@@ -210,15 +147,15 @@ async function wasm_bindgenSetup(onMessageHandler) {
// return await NymClient.newWithConfig(config, onMessageHandler)
//
// #2
return await NymClient.newWithConfig(config, onMessageHandler, { storagePassphrase: "foomp" })
return await NymClient.newWithConfig(config, onMessageHandler, {storagePassphrase: "foomp"})
//
// #3
// return await NymClient.newWithConfig(config, onMessageHandler, { storagePassphrase: "foomp", preferredGateway })
}
async function nativeSetup(onMessageHandler) {
const preferredGateway = "6qQYb4ArXANU6HJDxzH4PFCUqYb39Dae2Gem2KpxescM";
const validator = 'https://qa-nym-api.qa.nymte.ch/api';
const preferredGateway = "8ookuLkA9oWfRTjb7Jq4tLGcWrqoXKGQxw84MjMrv2S4";
const validator = 'https://sandbox-nym-api1.nymtech.net/api';
// those are just some examples, there are obviously more permutations;
// note, the extra optional argument is of the following type:
@@ -239,15 +176,20 @@ async function nativeSetup(onMessageHandler) {
// return new NymClient(onMessageHandler, { nymApiUrl: validator })
// #3
const noCoverTrafficOverride = {
traffic: { disableMainPoissonPacketDistribution: true },
coverTraffic: { disableLoopCoverTrafficStream: true },
traffic: {disableMainPoissonPacketDistribution: true},
coverTraffic: {disableLoopCoverTrafficStream: true},
}
return new NymClient(onMessageHandler, { storagePassphrase: "foomp", nymApiUrl: validator, clientId: "my-client", clientOverride: noCoverTrafficOverride } )
return new NymClient(onMessageHandler, {
// storagePassphrase: "foomp",
nymApiUrl: validator,
clientId: "my-client",
clientOverride: noCoverTrafficOverride
})
}
async function normalNymClientUsage() {
self.postMessage({ kind: 'DisableMagicTestButton' });
self.postMessage({kind: 'DisableMagicTestButton'});
const onMessageHandler = (message) => {
console.log(message);
@@ -264,7 +206,7 @@ async function normalNymClientUsage() {
let localClient = await nativeSetup(onMessageHandler)
console.log('WASM client running!');
const selfAddress = localClient.self_address();
const selfAddress = localClient.selfAddress();
// set the global (I guess we don't have to anymore?)
client = localClient;
File diff suppressed because it is too large Load Diff
+114 -53
View File
@@ -12,31 +12,30 @@ use crate::error::WasmClientError;
use crate::helpers::{InputSender, WasmTopologyExt};
use crate::response_pusher::ResponsePusher;
use js_sys::Promise;
use nym_bin_common::bin_info;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tsify::Tsify;
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::future_to_promise;
use wasm_client_core::client::base_client::storage::GatewaysDetailsStore;
use wasm_client_core::client::{
base_client::{BaseClientBuilder, ClientInput, ClientOutput, ClientState},
inbound_messages::InputMessage,
};
use wasm_client_core::config::r#override::DebugWasmOverride;
use wasm_client_core::helpers::{
parse_recipient, parse_sender_tag, setup_from_topology, setup_gateway_from_api,
add_gateway, generate_new_client_keys, parse_recipient, parse_sender_tag,
};
use wasm_client_core::init::types::GatewaySetup;
use wasm_client_core::nym_task::connections::TransmissionLane;
use wasm_client_core::nym_task::TaskManager;
use wasm_client_core::storage::core_client_traits::FullWasmClientStorage;
use wasm_client_core::storage::wasm_client_traits::WasmClientStorage;
use wasm_client_core::storage::ClientStorage;
use wasm_client_core::topology::{SerializableTopologyExt, WasmFriendlyNymTopology};
use wasm_client_core::{
HardcodedTopologyProvider, IdentityKey, NymTopology, PacketType, QueryReqwestRpcNyxdClient,
TopologyProvider,
};
use wasm_client_core::{IdentityKey, NymTopology, PacketType, QueryReqwestRpcNyxdClient};
use wasm_utils::error::PromisableResult;
use wasm_utils::{check_promise_result, console_log};
use wasm_utils::{check_promise_result, console_error, console_log};
#[cfg(feature = "node-tester")]
use crate::helpers::{NymClientTestRequest, WasmTopologyTestExt};
@@ -45,6 +44,7 @@ use crate::helpers::{NymClientTestRequest, WasmTopologyTestExt};
use rand::{rngs::OsRng, RngCore};
#[cfg(feature = "node-tester")]
#[allow(dead_code)]
pub(crate) const NODE_TESTER_CLIENT_ID: &str = "_nym-node-tester-client";
#[wasm_bindgen]
@@ -70,8 +70,8 @@ pub struct NymClient {
pub struct NymClientBuilder {
config: ClientConfig,
force_tls: bool,
custom_topology: Option<NymTopology>,
preferred_gateway: Option<IdentityKey>,
latency_based_selection: Option<bool>,
storage_passphrase: Option<String>,
on_message: js_sys::Function,
@@ -89,11 +89,11 @@ impl NymClientBuilder {
NymClientBuilder {
config,
force_tls,
custom_topology: None,
storage_passphrase,
on_message,
// on_mix_fetch_message: Some(on_mix_fetch_message),
preferred_gateway,
latency_based_selection: None,
}
}
@@ -113,29 +113,32 @@ impl NymClientBuilder {
}
}
let full_config = ClientConfig::new_tester_config(NODE_TESTER_CLIENT_ID);
let _ = on_message;
// let full_config = ClientConfig::new_tester_config(NODE_TESTER_CLIENT_ID);
// Ok(NymClientBuilder {
// config: full_config,
// force_tls: false,
// custom_topology: Some(topology.try_into()?),
// on_message,
// storage_passphrase: None,
// preferred_gateway: gateway,
// latency_based_selection: None,
// })
Ok(NymClientBuilder {
config: full_config,
force_tls: false,
custom_topology: Some(topology.try_into()?),
on_message,
storage_passphrase: None,
preferred_gateway: gateway,
})
Err(WasmClientError::DisabledTester)
}
fn start_reconstructed_pusher(client_output: ClientOutput, on_message: js_sys::Function) {
ResponsePusher::new(client_output, on_message).start()
}
fn topology_provider(&mut self) -> Option<Box<dyn TopologyProvider + Send + Sync>> {
if let Some(hardcoded_topology) = self.custom_topology.take() {
Some(Box::new(HardcodedTopologyProvider::new(hardcoded_topology)))
} else {
None
}
}
// fn topology_provider(&mut self) -> Option<Box<dyn TopologyProvider + Send + Sync>> {
// if let Some(hardcoded_topology) = self.custom_topology.take() {
// Some(Box::new(HardcodedTopologyProvider::new(hardcoded_topology)))
// } else {
// None
// }
// }
fn initialise_storage(
config: &ClientConfig,
@@ -144,48 +147,96 @@ impl NymClientBuilder {
FullWasmClientStorage::new(&config.base, base_storage)
}
async fn start_client_async(mut self) -> Result<NymClient, WasmClientError> {
console_log!("Starting the wasm client");
let nym_api_endpoints = self.config.base.client.nym_api_urls.clone();
// TODO: this will have to be re-used for surbs. but this is a problem for another PR.
async fn initialise_client_storage(&mut self) -> Result<ClientStorage, WasmClientError> {
let client_store =
ClientStorage::new_async(&self.config.base.client.id, self.storage_passphrase.take())
.await?;
if !client_store.has_identity_key().await? {
console_log!(
"no prior keys found - a new set will be generated for client {}",
self.config.base.client.id
);
generate_new_client_keys(&client_store).await?;
}
let user_chosen = self.preferred_gateway.clone();
Ok(client_store)
}
// if we provided hardcoded topology, get gateway from it, otherwise get it the 'standard' way
let init_res = if let Some(topology) = &self.custom_topology {
setup_from_topology(user_chosen, self.force_tls, topology, &client_store).await?
} else {
setup_gateway_from_api(
&client_store,
self.force_tls,
user_chosen,
&nym_api_endpoints,
self.config.base.debug.topology.minimum_gateway_performance,
)
.await?
async fn try_set_preferred_gateway(
&self,
client_store: &ClientStorage,
) -> Result<bool, WasmClientError> {
let Some(preferred) = self.preferred_gateway.as_ref() else {
return Ok(false);
};
if client_store
.has_gateway_details(&preferred.to_string())
.await?
{
GatewaysDetailsStore::set_active_gateway(client_store, &preferred.to_string()).await?;
return Ok(true);
}
Ok(false)
}
// async fn start_client(mut self) -> Result<NymClient, WasmClientError> {
// todo!()
// }
async fn has_active_gateway(
&self,
client_store: &ClientStorage,
) -> Result<bool, WasmClientError> {
Ok(client_store
.get_active_gateway_id()
.await?
.active_gateway_id_bs58
.is_some())
}
async fn start_client_async(mut self) -> Result<NymClient, WasmClientError> {
console_log!("Starting the wasm client");
// TODO: resolve this properly
self.config.base.debug.topology.ignore_egress_epoch_role = true;
// TODO: this will have to be re-used for surbs. but this is a problem for another PR.
let client_store = self.initialise_client_storage().await?;
// if we don't have an active gateway (i.e. no gateways), add one
// otherwise, see if we set a preferred gateway and attempt to set its details as active
if !self.has_active_gateway(&client_store).await?
|| !self.try_set_preferred_gateway(&client_store).await?
{
add_gateway(
self.preferred_gateway.clone(),
self.latency_based_selection,
self.force_tls,
&self.config.base.client.nym_api_urls,
bin_info!().into(),
self.config.base.debug.topology.minimum_gateway_performance,
&client_store,
)
.await?;
}
let packet_type = self.config.base.debug.traffic.packet_type;
let storage = Self::initialise_storage(&self.config, client_store);
let maybe_topology_provider = self.topology_provider();
let mut base_builder = BaseClientBuilder::<QueryReqwestRpcNyxdClient, _>::new(
let base_builder = BaseClientBuilder::<QueryReqwestRpcNyxdClient, _>::new(
&self.config.base,
storage,
None,
);
if let Some(topology_provider) = maybe_topology_provider {
base_builder = base_builder.with_topology_provider(topology_provider);
}
// if let Some(topology_provider) = maybe_topology_provider {
// base_builder = base_builder.with_topology_provider(topology_provider);
// }
if let Ok(reuse_setup) = GatewaySetup::try_reuse_connection(init_res) {
base_builder = base_builder.with_gateway_setup(reuse_setup);
}
// if let Ok(reuse_setup) = GatewaySetup::try_reuse_connection(init_res) {
// base_builder = base_builder.with_gateway_setup(reuse_setup);
// }
let mut started_client = base_builder.start_base().await?;
let self_address = started_client.address.to_string();
@@ -207,7 +258,12 @@ impl NymClientBuilder {
}
pub fn start_client(self) -> Promise {
future_to_promise(async move { self.start_client_async().await.into_promise_result() })
future_to_promise(async move {
self.start_client_async()
.await
.inspect_err(|err| console_error!("failed to start the client: {err}"))
.into_promise_result()
})
}
}
@@ -224,6 +280,9 @@ pub struct ClientOptsSimple {
#[tsify(optional)]
pub(crate) force_tls: Option<bool>,
#[tsify(optional)]
pub(crate) latency_based_selection: Option<bool>,
}
#[derive(Tsify, Debug, Default, Clone, Serialize, Deserialize)]
@@ -281,6 +340,7 @@ impl NymClient {
}
.start_client_async()
.await
.inspect_err(|err| console_error!("failed to start the client: {err}"))
}
#[wasm_bindgen(constructor)]
@@ -323,6 +383,7 @@ impl NymClient {
todo!()
}
#[wasm_bindgen(js_name = "selfAddress")]
pub fn self_address(&self) -> String {
self.self_address.clone()
}
+1
View File
@@ -52,6 +52,7 @@ impl ClientConfig {
}
#[cfg(feature = "node-tester")]
#[allow(dead_code)]
pub(crate) fn new_tester_config<S: Into<String>>(id: S) -> Self {
ClientConfig {
base: BaseClientConfig::new(id.into(), env!("CARGO_PKG_VERSION").to_string())
+3
View File
@@ -36,6 +36,9 @@ pub enum WasmClientError {
#[from]
source: NetworkTestingError,
},
#[error("the node testing features are currently disabled")]
DisabledTester,
}
// I dislike this so much - there must be a better way.
+2
View File
@@ -28,4 +28,6 @@ pub fn main() {
"wasm client version used: {}",
nym_bin_common::bin_info_owned!()
);
wasm_utils::console_log!("[rust main]: setting panic hook");
wasm_utils::set_panic_hook();
}
+2 -2
View File
@@ -1,7 +1,7 @@
[package]
name = "mix-fetch-wasm"
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
version = "1.3.0-rc.0"
version = "1.4.0-rc.0"
edition = "2021"
keywords = ["nym", "fetch", "wasm", "webassembly", "privacy"]
license = "Apache-2.0"
@@ -24,7 +24,7 @@ tokio = { workspace = true, features = ["sync"] }
url = { workspace = true }
wasm-bindgen = { workspace = true }
wasm-bindgen-futures = { workspace = true }
thiserror = { workspace = true }
thiserror = { workspace = true }
tsify = { workspace = true, features = ["js"] }
nym-bin-common = { path = "../../common/bin-common" }
+3
View File
@@ -12,6 +12,9 @@ build-rust:
wasm-pack build --scope nymproject --target web --out-dir ../../dist/wasm/mix-fetch
wasm-opt -Oz -o ../../dist/wasm/mix-fetch/mix_fetch_wasm_bg.wasm ../../dist/wasm/mix-fetch/mix_fetch_wasm_bg.wasm
build-rust-debug:
wasm-pack build --debug --scope nymproject --target no-modules
build-package-json:
node build.mjs
+2 -1
View File
@@ -1,2 +1,3 @@
build/wasm_exec.js
build/main.wasm
build/main.wasm
build
File diff suppressed because it is too large Load Diff
+14 -13
View File
@@ -36,7 +36,8 @@ const {
disconnectMixFetch,
setupMixFetchWithConfig,
mix_fetch_initialised,
finish_mixnet_connection} = wasm_bindgen;
finish_mixnet_connection
} = wasm_bindgen;
let client = null;
let tester = null;
@@ -69,7 +70,7 @@ async function wasm_bindgenSetup() {
const validator = 'https://qa-nym-api.qa.nymte.ch/api';
// local
const mixFetchNetworkRequesterAddress= "2o47bhnXWna6VEyt4mXMGQQAbXfpKmX7BkjkxUz8uQVi.6uQGnCqSczpXwh86NdbsCoDDXuqZQM9Uwko8GE7uC9g8@6qQYb4ArXANU6HJDxzH4PFCUqYb39Dae2Gem2KpxescM";
const mixFetchNetworkRequesterAddress = "2o47bhnXWna6VEyt4mXMGQQAbXfpKmX7BkjkxUz8uQVi.6uQGnCqSczpXwh86NdbsCoDDXuqZQM9Uwko8GE7uC9g8@6qQYb4ArXANU6HJDxzH4PFCUqYb39Dae2Gem2KpxescM";
// const mixFetchNetworkRequesterAddress= "GqiGWmKRCbGQFSqH88BzLKijvZgipnqhmbNFsmkZw84t.4L8sXFuAUyUYyHZYgMdM3AtiusKnYUft6Pd8e41rrCHA@6qQYb4ArXANU6HJDxzH4PFCUqYb39Dae2Gem2KpxescM";
// STEP 1. construct config
@@ -100,7 +101,7 @@ async function wasm_bindgenSetup() {
updatedTraffic.average_packet_delay_ms = 666;
differentDebug.traffic = updatedTraffic;
const config = new MixFetchConfig(mixFetchNetworkRequesterAddress, { debug: differentDebug } );
const config = new MixFetchConfig(mixFetchNetworkRequesterAddress, {debug: differentDebug});
//
// // STEP 2. setup the client
// // note, the extra optional argument is of the following type:
@@ -121,8 +122,8 @@ async function wasm_bindgenSetup() {
}
async function nativeSetup() {
// const preferredGateway = "6qQYb4ArXANU6HJDxzH4PFCUqYb39Dae2Gem2KpxescM";
// const validator = 'https://qa-nym-api.qa.nymte.ch/api';
const preferredGateway = "8ookuLkA9oWfRTjb7Jq4tLGcWrqoXKGQxw84MjMrv2S4";
const validator = 'https://sandbox-nym-api1.nymtech.net/api';
// local
// const preferredNetworkRequester= "2o47bhnXWna6VEyt4mXMGQQAbXfpKmX7BkjkxUz8uQVi.6uQGnCqSczpXwh86NdbsCoDDXuqZQM9Uwko8GE7uC9g8@6qQYb4ArXANU6HJDxzH4PFCUqYb39Dae2Gem2KpxescM";
@@ -153,17 +154,17 @@ async function nativeSetup() {
// await setupMixFetch(preferredNetworkRequester, { nymApiUrl: validator })
// // #3
const noCoverTrafficOverride = {
traffic: { disableMainPoissonPacketDistribution: true },
coverTraffic: { disableLoopCoverTrafficStream: true },
traffic: {disableMainPoissonPacketDistribution: true},
coverTraffic: {disableLoopCoverTrafficStream: true},
}
const mixFetchOverride = {
requestTimeoutMs: 10000
requestTimeoutMs: 20000
}
await setupMixFetch({
// preferredNetworkRequester,
preferredGateway: "E3mvZTHQCdBvhfr178Swx9g4QG3kkRUun7YnToLMcMbM",
storagePassphrase: "foomp",
// preferredGateway: preferredGateway,
// storagePassphrase: "foomp",
forceTls: true,
// nymApiUrl: validator,
clientId: "my-client",
@@ -190,7 +191,7 @@ async function testMixFetch() {
const url = target;
// const args = { mode: "ors", redirect: "manual", signal }
const args = { mode: "unsafe-ignore-cors" }
const args = {mode: "unsafe-ignore-cors"}
try {
console.log('using mixFetch...');
@@ -200,7 +201,7 @@ async function testMixFetch() {
console.log('done')
} catch(e) {
} catch (e) {
console.error("mix fetch request failure: ", e)
}
@@ -244,7 +245,7 @@ async function loadGoWasm() {
goWasm = wasmObj.instance
go.run(goWasm)
} else {
const bytes = await resp.arrayBuffer()
const bytes = await resp.arrayBuffer()
const wasmObj = await WebAssembly.instantiate(bytes, go.importObject)
goWasm = wasmObj.instance
go.run(goWasm)
File diff suppressed because it is too large Load Diff
+74 -19
View File
@@ -9,18 +9,20 @@ use crate::request_writer::RequestWriter;
use crate::socks_helpers::{socks5_connect_request, socks5_data_request};
use crate::{config, RequestId};
use js_sys::Promise;
use nym_bin_common::bin_info;
use nym_socks5_requests::RemoteAddress;
use std::sync::atomic::{AtomicBool, Ordering};
use tokio::sync::Mutex;
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::future_to_promise;
use wasm_client_core::client::base_client::storage::GatewaysDetailsStore;
use wasm_client_core::client::base_client::{BaseClientBuilder, ClientInput, ClientOutput};
use wasm_client_core::client::inbound_messages::InputMessage;
use wasm_client_core::helpers::setup_gateway_from_api;
use wasm_client_core::init::types::GatewaySetup;
use wasm_client_core::helpers::{add_gateway, generate_new_client_keys};
use wasm_client_core::nym_task::connections::TransmissionLane;
use wasm_client_core::nym_task::TaskManager;
use wasm_client_core::storage::core_client_traits::FullWasmClientStorage;
use wasm_client_core::storage::wasm_client_traits::WasmClientStorage;
use wasm_client_core::storage::ClientStorage;
use wasm_client_core::{IdentityKey, QueryReqwestRpcNyxdClient, Recipient};
use wasm_utils::console_log;
@@ -46,6 +48,7 @@ pub struct MixFetchClient {
pub struct MixFetchClientBuilder {
config: MixFetchConfig,
preferred_gateway: Option<IdentityKey>,
latency_based_selection: Option<bool>,
force_tls: bool,
storage_passphrase: Option<String>,
@@ -63,6 +66,7 @@ impl MixFetchClientBuilder {
MixFetchClientBuilder {
config,
preferred_gateway,
latency_based_selection: None,
force_tls,
storage_passphrase,
}
@@ -79,6 +83,51 @@ impl MixFetchClientBuilder {
RequestWriter::new(client_output, requests).start()
}
async fn initialise_client_storage(&mut self) -> Result<ClientStorage, MixFetchError> {
let client_store =
ClientStorage::new_async(&self.config.base.client.id, self.storage_passphrase.take())
.await?;
if !client_store.has_identity_key().await? {
console_log!(
"no prior keys found - a new set will be generated for client {}",
self.config.base.client.id
);
generate_new_client_keys(&client_store).await?;
}
Ok(client_store)
}
async fn try_set_preferred_gateway(
&self,
client_store: &ClientStorage,
) -> Result<bool, MixFetchError> {
let Some(preferred) = self.preferred_gateway.as_ref() else {
return Ok(false);
};
if client_store
.has_gateway_details(&preferred.to_string())
.await?
{
GatewaysDetailsStore::set_active_gateway(client_store, &preferred.to_string()).await?;
return Ok(true);
}
Ok(false)
}
async fn has_active_gateway(
&self,
client_store: &ClientStorage,
) -> Result<bool, MixFetchError> {
Ok(client_store
.get_active_gateway_id()
.await?
.active_gateway_id_bs58
.is_some())
}
// TODO: combine with normal wasm client
async fn start_client_async(mut self) -> Result<MixFetchClient, MixFetchError> {
console_log!("Starting the mix fetch client");
@@ -89,34 +138,40 @@ impl MixFetchClientBuilder {
}
goWasmSetMixFetchRequestTimeout(timeout_ms as u32);
let nym_api_endpoints = self.config.base.client.nym_api_urls.clone();
// TODO: resolve this properly
self.config.base.debug.topology.ignore_egress_epoch_role = true;
// TODO: this will have to be re-used for surbs. but this is a problem for another PR.
let client_store =
ClientStorage::new_async(&self.config.base.client.id, self.storage_passphrase.take())
.await?;
let client_store = self.initialise_client_storage().await?;
let user_chosen = self.preferred_gateway.clone();
let init_res = setup_gateway_from_api(
&client_store,
self.force_tls,
user_chosen,
&nym_api_endpoints,
self.config.base.debug.topology.minimum_gateway_performance,
)
.await?;
// if we don't have an active gateway (i.e. no gateways), add one
// otherwise, see if we set a preferred gateway and attempt to set its details as active
if !self.has_active_gateway(&client_store).await?
|| !self.try_set_preferred_gateway(&client_store).await?
{
add_gateway(
self.preferred_gateway.clone(),
self.latency_based_selection,
self.force_tls,
&self.config.base.client.nym_api_urls,
bin_info!().into(),
self.config.base.debug.topology.minimum_gateway_performance,
&client_store,
)
.await?;
}
let storage = Self::initialise_storage(&self.config, client_store);
let mut base_builder = BaseClientBuilder::<QueryReqwestRpcNyxdClient, _>::new(
let base_builder = BaseClientBuilder::<QueryReqwestRpcNyxdClient, _>::new(
&self.config.base,
storage,
None,
);
if let Ok(reuse_setup) = GatewaySetup::try_reuse_connection(init_res) {
base_builder = base_builder.with_gateway_setup(reuse_setup);
}
// if let Ok(reuse_setup) = GatewaySetup::try_reuse_connection(init_res) {
// base_builder = base_builder.with_gateway_setup(reuse_setup);
// }
let mut started_client = base_builder.start_base().await?;
let self_address = started_client.address;
+2
View File
@@ -35,4 +35,6 @@ pub fn main() {
"mix fetch version used: {}",
nym_bin_common::bin_info_owned!()
);
wasm_utils::console_log!("[rust main]: setting panic hook");
wasm_utils::set_panic_hook();
}