Compare commits

..

14 Commits

Author SHA1 Message Date
durch 1b33d8e2c9 Address part of PR comments 2024-06-25 15:01:18 +02:00
durch 2fe282d774 fmt 2024-06-19 13:11:18 +02:00
durch abfd121d4e Log decoding error 2024-06-19 13:09:34 +02:00
durch 46ebb7039a Cleanup prints 2024-06-18 19:54:39 +02:00
durch bc28cc70b7 Update IPR sig 2024-06-18 11:58:03 +02:00
durch 36e8468b73 WASM changes 2024-06-18 10:30:56 +02:00
durch cefb217c25 AsyncWrite 2024-06-18 10:30:56 +02:00
durch 4adcd32ebf Use tokio AsyncRead 2024-06-18 10:30:56 +02:00
durch 7820b816b5 ReconstructedMessageCodec 2024-06-18 10:30:56 +02:00
Drazen f54cee786c InputMessageCodec, Serde for MixPacket 2024-06-18 10:30:56 +02:00
Drazen edd9fef468 Use Sink always 2024-06-18 10:30:56 +02:00
Drazen eed87ff4c9 Switch to PollSender 2024-06-18 10:30:56 +02:00
durch 575056ac67 AsyncRead for MixnetClient 2024-06-18 10:30:56 +02:00
durch 34e9822f1d serde for ReconstructedMessage 2024-06-18 10:30:56 +02:00
96 changed files with 928 additions and 1187 deletions
@@ -104,9 +104,12 @@ jobs:
name: nym-binaries-artifacts
path: |
target/release/nym-client
target/release/nym-gateway
target/release/nym-mixnode
target/release/nym-socks5-client
target/release/nym-api
target/release/nym-network-requester
target/release/nym-network-statistics
target/release/nym-cli
target/release/nymvisor
target/release/nym-node
@@ -121,9 +124,12 @@ jobs:
OUTPUT_DIR: ci-builds/${{ github.ref_name }}
run: |
cp target/release/nym-client $OUTPUT_DIR
cp target/release/nym-gateway $OUTPUT_DIR
cp target/release/nym-mixnode $OUTPUT_DIR
cp target/release/nym-socks5-client $OUTPUT_DIR
cp target/release/nym-api $OUTPUT_DIR
cp target/release/nym-network-requester $OUTPUT_DIR
cp target/release/nym-network-statistics $OUTPUT_DIR
cp target/release/nymvisor $OUTPUT_DIR
cp target/release/nym-node $OUTPUT_DIR
cp target/release/nym-cli $OUTPUT_DIR
+2
View File
@@ -6,6 +6,7 @@ on:
- 'clients/**'
- 'common/**'
- 'explorer-api/**'
- 'ephemera/**'
- 'gateway/**'
- 'integrations/**'
- 'mixnode/**'
@@ -23,6 +24,7 @@ on:
- 'clients/**'
- 'common/**'
- 'explorer-api/**'
- 'ephemera/**'
- 'gateway/**'
- 'integrations/**'
- 'mixnode/**'
@@ -27,17 +27,23 @@ jobs:
release_id: ${{ steps.create-release.outputs.id }}
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].published_at }}
client_hash: ${{ steps.binary-hashes.outputs.client_hash }}
mixnode_hash: ${{ steps.binary-hashes.outputs.mixnode_hash }}
gateway_hash: ${{ steps.binary-hashes.outputs.gateway_hash }}
nymvisor_hash: ${{ steps.binary-hashes.outputs.nymvisor_hash }}
nymnode_hash: ${{ steps.binary-hashes.outputs.nymnode_hash }}
socks5_hash: ${{ steps.binary-hashes.outputs.socks5_hash }}
netreq_hash: ${{ steps.binary-hashes.outputs.netreq_hash }}
cli_hash: ${{ steps.binary-hashes.outputs.cli_hash }}
netstat_hash: ${{ steps.binary-hashes.outputs.netstat_hash }}
client_version: ${{ steps.binary-versions.outputs.client_version }}
mixnode_version: ${{ steps.binary-versions.outputs.mixnode_version }}
gateway_version: ${{ steps.binary-versions.outputs.gateway_version }}
nymvisor_version: ${{ steps.binary-versions.outputs.nymvisor_version }}
nymnode_version: ${{ steps.binary-versions.outputs.nymnode_version }}
socks5_version: ${{ steps.binary-versions.outputs.socks5_version }}
netreq_version: ${{ steps.binary-versions.outputs.netreq_version }}
cli_version: ${{ steps.binary-versions.outputs.cli_version }}
netstat_version: ${{ steps.binary-versions.outputs.netstat_version }}
steps:
- uses: actions/checkout@v3
@@ -69,9 +75,12 @@ jobs:
path: |
target/release/explorer-api
target/release/nym-client
target/release/nym-gateway
target/release/nym-mixnode
target/release/nym-socks5-client
target/release/nym-api
target/release/nym-network-requester
target/release/nym-network-statistics
target/release/nym-cli
target/release/nymvisor
target/release/nym-node
@@ -85,9 +94,12 @@ jobs:
files: |
target/release/explorer-api
target/release/nym-client
target/release/nym-gateway
target/release/nym-mixnode
target/release/nym-socks5-client
target/release/nym-api
target/release/nym-network-requester
target/release/nym-network-statistics
target/release/nym-cli
target/release/nymvisor
target/release/nym-node
-72
View File
@@ -4,78 +4,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [2024.7-doubledecker] (2024-07-04)
- Add an early return in `parse_raw_str_logs` for empty raw log strings. ([#4686])
- Bump braces from 3.0.2 to 3.0.3 in /wasm/mix-fetch/internal-dev ([#4672])
- add expiry returned on import ([#4670])
- [bugfix] missing rustls feature ([#4666])
- Bump ws from 8.13.0 to 8.17.1 in /wasm/client/internal-dev-node ([#4665])
- Bump braces from 3.0.2 to 3.0.3 in /clients/native/examples/js-examples/websocket ([#4663])
- Bump ws from 8.14.2 to 8.17.1 in /sdk/typescript/packages/nodejs-client ([#4662])
- Update setup.md ([#4661])
- New clippy lints ([#4660])
- Bump braces from 3.0.2 to 3.0.3 in /nym-api/tests ([#4659])
- Bump braces from 3.0.2 to 3.0.3 in /docker/typescript_client/upload_contract ([#4658])
- Update vps-setup.md ([#4656])
- Update configuration.md ([#4655])
- Remove old PR template ([#4639])
[#4686]: https://github.com/nymtech/nym/pull/4686
[#4672]: https://github.com/nymtech/nym/pull/4672
[#4670]: https://github.com/nymtech/nym/pull/4670
[#4666]: https://github.com/nymtech/nym/pull/4666
[#4665]: https://github.com/nymtech/nym/pull/4665
[#4663]: https://github.com/nymtech/nym/pull/4663
[#4662]: https://github.com/nymtech/nym/pull/4662
[#4661]: https://github.com/nymtech/nym/pull/4661
[#4660]: https://github.com/nymtech/nym/pull/4660
[#4659]: https://github.com/nymtech/nym/pull/4659
[#4658]: https://github.com/nymtech/nym/pull/4658
[#4656]: https://github.com/nymtech/nym/pull/4656
[#4655]: https://github.com/nymtech/nym/pull/4655
[#4639]: https://github.com/nymtech/nym/pull/4639
## [2024.6-chomp] (2024-06-25)
- Remove additional code as part of Ephemera Purge and SP and contracts ([#4650])
- bugfix: make sure nym-api can handle non-cw2 (or without detailed build info) compliant contracts ([#4648])
- introduced a flag to accept toc and exposed it via self-described API ([#4647])
- bugfix: make sure to return an error on invalid public ip ([#4646])
- Add ci check for PR having an assigned milestone ([#4644])
- Removed ephemera code ([#4642])
- Remove stale peers ([#4640])
- Add generic wg private network routing ([#4636])
- Feature/new node endpoints ([#4635])
- standarised ContractBuildInformation and added it to all contracts ([#4631])
- validate nym-node public ips on startup ([#4630])
- Bump defguard wg ([#4625])
- Fix cargo warnings ([#4624])
- Update kernel peers on peer modification ([#4622])
- Handle v6 and v7 requests in the IPR, but reply with v6 ([#4620])
- fix typo ([#4619])
- Update crypto and rand crates ([#4607])
- Purge name service and service provider directory contracts ([#4603])
[#4650]: https://github.com/nymtech/nym/pull/4650
[#4648]: https://github.com/nymtech/nym/pull/4648
[#4647]: https://github.com/nymtech/nym/pull/4647
[#4646]: https://github.com/nymtech/nym/pull/4646
[#4644]: https://github.com/nymtech/nym/pull/4644
[#4642]: https://github.com/nymtech/nym/pull/4642
[#4640]: https://github.com/nymtech/nym/pull/4640
[#4636]: https://github.com/nymtech/nym/pull/4636
[#4635]: https://github.com/nymtech/nym/pull/4635
[#4631]: https://github.com/nymtech/nym/pull/4631
[#4630]: https://github.com/nymtech/nym/pull/4630
[#4625]: https://github.com/nymtech/nym/pull/4625
[#4624]: https://github.com/nymtech/nym/pull/4624
[#4622]: https://github.com/nymtech/nym/pull/4622
[#4620]: https://github.com/nymtech/nym/pull/4620
[#4619]: https://github.com/nymtech/nym/pull/4619
[#4607]: https://github.com/nymtech/nym/pull/4607
[#4603]: https://github.com/nymtech/nym/pull/4603
## [2024.5-ragusa] (2024-05-22)
- Feature/nym node api location ([#4605])
Generated
+18 -8
View File
@@ -2093,7 +2093,7 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
[[package]]
name = "explorer-api"
version = "1.1.36"
version = "1.1.34"
dependencies = [
"chrono",
"clap 4.5.4",
@@ -3858,7 +3858,7 @@ dependencies = [
[[package]]
name = "nym-api"
version = "1.1.40"
version = "1.1.38"
dependencies = [
"anyhow",
"async-trait",
@@ -4018,7 +4018,7 @@ dependencies = [
[[package]]
name = "nym-cli"
version = "1.1.38"
version = "1.1.36"
dependencies = [
"anyhow",
"base64 0.13.1",
@@ -4097,7 +4097,7 @@ dependencies = [
[[package]]
name = "nym-client"
version = "1.1.37"
version = "1.1.35"
dependencies = [
"bs58 0.5.1",
"clap 4.5.4",
@@ -4138,6 +4138,7 @@ version = "1.1.15"
dependencies = [
"async-trait",
"base64 0.21.7",
"bincode",
"bs58 0.5.1",
"cfg-if",
"clap 4.5.4",
@@ -4180,6 +4181,7 @@ dependencies = [
"tokio",
"tokio-stream",
"tokio-tungstenite",
"tokio-util",
"tungstenite",
"url",
"wasm-bindgen",
@@ -4253,6 +4255,7 @@ dependencies = [
"serde-wasm-bindgen 0.6.5",
"serde_json",
"thiserror",
"tokio",
"tsify",
"wasm-bindgen",
"wasm-bindgen-futures",
@@ -4910,7 +4913,7 @@ dependencies = [
[[package]]
name = "nym-network-requester"
version = "1.1.38"
version = "1.1.36"
dependencies = [
"addr",
"anyhow",
@@ -4956,6 +4959,7 @@ dependencies = [
"time",
"tokio",
"tokio-tungstenite",
"tokio-util",
"url",
"zeroize",
]
@@ -4979,7 +4983,7 @@ dependencies = [
[[package]]
name = "nym-node"
version = "1.1.4"
version = "1.1.2"
dependencies = [
"anyhow",
"bip39",
@@ -5241,7 +5245,7 @@ dependencies = [
[[package]]
name = "nym-socks5-client"
version = "1.1.37"
version = "1.1.35"
dependencies = [
"bs58 0.5.1",
"clap 4.5.4",
@@ -5300,6 +5304,7 @@ dependencies = [
"tap",
"thiserror",
"tokio",
"tokio-util",
"url",
]
@@ -5359,6 +5364,7 @@ dependencies = [
name = "nym-sphinx"
version = "0.1.0"
dependencies = [
"bincode",
"log",
"nym-crypto",
"nym-mixnet-contract-common",
@@ -5375,8 +5381,10 @@ dependencies = [
"nym-topology",
"rand 0.8.5",
"rand_distr",
"serde",
"thiserror",
"tokio",
"tokio-util",
]
[[package]]
@@ -5463,6 +5471,7 @@ dependencies = [
"nym-sphinx-addressing",
"nym-sphinx-params",
"nym-sphinx-types",
"serde",
"thiserror",
]
@@ -5540,6 +5549,7 @@ version = "0.1.0"
dependencies = [
"futures",
"log",
"serde",
"thiserror",
"tokio",
"wasm-bindgen",
@@ -5767,7 +5777,7 @@ dependencies = [
[[package]]
name = "nymvisor"
version = "0.1.3"
version = "0.1.1"
dependencies = [
"anyhow",
"bytes",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client"
version = "1.1.37"
version = "1.1.35"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021"
-1
View File
@@ -73,7 +73,6 @@ impl From<Init> for OverrideConfig {
fn from(init_config: Init) -> Self {
OverrideConfig {
nym_apis: init_config.common_args.nym_apis,
fronting_domains: init_config.common_args.fronting_domains,
disable_socket: init_config.disable_socket,
port: init_config.port,
host: init_config.host,
-5
View File
@@ -97,7 +97,6 @@ pub(crate) enum Commands {
// Configuration that can be overridden.
pub(crate) struct OverrideConfig {
nym_apis: Option<Vec<url::Url>>,
fronting_domains: Option<Vec<url::Url>>,
disable_socket: Option<bool>,
port: Option<u16>,
host: Option<IpAddr>,
@@ -134,10 +133,6 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
.with_base(BaseClientConfig::with_disabled_cover_traffic, args.no_cover)
.with_optional(Config::with_port, args.port)
.with_optional(Config::with_host, args.host)
.with_optional_ext(
BaseClientConfig::with_fronting_domains,
args.fronting_domains,
)
.with_optional_custom_env_ext(
BaseClientConfig::with_custom_nym_apis,
args.nym_apis,
-1
View File
@@ -36,7 +36,6 @@ impl From<Run> for OverrideConfig {
fn from(run_config: Run) -> Self {
OverrideConfig {
nym_apis: run_config.common_args.nym_apis,
fronting_domains: run_config.common_args.fronting_domains,
disable_socket: run_config.disable_socket,
port: run_config.port,
host: run_config.host,
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "1.1.37"
version = "1.1.35"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021"
+5 -1
View File
@@ -10,6 +10,7 @@ license.workspace = true
[dependencies]
async-trait = { workspace = true }
bincode = { workspace = true }
base64 = "0.21.2"
bs58 = { workspace = true }
cfg-if = { workspace = true }
@@ -26,6 +27,7 @@ tap = { workspace = true }
thiserror = { workspace = true }
url = { workspace = true, features = ["serde"] }
tokio = { workspace = true, features = ["macros"] }
tokio-util = { workspace = true, features = ["codec"] }
time = { workspace = true }
zeroize = { workspace = true }
@@ -47,7 +49,9 @@ nym-validator-client = { path = "../client-libs/validator-client", default-featu
nym-task = { path = "../task" }
nym-credential-storage = { path = "../credential-storage" }
nym-network-defaults = { path = "../network-defaults" }
nym-client-core-config-types = { path = "./config-types", features = ["disk-persistence"] }
nym-client-core-config-types = { path = "./config-types", features = [
"disk-persistence",
] }
nym-client-core-surb-storage = { path = "./surb-storage" }
nym-client-core-gateways-storage = { path = "./gateways-storage" }
@@ -117,11 +117,6 @@ impl Config {
self
}
pub fn with_fronting_domains(mut self, fronting_domains: Vec<Url>) -> Self {
self.client.fronting_domains = Some(fronting_domains);
self
}
pub fn set_custom_nym_apis(&mut self, nym_api_urls: Vec<Url>) {
self.client.nym_api_urls = nym_api_urls;
}
@@ -294,10 +289,6 @@ impl Config {
pub fn get_nym_api_endpoints(&self) -> Vec<Url> {
self.client.nym_api_urls.clone()
}
pub fn get_fronting_domains(&self) -> Option<Vec<Url>> {
self.client.fronting_domains.clone()
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
@@ -323,9 +314,6 @@ pub struct Client {
/// Addresses to APIs running on validator from which the client gets the view of the network.
#[serde(alias = "validator_api_urls")]
pub nym_api_urls: Vec<Url>,
/// Domain to use for domain fronting censorship circumvention
pub fronting_domains: Option<Vec<Url>>,
}
impl Client {
@@ -352,7 +340,6 @@ impl Client {
disabled_credentials_mode: true,
nyxd_urls,
nym_api_urls,
fronting_domains: None,
}
}
@@ -362,7 +349,6 @@ impl Client {
disabled_credentials_mode: bool,
nyxd_urls: Vec<Url>,
nym_api_urls: Vec<Url>,
fronting_domains: Option<Vec<Url>>,
) -> Self {
Client {
version: version.into(),
@@ -370,7 +356,6 @@ impl Client {
disabled_credentials_mode,
nyxd_urls,
nym_api_urls,
fronting_domains,
}
}
}
@@ -95,7 +95,6 @@ impl From<ConfigV5> for Config {
id: value.client.id,
disabled_credentials_mode: value.client.disabled_credentials_mode,
nyxd_urls: value.client.nyxd_urls,
fronting_domains: None, //SW need proper migrations if it gets applied
nym_api_urls: value.client.nym_api_urls,
},
debug: DebugConfig {
@@ -111,7 +111,7 @@ where
hardcoded_topology.get_gateways()
} else {
let mut rng = rand::thread_rng();
crate::init::helpers::current_gateways(&mut rng, &core.client.nym_api_urls, None).await?
crate::init::helpers::current_gateways(&mut rng, &core.client.nym_api_urls).await?
};
// since we're registering with a brand new gateway,
@@ -70,13 +70,6 @@ pub struct CommonClientInitArgs {
)]
pub nym_apis: Option<Vec<url::Url>>,
///Comma separated list of urls to use for domain fronting
#[cfg_attr(
feature = "cli",
clap(long, value_delimiter = ',', requires = "nym_apis", hide = true)
)]
pub fronting_domains: Option<Vec<url::Url>>,
/// Path to .json file containing custom network specification.
#[cfg_attr(feature = "cli", clap(long, group = "network", hide = true))]
pub custom_mixnet: Option<PathBuf>,
@@ -151,16 +144,6 @@ where
.collect::<Vec<&str>>()
.join(",")
);
if let Some(fronting_domains) = &core.client.fronting_domains {
log::info!(
"fronted by : {}",
fronting_domains
.iter()
.map(|url| url.host_str().unwrap_or_default())
.collect::<Vec<&str>>()
.join(",")
);
}
let key_store = OnDiskKeys::new(paths.keys.clone());
let details_store = setup_fs_gateways_storage(&paths.gateway_registrations).await?;
@@ -180,12 +163,7 @@ where
hardcoded_topology.get_gateways()
} else {
let mut rng = rand::thread_rng();
crate::init::helpers::current_gateways(
&mut rng,
&core.client.nym_api_urls,
core.client.fronting_domains.as_ref(),
)
.await?
crate::init::helpers::current_gateways(&mut rng, &core.client.nym_api_urls).await?
};
let gateway_setup = GatewaySetup::New {
@@ -35,13 +35,6 @@ pub struct CommonClientRunArgs {
)]
pub nym_apis: Option<Vec<url::Url>>,
///Comma separated list of urls to use for domain fronting
#[cfg_attr(
feature = "cli",
clap(long, value_delimiter = ',', requires = "nym_apis", hide = true)
)]
pub fronting_domains: Option<Vec<url::Url>>,
/// Path to .json file containing custom network specification.
#[cfg_attr(feature = "cli", clap(long, group = "network", hide = true))]
pub custom_mixnet: Option<PathBuf>,
@@ -3,7 +3,6 @@
use super::packet_statistics_control::PacketStatisticsReporter;
use super::received_buffer::ReceivedBufferMessage;
use super::topology_control::fronted_api_provider::FrontedApiTopologyProvider;
use super::topology_control::geo_aware_provider::GeoAwareTopologyProvider;
use crate::client::base_client::storage::helpers::store_client_keys;
use crate::client::base_client::storage::MixnetClientStorage;
@@ -36,6 +35,7 @@ use crate::init::{
};
use crate::{config, spawn_future};
use futures::channel::mpsc;
use futures::SinkExt;
use log::{debug, error, info, warn};
use nym_bandwidth_controller::BandwidthController;
use nym_client_core_gateways_storage::{GatewayDetails, GatewaysDetailsStore};
@@ -60,6 +60,7 @@ use std::fmt::Debug;
use std::os::raw::c_int as RawFd;
use std::path::Path;
use std::sync::Arc;
use tokio_util::sync::{PollSendError, PollSender};
use url::Url;
#[cfg(all(
@@ -79,10 +80,7 @@ pub struct ClientInput {
}
impl ClientInput {
pub async fn send(
&self,
message: InputMessage,
) -> Result<(), tokio::sync::mpsc::error::SendError<InputMessage>> {
pub async fn send(&mut self, message: InputMessage) -> Result<(), PollSendError<InputMessage>> {
self.input_sender.send(message).await
}
}
@@ -468,29 +466,17 @@ where
custom_provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
config_topology: config::Topology,
nym_api_urls: Vec<Url>,
fronting_domains: Option<Vec<Url>>,
) -> Box<dyn TopologyProvider + Send + Sync> {
// if no custom provider was ... provided ..., create one using nym-api
custom_provider.unwrap_or_else(|| match config_topology.topology_structure {
config::TopologyStructure::NymApi => match fronting_domains {
Some(domains) => Box::new(FrontedApiTopologyProvider::new(
nym_api_provider::Config {
min_mixnode_performance: config_topology.minimum_mixnode_performance,
min_gateway_performance: config_topology.minimum_gateway_performance,
},
nym_api_urls,
domains,
env!("CARGO_PKG_VERSION").to_string(),
)),
None => Box::new(NymApiTopologyProvider::new(
nym_api_provider::Config {
min_mixnode_performance: config_topology.minimum_mixnode_performance,
min_gateway_performance: config_topology.minimum_gateway_performance,
},
nym_api_urls,
env!("CARGO_PKG_VERSION").to_string(),
)),
},
config::TopologyStructure::NymApi => Box::new(NymApiTopologyProvider::new(
nym_api_provider::Config {
min_mixnode_performance: config_topology.minimum_mixnode_performance,
min_gateway_performance: config_topology.minimum_gateway_performance,
},
nym_api_urls,
env!("CARGO_PKG_VERSION").to_string(),
)),
config::TopologyStructure::GeoAware(group_by) => {
Box::new(GeoAwareTopologyProvider::new(
nym_api_urls,
@@ -702,7 +688,6 @@ where
self.custom_topology_provider.take(),
self.config.debug.topology,
self.config.get_nym_api_endpoints(),
self.config.get_fronting_domains(),
);
// needs to be started as the first thing to block if required waiting for the gateway
@@ -818,7 +803,7 @@ where
client_input: ClientInputStatus::AwaitingProducer {
client_input: ClientInput {
connection_command_sender: client_connection_tx,
input_sender,
input_sender: PollSender::new(input_sender),
},
},
client_output: ClientOutputStatus::AwaitingConsumer {
@@ -1,16 +1,26 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
use nym_sphinx::forwarding::packet::MixPacket;
use nym_sphinx::params::PacketType;
use nym_task::connections::TransmissionLane;
use serde::{Deserialize, Serialize};
use std::convert::TryInto;
use tokio_util::{
bytes::Buf,
bytes::BytesMut,
codec::{Decoder, Encoder},
};
pub type InputMessageSender = tokio::sync::mpsc::Sender<InputMessage>;
use crate::error::ClientCoreError;
pub type InputMessageSender = tokio_util::sync::PollSender<InputMessage>;
pub type InputMessageReceiver = tokio::sync::mpsc::Receiver<InputMessage>;
#[derive(Debug)]
const LENGHT_ENCODING_PREFIX_SIZE: usize = 4;
#[derive(Serialize, Deserialize, Debug)]
pub enum InputMessage {
/// Fire an already prepared mix packets into the network.
/// No guarantees are made about it. For example no retransmssion
@@ -64,6 +74,10 @@ pub enum InputMessage {
}
impl InputMessage {
pub fn simple(data: &[u8], recipient: Recipient) -> Self {
InputMessage::new_regular(recipient, data.to_vec(), TransmissionLane::General, None)
}
pub fn new_premade(
msgs: Vec<MixPacket>,
lane: TransmissionLane,
@@ -197,4 +211,70 @@ impl InputMessage {
InputMessage::MessageWrapper { message, .. } => message.lane(),
}
}
pub fn serialized_size(&self) -> u64 {
bincode::serialized_size(self).expect("failed to get serialized InputMessage size")
+ LENGHT_ENCODING_PREFIX_SIZE as u64
}
}
// TODO: Tests
pub struct AdressedInputMessageCodec(pub Recipient);
impl Encoder<&[u8]> for AdressedInputMessageCodec {
type Error = ClientCoreError;
fn encode(&mut self, item: &[u8], buf: &mut BytesMut) -> Result<(), Self::Error> {
let mut codec = InputMessageCodec;
let input_message = InputMessage::simple(item, self.0.clone());
codec.encode(input_message, buf)?;
Ok(())
}
}
pub struct InputMessageCodec;
impl Encoder<InputMessage> for InputMessageCodec {
type Error = ClientCoreError;
fn encode(&mut self, item: InputMessage, buf: &mut BytesMut) -> Result<(), Self::Error> {
let encoded = bincode::serialize(&item).expect("failed to serialize InputMessage");
let encoded_len = encoded.len() as u32;
let mut encoded_with_len = encoded_len.to_le_bytes().to_vec();
encoded_with_len.extend(encoded);
buf.reserve(encoded_with_len.len());
buf.extend_from_slice(&encoded_with_len);
Ok(())
}
}
impl Decoder for InputMessageCodec {
type Item = InputMessage;
type Error = ClientCoreError;
fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
if buf.len() < LENGHT_ENCODING_PREFIX_SIZE {
return Ok(None);
}
let len = u32::from_le_bytes(
buf[0..LENGHT_ENCODING_PREFIX_SIZE]
.try_into()
.expect("Could not coarce to array"),
) as usize;
if buf.len() < len + LENGHT_ENCODING_PREFIX_SIZE {
return Ok(None);
}
let decoded = match bincode::deserialize(
&buf[LENGHT_ENCODING_PREFIX_SIZE..len + LENGHT_ENCODING_PREFIX_SIZE],
) {
Ok(decoded) => decoded,
Err(_) => return Ok(None),
};
buf.advance(len + LENGHT_ENCODING_PREFIX_SIZE);
Ok(Some(decoded))
}
}
@@ -1,147 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use async_trait::async_trait;
use log::{debug, error, warn};
use nym_topology::provider_trait::TopologyProvider;
use nym_topology::{NymTopology, NymTopologyError};
use rand::prelude::SliceRandom;
use rand::thread_rng;
use url::Url;
use super::nym_api_provider::Config;
pub(crate) struct FrontedApiTopologyProvider {
config: Config,
validator_client: nym_validator_client::client::NymApiClient,
nym_api_urls: Vec<Url>,
fronting_domains: Vec<Url>,
shuffling: Vec<usize>,
client_version: String,
currently_used_api: usize,
}
impl FrontedApiTopologyProvider {
pub(crate) fn new(
config: Config,
nym_api_urls: Vec<Url>,
fronting_domains: Vec<Url>,
client_version: String,
) -> Self {
//SW for the PoC, we assume same lenght between fronting domains and api_urls
let mut shuffling = (0..nym_api_urls.len()).collect::<Vec<_>>();
shuffling.shuffle(&mut thread_rng());
FrontedApiTopologyProvider {
config,
validator_client: nym_validator_client::client::NymApiClient::new_fronted(
nym_api_urls[shuffling[0]].clone(),
fronting_domains[shuffling[0]].clone(),
),
nym_api_urls,
fronting_domains,
shuffling,
client_version,
currently_used_api: 0,
}
}
fn use_next_nym_api(&mut self) {
if self.nym_api_urls.len() == 1 {
warn!("There's only a single nym API available - it won't be possible to use a different one");
return;
}
self.currently_used_api = (self.currently_used_api + 1) % self.nym_api_urls.len();
self.validator_client.change_nym_api_with_fronting(
self.nym_api_urls[self.shuffling[self.currently_used_api]].clone(),
self.fronting_domains[self.shuffling[self.currently_used_api]].clone(),
);
}
/// Verifies whether nodes a reasonably distributed among all mix layers.
///
/// In ideal world we would have 33% nodes on layer 1, 33% on layer 2 and 33% on layer 3.
/// However, this is a rather unrealistic expectation, instead we check whether there exists
/// a layer with more than 66% of nodes or with fewer than 15% and if so, we trigger a failure.
///
/// # Arguments
///
/// * `topology`: active topology constructed from validator api data
fn check_layer_distribution(
&self,
active_topology: &NymTopology,
) -> Result<(), NymTopologyError> {
let lower_threshold = 0.15;
let upper_threshold = 0.66;
active_topology.ensure_even_layer_distribution(lower_threshold, upper_threshold)
}
async fn get_current_compatible_topology(&mut self) -> Option<NymTopology> {
let mixnodes = match self
.validator_client
.get_basic_mixnodes(Some(self.client_version.clone()))
.await
{
Err(err) => {
error!("failed to get network mixnodes - {err}");
return None;
}
Ok(mixes) => mixes,
};
let gateways = match self
.validator_client
.get_basic_gateways(Some(self.client_version.clone()))
.await
{
Err(err) => {
error!("failed to get network gateways - {err}");
return None;
}
Ok(gateways) => gateways,
};
debug!(
"there are {} mixnodes and {} gateways in total (before performance filtering)",
mixnodes.len(),
gateways.len()
);
let topology = NymTopology::from_unordered(
mixnodes.iter().filter(|m| {
m.performance.round_to_integer() >= self.config.min_mixnode_performance
}),
gateways.iter().filter(|g| {
g.performance.round_to_integer() >= self.config.min_gateway_performance
}),
);
if let Err(err) = self.check_layer_distribution(&topology) {
warn!("The current filtered active topology has extremely skewed layer distribution. It cannot be used: {err}");
self.use_next_nym_api();
None
} else {
Some(topology)
}
}
}
// hehe, wasm
#[cfg(not(target_arch = "wasm32"))]
#[async_trait]
impl TopologyProvider for FrontedApiTopologyProvider {
async fn get_new_topology(&mut self) -> Option<NymTopology> {
self.get_current_compatible_topology().await
}
}
#[cfg(target_arch = "wasm32")]
#[async_trait(?Send)]
impl TopologyProvider for FrontedApiTopologyProvider {
async fn get_new_topology(&mut self) -> Option<NymTopology> {
self.get_current_compatible_topology().await
}
}
@@ -17,7 +17,6 @@ use tokio::time::sleep;
use wasmtimer::tokio::sleep;
mod accessor;
pub(crate) mod fronted_api_provider;
pub mod geo_aware_provider;
pub(crate) mod nym_api_provider;
+5 -22
View File
@@ -9,7 +9,6 @@ use nym_crypto::asymmetric::identity;
use nym_gateway_client::GatewayClient;
use nym_topology::{filter::VersionFilterable, gateway, mix};
use nym_validator_client::client::IdentityKeyRef;
use rand::seq::IteratorRandom;
use rand::{seq::SliceRandom, Rng};
use std::{sync::Arc, time::Duration};
use tungstenite::Message;
@@ -60,29 +59,13 @@ impl<'a> GatewayWithLatency<'a> {
pub async fn current_gateways<R: Rng>(
rng: &mut R,
nym_apis: &[Url],
fronting_domains: Option<&Vec<Url>>,
) -> Result<Vec<gateway::Node>, ClientCoreError> {
let client = match fronting_domains {
Some(domains) => {
let (api_url, fronting_url) = nym_apis
.iter()
.zip(domains)
.choose(rng)
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
let nym_api = nym_apis
.choose(rng)
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
let client = nym_validator_client::client::NymApiClient::new(nym_api.clone());
nym_validator_client::client::NymApiClient::new_fronted(
api_url.clone(),
fronting_url.clone(),
)
}
None => {
let nym_api = nym_apis
.choose(rng)
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
nym_validator_client::client::NymApiClient::new(nym_api.clone())
}
};
log::debug!("Fetching list of gateways from: {}", client.api_url());
log::debug!("Fetching list of gateways from: {nym_api}");
let gateways = client.get_cached_described_gateways().await?;
log::debug!("Found {} gateways", gateways.len());
@@ -258,12 +258,6 @@ impl NymApiClient {
NymApiClient { nym_api }
}
pub fn new_fronted(api_url: Url, fronting_url: Url) -> Self {
let nym_api = nym_api::Client::new_fronted(api_url, fronting_url, None);
NymApiClient { nym_api }
}
pub fn api_url(&self) -> &Url {
self.nym_api.current_url()
}
@@ -272,15 +266,6 @@ impl NymApiClient {
self.nym_api.change_base_url(new_endpoint);
}
pub fn change_nym_api_with_fronting(
&mut self,
new_api_endpoint: Url,
new_fronting_domain: Url,
) {
self.nym_api
.change_fronted_url(new_api_endpoint, new_fronting_domain);
}
pub async fn get_basic_mixnodes(
&self,
semver_compatibility: Option<String>,
@@ -9,12 +9,16 @@ pub use nym_coconut_bandwidth_contract_common::event_attributes::*;
pub use nym_coconut_dkg_common::event_attributes::*;
// it seems that currently validators just emit stringified events (which are also returned as part of deliverTx response)
// as their logs
// as theirs logs
#[derive(Debug, Serialize, Deserialize)]
pub struct Log {
#[serde(default)]
// weird thing is that the first msg_index seems to always be undefined on the raw logs
pub msg_index: usize,
// unless I'm missing something obvious, the "log" type in cosmjs is always an empty string
// and launchpad cosmos validator was setting it to what essentially is just the raw version of what
// we received (and we don't care about launchpad, we, as the time of writing this, work on the stargate)
// log: String,
pub events: Vec<cosmwasm_std::Event>,
}
@@ -33,12 +37,8 @@ pub fn find_attribute<'a>(
.find(|attr| attr.key == attribute_key)
}
// these two functions were separated so that the internal logic could actually be tested
// those two functions were separated so that the internal logic could actually be tested
fn parse_raw_str_logs(raw: &str) -> Result<Vec<Log>, NyxdError> {
if raw.is_empty() {
return Ok(Vec::new());
}
let logs: Vec<Log> = serde_json::from_str(raw).map_err(|_| NyxdError::MalformedLogString)?;
if logs.len() != logs.iter().unique_by(|log| log.msg_index).count() {
// this check is only here because I don't yet fully understand raw log string generation and
-40
View File
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
use async_trait::async_trait;
use http::header;
use reqwest::header::HeaderValue;
use reqwest::{RequestBuilder, Response, StatusCode};
use serde::de::DeserializeOwned;
@@ -97,13 +96,6 @@ impl ClientBuilder {
self
}
pub fn with_host_header(mut self, host: &str) -> Self {
let mut headers = header::HeaderMap::new();
headers.insert(header::HOST, HeaderValue::from_str(host).unwrap()); //SW Handle this unwrap later
self.reqwest_client_builder = self.reqwest_client_builder.default_headers(headers);
self
}
pub fn with_user_agent<V>(mut self, value: V) -> Self
where
V: TryInto<HeaderValue>,
@@ -163,23 +155,6 @@ impl Client {
)
}
pub fn new_fronted(base_url: Url, fronting_url: Url, timeout: Option<Duration>) -> Self {
let host = base_url.host_str().unwrap();
let mut fronted_url = base_url.clone();
fronted_url.set_host(fronting_url.host_str()).unwrap();
let builder = ClientBuilder::new::<_, String>(fronted_url)
.expect(
"we provided valid url and we were unwrapping previous construction errors anyway",
)
.with_host_header(host);
//SW polish that later if needed
match timeout {
Some(timeout) => builder.with_timeout(timeout).build::<String>().unwrap(),
None => builder.build::<String>().unwrap(),
}
}
pub fn new_url<U, E>(url: U, timeout: Option<Duration>) -> Result<Self, HttpClientError<E>>
where
U: IntoUrl,
@@ -204,21 +179,6 @@ impl Client {
self.base_url = new_url
}
pub fn change_fronted_url(&mut self, new_api_url: Url, new_fronting_url: Url) {
let host = new_api_url.host_str().unwrap();
let mut new_fronted_url = new_api_url.clone();
new_fronted_url
.set_host(new_fronting_url.host_str())
.unwrap();
let mut headers = header::HeaderMap::new();
headers.insert(header::HOST, HeaderValue::from_str(host).unwrap()); //SW Handle this unwrap later
self.reqwest_client = reqwest::ClientBuilder::new()
.default_headers(headers)
.build()
.unwrap();
self.base_url = new_fronted_url
}
pub fn current_url(&self) -> &Url {
&self.base_url
}
+33 -1
View File
@@ -31,6 +31,8 @@ pub struct NymContracts {
pub group_contract_address: Option<String>,
pub multisig_contract_address: Option<String>,
pub coconut_dkg_contract_address: Option<String>,
pub service_provider_directory_contract_address: Option<String>,
pub name_service_contract_address: Option<String>,
}
// I wanted to use the simpler `NetworkDetails` name, but there's a clash
@@ -125,6 +127,10 @@ impl NymNetworkDetails {
.with_group_contract(get_optional_env(var_names::GROUP_CONTRACT_ADDRESS))
.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_service_provider_directory_contract(get_optional_env(
var_names::SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS,
))
.with_name_service_contract(get_optional_env(var_names::NAME_SERVICE_CONTRACT_ADDRESS))
.with_explorer_api(get_optional_env(var_names::EXPLORER_API))
}
@@ -153,6 +159,8 @@ impl NymNetworkDetails {
coconut_dkg_contract_address: parse_optional_str(
mainnet::COCONUT_DKG_CONTRACT_ADDRESS,
),
service_provider_directory_contract_address: None,
name_service_contract_address: None,
},
explorer_api: parse_optional_str(mainnet::EXPLORER_API),
}
@@ -258,6 +266,21 @@ impl NymNetworkDetails {
self
}
#[must_use]
pub fn with_service_provider_directory_contract<S: Into<String>>(
mut self,
contract: Option<S>,
) -> Self {
self.contracts.service_provider_directory_contract_address = contract.map(Into::into);
self
}
#[must_use]
pub fn with_name_service_contract<S: Into<String>>(mut self, contract: Option<S>) -> Self {
self.contracts.name_service_contract_address = contract.map(Into::into);
self
}
#[must_use]
pub fn with_explorer_api<S: Into<String>>(mut self, endpoint: Option<S>) -> Self {
self.explorer_api = endpoint.map(Into::into);
@@ -421,13 +444,22 @@ pub fn setup_env<P: AsRef<Path>>(config_env_file: Option<P>) {
}
}
// Name of the event triggered by the eth contract. If the event name is changed,
// this would also need to be changed; It is currently tested against the json abi
pub const ETH_EVENT_NAME: &str = "BBCredentialPurchased";
pub const ETH_BURN_FUNCTION_NAME: &str = "generateBasicBandwidthCredential";
pub const ETH_ERC20_APPROVE_FUNCTION_NAME: &str = "approve";
// Ethereum constants used for token bridge
/// How much bandwidth (in bytes) one token can buy
pub const BYTES_PER_UTOKEN: u64 = 1024;
/// How much bandwidth (in bytes) one freepass provides
pub const BYTES_PER_FREEPASS: u64 = 1024 * 1024 * 1024; // 1GB
/// Threshold for claiming more bandwidth: 1 MB
pub const REMAINING_BANDWIDTH_THRESHOLD: i64 = 1024 * 1024;
/// How many tokens should be burned to buy bandwidth
/// How many ERC20 tokens should be burned to buy bandwidth
pub const TOKENS_TO_BURN: u64 = 1;
/// How many ERC20 utokens should be burned to buy bandwidth
pub const UTOKENS_TO_BURN: u64 = TOKENS_TO_BURN * 1000000;
+3
View File
@@ -20,6 +20,9 @@ pub const MULTISIG_CONTRACT_ADDRESS: &str = "MULTISIG_CONTRACT_ADDRESS";
pub const COCONUT_DKG_CONTRACT_ADDRESS: &str = "COCONUT_DKG_CONTRACT_ADDRESS";
pub const REWARDING_VALIDATOR_ADDRESS: &str = "REWARDING_VALIDATOR_ADDRESS";
pub const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "STATISTICS_SERVICE_DOMAIN_ADDRESS";
pub const SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS: &str =
"SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS";
pub const NAME_SERVICE_CONTRACT_ADDRESS: &str = "NAME_SERVICE_CONTRACT_ADDRESS";
pub const NYXD: &str = "NYXD";
pub const NYM_API: &str = "NYM_API";
pub const NYXD_WEBSOCKET: &str = "NYXD_WS";
+5 -9
View File
@@ -6,7 +6,6 @@ use nym_credential_storage::models::StorableIssuedCredential;
use nym_credential_storage::storage::Storage;
use nym_credentials::coconut::bandwidth::issued::BandwidthCredentialIssuedDataVariant;
use nym_credentials::IssuedBandwidthCredential;
use time::OffsetDateTime;
use tracing::{debug, warn};
use zeroize::Zeroizing;
@@ -14,7 +13,7 @@ pub async fn import_credential<S>(
credentials_store: S,
raw_credential: Vec<u8>,
credential_version: impl Into<Option<u8>>,
) -> Result<Option<OffsetDateTime>, NymIdError>
) -> Result<(), NymIdError>
where
S: Storage,
<S as Storage>::StorageError: Send + Sync + 'static,
@@ -30,10 +29,9 @@ where
credential.typ()
);
let expiry_date = match credential.variant_data() {
match credential.variant_data() {
BandwidthCredentialIssuedDataVariant::Voucher(voucher_info) => {
debug!("with value of {}", voucher_info.value());
None
debug!("with value of {}", voucher_info.value())
}
BandwidthCredentialIssuedDataVariant::FreePass(freepass_info) => {
debug!("with expiry at {}", freepass_info.expiry_date());
@@ -44,11 +42,9 @@ where
return Err(NymIdError::ExpiredCredentialImport {
expiration: freepass_info.expiry_date(),
});
} else {
Some(freepass_info.expiry_date())
}
}
};
}
// SAFETY:
// for the epoch to run over u32::MAX, we'd have to advance it for few centuries every block...
@@ -71,5 +67,5 @@ where
.map_err(|source| NymIdError::StorageError {
source: Box::new(source),
})?;
Ok(expiry_date)
Ok(())
}
+16 -3
View File
@@ -12,6 +12,9 @@ log = { workspace = true }
rand = { workspace = true }
rand_distr = { workspace = true }
thiserror = { workspace = true }
serde = { workspace = true }
bincode = { workspace = true }
tokio-util = { workspace = true, features = ["codec"] }
nym-sphinx-acknowledgements = { path = "acknowledgements" }
nym-sphinx-addressing = { path = "addressing" }
@@ -30,7 +33,9 @@ nym-topology = { path = "../topology" }
[dev-dependencies]
nym-mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" }
nym-crypto = { path = "../crypto", version = "0.4.0", features = ["asymmetric"] }
nym-crypto = { path = "../crypto", version = "0.4.0", features = [
"asymmetric",
] }
# do not include this when compiling into wasm as it somehow when combined together with reqwest, it will require
# net2 via tokio-util -> tokio -> mio -> net2
@@ -43,5 +48,13 @@ features = ["sync"]
[features]
default = ["sphinx"]
sphinx = ["nym-crypto/sphinx", "nym-sphinx-params/sphinx", "nym-sphinx-types/sphinx"]
outfox = ["nym-crypto/outfox", "nym-sphinx-params/outfox", "nym-sphinx-types/outfox"]
sphinx = [
"nym-crypto/sphinx",
"nym-sphinx-params/sphinx",
"nym-sphinx-types/sphinx",
]
outfox = [
"nym-crypto/outfox",
"nym-sphinx-params/outfox",
"nym-sphinx-types/outfox",
]
@@ -4,6 +4,7 @@
use crate::{ReplySurb, ReplySurbError};
use nym_sphinx_addressing::clients::{Recipient, RecipientFormattingError};
use rand::{CryptoRng, RngCore};
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use std::mem;
use thiserror::Error;
@@ -24,7 +25,7 @@ pub enum InvalidAnonymousSenderTagRepresentation {
InvalidLength { received: usize, expected: usize },
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
pub struct AnonymousSenderTag([u8; SENDER_TAG_SIZE]);
+1
View File
@@ -13,3 +13,4 @@ nym-sphinx-params = { path = "../params" }
nym-sphinx-types = { path = "../types", features = ["sphinx", "outfox"] }
nym-outfox = { path = "../../../nym-outfox" }
thiserror = { workspace = true }
serde = { workspace = true }
+37
View File
@@ -4,6 +4,10 @@
use nym_sphinx_addressing::nodes::{NymNodeRoutingAddress, NymNodeRoutingAddressError};
use nym_sphinx_params::{PacketSize, PacketType};
use nym_sphinx_types::{NymPacket, NymPacketError};
use serde::{
de::{self, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};
use std::fmt::{self, Debug, Formatter};
use thiserror::Error;
@@ -110,6 +114,39 @@ impl MixPacket {
.chain(self.packet.to_bytes()?)
.collect())
}
pub fn to_bytes(&self) -> Result<Vec<u8>, MixPacketFormattingError> {
Ok(std::iter::once(self.packet_type as u8)
.chain(self.next_hop.as_bytes())
.chain(self.packet.to_bytes()?)
.collect())
}
}
impl Serialize for MixPacket {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_bytes(&self.to_bytes().map_err(serde::ser::Error::custom)?)
}
}
struct MixPacketVisitor;
impl<'de> Visitor<'de> for MixPacketVisitor {
type Value = MixPacket;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a byte array representing a mix packet")
}
fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<Self::Value, E> {
MixPacket::try_from_bytes(v).map_err(serde::de::Error::custom)
}
}
impl<'de> Deserialize<'de> for MixPacket {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_bytes(MixPacketVisitor)
}
}
// TODO: test for serialization and errors!
+66 -1
View File
@@ -1,7 +1,10 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::io;
use crate::message::{NymMessage, NymMessageError, PaddedMessage, PlainMessage};
use log::debug;
use nym_crypto::aes::cipher::{KeyIvInit, StreamCipher};
use nym_crypto::asymmetric::encryption;
use nym_crypto::shared_key::recompute_shared_key;
@@ -16,10 +19,13 @@ use nym_sphinx_params::{
PacketEncryptionAlgorithm, PacketHkdfAlgorithm, ReplySurbEncryptionAlgorithm,
DEFAULT_NUM_MIX_HOPS,
};
use serde::{Deserialize, Serialize};
use thiserror::Error;
use tokio_util::bytes::{Buf, BytesMut};
use tokio_util::codec::{Decoder, Encoder};
// TODO: should this live in this file?
#[derive(Debug)]
#[derive(Debug, Serialize, Deserialize)]
pub struct ReconstructedMessage {
/// The actual plaintext message that was received.
pub message: Vec<u8>,
@@ -57,6 +63,62 @@ impl From<PlainMessage> for ReconstructedMessage {
}
}
pub struct ReconstructedMessageCodec;
const LENGHT_ENCODING_PREFIX_SIZE: usize = 4;
impl Encoder<ReconstructedMessage> for ReconstructedMessageCodec {
type Error = MessageRecoveryError;
fn encode(
&mut self,
item: ReconstructedMessage,
buf: &mut BytesMut,
) -> Result<(), Self::Error> {
let encoded = bincode::serialize(&item).expect("failed to serialize ReconstructedMessage");
let encoded_len = encoded.len() as u32;
let mut encoded_with_len = encoded_len.to_le_bytes().to_vec();
encoded_with_len.extend(encoded);
buf.reserve(encoded_with_len.len());
buf.extend_from_slice(&encoded_with_len);
Ok(())
}
}
impl Decoder for ReconstructedMessageCodec {
type Item = ReconstructedMessage;
type Error = MessageRecoveryError;
fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
if buf.len() < LENGHT_ENCODING_PREFIX_SIZE {
return Ok(None);
}
let len = u32::from_le_bytes(
buf[0..LENGHT_ENCODING_PREFIX_SIZE]
.try_into()
.expect("We know that we have at least LENGHT_ENCODING_PREFIX_SIZE bytes in there"),
) as usize;
if buf.len() < len + LENGHT_ENCODING_PREFIX_SIZE {
return Ok(None);
}
let decoded = match bincode::deserialize(
&buf[LENGHT_ENCODING_PREFIX_SIZE..len + LENGHT_ENCODING_PREFIX_SIZE],
) {
Ok(decoded) => decoded,
Err(e) => {
debug!("Failed to decode the message - {:?}", e);
return Ok(None);
}
};
buf.advance(len + LENGHT_ENCODING_PREFIX_SIZE);
Ok(Some(decoded))
}
}
#[derive(Debug, Error)]
pub enum MessageRecoveryError {
#[error("The received message did not contain enough bytes to recover the ephemeral public key. Got {provided}. required: {required}")]
@@ -74,6 +136,9 @@ pub enum MessageRecoveryError {
#[error("Failed to recover message fragment - {0}")]
FragmentRecoveryError(#[from] ChunkingError),
#[error("Failed to recover message fragment - {0}")]
MessageRecoveryError(#[from] io::Error),
}
pub trait MessageReceiver {
+1
View File
@@ -19,6 +19,7 @@ serde = { workspace = true, features = ["derive"] } # for config serialization/d
tap = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true, features = ["rt-multi-thread", "net", "signal"] }
tokio-util = { workspace = true }
url = { workspace = true }
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
@@ -7,6 +7,7 @@ use super::{SocksVersion, RESERVED, SOCKS4_VERSION, SOCKS5_VERSION};
use crate::config;
use futures::channel::mpsc;
use futures::task::{Context, Poll};
use futures::SinkExt;
use log::*;
use nym_client_core::client::inbound_messages::{InputMessage, InputMessageSender};
use nym_service_providers_common::interface::{ProviderInterfaceVersion, RequestVersion};
@@ -3,11 +3,12 @@
use crate::proxy_runner::MixProxySender;
use bytes::Bytes;
use futures::SinkExt;
use log::{debug, error};
use nym_socks5_requests::{ConnectionId, SocketData};
use std::io;
pub(crate) struct OrderedMessageSender<F, S> {
pub(crate) struct OrderedMessageSender<F, S: Send + 'static> {
connection_id: ConnectionId,
// addresses are provided for better logging
local_destination_address: String,
@@ -18,7 +19,7 @@ pub(crate) struct OrderedMessageSender<F, S> {
mix_message_adapter: F,
}
impl<F, S> OrderedMessageSender<F, S>
impl<F, S: Send + 'static> OrderedMessageSender<F, S>
where
F: Fn(SocketData) -> S,
{
@@ -55,7 +56,7 @@ where
(self.mix_message_adapter)(data)
}
async fn send_message(&self, message: S) {
async fn send_message(&mut self, message: S) {
if self.mixnet_sender.send(message).await.is_err() {
panic!("BatchRealMessageReceiver has stopped receiving!")
}
@@ -74,7 +74,7 @@ async fn wait_for_lane(
}
}
pub(super) async fn run_inbound<F, S>(
pub(super) async fn run_inbound<F, S: Send>(
mut reader: OwnedReadHalf,
mut message_sender: OrderedMessageSender<F, S>,
connection_id: ConnectionId,
@@ -9,6 +9,7 @@ use nym_task::TaskClient;
use std::fmt::Debug;
use std::{sync::Arc, time::Duration};
use tokio::{net::TcpStream, sync::Notify};
use tokio_util::sync::PollSender;
mod inbound;
mod outbound;
@@ -35,7 +36,7 @@ impl From<(Vec<u8>, bool)> for ProxyMessage {
}
}
pub type MixProxySender<S> = tokio::sync::mpsc::Sender<S>;
pub type MixProxySender<S> = PollSender<S>;
pub type MixProxyReader<S> = tokio::sync::mpsc::Receiver<S>;
// TODO: when we finally get to implementing graceful shutdown,
+1 -1
View File
@@ -19,7 +19,7 @@ pub trait StatisticsCollector {
interval: Duration,
timestamp: DateTime<Utc>,
) -> StatsMessage;
async fn send_stats_message(&self, stats_message: StatsMessage) -> Result<(), StatsError>;
async fn send_stats_message(&mut self, stats_message: StatsMessage) -> Result<(), StatsError>;
async fn reset_stats(&mut self);
}
+1
View File
@@ -12,6 +12,7 @@ futures = { workspace = true }
log = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true, features = ["macros", "sync"] }
serde = { workspace = true }
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio]
workspace = true
+2 -1
View File
@@ -2,11 +2,12 @@
// SPDX-License-Identifier: Apache-2.0
use futures::channel::mpsc;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
pub type ConnectionId = u64;
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub enum TransmissionLane {
General,
// we need to treat surb-related requests and responses at higher priority
+1 -1
View File
@@ -122,7 +122,7 @@ pub async fn setup_gateway_from_api(
nym_apis: &[Url],
) -> Result<InitialisationResult, WasmCoreError> {
let mut rng = thread_rng();
let gateways = current_gateways(&mut rng, nym_apis, None).await?;
let gateways = current_gateways(&mut rng, nym_apis).await?;
setup_gateway_wasm(client_store, force_tls, chosen_gateway, &gateways).await
}
-1
View File
@@ -32,7 +32,6 @@
- [Performance Monitoring & Testing](testing/performance.md)
<!--- [Node Setup](testing/node-setup.md)-->
- [Gateway Probe](testing/gateway-probe.md)
- [Node API Check](testing/node-api-check.md)
- [Prometheus & Grafana](testing/prometheus-grafana.md)
- [ExploreNYM scripts](testing/explorenym-scripts.md)
<!-- - [Run in a Docker](testing/docker-monitor.md) -->
+1 -1
View File
@@ -143,7 +143,7 @@ As part of the transition, `allowed.list` on Exit Gateway embedded Network Reque
./nym-node run --init-only --mode exit-gateway
# with a custom `--id` and other options
./nym-node run --id <ID> --init-only --mode exit-gateway --public-ips "$(curl -4 https://ifconfig.me)" --hostname "<YOUR_DOMAIN>" --http-bind-address 0.0.0.0:8080 --mixnet-bind-address 0.0.0.0:1789 --location <COUNTRY_FULL_NAME> --wireguard-enabled false
./nym-node run --id <ID> --init-only --mode exit-gateway --public-ips "$(curl -4 https://ifconfig.me)" --hostname "<YOUR_DOMAIN>" --http-bind-address 0.0.0.0:8080 --mixnet-bind-address 0.0.0.0:1789 true --location <COUNTRY_FULL_NAME> --wireguard-enabled false
# <YOUR_DOMAIN> is in format without 'https://' prefix
# <COUNTRY_FULL_NAME> is format like 'Jamaica', or two-letter alpha2 (e.g. 'JM'), three-letter alpha3 (e.g. 'JAM') or three-digit numeric-3 (e.g. '388') can be provided.
@@ -1,118 +0,0 @@
# Node API Check
> Any syntax in `<>` brackets is a user's unique variable/version. Exchange with a corresponding name without the `<>` brackets.
Operating a `nym-node` is not a *"set and forget"* endeavor, it takes some work. To diagnose node performance querying APIs is a good knowledge to have. There are two main places to look for API endpoints regarding `nym-node`:
- [`openapi.json`](https://validator.nymtech.net/api/v1/openapi.json): a list of all endpoints
- [Swagger UI page](https://validator.nymtech.net/api/swagger/index.html)
Besides that, Gateway operators can check out their node performance, connectivity and much more on [harbourmaster.nymtech.net](https://harbourmaster.nymtech.net/).
### Basic API usage
For information about available endpoints and their status, you can refer to:
```
# for http
http://<IP>:8080/api/v1/swagger/#/
# for https reversed proxy
https://<DOMAIN>/api/v1/swagger/#/
```
For example to determine which mode your node is running, you can check `:8080/api/v1/roles` endpoint:
```
# for http
http://<IP_ADDRESS>:8080/api/v1/roles
# for https reversed proxy
https://<DOMAIN>/api/v1/roles
```
## `node_api_check.py`
To make this a bit easier, we made a CLI tool quering all vailable API endpoints based on node `Identity Key` (further denoted as `<ID_KEY>`) called `node_api_check.py`. To diagnose your node performance, whether by yourself or by sharing an output in our [operator channel](https://matrix.to/#/#operators:nymtech.chat), this tool provides you with a quick overview of live data. We recommend to run this checker alongside [`nym_gateway_probe`](gateway-probe.md) to triage both performance and an actual routing.
Besides querying any bonded node APIs, `nym_api_check` has a function counting all existing nodes in provided version.
### Setup
#### Pre-requsities
**Python3**
1. Start with installing Python3:
```sh
sudo apt install Python3
```
2. Make sure Python3 is your default Python version:
```sh
update-alternatives --install /usr/bin/python python /usr/bin/python3 1
# controll
python --version
# should return higher than 3
```
3. Install Python modules `tabulate`, `pandas` and `argparse`:
- either using [`pip`](https://python.land/virtual-environments/installing-packages-with-pip) and then running:
```sh
pip install tabulate pandas argparse
```
- or if you installed Python3 system-wide you can install modules directly:
```sh
sudo apt install python3-tabulate python3-pandas python3-argparse
```
**Installation**
4. Get [`node_api_check.py`](https://github.com/nymtech/nym/tree/develop/scripts/node_api_check.py) and [`api_endpoints.json`](https://github.com/nymtech/nym/tree/develop/scripts/api_endpoints.json). If you [compiled from source](../binaries/building-nym.md), you already have both of these files. If you prefer to download them individually, do it by opening terminal in your desired location and running:
```sh
wget https://raw.githubusercontent.com/nymtech/nym/tree/develop/node_api_check.py
wget https://raw.githubusercontent.com/nymtech/nym/tree/develop/api_endpoints.json
```
5. Make executable:
```sh
chmod u+x node_api_check.py
```
Now you are ready to check your node.
### Usage
Run with `--help` flag to see the available commands:
~~~admonish example collapsible=true title="./node_api_check.py --help"
```python
<!--cmdrun cd ../../../../scripts && python ./node_api_check.py --help-->
```
~~~
#### `query_stats`
When you want to see all the options connected to a command, add a `--help` flag after the command of your choice, like in this example:
~~~admonish example collapsible=true title="./node_api_check.py query_stats --help"
```python
<!--cmdrun cd ../../../../scripts && python ./node_api_check.py query_stats --help-->
```
~~~
The most common usage may be `./node_api_check.py query_stats <ID_KEY>` where `<ID_KEY>` is required, sustitute it with node Identity Key.
**Optional arguments**
| Flag | Shortcut | Description |
| :--- | :--- | :--- |
| `--markdown` | `-m` | returns output in markdown format |
| `--no_routing_history` | `-n` | returns output without routing history which can be lenghty |
| `--output` | `-o` | exports output to a file, possible to add a target path |
#### `version_count`
Another command is `version_count` where at least one `nym-node` version is required. In case of multiple version count, separate the versions with space. We recommend to run this command with `--markdown` flag for a nicer output. This is an example where we want to look up how many registered nodes are on versions `1.1.0`, `1.1.1`, `1.1.2` and `1.1.3`:
```sh
./node_api_check version_count 1.1.0 1.1.1 1.1.2 1.1.3 --markdown
```
-22
View File
@@ -1,22 +0,0 @@
CONFIGURED=true
RUST_LOG=info
RUST_BACKTRACE=1
NETWORK_NAME=sandbox
BECH32_PREFIX=n
MIX_DENOM=unym
MIX_DENOM_DISPLAY=nym
STAKE_DENOM=unyx
STAKE_DENOM_DISPLAY=nyx
DENOMS_EXPONENT=6
REWARDING_VALIDATOR_ADDRESS=n1duuyj2th2y0z4u4f4wtljpdz9s3pxtu0xx6zdz
MIXNET_CONTRACT_ADDRESS=n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g
COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n1mf6ptkssddfmxvhdx0ech0k03ktp6kf9yk59renau2gvht3nq2gqt5tdrk
GROUP_CONTRACT_ADDRESS=n1qg5ega6dykkxc307y25pecuufrjkxkaggkkxh7nad0vhyhtuhw3sa07c47
MULTISIG_CONTRACT_ADDRESS=n1zwv6feuzhy6a9wekh96cd57lsarmqlwxdypdsplw6zhfncqw6ftqx5a364
COCONUT_DKG_CONTRACT_ADDRESS=n1aakfpghcanxtc45gpqlx8j3rq0zcpyf49qmhm9mdjrfx036h4z5sy2vfh9
EXPLORER_API=https://canary-explorer.performance.nymte.ch/api
NYXD="https://canary-validator.performance.nymte.ch"
NYM_API="https://canary-api.performance.nymte.ch/api"
+1
View File
@@ -17,6 +17,7 @@ COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n1lp5zex6685kd22agzskhqsylpnssxnweyuvsz4edr4p
GROUP_CONTRACT_ADDRESS=n1rw8fw2mpcpzzq3jpa4e52ufawnmj5a4u68p35umvgskewuw0nlzsaa5w4m
MULTISIG_CONTRACT_ADDRESS=n1rw8fw2mpcpzzq3jpa4e52ufawnmj5a4u68p35umvgskewuw0nlzsaa5w4m
COCONUT_DKG_CONTRACT_ADDRESS=n1vwtgazgpancsfel04y7syc95ausmat47cjtldqzkdmx6phyrwx2qvkv32p
EPHEMERA_CONTRACT_ADDRESS=n1vwtgazgpancsfel04y7syc95ausmat47cjtldqzkdmx6phyrwx2qvkv32p
REWARDING_VALIDATOR_ADDRESS=n1tfzd4qz3a45u8p4mr5zmzv66457uwjgcl05jdq
STATISTICS_SERVICE_DOMAIN_ADDRESS="http://0.0.0.0"
NYXD="http://127.0.0.1:26657"
+10 -8
View File
@@ -10,17 +10,19 @@ STAKE_DENOM=unyx
STAKE_DENOM_DISPLAY=nyx
DENOMS_EXPONENT=6
MIXNET_CONTRACT_ADDRESS=n1hm4y6fzgxgu688jgf7ek66px6xkrtmn3gyk8fax3eawhp68c2d5qujz296
COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n14y2x8a60knc5jjfeztt84kw8x8l5pwdgnqg256v0p9v4p7t2q6eswxyusw
GROUP_CONTRACT_ADDRESS=n1qp35fcj0v9u3trhaps5v9q0lc42t4m6aty2wryss75ee8zuqnsqqdcreyq
MULTISIG_CONTRACT_ADDRESS=n1qa4hswlcjmttulj0q9qa46jf64f93pecl6tydcsjldfe0hy5ju0sdmwzya
COCONUT_DKG_CONTRACT_ADDRESS=n1ayrk6wp6w5lf6njtnfjwljmtcc9vevv5sxwkz7uq24rp2pw67t0qhmmxdd
VESTING_CONTRACT_ADDRESS=n1jlzdxnyces4hrhqz68dqk28mrw5jgwtcfq0c2funcwrmw0dx9l9s8nnnvj
MIXNET_CONTRACT_ADDRESS=n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g
VESTING_CONTRACT_ADDRESS=n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw
COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n1aht6sekk0302hh30eje9a4lq84juwehw2hxv65kjh863ptay0sxs84mhx8
GROUP_CONTRACT_ADDRESS=n17akgf5y2t775k27kulyw8durzsgfedym9wemg0qv3msc4ry0cg2senyl36
MULTISIG_CONTRACT_ADDRESS=n1075wwvq8ypag74tmzeu7a05mplwsa95g2p8wnxhhctv7hlp6y3wq9ttqcg
COCONUT_DKG_CONTRACT_ADDRESS=n1hk7jy0m6ec5gventvgsx8skrz8mrudhqhun0ufg5gaa3mrr83qyq45v2zk
REWARDING_VALIDATOR_ADDRESS=n1rfvpsynktze6wvn6ldskj8xgwfzzk5v6pnff39
NAME_SERVICE_CONTRACT_ADDRESS=n1qum2tr7hh4y7ruzew68c64myjec0dq2s2njf6waja5t0w879lutqadamme
SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS=n13ehuhysn5mqjeaheeuew2gjs785f6k7jm8vfsqg3jhtpkwppcmzq6m2hmz
STATISTICS_SERVICE_DOMAIN_ADDRESS="https://mainnet-stats.nymte.ch:8090"
EXPLORER_API=https://qa-network-explorer.qa.nymte.ch/api
NYXD="https://qa-validator.qa.nymte.ch"
NYM_API="https://qa-nym-api.qa.nymte.ch/api"
DKG_TIME_CONFIGURATION="600,300,300,60,60,1209600"
EXIT_POLICY="https://nymtech.net/.wellknown/network-requester/exit-policy.txt"
EXIT_POLICY="https://nymtech.net/.wellknown/network-requester/exit-policy.txt"
+3 -1
View File
@@ -17,7 +17,9 @@ COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n1ljlwey4xdj0zs7zueepc48nkr033fca6fjgvurfvttq
GROUP_CONTRACT_ADDRESS=n10v3rjnq4cjyccfykyams68ztce337gksuu6f0lvtl4meuwvkewaqru4uav
MULTISIG_CONTRACT_ADDRESS=n1cemnu8as0ls45v3caunpesl8jlsfw2ff9rlwnltlecp7zrxct4dsqc2y42
COCONUT_DKG_CONTRACT_ADDRESS=n1zx96qgd88vqlzcxkpwzks7kqs5ctrx36xtzfc58p7q6c4ng9anlqzc4nh8
NAME_SERVICE_CONTRACT_ADDRESS=n12ne7qtmdwd0j03t9t5es8md66wq4e5xg9neladrsag8fx3y89rcs36asfp
SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS=n1ps5yutd7sufwg058qd7ac7ldnlazsvmhzqwucsfxmm445d70u8asqxpur4
EPHEMERA_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
STATISTICS_SERVICE_DOMAIN_ADDRESS="http://0.0.0.0"
EXPLORER_API=https://sandbox-explorer.nymtech.net/api
+2 -1
View File
@@ -18,7 +18,8 @@ COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n16a32stm6kknhq5cc8rx77elr66pygf2hfszw7wvpq74
GROUP_CONTRACT_ADDRESS=n1pd7kfgvr5tpcv0xnlv46c4jsq9jg2r799xxrcwqdm4l2jhq2pjwqrmz5ju
MULTISIG_CONTRACT_ADDRESS=n14ph4e660eyqz0j36zlkaey4zgzexm5twkmjlqaequxr2cjm9eprqsmad6k
COCONUT_DKG_CONTRACT_ADDRESS=n1ahg0erc2fs6xx3j5m8sfx3ryuzdjh6kf6qm9plsf865fltekyrfsesac6a
NAME_SERVICE_CONTRACT_ADDRESS=n12ne7qtmdwd0j03t9t5es8md66wq4e5xg9neladrsag8fx3y89rcs36asfp
SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS=n1ps5yutd7sufwg058qd7ac7ldnlazsvmhzqwucsfxmm445d70u8asqxpur4
STATISTICS_SERVICE_DOMAIN_ADDRESS="http://0.0.0.0"
NYXD="https://rpc.sandbox.nymtech.net"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "explorer-api"
version = "1.1.36"
version = "1.1.34"
edition = "2021"
license.workspace = true
@@ -0,0 +1,104 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::node::client_handling::websocket::connection_handler::authenticated::RequestHandlingError;
use nym_bandwidth_claim_contract::msg::ExecuteMsg;
use nym_bandwidth_claim_contract::payment::LinkPaymentData;
use nym_credentials::token::bandwidth::TokenCredential;
use nym_crypto::asymmetric::identity::{PublicKey, Signature, SIGNATURE_LENGTH};
use nym_network_defaults::{ETH_EVENT_NAME, ETH_MIN_BLOCK_DEPTH};
use nym_validator_client::nxmd::traits::MixnetQueryClient;
use nym_validator_client::nyxd::{AccountId, NyxdClient, SigningNyxdClient};
use std::str::FromStr;
use web3::contract::tokens::Detokenize;
use web3::contract::{Contract, Error};
use web3::transports::Http;
use web3::types::{BlockNumber, FilterBuilder, H256};
use web3::Web3;
#[derive(Debug)]
pub struct Burned {
/// The bandwidth bought by the client
pub bandwidth: u64,
/// Client public verification key
pub verification_key: PublicKey,
/// Signed verification key
pub signed_verification_key: Signature,
/// Address for the owner of the gateway
pub cosmos_recipient: String,
}
impl Burned {
pub fn verify(&self, verification_key: PublicKey) -> bool {
self.verification_key == verification_key
&& verification_key
.verify(
&self.verification_key.to_bytes(),
&self.signed_verification_key,
)
.is_ok()
}
}
impl Detokenize for Burned {
fn from_tokens(tokens: Vec<Token>) -> Result<Self, Error>
where
Self: Sized,
{
if tokens.len() != 4 {
return Err(Error::InvalidOutputType(format!(
"Expected three elements, got: {:?}",
tokens
)));
}
let bandwidth = tokens
.get(0)
.unwrap()
.clone()
.into_uint()
.ok_or_else(|| Error::InvalidOutputType(String::from("Expected Uint for bandwidth")))?
.as_u64();
let verification_key: [u8; 32] = tokens
.get(1)
.unwrap()
.clone()
.into_uint()
.ok_or_else(|| {
Error::InvalidOutputType(String::from("Expected Uint for verification key"))
})?
.into();
let verification_key = PublicKey::from_bytes(&verification_key).map_err(|_| {
Error::InvalidOutputType(format!(
"Expected verification key of 32 bytes, got: {}",
verification_key.len()
))
})?;
let signed_verification_key =
tokens.get(2).unwrap().clone().into_bytes().ok_or_else(|| {
Error::InvalidOutputType(String::from("Expected Bytes for the last two fields"))
})?;
let signed_verification_key =
Signature::from_bytes(&signed_verification_key[..SIGNATURE_LENGTH]).map_err(|_| {
Error::InvalidOutputType(format!(
"Expected signature of {} bytes, got: {}",
SIGNATURE_LENGTH,
signed_verification_key.len()
))
})?;
let cosmos_recipient = tokens
.get(3)
.unwrap()
.clone()
.into_string()
.ok_or_else(|| {
Error::InvalidOutputType(String::from("Expected utf8 encoded owner address"))
})?;
Ok(Burned {
bandwidth,
verification_key,
signed_verification_key,
cosmos_recipient,
})
}
}
-2
View File
@@ -318,7 +318,6 @@ impl<St> Gateway<St> {
.with_shutdown(shutdown)
.with_custom_gateway_transceiver(Box::new(transceiver))
.with_wait_for_gateway(true)
.with_minimum_gateway_performance(0)
.with_on_start(on_start_tx);
if let Some(custom_mixnet) = &nr_opts.custom_mixnet_path {
@@ -383,7 +382,6 @@ impl<St> Gateway<St> {
.with_shutdown(shutdown)
.with_custom_gateway_transceiver(Box::new(transceiver))
.with_wait_for_gateway(true)
.with_minimum_gateway_performance(0)
.with_on_start(on_start_tx);
if let Some(custom_mixnet) = &ip_opts.custom_mixnet_path {
+1 -1
View File
@@ -52,7 +52,7 @@ impl StatisticsCollector for GatewayStatisticsCollector {
}
}
async fn send_stats_message(&self, stats_message: StatsMessage) -> Result<(), StatsError> {
async fn send_stats_message(&mut self, stats_message: StatsMessage) -> Result<(), StatsError> {
build_and_send_statistics_request(stats_message, self.statistics_service_url.to_string())
.await
}
+1 -1
View File
@@ -4,7 +4,7 @@
[package]
name = "nym-api"
license = "GPL-3.0"
version = "1.1.40"
version = "1.1.38"
authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
"Jędrzej Stuczyński <andrew@nymtech.net>",
@@ -0,0 +1,19 @@
CREATE TABLE contract_mixnode_reward
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
mix_id INTEGER NOT NULL,
epoch INTEGER NOT NULL,
nym_api_id INTEGER NOT NULL,
reliability INTEGER NOT NULL,
timestamp INTEGER NOT NULL,
UNIQUE (mix_id, epoch)
);
CREATE TABLE epoch_info
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
epoch_id INTEGER NOT NULL,
start_time INTEGER NOT NULL,
duration INTEGER NOT NULL,
UNIQUE (epoch_id)
);
@@ -0,0 +1,23 @@
CREATE TABLE mixnode_status
(
mix_id INTEGER NOT NULL,
reliability INTEGER NOT NULL,
timestamp INTEGER NOT NULL,
UNIQUE (mix_id, timestamp)
);
CREATE TABLE rewarding_report
(
epoch_id INTEGER NOT NULL,
eligible_mixnodes INTEGER NOT NULL,
timestamp INTEGER NOT NULL,
UNIQUE (epoch_id)
);
CREATE TABLE epoch_blocks
(
epoch_id INTEGER NOT NULL,
block_id INTEGER NOT NULL,
timestamp INTEGER NOT NULL,
UNIQUE (epoch_id, block_id)
);
@@ -0,0 +1,6 @@
CREATE TABLE mixnode_status
(
mix_id INTEGER NOT NULL,
reliability INTEGER NOT NULL,
timestamp INTEGER NOT NULL
);
+3
View File
@@ -30,6 +30,9 @@ export interface Contracts {
group_contract_address: string;
multisig_contract_address: string;
coconut_dkg_contract_address: string;
ephemera_contract_address: string;
service_provider_directory_contract_address: string;
name_service_contract_address: string;
}
export interface Endpoint {
+1 -1
View File
@@ -3,7 +3,7 @@
[package]
name = "nym-node"
version = "1.1.4"
version = "1.1.2"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
@@ -179,5 +179,5 @@ pub struct AuxiliaryDetails {
/// as defined at <https://nymtech.net/terms-and-conditions/operators/v1.0.0>
// make sure to include the default deserialisation as this field hasn't existed when the struct was first created
#[serde(default)]
pub accepted_operator_terms_and_conditions: bool,
pub accepted_toc: bool,
}
+4 -4
View File
@@ -22,12 +22,12 @@ pub(crate) struct Args {
/// as defined at <https://nymtech.net/terms-and-conditions/operators/v1.0.0>
#[clap(
long,
env = NYMNODE_ACCEPT_OPERATOR_TERMS,
env = NYMNODE_ACCEPT_TOC,
alias = "accept-toc",
alias = "accept-t&c",
alias = "accept-operator-terms",
alias = "accept-operator-t&c",
alias = "accept-operator-terms-and-conditions"
)]
pub(crate) accept_operator_terms_and_conditions: bool,
pub(crate) accept_operator_terms: bool,
/// Forbid a new node from being initialised if configuration file for the provided specification doesn't already exist
#[clap(
+4 -6
View File
@@ -46,11 +46,11 @@ pub(crate) async fn execute(mut args: Args) -> Result<(), NymNodeError> {
let bonding_info_path = args.bonding_information_output.clone();
let init_only = args.init_only;
let local = args.local;
let accepted_operator_terms_and_conditions = args.accept_operator_terms_and_conditions;
let accepted_toc = args.accept_operator_terms;
if !accepted_operator_terms_and_conditions {
if !accepted_toc {
warn!("you don't seem to have accepted the terms and conditions of a Nym node operator");
warn!("please familiarise yourself with <https://nymtech.net/terms-and-conditions/operators/v1.0.0> and run the binary with '--accept-operator-terms-and-conditions' flag if you agree with them");
warn!("please familiarise yourself with <https://nymtech.net/terms-and-conditions/operators/v1.0.0> and run the binary with '--accept-toc' flag if you agree with them");
}
let config = if !config_path.exists() {
@@ -85,9 +85,7 @@ pub(crate) async fn execute(mut args: Args) -> Result<(), NymNodeError> {
}
check_public_ips(&config.host.public_ips, local)?;
let nym_node = NymNode::new(config)
.await?
.with_accepted_operator_terms_and_conditions(accepted_operator_terms_and_conditions);
let nym_node = NymNode::new(config).await?.with_accepted_toc(accepted_toc);
// if requested, write bonding info
if let Some(bonding_info_path) = bonding_info_path {
+1 -1
View File
@@ -18,7 +18,7 @@ pub mod vars {
pub const NYMNODE_MODE_ARG: &str = "NYMNODE_MODE";
pub const NYMNODE_ACCEPT_OPERATOR_TERMS: &str = "NYMNODE_ACCEPT_OPERATOR_TERMS";
pub const NYMNODE_ACCEPT_TOC: &str = "NYMNODE_ACCEPT_TOC";
// host:
pub const NYMNODE_PUBLIC_IPS_ARG: &str = "NYMNODE_PUBLIC_IPS";
+5 -8
View File
@@ -293,7 +293,7 @@ impl From<WireguardData> for nym_wireguard::WireguardData {
pub(crate) struct NymNode {
config: Config,
accepted_operator_terms_and_conditions: bool,
accepted_toc: bool,
description: NodeDescription,
@@ -394,15 +394,12 @@ impl NymNode {
exit_gateway: ExitGatewayData::new(&config.exit_gateway)?,
wireguard: wireguard_data,
config,
accepted_operator_terms_and_conditions: false,
accepted_toc: false,
})
}
pub(crate) fn with_accepted_operator_terms_and_conditions(
mut self,
accepted_operator_terms_and_conditions: bool,
) -> Self {
self.accepted_operator_terms_and_conditions = accepted_operator_terms_and_conditions;
pub(crate) fn with_accepted_toc(mut self, accepted_toc: bool) -> Self {
self.accepted_toc = accepted_toc;
self
}
@@ -541,7 +538,7 @@ impl NymNode {
let auxiliary_details = api_requests::v1::node::models::AuxiliaryDetails {
location: self.config.host.location,
accepted_operator_terms_and_conditions: self.accepted_operator_terms_and_conditions,
accepted_toc: self.accepted_toc,
};
// mixnode info
+10 -2
View File
@@ -12,9 +12,9 @@ pub(crate) const STAKE_DENOM: DenomDetails = DenomDetails::new("unyx", "nyx", 6)
// -- Contract addresses --
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str =
"n1hm4y6fzgxgu688jgf7ek66px6xkrtmn3gyk8fax3eawhp68c2d5qujz296";
"n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g";
pub(crate) const VESTING_CONTRACT_ADDRESS: &str =
"n1jlzdxnyces4hrhqz68dqk28mrw5jgwtcfq0c2funcwrmw0dx9l9s8nnnvj";
"n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw";
pub(crate) const COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str =
"n1w798gp0zqv3s9hjl3jlnwxtwhykga6rn93p46q2crsdqhaj3y4gs68f74j";
pub(crate) const GROUP_CONTRACT_ADDRESS: &str =
@@ -23,6 +23,10 @@ pub(crate) const MULTISIG_CONTRACT_ADDRESS: &str =
"n1sr06m8yqg0wzqqyqvzvp5t07dj4nevx9u8qc7j4qa72qu8e3ct8qledthy";
pub(crate) const COCONUT_DKG_CONTRACT_ADDRESS: &str =
"n1udfs22xpxle475m2nz7u47jfa3vngncdegmczwwdx00cmetypa3s7uyuqn";
pub(crate) const SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS: &str =
"n13ehuhysn5mqjeaheeuew2gjs785f6k7jm8vfsqg3jhtpkwppcmzq6m2hmz";
pub(crate) const NAME_SERVICE_CONTRACT_ADDRESS: &str =
"n1qum2tr7hh4y7ruzew68c64myjec0dq2s2njf6waja5t0w879lutqadamme";
// -- Constructor functions --
@@ -54,6 +58,10 @@ pub(crate) fn network_details() -> nym_network_defaults::NymNetworkDetails {
group_contract_address: parse_optional_str(GROUP_CONTRACT_ADDRESS),
multisig_contract_address: parse_optional_str(MULTISIG_CONTRACT_ADDRESS),
coconut_dkg_contract_address: parse_optional_str(COCONUT_DKG_CONTRACT_ADDRESS),
service_provider_directory_contract_address: parse_optional_str(
SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS,
),
name_service_contract_address: parse_optional_str(NAME_SERVICE_CONTRACT_ADDRESS),
},
explorer_api: parse_optional_str(EXPLORER_API),
}
@@ -54,6 +54,8 @@ pub(crate) fn network_details() -> nym_network_defaults::NymNetworkDetails {
group_contract_address: parse_optional_str(GROUP_CONTRACT_ADDRESS),
multisig_contract_address: parse_optional_str(MULTISIG_CONTRACT_ADDRESS),
coconut_dkg_contract_address: parse_optional_str(COCONUT_DKG_CONTRACT_ADDRESS),
service_provider_directory_contract_address: None,
name_service_contract_address: None,
},
explorer_api: parse_optional_str(EXPLORER_API),
}
-43
View File
@@ -1,43 +0,0 @@
{
"mixnode":[
"/status/mixnode/{mix_id}/report",
"/status/mixnode/{mix_id}/history",
"/status/mixnode/{mix_id}/core-status-count",
"/status/mixnode/{mix_id}/status",
"/status/mixnode/{mix_id}/reward-estimation",
"/status/mixnode/{mix_id}/compute-reward-estimation",
"/status/mixnode/{mix_id}/stake-saturation",
"/status/mixnode/{mix_id}/inclusion-probability",
"/status/mixnode/{mix_id}/avg_uptime"
],
"gateway":[
"/status/gateway/{identity}/report",
"/status/gateway/{identity}/history",
"/status/gateway/{identity}/core-status-count",
"/status/gateway/{identity}/avg_uptime"
],
"swagger":[
"/roles",
"/build-information",
"/description",
"/host-information",
"/system-info",
"/gateway",
"/gateway/client-interfaces",
"/gateway/client-interfaces/mixnet-websockets",
"/gateway/client-interfaces/wireguard",
"/health",
"/ip-packet-router",
"/metrics/mixing",
"/metrics/prometheus",
"/metrics/verloc",
"/mixnode",
"/network-requester",
"/network-requester/exit-policy"
]
}
-401
View File
@@ -1,401 +0,0 @@
#!/usr/bin/python3
# TODO (in later versions)
# - make option to run in sandbox env
# - pull endpoints from: https://validator.nymtech.net/api/v1/openapi.json
# - try this https://stackoverflow.com/questions/15431044/can-i-set-max-retries-for-requests-request/35504626#35504626
import requests as r
import argparse
import sys
import os
import pandas as pd
import json
import urllib3
import time
from json import JSONDecodeError
from tabulate import tabulate
class MainFunctions:
def __init__(self):
self.api_url = "https://validator.nymtech.net/api/v1"
self.api_endpoints_json = "api_endpoints.json"
self.output = Output()
def display_results(self, args):
id_key = args.id
mode, host, version, mix_id, role, node_df, node_dict, api_data, swagger_data, routing_history = self.collect_all_results(args)
print("\n============================================================")
print("\nNYM NODE INFO\n")
print(f"Type = {mode}")
if role:
print(f"Mode = {role}")
print(f"Identity Key = {id_key}")
print(f"Host = {host}")
print(f"Version = {version}")
if mix_id:
print(f"Mix ID = {mix_id}")
print("\n\nNODE RESULTS FROM UNFILTERED QUERY\n")
if args.markdown:
node_markdown = self._dataframe_to_markdown(node_df, ["RESULT"], ["API EDNPOINT"])
print(node_markdown, "\n")
else:
self.print_neat_dict(node_dict)
print(f"\n\nNODE RESULTS FROM {self.api_url.upper()}\n")
if args.markdown:
api_df = self._json_to_dataframe(api_data)
node_markdown = self._dataframe_to_markdown(api_df, ["RESULT"], ["API EDNPOINT"])
print(node_markdown, "\n")
else:
self.print_neat_dict(api_data)
if swagger_data:
print(f"\n\nNODE RESULTS FROM SWAGGER PAGE\n")
if args.markdown:
swagger_df = self._json_to_dataframe(swagger_data)
node_markdown = self._dataframe_to_markdown(swagger_df, ["RESULT"], ["API EDNPOINT"])
print(node_markdown, "\n")
else:
swagger_data = self._json_neat_format(swagger_data)
print(swagger_data)
else:
swagger_data = f"\nSwagger API endpoints of node {id_key} hosted on IP: {host} are not responding. Maybe you querying a deprecated version of nym-mixnode or the VPS ports are not open correctly.\n"
if routing_history:
print(f"\n\nNODE UPTIME HISTORY\n")
if args.markdown:
routing_history_df = self._json_to_dataframe(routing_history)
print(routing_history_df.to_markdown(index = False))
else:
self.print_neat_dict(routing_history)
routing_history = self._json_neat_format(routing_history)
else:
routing_history = " "
if args.output or args.output == "":
node_dict = self._json_neat_format(node_dict)
api_data = self._json_neat_format(api_data)
if role:
data_list = [f"Id. Key = {id_key}", f"Host = {host}", f"Type = {mode}", f"Mode = {role}", node_dict, api_data, swagger_data, routing_history]
else:
data_list = [f"Id. Key = {id_key}", f"Host = {host}", f"Type = {mode}", node_dict, api_data, swagger_data, routing_history]
self.output.concat_to_file(args, data_list)
def collect_all_results(self,args):
id_key = args.id
gateways_unfiltered, mixnodes_unfiltered = self.get_unfiltered_data()
gateways_df = self._json_to_dataframe(gateways_unfiltered)
gateways_df = self._set_index_to_empty(gateways_df)
mixnodes_df = self._json_to_dataframe(mixnodes_unfiltered)
mixnodes_df = self._set_index_to_empty(mixnodes_df)
mode, node_df, node_dict = self.get_node_df(id_key, gateways_df, mixnodes_df, gateways_unfiltered, mixnodes_unfiltered)
host, version, mix_id, role, api_data, swagger_data, routing_history = self.get_node_data(mode, node_dict, id_key, args)
return mode, host, version, mix_id, role, node_df, node_dict, api_data, swagger_data, routing_history
def get_node_df(self,id_key, gateways_df, mixnodes_df, gateways_unfiltered,mixnodes_unfiltered):
if id_key in mixnodes_df['mixnode_details.bond_information.mix_node.identity_key'].values:
node_df = mixnodes_df.loc[mixnodes_df['mixnode_details.bond_information.mix_node.identity_key'] == id_key]
node_dict = next((mn for mn in mixnodes_unfiltered if mn['mixnode_details']['bond_information']['mix_node']['identity_key'] == f"{id_key}"), None)
mode = "mixnode"
elif id_key in gateways_df["gateway_bond.gateway.identity_key"].values:
node_df = gateways_df.loc[gateways_df["gateway_bond.gateway.identity_key"] == id_key]
node_dict = next((gw for gw in gateways_unfiltered if gw['gateway_bond']['gateway']['identity_key'] == f"{id_key}"), None)
mode = "gateway"
else:
print(f"The identity key '{id_key}' does not exist.")
return mode, node_df, node_dict
def get_unfiltered_data(self):
print("INFO: Starting to query /detailed-unfiltered endpoint...")
gateways_unfiltered = r.get(f"{self.api_url}/status/gateways/detailed-unfiltered").json()
mixnodes_unfiltered = r.get(f"{self.api_url}/status/mixnodes/detailed-unfiltered").json()
return gateways_unfiltered, mixnodes_unfiltered
def get_node_data(self,mode, node_dict, id_key, args):
print("INFO: Sorting out data from the unfiltered endpoint...")
identity = id_key
endpoint_json = self.api_endpoints_json
with open(endpoint_json, "r") as f:
dicts = json.load(f)
endpoints = dicts[mode]
swagger = dicts["swagger"]
api_data = {}
swagger_data = {}
routing_history = {}
mix_id = None
role = None
if mode == "gateway":
host = node_dict["gateway_bond"]["gateway"]["host"]
version = node_dict["gateway_bond"]["gateway"]["version"]
for key in endpoints:
endpoint = key.replace("{identity}", identity)
url = f"{self.api_url}{endpoint}"
print(f"Querying {url}")
value = r.get(url).json()
api_data[endpoint] = value
routing_history = api_data[f"/status/gateway/{identity}/history"]["history"]
del api_data[f"/status/gateway/{identity}/history"]["history"]
swagger_data = self.get_swagger_data(host,swagger,swagger_data)
if swagger_data["/roles"]["network_requester_enabled"]== True and swagger_data["/roles"]["ip_packet_router_enabled"] == True:
role = "exit-gateway"
else:
role = "entry-gateway"
elif mode == "mixnode":
mix_id = str(node_dict["mixnode_details"]["bond_information"]["mix_id"])
for key in endpoints:
endpoint = key.replace("{mix_id}", mix_id)
url = f"{self.api_url}{endpoint}"
print(f"Querying {url}")
try:
value = r.get(url).json()
api_data[endpoint] = value
except (JSONDecodeError, json.JSONDecodeError, r.exceptions.JSONDecodeError):
print(f"Error: Endpoint {url} results in 404: Not Found!")
host = node_dict["mixnode_details"]["bond_information"]["mix_node"]["host"]
version = node_dict["mixnode_details"]["bond_information"]["mix_node"]["version"]
routing_history = api_data[f"/status/mixnode/{mix_id}/history"]["history"]
del api_data[f"/status/mixnode/{mix_id}/history"]["history"]
swagger_data = self.get_swagger_data(host,swagger,swagger_data)
else:
print(f"The mode type {mode} is not recognized!")
sys.exit(-1)
host = str(host)
if args.no_routing_history == True:
routing_history = None
else:
routing_history = routing_history
return host, version, mix_id, role, api_data, swagger_data, routing_history
def get_swagger_data(self,host,swagger,swagger_data):
print("INFO: Starting to query SWAGGER API endpoints...")
urls = [
f"http://{host}:8080/api/v1",
f"https://{host}/api/v1",
f"http://{host}/api/v1"
]
responding_url = self.get_swagger_response(urls)
if responding_url:
for endpoint in swagger:
print(f"Querying {responding_url}{endpoint}")
value = self.try_query_swagger(responding_url,endpoint)
if value:
swagger_data[endpoint] = value
else:
swagger_data = {}
return swagger_data
def try_query_swagger(self, base_url, endpoint):
url = f"{base_url}{endpoint}"
value = None
try:
value = r.get(url, timeout=2).json()
except (r.exceptions.ConnectionError, urllib3.exceptions.ProtocolError) as e:
print(f"Error: Connection error when querying {url}: {e}") # No break because you could be dealing with a different protocol
except (JSONDecodeError, json.JSONDecodeError, r.exceptions.JSONDecodeError, ConnectionResetError) as e:
print(f"Error: JSON decode error when querying {url}: {e}")
except r.exceptions.ConnectTimeout as e:
print(f"Error: Connection timeout when querying {url}: {e}")
except Exception as e:
print(f"Error: An unexpected error occurred when querying {url}: {e}")
return value
def get_swagger_response(self,urls):
responding_url = None
for base_url in urls:
endpoint = "/health"
value = self.try_query_swagger(base_url, endpoint)
if value:
responding_url = base_url
print(f"INFO: Swagger API is accessible via {responding_url}, we are going to proceed with querying Swagger endpoints...")
break
else:
print(f"Swagger API was unreachable via {base_url}, we cannot proceed with querying Swagger end points!")
return responding_url
def _set_index_to_empty(self, df):
index_len = pd.RangeIndex(len(df.index))
new_index = []
for x in index_len:
x = ""
new_index.append(x)
df.index = new_index
return df
def _dataframe_to_markdown(self,df,col_names, index_names=""):
df = df.T
df.index.names = index_names
df.columns = col_names
markdown = df.to_markdown()
return markdown
def format_dataframe(self, df):
df = self._json_to_dataframe(df)
df = df.T
return df
def print_neat_dict(self, dictionary, indent=4):
neat_dictionary = self._json_neat_format(dictionary)
print(neat_dictionary)
def _json_neat_format(self,dictionary,indent=4):
dictionary = json.dumps(dictionary, indent = indent)
return dictionary
def _json_to_dataframe(self,json):
df = pd.json_normalize(json)
return df
class Output():
def __init__(self):
self.home = os.path.expanduser('~')
self.pwd = os.path.dirname(os.path.realpath(__file__))
def concat_to_file(self,args, data_list):
filename = self.init_output_file(args)
with open(f"{filename}", "w") as output_file:
for name in data_list:
output_file.write(name)
output_file.write("\n")
print(f"\nResults were exported to {filename}.")
def init_output_file(self,args):
filename = self.get_filename(args)
os.system(f"touch {filename}")
return filename
def get_filename(self,args):
path = args.output
id_key = args.id
file = f"api_output_{id_key}.txt"
if path == "":
filename = file
else:
if path[-1] != "/":
path = path + "/"
if path[0] == "~":
path = self.home + path[1:]
filename = f"{path}{file}"
return filename
class VersionCount():
def __init__(self):
self.functions = MainFunctions()
self.mixnodes_version_column = 'mixnode_details.bond_information.mix_node.version'
self.gateways_version_column = 'gateway_bond.gateway.version'
def display_results(self, args):
df_final = self.fetch_results(args)
if args.markdown:
table = df_final.to_markdown(index=False)
else:
table = tabulate(df_final)
print(table)
def fetch_results(self, args):
gateways_unfiltered, mixnodes_unfiltered = self.functions.get_unfiltered_data()
df_gateways = self.functions._json_to_dataframe(gateways_unfiltered)
df_mixnodes = self.functions._json_to_dataframe(mixnodes_unfiltered)
versions = list(args.version)
mixnodes_version_column = self.mixnodes_version_column
gateways_version_column = self.gateways_version_column
mixnodes_sum = self.version_count(df_mixnodes, mixnodes_version_column, versions, "mixnode")
gateways_sum = self.version_count(df_gateways, gateways_version_column, versions, "gateway")
df_final = self.final_summary(mixnodes_sum, gateways_sum, versions)
return df_final
def version_count(self, df, column, versions, mode):
count_all = []
for version in versions:
version_sum = df[f'{column}'].value_counts()[f'{version}']
result = {"Node type": mode, "Version": version, "Summary":version_sum}
count_all.append(result)
return count_all
def final_summary(self, mixnodes_sum, gateways_sum, versions):
list_final = mixnodes_sum + gateways_sum
df_final = pd.DataFrame(list_final)
col_names = df_final.columns
total_summary = df_final['Summary'].sum()
if len(versions) > 1:
mixnodes_total = df_final.loc[df_final['Node type'] == 'mixnode', 'Summary'].sum()
gateways_total = df_final.loc[df_final['Node type'] == 'gateway', 'Summary'].sum()
df_append = pd.DataFrame([["mixnodes",f"versions: {versions}", f"{mixnodes_total}"],["gateways",f"versions: {versions}",f"{gateways_total}"]],columns=col_names)
df_final = pd.concat([df_final, df_append], ignore_index=True)
for version in versions:
version_total = df_final.loc[df_final['Version'] == f'{version}', 'Summary'].sum()
df_append = pd.DataFrame([["all nodes",f"{version}", f"{version_total}"]],columns=col_names)
df_final = pd.concat([df_final, df_append], ignore_index=True)
df_append = pd.DataFrame([["TOTAL SUMMARY",f"{versions}", f"{total_summary}"]],columns=col_names)
df_final = pd.concat([df_final, df_append], ignore_index=True)
return df_final
class ArgParser:
def __init__(self):
"""init for parser"""
self.functions = MainFunctions()
self.version_count = VersionCount()
def parser_main(self):
"""Main function initializing ArgumentParser, storing arguments and executing commands."""
# Top level parser
parser = argparse.ArgumentParser(
prog= "Nym-node API check",
description='''Run through all endpoints and print results.'''
)
parser.add_argument("-V","--version", action="version", version='%(prog)s 0.1.1')
# sub-command parsers
subparsers = parser.add_subparsers()
parser_pull_stats = subparsers.add_parser('query_stats',help='Get all nodes API endpoints', aliases=['q','query'])
parser_version_count = subparsers.add_parser('version_count', help='Sum of nodes in given version(s)', aliases=['v','version'])
# pull_stats arguments
parser_pull_stats.add_argument("id", help="supply nym-node identity key")
parser_pull_stats.add_argument("-n","--no_routing_history", help="Display node stats without routing history", action="store_true")
parser_pull_stats.add_argument("-m","--markdown",help="Display results in markdown format", action="store_true")
parser_pull_stats.add_argument("-o","--output",help="Save results to file (in current dir or supply with path without filename)", nargs='?',const="", type=str)
parser_pull_stats.set_defaults(func=self.functions.display_results)
# version_count arguments
parser_version_count.add_argument('version', help="supply node versions separated with space", nargs='+')
parser_version_count.add_argument("-m","--markdown",help="Display results in markdown format", action="store_true")
parser_version_count.set_defaults(func=self.version_count.display_results)
args = parser.parse_args()
try:
func = args.func
try:
args.func(args)
except (AttributeError, KeyError) as e:
msg = f"{e}.\nPlease run python {__file__} --help"
self.panic(msg)
except UnboundLocalError as e:
msg = f"{e}.\nPlease provide a correct node identity key."
self.panic(msg)
except FileNotFoundError as e:
msg = f"{e}.\nMake sure your <PATH> supplied to --output is correct."
self.panic(msg)
except AttributeError:
parser.print_help(sys.stderr)
def panic(self,msg):
"""Error message print"""
print(f"error: {msg}", file=sys.stderr)
sys.exit(-1)
if __name__ == '__main__':
node_check = ArgParser()
node_check.parser_main()
+1 -1
View File
@@ -274,7 +274,7 @@ where
specification: GatewaySelectionSpecification::UniformRemote {
must_use_tls: false,
},
available_gateways: current_gateways(&mut rng, &nym_apis, None).await?,
available_gateways: current_gateways(&mut rng, &nym_apis).await?,
wg_tun_address: None,
});
+9 -2
View File
@@ -9,7 +9,10 @@ license.workspace = true
[dependencies]
async-trait = { workspace = true }
bip39 = { workspace = true }
nym-client-core = { path = "../../../common/client-core", features = ["fs-surb-storage", "fs-gateways-storage"] }
nym-client-core = { path = "../../../common/client-core", features = [
"fs-surb-storage",
"fs-gateways-storage",
] }
nym-crypto = { path = "../../../common/crypto" }
nym-gateway-requests = { path = "../../../gateway/gateway-requests" }
nym-bandwidth-controller = { path = "../../../common/bandwidth-controller" }
@@ -21,7 +24,9 @@ nym-sphinx = { path = "../../../common/nymsphinx" }
nym-task = { path = "../../../common/task" }
nym-topology = { path = "../../../common/topology" }
nym-socks5-client-core = { path = "../../../common/socks5-client-core" }
nym-validator-client = { path = "../../../common/client-libs/validator-client", features = ["http-client"] }
nym-validator-client = { path = "../../../common/client-libs/validator-client", features = [
"http-client",
] }
nym-socks5-requests = { path = "../../../common/socks5/requests" }
nym-ordered-buffer = { path = "../../../common/socks5/ordered-buffer" }
nym-service-providers-common = { path = "../../../service-providers/common" }
@@ -37,6 +42,8 @@ tap = { workspace = true }
thiserror = { workspace = true }
url = { workspace = true }
toml = "0.5.10"
tokio = { workspace = true }
tokio-util = { workspace = true, features = ["codec"] }
[dev-dependencies]
anyhow = { workspace = true }
@@ -16,7 +16,7 @@ async fn main() {
let our_address = *client.nym_address();
println!("Our client nym address is: {our_address}");
let sender = client.split_sender();
let mut sender = client.split_sender();
// receiving task
let receiving_task_handle = tokio::spawn(async move {
+1 -1
View File
@@ -459,7 +459,7 @@ where
);
let mut rng = OsRng;
let available_gateways = current_gateways(&mut rng, &nym_api_endpoints, None).await?;
let available_gateways = current_gateways(&mut rng, &nym_api_endpoints).await?;
Ok(GatewaySetup::New {
specification: selection_spec,
+249 -4
View File
@@ -2,9 +2,11 @@ use crate::mixnet::client::MixnetClientBuilder;
use crate::mixnet::traits::MixnetMessageSender;
use crate::{Error, Result};
use async_trait::async_trait;
use futures::{ready, Stream, StreamExt};
use bytes::{Buf as _, BytesMut};
use futures::{ready, FutureExt, Sink, SinkExt, Stream, StreamExt};
use log::error;
use nym_client_core::client::base_client::GatewayConnection;
use nym_client_core::client::inbound_messages::InputMessageCodec;
use nym_client_core::client::{
base_client::{ClientInput, ClientOutput, ClientState},
inbound_messages::InputMessage,
@@ -12,15 +14,18 @@ use nym_client_core::client::{
};
use nym_crypto::asymmetric::identity;
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::receiver::ReconstructedMessageCodec;
use nym_sphinx::{params::PacketType, receiver::ReconstructedMessage};
use nym_task::{
connections::{ConnectionCommandSender, LaneQueueLengths},
TaskHandle,
};
use nym_topology::NymTopology;
use std::pin::Pin;
use std::pin::{pin, Pin};
use std::sync::Arc;
use std::task::{Context, Poll};
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use tokio_util::codec::{Encoder, FramedRead};
/// Client connected to the Nym mixnet.
pub struct MixnetClient {
@@ -51,6 +56,24 @@ pub struct MixnetClient {
// internal state used for the `Stream` implementation
_buffered: Vec<ReconstructedMessage>,
// internal state used for the `AsyncRead` implementation
_read: ReadBuffer,
}
#[derive(Debug, Default)]
struct ReadBuffer {
buffer: BytesMut,
}
impl ReadBuffer {
fn clear(&mut self) {
self.buffer.clear();
}
fn pending(&self) -> bool {
!self.buffer.is_empty()
}
}
impl MixnetClient {
@@ -75,6 +98,7 @@ impl MixnetClient {
task_handle,
packet_type,
_buffered: Vec::new(),
_read: ReadBuffer::default(),
}
}
@@ -190,6 +214,25 @@ impl MixnetClient {
// note: it's important to take ownership of the struct as if the shutdown is `TaskHandle::External`,
// it must be dropped to finalize the shutdown
}
fn read_buffer_to_slice(
&mut self,
buf: &mut ReadBuf,
cx: &mut Context<'_>,
) -> Poll<tokio::io::Result<()>> {
if self._read.buffer.len() < buf.capacity() {
// let written = self._read.buffer.len();
buf.put_slice(&self._read.buffer);
self._read.clear();
Poll::Ready(Ok(()))
} else {
let written = buf.capacity();
buf.put_slice(&self._read.buffer.split_off(written));
self._read.buffer.advance(written);
cx.waker().wake_by_ref();
Poll::Ready(Ok(()))
}
}
}
#[derive(Clone)]
@@ -198,6 +241,200 @@ pub struct MixnetClientSender {
packet_type: Option<PacketType>,
}
impl AsyncRead for MixnetClient {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf,
) -> Poll<tokio::io::Result<()>> {
let mut codec = ReconstructedMessageCodec {};
if self._read.pending() {
return self.read_buffer_to_slice(buf, cx);
}
let msg = match self.as_mut().poll_next(cx) {
Poll::Ready(Some(msg)) => msg,
Poll::Ready(None) => return Poll::Ready(Ok(())),
Poll::Pending => return Poll::Pending,
};
match codec.encode(msg, &mut self._read.buffer) {
Ok(_) => {}
Err(e) => {
error!("failed to encode reconstructed message: {:?}", e);
return Poll::Ready(Err(tokio::io::Error::new(
tokio::io::ErrorKind::Other,
"failed to encode reconstructed message",
)));
}
};
self.read_buffer_to_slice(buf, cx)
}
}
impl AsyncWrite for MixnetClient {
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<Result<usize, std::io::Error>> {
let codec = InputMessageCodec {};
let mut reader = FramedRead::new(buf, codec);
let mut fut = reader.next();
let msg = match fut.poll_unpin(cx) {
Poll::Ready(Some(Ok(msg))) => msg,
Poll::Ready(Some(Err(_))) => {
return Poll::Ready(Err(std::io::Error::new(
std::io::ErrorKind::Other,
"failed to read message from input",
)))
}
Poll::Pending => return Poll::Pending,
Poll::Ready(None) => return Poll::Ready(Ok(0)),
};
let msg_size = msg.serialized_size();
let mut fut = pin!(self.client_input.send(msg));
match fut.poll_unpin(cx) {
Poll::Ready(Ok(())) => Poll::Ready(Ok(msg_size as usize)),
Poll::Ready(Err(_)) => Poll::Ready(Err(std::io::Error::new(
std::io::ErrorKind::Other,
"failed to send message to mixnet",
))),
Poll::Pending => Poll::Pending,
}
}
fn poll_flush(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<std::prelude::v1::Result<(), std::io::Error>> {
Sink::poll_flush(self, cx)
.map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "failed to flush the sink"))
}
fn poll_shutdown(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<std::prelude::v1::Result<(), std::io::Error>> {
AsyncWrite::poll_flush(self, cx)
}
}
impl Sink<InputMessage> for MixnetClient {
type Error = Error;
fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
match self.sender().poll_ready_unpin(cx) {
Poll::Ready(Ok(())) => Poll::Ready(Ok(())),
Poll::Ready(Err(_)) => Poll::Ready(Err(Error::MessageSendingFailure)),
Poll::Pending => Poll::Pending,
}
}
fn start_send(mut self: Pin<&mut Self>, item: InputMessage) -> Result<()> {
self.sender()
.start_send_unpin(item)
.map_err(|_| Error::MessageSendingFailure)
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
self.sender()
.poll_flush_unpin(cx)
.map_err(|_| Error::MessageSendingFailure)
}
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
self.sender()
.poll_close_unpin(cx)
.map_err(|_| Error::MessageSendingFailure)
}
}
// TODO: there should be a better way of implementing Sink and AsyncWrite over T: MixnetMessageSender
impl AsyncWrite for MixnetClientSender {
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<Result<usize, std::io::Error>> {
let codec = InputMessageCodec {};
let mut reader = FramedRead::new(buf, codec);
let mut fut = reader.next();
let msg = match fut.poll_unpin(cx) {
Poll::Ready(Some(Ok(msg))) => msg,
Poll::Ready(Some(Err(_))) => {
return Poll::Ready(Err(std::io::Error::new(
std::io::ErrorKind::Other,
"failed to read message from input",
)))
}
Poll::Pending => return Poll::Pending,
Poll::Ready(None) => return Poll::Ready(Ok(0)),
};
let msg_size = msg.serialized_size();
let mut fut = pin!(self.client_input.send(msg));
match fut.poll_unpin(cx) {
Poll::Ready(Ok(())) => Poll::Ready(Ok(msg_size as usize)),
Poll::Ready(Err(_)) => Poll::Ready(Err(std::io::Error::new(
std::io::ErrorKind::Other,
"failed to send message to mixnet",
))),
Poll::Pending => Poll::Pending,
}
}
fn poll_flush(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<std::prelude::v1::Result<(), std::io::Error>> {
Sink::poll_flush(self, cx)
.map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "failed to flush the sink"))
}
fn poll_shutdown(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<std::prelude::v1::Result<(), std::io::Error>> {
AsyncWrite::poll_flush(self, cx)
}
}
impl Sink<InputMessage> for MixnetClientSender {
type Error = Error;
fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
match self.sender().poll_ready_unpin(cx) {
Poll::Ready(Ok(())) => Poll::Ready(Ok(())),
Poll::Ready(Err(_)) => Poll::Ready(Err(Error::MessageSendingFailure)),
Poll::Pending => Poll::Pending,
}
}
fn start_send(mut self: Pin<&mut Self>, item: InputMessage) -> Result<()> {
self.sender()
.start_send_unpin(item)
.map_err(|_| Error::MessageSendingFailure)
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
self.sender()
.poll_flush_unpin(cx)
.map_err(|_| Error::MessageSendingFailure)
}
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
self.sender()
.poll_close_unpin(cx)
.map_err(|_| Error::MessageSendingFailure)
}
}
impl Stream for MixnetClient {
type Item = ReconstructedMessage;
@@ -234,12 +471,16 @@ impl MixnetMessageSender for MixnetClient {
self.packet_type
}
async fn send(&self, message: InputMessage) -> Result<()> {
async fn send(&mut self, message: InputMessage) -> Result<()> {
self.client_input
.send(message)
.await
.map_err(|_| Error::MessageSendingFailure)
}
fn sender(&mut self) -> &mut tokio_util::sync::PollSender<InputMessage> {
&mut self.client_input.input_sender
}
}
#[async_trait]
@@ -248,10 +489,14 @@ impl MixnetMessageSender for MixnetClientSender {
self.packet_type
}
async fn send(&self, message: InputMessage) -> Result<()> {
async fn send(&mut self, message: InputMessage) -> Result<()> {
self.client_input
.send(message)
.await
.map_err(|_| Error::MessageSendingFailure)
}
fn sender(&mut self) -> &mut tokio_util::sync::PollSender<InputMessage> {
&mut self.client_input.input_sender
}
}
+6 -4
View File
@@ -16,9 +16,11 @@ pub trait MixnetMessageSender {
None
}
fn sender(&mut self) -> &mut tokio_util::sync::PollSender<InputMessage>;
/// Sends a [`InputMessage`] to the mixnet. This is the most low-level sending function, for
/// full customization.
async fn send(&self, message: InputMessage) -> Result<()>;
async fn send(&mut self, message: InputMessage) -> Result<()>;
/// Sends data to the supplied Nym address with the default surb behaviour.
///
@@ -35,7 +37,7 @@ pub trait MixnetMessageSender {
/// client.send_plain_message(recipient, "hi").await.unwrap();
/// }
/// ```
async fn send_plain_message<M>(&self, address: Recipient, message: M) -> Result<()>
async fn send_plain_message<M>(&mut self, address: Recipient, message: M) -> Result<()>
where
M: AsRef<[u8]> + Send,
{
@@ -61,7 +63,7 @@ pub trait MixnetMessageSender {
/// }
/// ```
async fn send_message<M>(
&self,
&mut self,
address: Recipient,
message: M,
surbs: IncludedSurbs,
@@ -103,7 +105,7 @@ pub trait MixnetMessageSender {
/// client.send_reply(tag, b"hi").await.unwrap();
/// }
/// ```
async fn send_reply<M>(&self, recipient_tag: AnonymousSenderTag, message: M) -> Result<()>
async fn send_reply<M>(&mut self, recipient_tag: AnonymousSenderTag, message: M) -> Result<()>
where
M: AsRef<[u8]> + Send,
{
@@ -13,10 +13,11 @@ import type { SetupMixFetchOps } from '@nymproject/mix-fetch';
const defaultUrl = 'https://nymtech.net/favicon.svg';
const args = { mode: 'unsafe-ignore-cors' };
const mixFetchOptions: SetupMixFetchOps = {
preferredGateway: '6Gb7ftQdKveMjPyrxDXeAtfYAX7Zg5mVZHtnRC5MmZ1B', // with WSS
preferredGateway: '983r9LKDT9UUxx4Zsn2AH49poJ7Ep24ueR8ENfWFgCX6', // with WSS
preferredNetworkRequester:
'8rRGWy54oC8drFL9DepMegBt2DLrsqQwCoHMXt9nsnTo.2XjCPVbb4FpQ9hNRcXwb9mTzEAVVk1zf1tcch3wdtNEA@6Gb7ftQdKveMjPyrxDXeAtfYAX7Zg5mVZHtnRC5MmZ1B',
'DxAc9J4eqREc8hYfDobkSc81JLkmmrhJ77zJvHShUPoi.92bnebXtBuwKiYycrpioaAiYgta5hHWkys5aSGBQg5av@983r9LKDT9UUxx4Zsn2AH49poJ7Ep24ueR8ENfWFgCX6',
mixFetchOverride: {
requestTimeoutMs: 60_000,
},
@@ -81,4 +82,5 @@ export const MixFetch = () => {
</div>
);
};
```
+2 -3
View File
@@ -13,14 +13,13 @@ const defaultUrl = 'https://nymtech.net/favicon.svg';
const args = { mode: 'unsafe-ignore-cors' };
const mixFetchOptions: SetupMixFetchOps = {
preferredGateway: '6Gb7ftQdKveMjPyrxDXeAtfYAX7Zg5mVZHtnRC5MmZ1B', // with WSS
preferredGateway: '983r9LKDT9UUxx4Zsn2AH49poJ7Ep24ueR8ENfWFgCX6', // with WSS
preferredNetworkRequester:
'8rRGWy54oC8drFL9DepMegBt2DLrsqQwCoHMXt9nsnTo.2XjCPVbb4FpQ9hNRcXwb9mTzEAVVk1zf1tcch3wdtNEA@6Gb7ftQdKveMjPyrxDXeAtfYAX7Zg5mVZHtnRC5MmZ1B',
'DxAc9J4eqREc8hYfDobkSc81JLkmmrhJ77zJvHShUPoi.92bnebXtBuwKiYycrpioaAiYgta5hHWkys5aSGBQg5av@983r9LKDT9UUxx4Zsn2AH49poJ7Ep24ueR8ENfWFgCX6',
mixFetchOverride: {
requestTimeoutMs: 60_000,
},
forceTls: true, // force WSS
extra: {},
};
export const MixFetch = () => {
@@ -1,4 +1,4 @@
import { Callout } from 'nextra/components';
import { Callout } from 'nextra/components'
# `mixFetch`
@@ -7,27 +7,32 @@ An easy way to secure parts or all of your web app is to replace calls to [`fetc
MixFetch works the same as vanilla `fetch` as it's a proxied wrapper around the original function.
Sounds great, are there any catches? Well, there are a few (for now):
1. Currently, the operators of Network Requesters that make the final request at the egress part of the Nym mixnet to the internet use a [standard allow list](https://nymtech.net/.wellknown/network-requester/standard-allowed-list.txt) in combination with their own configuration. If you are trying to access something that is not on the allow list, please check the FAQ page.
1. Currently, the operators of Network Requesters that make the final request at the egress part of the Nym mixnet to
the internet use a [standard allow list](https://nymtech.net/.wellknown/network-requester/standard-allowed-list.txt)
in combination with their own configuration. If you are trying to access something that is not on the allow list, please check the FAQ page.
2. CA certificates in `mixFetch` are periodically updated, so if you get a certificate error, the root certificate you need might not be valid. If that's the case, [send a PR](https://github.com/nymtech/nym/pulls) if you need changes to the Certificates.
3. If you are using `mixFetch` in a web app with HTTPS you will need to use a gateway that has Secure Websockets to avoid getting a [mixed content](https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content) error.
3. If you are using `mixFetch` in a web app with HTTPS you will need to use a gateway that has Secure Websockets to
avoid getting a [mixed content](https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content) error.
4. For now, mixfetch doesn't work with SURBS, altough this may change in the future.
Read [this article](https://blog.nymtech.net/mixfetch-like-the-fetch-api-but-via-the-mixnet-82acfd435c62) to learn more about mixFetch.
<Callout type="info" emoji="️">
Right now Gateways are not required to run a Secure Websocket (WSS) listener, so only a subset of nodes running in Gateway mode have configured their nodes to do so.
For the moment you have to select a Gateway that has WSS enabled and Network Requester from [Harbourmaster Gateways for mixFetch list](https://harbourmaster.nymtech.net/).
For the moment you have to select a Gateway that has WSS enabled from [this list](https://harbourmaster.nymtech.net/v1/services?wss=true).
```
You can also find WSS-enabled nodes by querying the `gateways/described` endpoint on the Nym API, either via the [Swagger webpage](https://validator.nymtech.net/api/swagger/index.html) or with `curl`:
```
curl -X 'GET' \
'https://validator.nymtech.net/api/v1/gateways/described' \
-H 'accept: application/json'
```
'https://validator.nymtech.net/api/v1/gateways/described' \
-H 'accept: application/json'
```
</Callout>
```ts
@@ -35,9 +40,9 @@ curl -X 'GET' \
import type { SetupMixFetchOps } from '@nymproject/mix-fetch';
const mixFetchOptions: SetupMixFetchOps = {
preferredGateway: '6Gb7ftQdKveMjPyrxDXeAtfYAX7Zg5mVZHtnRC5MmZ1B', // with WSS
preferredGateway: '983r9LKDT9UUxx4Zsn2AH49poJ7Ep24ueR8ENfWFgCX6', // with WSS
preferredNetworkRequester:
'8rRGWy54oC8drFL9DepMegBt2DLrsqQwCoHMXt9nsnTo.2XjCPVbb4FpQ9hNRcXwb9mTzEAVVk1zf1tcch3wdtNEA@6Gb7ftQdKveMjPyrxDXeAtfYAX7Zg5mVZHtnRC5MmZ1B',
'DxAc9J4eqREc8hYfDobkSc81JLkmmrhJ77zJvHShUPoi.92bnebXtBuwKiYycrpioaAiYgta5hHWkys5aSGBQg5av@983r9LKDT9UUxx4Zsn2AH49poJ7Ep24ueR8ENfWFgCX6',
mixFetchOverride: {
requestTimeoutMs: 60_000,
},
@@ -47,7 +52,6 @@ const mixFetchOptions: SetupMixFetchOps = {
```
##### Environment Setup
Begin by creating a directory and configuring your application environment:
```bash
@@ -55,7 +59,6 @@ npm create vite@latest
```
During the environment setup, choose React and subsequently opt for Typescript if you want your application to function smoothly following this tutorial. Next, navigate to your application directory and run the following commands:
```bash
cd < YOUR_APP >
npm i
@@ -63,36 +66,33 @@ npm run dev
```
##### Installation
Install the required package:
```bash
npm install @nymproject/mix-fetch-full-fat
```
##### Imports
In the `src` folder, open the `App.tsx` file and delete all the code.
Import the client in your app:
````js
import { mixFetch } from "@nymproject/mix-fetch-full-fat";
````
```js
import { mixFetch } from '@nymproject/mix-fetch-full-fat';
```
##### Example: using the `mixFetch` client:
`Get` and `Post` outputs will be observable from your console.
```ts
import './App.css';
import "./App.css";
import { mixFetch, SetupMixFetchOps } from '@nymproject/mix-fetch-full-fat';
import React from 'react';
const mixFetchOptions: SetupMixFetchOps = {
preferredGateway: '6Gb7ftQdKveMjPyrxDXeAtfYAX7Zg5mVZHtnRC5MmZ1B', // with WSS
preferredGateway: '983r9LKDT9UUxx4Zsn2AH49poJ7Ep24ueR8ENfWFgCX6', // with WSS
preferredNetworkRequester:
'8rRGWy54oC8drFL9DepMegBt2DLrsqQwCoHMXt9nsnTo.2XjCPVbb4FpQ9hNRcXwb9mTzEAVVk1zf1tcch3wdtNEA@6Gb7ftQdKveMjPyrxDXeAtfYAX7Zg5mVZHtnRC5MmZ1B',
'DxAc9J4eqREc8hYfDobkSc81JLkmmrhJ77zJvHShUPoi.92bnebXtBuwKiYycrpioaAiYgta5hHWkys5aSGBQg5av@983r9LKDT9UUxx4Zsn2AH49poJ7Ep24ueR8ENfWFgCX6',
mixFetchOverride: {
requestTimeoutMs: 60_000,
},
@@ -100,62 +100,48 @@ const mixFetchOptions: SetupMixFetchOps = {
extra: {},
};
export function HttpGET() {
const [html, setHtml] = React.useState('');
async function get() {
//Make sure the URL is whitelisted (see 'standard allowed list') otherwise you will get a network requester filter check error
const response = await mixFetch('https://nymtech.net/favicon.svg', { mode: 'unsafe-ignore-cors' }, mixFetchOptions);
const text = await response.text();
console.log('response was', text);
setHtml(html);
}
const [html, setHtml] = React.useState('')
async function get () {
//Make sure the URL is whitelisted (see 'standard allowed list') otherwise you will get a network requester filter check error
const response = await mixFetch('https://nymtech.net/favicon.svg', { mode: 'unsafe-ignore-cors' }, mixFetchOptions)
const text = await response.text()
console.log('response was', text)
setHtml(html)
}
return (
<>
<button
onClick={() => {
get();
}}
>
Get
</button>
<button onClick={() => { get() }}>Get</button>
</>
);
)
}
export function HttpPOST() {
async function post() {
//Make sure the URL is whitelisted (see 'standard allowed list') otherwise you will get a network requester filter check error
const apiResponse = await mixFetch(
'https://httpbin.org/post',
{
async function post () {
//Make sure the URL is whitelisted (see 'standard allowed list') otherwise you will get a network requester filter check error
const apiResponse = await mixFetch('https://httpbin.org/post', {
method: 'POST',
body: JSON.stringify({ foo: 'bar' }),
headers: { 'Content-Type': 'application/json' },
},
mixFetchOptions,
);
console.log(apiResponse);
}
headers: { 'Content-Type': 'application/json' }
}, mixFetchOptions)
console.log(apiResponse)
}
return (
<>
<button
onClick={() => {
post();
}}
>
Post
</button>
<button onClick={() => { post() }}>Post</button>
</>
);
)
}
export default function App() {
return (
<>
<HttpGET />
<HttpPOST />
<HttpGET/>
<HttpPOST/>
</>
);
)
}
```
@@ -4,9 +4,9 @@ import { appendOutput, appendImageOutput } from './utils';
async function main() {
// options for mixFetch (you can also set these with the `createMixFetch` function
const mixFetchOptions = {
preferredGateway: '6Gb7ftQdKveMjPyrxDXeAtfYAX7Zg5mVZHtnRC5MmZ1B', // with WSS
preferredGateway: 'E3mvZTHQCdBvhfr178Swx9g4QG3kkRUun7YnToLMcMbM', // with WSS
preferredNetworkRequester:
'8rRGWy54oC8drFL9DepMegBt2DLrsqQwCoHMXt9nsnTo.2XjCPVbb4FpQ9hNRcXwb9mTzEAVVk1zf1tcch3wdtNEA@6Gb7ftQdKveMjPyrxDXeAtfYAX7Zg5mVZHtnRC5MmZ1B',
'GiRjFWrMxt58pEMuusm4yT3RxoMD1MMPrR9M2N4VWRJP.3CNZBPq4vg7v7qozjGjdPMXcvDmkbWPCgbGCjQVw9n6Z@2xU4CBE6QiiYt6EyBXSALwxkNvM7gqJfjHXaMkjiFmYW',
mixFetchOverride: {
requestTimeoutMs: 60_000,
},
@@ -29,7 +29,7 @@
"comlink": "^4.3.1",
"fake-indexeddb": "^4.0.2",
"rollup-plugin-polyfill": "^4.2.0",
"ws": "^8.17.1"
"ws": "^8.14.2"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^24.0.1",
@@ -2847,10 +2847,10 @@ wrappy@1:
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
ws@^8.17.1:
version "8.17.1"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b"
integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==
ws@^8.14.2:
version "8.14.2"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f"
integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==
ws@~8.11.0:
version "8.11.0"
@@ -29,9 +29,9 @@ async function main() {
const args = { mode: 'unsafe-ignore-cors' };
const resp = await mixFetch(url, args, {
preferredGateway: '6Gb7ftQdKveMjPyrxDXeAtfYAX7Zg5mVZHtnRC5MmZ1B', // with WSS
preferredGateway: '2BuMSfMW3zpeAjKXyKLhmY4QW1DXurrtSPEJ6CjX3SEh',
preferredNetworkRequester:
'8rRGWy54oC8drFL9DepMegBt2DLrsqQwCoHMXt9nsnTo.2XjCPVbb4FpQ9hNRcXwb9mTzEAVVk1zf1tcch3wdtNEA@6Gb7ftQdKveMjPyrxDXeAtfYAX7Zg5mVZHtnRC5MmZ1B',
'GiRjFWrMxt58pEMuusm4yT3RxoMD1MMPrR9M2N4VWRJP.3CNZBPq4vg7v7qozjGjdPMXcvDmkbWPCgbGCjQVw9n6Z@2xU4CBE6QiiYt6EyBXSALwxkNvM7gqJfjHXaMkjiFmYW',
});
console.log({ resp });
const text = await resp.text();
@@ -36,7 +36,6 @@ impl OnStartData {
pub struct IpPacketRouter {
#[allow(unused)]
config: Config,
wait_for_gateway: bool,
custom_topology_provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
custom_gateway_transceiver: Option<Box<dyn GatewayTransceiver + Send + Sync>>,
@@ -80,12 +79,6 @@ impl IpPacketRouter {
self
}
#[must_use]
pub fn with_minimum_gateway_performance(mut self, minimum_gateway_performance: u8) -> Self {
self.config.base.debug.topology.minimum_gateway_performance = minimum_gateway_performance;
self
}
#[must_use]
#[allow(unused)]
pub fn with_on_start(mut self, on_start: oneshot::Sender<OnStartData>) -> Self {
@@ -605,7 +605,7 @@ impl MixnetListener {
// When an incoming mixnet message triggers a response that we send back, such as during
// connect handshake.
async fn handle_response(&self, response: IpPacketResponse) -> Result<()> {
async fn handle_response(&mut self, response: IpPacketResponse) -> Result<()> {
let Some(recipient) = response.recipient() else {
log::error!("No recipient in response packet, this should NOT happen!");
return Err(IpPacketRouterError::NoRecipientInResponse);
@@ -635,7 +635,7 @@ impl MixnetListener {
// A single incoming request can trigger multiple responses, such as when data requests contain
// multiple IP packets.
async fn handle_responses(&self, responses: Vec<PacketHandleResult>) {
async fn handle_responses(&mut self, responses: Vec<PacketHandleResult>) {
for response in responses {
match response {
Ok(Some(response)) => {
+13 -6
View File
@@ -4,7 +4,7 @@
[package]
name = "nym-network-requester"
license = "GPL-3.0"
version = "1.1.38"
version = "1.1.36"
authors.workspace = true
edition.workspace = true
rust-version = "1.70"
@@ -20,7 +20,7 @@ anyhow = { workspace = true }
addr = { workspace = true }
async-trait = { workspace = true }
bs58 = { workspace = true }
clap = { workspace = true, features = ["cargo", "derive"]}
clap = { workspace = true, features = ["cargo", "derive"] }
dirs = "4.0"
futures = { workspace = true }
humantime-serde = { workspace = true }
@@ -33,19 +33,26 @@ regex = { workspace = true }
reqwest = { workspace = true, features = ["json"] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "chrono"]}
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "chrono"] }
tap = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true, features = [ "net", "rt-multi-thread", "macros" ] }
tokio = { workspace = true, features = ["net", "rt-multi-thread", "macros"] }
tokio-tungstenite = { workspace = true }
tokio-util = { workspace = true }
url = { workspace = true }
time = { workspace = true }
zeroize = { workspace = true }
# internal
nym-async-file-watcher = { path = "../../common/async-file-watcher" }
nym-bin-common = { path = "../../common/bin-common", features = ["output_format"] }
nym-client-core = { path = "../../common/client-core", features = ["cli", "fs-gateways-storage", "fs-surb-storage"] }
nym-bin-common = { path = "../../common/bin-common", features = [
"output_format",
] }
nym-client-core = { path = "../../common/client-core", features = [
"cli",
"fs-gateways-storage",
"fs-surb-storage",
] }
nym-client-websocket-requests = { path = "../../clients/native/websocket-requests" }
nym-config = { path = "../../common/config" }
nym-credentials = { path = "../../common/credentials" }
@@ -10,13 +10,14 @@ use crate::{reply, socks5};
use async_trait::async_trait;
use futures::channel::{mpsc, oneshot};
use futures::stream::StreamExt;
use futures::SinkExt;
use log::{debug, warn};
use nym_bin_common::bin_info_owned;
use nym_client_core::client::mix_traffic::transceiver::GatewayTransceiver;
use nym_client_core::config::disk_persistence::CommonClientPaths;
use nym_client_core::HardcodedTopologyProvider;
use nym_network_defaults::NymNetworkDetails;
use nym_sdk::mixnet::{MixnetMessageSender, TopologyProvider};
use nym_sdk::mixnet::TopologyProvider;
use nym_service_providers_common::interface::{
BinaryInformation, ProviderInterfaceVersion, Request, RequestVersion,
};
@@ -40,6 +41,7 @@ use nym_task::manager::TaskHandle;
use nym_task::TaskClient;
use std::path::Path;
use std::sync::atomic::{AtomicUsize, Ordering};
use tokio_util::sync::PollSender;
// Since it's an atomic, it's safe to be kept static and shared across threads
static ACTIVE_PROXIES: AtomicUsize = AtomicUsize::new(0);
@@ -222,15 +224,6 @@ impl NRServiceProviderBuilder {
self
}
#[must_use]
// this is a false positive, this method is actually called when used as a library
// but clippy complains about it when building the binary
#[allow(unused)]
pub fn with_minimum_gateway_performance(mut self, minimum_gateway_performance: u8) -> Self {
self.config.base.debug.topology.minimum_gateway_performance = minimum_gateway_performance;
self
}
#[must_use]
// this is a false positive, this method is actually called when used as a library
// but clippy complains about it when building the binary
@@ -293,6 +286,8 @@ impl NRServiceProviderBuilder {
// going to be used by `mixnet_response_listener`
let (mix_input_sender, mix_input_receiver) = tokio::sync::mpsc::channel::<MixnetMessage>(1);
let mix_input_sender = PollSender::new(mix_input_sender);
// Controller for managing all active connections.
let (mut active_connections_controller, controller_sender) = Controller::new(
mixnet_client.connection_command_sender(),
@@ -409,7 +404,7 @@ impl NRServiceProvider {
/// Listens for any messages from `mix_reader` that should be written back to the mix network
/// via the `websocket_writer`.
async fn mixnet_response_listener(
mixnet_client_sender: nym_sdk::mixnet::MixnetClientSender,
mut mixnet_client_sender: nym_sdk::mixnet::MixnetClientSender,
mut mix_input_reader: MixProxyReader<MixnetMessage>,
stats_collector: Option<ServiceStatisticsCollector>,
packet_type: PacketType,
@@ -434,7 +429,7 @@ impl NRServiceProvider {
}
let response_message = msg.into_input_message(packet_type);
mixnet_client_sender.send(response_message).await.unwrap();
nym_sdk::mixnet::MixnetMessageSender::send(&mut mixnet_client_sender, response_message).await.unwrap();
} else {
log::error!("Exiting: channel closed!");
break;
@@ -452,7 +447,7 @@ impl NRServiceProvider {
return_address: reply::MixnetAddress,
biggest_packet_size: PacketSize,
controller_sender: ControllerSender,
mix_input_sender: MixProxySender<MixnetMessage>,
mut mix_input_sender: MixProxySender<MixnetMessage>,
lane_queue_lengths: LaneQueueLengths,
mut shutdown: TaskClient,
) {
@@ -546,7 +541,7 @@ impl NRServiceProvider {
.unwrap_or(traffic_config.primary_packet_size);
let controller_sender_clone = self.controller_sender.clone();
let mix_input_sender_clone = self.mix_input_sender.clone();
let mut mix_input_sender_clone = self.mix_input_sender.clone();
let lane_queue_lengths_clone = self.mixnet_client.shared_lane_queue_lengths();
let mut shutdown = self.shutdown.get_handle();
@@ -5,6 +5,7 @@ use super::error::StatsError;
use crate::core::new_legacy_request_version;
use crate::reply::MixnetMessage;
use async_trait::async_trait;
use futures::SinkExt;
use log::*;
use nym_service_providers_common::interface::RequestVersion;
use nym_socks5_proxy_helpers::proxy_runner::MixProxySender;
@@ -165,7 +166,7 @@ impl StatisticsCollector for ServiceStatisticsCollector {
}
async fn send_stats_message(
&self,
&mut self,
stats_message: StatsMessage,
) -> Result<(), CommonStatsError> {
let msg = build_statistics_request_bytes(stats_message)?;
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-cli"
version = "1.1.38"
version = "1.1.36"
authors.workspace = true
edition = "2021"
license.workspace = true
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "nymvisor"
version = "0.1.3"
version = "0.1.1"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
@@ -21,7 +21,7 @@ hex = { workspace = true }
humantime = { workspace = true }
humantime-serde = { workspace = true }
nix = { workspace = true, features = ["signal", "fs"] }
reqwest = { workspace = true, features = ["json", "stream", "rustls-tls"] }
reqwest = { workspace = true, features = ["json", "stream"] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
sha2 = "0.10.8"
+7 -3
View File
@@ -1,6 +1,9 @@
[package]
name = "nym-client-wasm"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jedrzej Stuczynski <andrew@nymtech.net>"]
authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
"Jedrzej Stuczynski <andrew@nymtech.net>",
]
version = "1.3.0-rc.0"
edition = "2021"
keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"]
@@ -22,15 +25,16 @@ serde_json = { workspace = true }
serde-wasm-bindgen = { workspace = true }
wasm-bindgen = { workspace = true }
wasm-bindgen-futures = { workspace = true }
thiserror = { workspace = true }
thiserror = { workspace = true }
tsify = { workspace = true, features = ["js"] }
tokio = { workspace = true, default-features = false, features = ["sync"] }
nym-bin-common = { path = "../../common/bin-common" }
wasm-client-core = { path = "../../common/wasm/client-core" }
wasm-utils = { path = "../../common/wasm/utils" }
nym-node-tester-utils = { path = "../../common/node-tester-utils", optional = true }
nym-node-tester-wasm = { path = "../node-tester", optional = true}
nym-node-tester-wasm = { path = "../node-tester", optional = true }
[dev-dependencies]
wasm-bindgen-test = { workspace = true }
+1 -1
View File
@@ -11,6 +11,6 @@
"dependencies": {
"@nymproject/nym-client-wasm": "file:../../../dist/node/wasm/client",
"indexeddbshim": "^13.0.0-pre.1",
"ws": "^8.17.1"
"ws": "^8.13.0"
}
}
+4 -4
View File
@@ -888,10 +888,10 @@ wrappy@1:
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
ws@^8.17.1:
version "8.17.1"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b"
integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==
ws@^8.13.0:
version "8.13.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0"
integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==
yallist@^4.0.0:
version "4.0.0"
+3 -2
View File
@@ -14,6 +14,7 @@ use crate::response_pusher::ResponsePusher;
use js_sys::Promise;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tokio::sync::RwLock;
use tsify::Tsify;
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::future_to_promise;
@@ -50,7 +51,7 @@ pub(crate) const NODE_TESTER_CLIENT_ID: &str = "_nym-node-tester-client";
#[wasm_bindgen]
pub struct NymClient {
self_address: String,
client_input: Arc<ClientInput>,
client_input: Arc<RwLock<ClientInput>>,
client_state: Arc<ClientState>,
// keep track of the "old" topology for the purposes of node tester
@@ -196,7 +197,7 @@ impl NymClientBuilder {
Ok(NymClient {
self_address,
client_input: Arc::new(client_input),
client_input: Arc::new(RwLock::new(client_input)),
client_state: Arc::new(started_client.client_state),
_full_topology: None,
// this cannot failed as we haven't passed an external task manager
+5 -1
View File
@@ -1,8 +1,10 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use futures::SinkExt;
use js_sys::Promise;
use std::sync::Arc;
use tokio::sync::RwLock;
use wasm_bindgen::JsValue;
use wasm_bindgen_futures::future_to_promise;
use wasm_client_core::client::base_client::{ClientInput, ClientState};
@@ -48,10 +50,11 @@ pub(crate) trait InputSender {
fn send_messages(&self, messages: Vec<InputMessage>) -> Promise;
}
impl InputSender for Arc<ClientInput> {
impl InputSender for Arc<RwLock<ClientInput>> {
fn send_message(&self, message: InputMessage) -> Promise {
let this = Arc::clone(self);
future_to_promise(async move {
let mut this = this.write().await;
match this.input_sender.send(message).await {
Ok(_) => Ok(JsValue::null()),
Err(_) => Err(simple_js_error(
@@ -64,6 +67,7 @@ impl InputSender for Arc<ClientInput> {
fn send_messages(&self, messages: Vec<InputMessage>) -> Promise {
let this = Arc::clone(self);
future_to_promise(async move {
let mut this = this.write().await;
for message in messages {
if this.input_sender.send(message).await.is_err() {
return Err(simple_js_error(
+1 -1
View File
@@ -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" }
+8 -8
View File
@@ -497,11 +497,11 @@ brace-expansion@^1.1.7:
concat-map "0.0.1"
braces@^3.0.2, braces@~3.0.2:
version "3.0.3"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
dependencies:
fill-range "^7.1.1"
fill-range "^7.0.1"
browserslist@^4.14.5:
version "4.21.5"
@@ -903,10 +903,10 @@ faye-websocket@^0.11.3:
dependencies:
websocket-driver ">=0.5.1"
fill-range@^7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
dependencies:
to-regex-range "^5.0.1"
+9 -2
View File
@@ -8,10 +8,13 @@ use crate::go_bridge::goWasmSetMixFetchRequestTimeout;
use crate::request_writer::RequestWriter;
use crate::socks_helpers::{socks5_connect_request, socks5_data_request};
use crate::{config, RequestId};
use futures::SinkExt;
use js_sys::Promise;
use nym_socks5_requests::RemoteAddress;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use tokio::sync::Mutex;
use tokio::sync::RwLock;
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::future_to_promise;
use wasm_client_core::client::base_client::{BaseClientBuilder, ClientInput, ClientOutput};
@@ -34,7 +37,7 @@ pub struct MixFetchClient {
self_address: Recipient,
client_input: ClientInput,
client_input: Arc<RwLock<ClientInput>>,
requests: ActiveRequests,
@@ -131,7 +134,7 @@ impl MixFetchClientBuilder {
invalidated: AtomicBool::new(false),
mix_fetch_config: self.config.mix_fetch,
self_address,
client_input,
client_input: Arc::new(RwLock::new(client_input)),
requests: active_requests,
// this cannot failed as we haven't passed an external task manager
_task_manager: Mutex::new(started_client.task_handle.try_into_task_manager().unwrap()),
@@ -208,6 +211,8 @@ impl MixFetchClient {
// the expect here is fine as it implies an unrecoverable failure since one of the client core
// tasks has terminated
self.client_input
.write()
.await
.input_sender
.send(input)
.await
@@ -235,6 +240,8 @@ impl MixFetchClient {
// the expect here is fine as it implies an unrecoverable failure since one of the client core
// tasks has terminated
self.client_input
.write()
.await
.input_sender
.send(input)
.await