Compare commits

..

1 Commits

Author SHA1 Message Date
Jon Häggblad 6f041f7b38 Add query request id 2023-06-12 10:41:43 +02:00
72 changed files with 1045 additions and 1406 deletions
-18
View File
@@ -4,24 +4,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [v1.1.21] (2023-06-13)
- mixFetch: Change socks5 `SendRequest` to include OrderedMessage index as a field rather than making it serialized inside the `data` field
([#3534])
- Explorer - add more data columns to the Service Provider section: ([#3474])
- network-requester: support report if they run an open proxy using `ControlRequest` API ([#3461])
- Refactor client configs (London discussion) ([#3444])
- Increase `DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE` to 2000 to improve reliability ([#3433])
- socks5: sender waits for lanes to clear even though the connection is closed ([#3366])
- version bump for variables ([#3545])
[#3534]: https://github.com/nymtech/nym/issues/3534
[#3474]: https://github.com/nymtech/nym/issues/3474
[#3461]: https://github.com/nymtech/nym/issues/3461
[#3444]: https://github.com/nymtech/nym/issues/3444
[#3433]: https://github.com/nymtech/nym/issues/3433
[#3366]: https://github.com/nymtech/nym/issues/3366
[#3545]: https://github.com/nymtech/nym/pull/3545
## [v1.1.20] (2023-06-06)
- Explorer - Fix SP supported apps list ([#3458])
Generated
+23 -23
View File
@@ -68,7 +68,7 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [
"getrandom 0.2.10",
"getrandom 0.2.9",
"once_cell",
"version_check",
]
@@ -946,7 +946,7 @@ dependencies = [
"cosmos-sdk-proto",
"ecdsa",
"eyre",
"getrandom 0.2.10",
"getrandom 0.2.9",
"k256",
"prost 0.10.4",
"prost-types 0.10.1",
@@ -1732,7 +1732,7 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
[[package]]
name = "explorer-api"
version = "1.1.21"
version = "1.1.20"
dependencies = [
"chrono",
"clap 4.2.7",
@@ -2117,9 +2117,9 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.10"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
dependencies = [
"cfg-if",
"js-sys",
@@ -3322,7 +3322,7 @@ dependencies = [
[[package]]
name = "nym-api"
version = "1.1.22"
version = "1.1.21"
dependencies = [
"anyhow",
"async-trait",
@@ -3459,7 +3459,7 @@ dependencies = [
[[package]]
name = "nym-cli"
version = "1.1.21"
version = "1.1.20"
dependencies = [
"anyhow",
"base64 0.13.1",
@@ -3523,7 +3523,7 @@ dependencies = [
[[package]]
name = "nym-client"
version = "1.1.21"
version = "1.1.20"
dependencies = [
"clap 4.2.7",
"dirs 4.0.0",
@@ -3622,7 +3622,7 @@ dependencies = [
"digest 0.9.0",
"doc-comment",
"ff 0.11.1",
"getrandom 0.2.10",
"getrandom 0.2.9",
"group 0.11.0",
"itertools",
"nym-dkg",
@@ -3797,7 +3797,7 @@ dependencies = [
[[package]]
name = "nym-gateway"
version = "1.1.21"
version = "1.1.20"
dependencies = [
"anyhow",
"async-trait",
@@ -3850,7 +3850,7 @@ name = "nym-gateway-client"
version = "0.1.0"
dependencies = [
"futures",
"getrandom 0.2.10",
"getrandom 0.2.9",
"log",
"nym-bandwidth-controller",
"nym-coconut-interface",
@@ -3948,7 +3948,7 @@ dependencies = [
[[package]]
name = "nym-mixnode"
version = "1.1.22"
version = "1.1.21"
dependencies = [
"anyhow",
"bs58",
@@ -4058,7 +4058,7 @@ dependencies = [
[[package]]
name = "nym-network-requester"
version = "1.1.21"
version = "1.1.20"
dependencies = [
"anyhow",
"async-file-watcher",
@@ -4104,7 +4104,7 @@ dependencies = [
[[package]]
name = "nym-network-statistics"
version = "1.1.21"
version = "1.1.20"
dependencies = [
"dirs 4.0.0",
"log",
@@ -4162,6 +4162,7 @@ dependencies = [
"nym-sdk",
"nym-service-providers-common",
"nym-socks5-requests",
"rand 0.8.5",
"serde",
"tokio",
]
@@ -4184,7 +4185,7 @@ dependencies = [
"criterion",
"curve25519-dalek",
"fastrand",
"getrandom 0.2.10",
"getrandom 0.2.9",
"log",
"rand 0.7.3",
"rayon",
@@ -4263,7 +4264,7 @@ dependencies = [
[[package]]
name = "nym-socks5-client"
version = "1.1.21"
version = "1.1.20"
dependencies = [
"clap 4.2.7",
"lazy_static",
@@ -4537,7 +4538,7 @@ dependencies = [
"aes-gcm",
"argon2",
"generic-array 0.14.7",
"getrandom 0.2.10",
"getrandom 0.2.9",
"rand 0.8.5",
"serde",
"serde_json",
@@ -4631,7 +4632,6 @@ dependencies = [
"nym-service-provider-directory-common",
"nym-vesting-contract",
"nym-vesting-contract-common",
"openssl",
"prost 0.10.4",
"reqwest",
"serde",
@@ -5517,7 +5517,7 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom 0.2.10",
"getrandom 0.2.9",
]
[[package]]
@@ -5674,7 +5674,7 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
dependencies = [
"getrandom 0.2.10",
"getrandom 0.2.9",
"redox_syscall 0.2.16",
"thiserror",
]
@@ -6964,7 +6964,7 @@ dependencies = [
"bytes",
"flex-error",
"futures",
"getrandom 0.2.10",
"getrandom 0.2.9",
"http",
"hyper",
"hyper-proxy",
@@ -7734,7 +7734,7 @@ version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2"
dependencies = [
"getrandom 0.2.10",
"getrandom 0.2.9",
"wasm-bindgen",
]
@@ -7902,7 +7902,7 @@ name = "wasm-utils"
version = "0.1.0"
dependencies = [
"futures",
"getrandom 0.2.10",
"getrandom 0.2.9",
"indexed_db_futures",
"js-sys",
"nym-store-cipher",
+1 -2
View File
@@ -113,8 +113,8 @@ edition = "2021"
license = "Apache-2.0"
[workspace.dependencies]
anyhow = "1.0.71"
async-trait = "0.1.64"
anyhow = "1.0.71"
bip39 = { version = "2.0.0", features = ["zeroize"] }
cfg-if = "1.0.0"
cosmwasm-derive = "=1.0.0"
@@ -130,7 +130,6 @@ cw3-fixed-multisig = { version = "=0.13.4" }
cw4 = { version = "=0.13.4" }
dotenvy = "0.15.6"
generic-array = "0.14.7"
getrandom = "0.2.10"
lazy_static = "1.4.0"
log = "0.4"
once_cell = "1.7.2"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client"
version = "1.1.21"
version = "1.1.20"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021"
+1 -1
View File
@@ -75,7 +75,7 @@ impl NymConfigTemplate for Config {
impl Config {
pub fn new<S: AsRef<str>>(id: S) -> Self {
Config {
base: BaseClientConfig::new(id.as_ref(), env!("CARGO_PKG_VERSION")),
base: BaseClientConfig::new(id.as_ref()),
storage_paths: ClientPaths::new_default(default_data_directory(id.as_ref())),
logging: Default::default(),
socket: Default::default(),
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "1.1.21"
version = "1.1.20"
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"
+1 -5
View File
@@ -70,11 +70,7 @@ impl NymConfigTemplate for Config {
impl Config {
pub fn new<S: AsRef<str>>(id: S, provider_mix_address: S) -> Self {
Config {
core: CoreConfig::new(
id.as_ref(),
env!("CARGO_PKG_VERSION"),
provider_mix_address.as_ref(),
),
core: CoreConfig::new(id.as_ref(), provider_mix_address.as_ref()),
storage_paths: SocksClientPaths::new_default(default_data_directory(id.as_ref())),
logging: Default::default(),
}
+13 -13
View File
@@ -62,7 +62,7 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [
"getrandom 0.2.10",
"getrandom 0.2.9",
"once_cell",
"version_check",
]
@@ -622,7 +622,7 @@ dependencies = [
"cosmos-sdk-proto",
"ecdsa",
"eyre",
"getrandom 0.2.10",
"getrandom 0.2.9",
"k256",
"prost",
"prost-types",
@@ -1442,9 +1442,9 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.10"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
dependencies = [
"cfg-if 1.0.0",
"js-sys",
@@ -2345,7 +2345,7 @@ dependencies = [
"bs58",
"digest 0.9.0",
"ff 0.11.1",
"getrandom 0.2.10",
"getrandom 0.2.9",
"group 0.11.0",
"itertools",
"nym-dkg",
@@ -2489,7 +2489,7 @@ name = "nym-gateway-client"
version = "0.1.0"
dependencies = [
"futures",
"getrandom 0.2.10",
"getrandom 0.2.9",
"log",
"nym-bandwidth-controller",
"nym-coconut-interface",
@@ -2636,7 +2636,7 @@ dependencies = [
"chacha20",
"chacha20poly1305",
"curve25519-dalek",
"getrandom 0.2.10",
"getrandom 0.2.9",
"log",
"rand 0.7.3",
"rayon",
@@ -2816,7 +2816,7 @@ dependencies = [
"aes-gcm",
"argon2",
"generic-array 0.14.7",
"getrandom 0.2.10",
"getrandom 0.2.9",
"rand 0.8.5",
"serde",
"serde_json",
@@ -3402,7 +3402,7 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom 0.2.10",
"getrandom 0.2.9",
]
[[package]]
@@ -3470,7 +3470,7 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
dependencies = [
"getrandom 0.2.10",
"getrandom 0.2.9",
"redox_syscall 0.2.16",
"thiserror",
]
@@ -4388,7 +4388,7 @@ dependencies = [
"bytes",
"flex-error",
"futures",
"getrandom 0.2.10",
"getrandom 0.2.9",
"http",
"hyper",
"hyper-proxy",
@@ -4788,7 +4788,7 @@ version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2"
dependencies = [
"getrandom 0.2.10",
"getrandom 0.2.9",
"wasm-bindgen",
]
@@ -4964,7 +4964,7 @@ name = "wasm-utils"
version = "0.1.0"
dependencies = [
"futures",
"getrandom 0.2.10",
"getrandom 0.2.9",
"indexed_db_futures",
"js-sys",
"nym-store-cipher",
+2 -2
View File
@@ -29,7 +29,7 @@ impl Config {
#[wasm_bindgen(constructor)]
pub fn new(id: String, validator_server: String, debug: Option<DebugWasm>) -> Self {
Config {
base: BaseClientConfig::new(id, env!("CARGO_PKG_VERSION").to_string())
base: BaseClientConfig::new(id)
.with_custom_nyxd(vec![validator_server
.parse()
.expect("provided url was malformed")])
@@ -39,7 +39,7 @@ impl Config {
pub(crate) fn new_tester_config<S: Into<String>>(id: S) -> Self {
Config {
base: BaseClientConfig::new(id.into(), env!("CARGO_PKG_VERSION").to_string())
base: BaseClientConfig::new(id)
.with_disabled_credentials(true)
.with_disabled_cover_traffic(true)
.with_disabled_topology_refresh(true),
+4 -4
View File
@@ -66,9 +66,9 @@ pub struct Config {
}
impl Config {
pub fn new<S: Into<String>>(id: S, version: S) -> Self {
pub fn new<S: Into<String>>(id: S) -> Self {
Config {
client: Client::new_default(id, version),
client: Client::new_default(id),
debug: Default::default(),
}
}
@@ -243,7 +243,7 @@ pub struct Client {
}
impl Client {
pub fn new_default<S: Into<String>>(id: S, version: S) -> Self {
pub fn new_default<S: Into<String>>(id: S) -> Self {
let network = NymNetworkDetails::new_mainnet();
let nyxd_urls = network
.endpoints
@@ -257,7 +257,7 @@ impl Client {
.collect::<Vec<_>>();
Client {
version: version.into(),
version: env!("CARGO_PKG_VERSION").to_string(),
id: id.into(),
disabled_credentials_mode: true,
nyxd_urls,
@@ -29,7 +29,6 @@ log = { workspace = true }
url = { version = "2.2", features = ["serde"] }
tokio = { version = "1.24.1", features = ["sync", "time"] }
futures = "0.3"
openssl = { version = "0.10", features = ["vendored"], optional = true }
nym-coconut-interface = { path = "../../coconut-interface" }
nym-network-defaults = { path = "../../network-defaults" }
@@ -78,14 +77,13 @@ required-features = ["nyxd-client"]
nyxd-client = [
"async-trait",
"cosmrs",
"cosmwasm-std",
"cw3",
"cw4",
"flate2",
"itertools",
"openssl",
"prost",
"flate2",
"sha2",
"itertools",
"cosmwasm-std",
"signing"
]
signing = [
+2 -2
View File
@@ -27,9 +27,9 @@ pub struct Config {
}
impl Config {
pub fn new<S: Into<String>>(id: S, version: S, provider_mix_address: S) -> Self {
pub fn new<S: Into<String>>(id: S, provider_mix_address: S) -> Self {
Config {
base: BaseClientConfig::new(id, version),
base: BaseClientConfig::new(id),
socks5: Socks5::new(provider_mix_address),
}
}
+2 -15
View File
@@ -19,7 +19,6 @@ use nym_client_core::client::base_client::{
use nym_client_core::client::key_manager::persistence::KeyStore;
use nym_client_core::client::replies::reply_storage::ReplyStorageBackend;
use nym_client_core::config::DebugConfig;
use nym_client_core::init::GatewaySetup;
use nym_credential_storage::storage::Storage as CredentialStorage;
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::params::PacketType;
@@ -54,8 +53,6 @@ pub struct NymClient<S> {
config: Config,
storage: S,
setup_method: GatewaySetup,
}
impl<S> NymClient<S>
@@ -68,16 +65,7 @@ where
<S::KeyStore as KeyStore>::StorageError: Send + Sync,
{
pub fn new(config: Config, storage: S) -> Self {
NymClient {
config,
storage,
setup_method: GatewaySetup::MustLoad,
}
}
pub fn with_gateway_setup(mut self, setup: GatewaySetup) -> Self {
self.setup_method = setup;
self
NymClient { config, storage }
}
#[allow(clippy::too_many_arguments)]
@@ -210,8 +198,7 @@ where
};
let base_builder =
BaseClientBuilder::new(&self.config.base, self.storage, dkg_query_client)
.with_gateway_setup(self.setup_method);
BaseClientBuilder::new(&self.config.base, self.storage, dkg_query_client);
let packet_type = self.config.base.debug.traffic.packet_type;
let mut started_client = base_builder.start_base().await?;
@@ -428,14 +428,18 @@ impl SocksClient {
Some(self.lane_queue_lengths.clone()),
self.shutdown_listener.clone(),
)
.run(move |socket_data| {
let lane = TransmissionLane::ConnectionId(socket_data.header.connection_id);
let provider_request =
Socks5Request::new_send(request_version.provider_protocol, socket_data);
.run(move |conn_id, read_data, socket_closed| {
let provider_request = Socks5Request::new_send(
request_version.provider_protocol,
conn_id,
read_data,
socket_closed,
);
let provider_message = Socks5ProviderRequest::new_provider_data(
request_version.provider_interface,
provider_request,
);
let lane = TransmissionLane::ConnectionId(conn_id);
if anonymous {
InputMessage::new_anonymous(
recipient,
@@ -1,20 +1,19 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::error::Socks5ClientCoreError;
use futures::channel::mpsc;
use futures::StreamExt;
use log::*;
use nym_client_core::client::received_buffer::ReconstructedMessagesReceiver;
use nym_client_core::client::received_buffer::{
ReceivedBufferMessage, ReceivedBufferRequestSender,
};
use nym_service_providers_common::interface::{ControlResponse, ResponseContent};
use nym_socks5_proxy_helpers::connection_controller::{ControllerCommand, ControllerSender};
use nym_socks5_proxy_helpers::connection_controller::ControllerSender;
use nym_socks5_requests::{Socks5ProviderResponse, Socks5Response, Socks5ResponseContent};
use nym_sphinx::receiver::ReconstructedMessage;
use nym_task::TaskClient;
use crate::error::Socks5ClientCoreError;
pub(crate) struct MixnetResponseListener {
buffer_requester: ReceivedBufferRequestSender,
mix_response_receiver: ReconstructedMessagesReceiver,
@@ -80,9 +79,9 @@ impl MixnetResponseListener {
);
Err(err_response.into())
}
Socks5ResponseContent::NetworkData { content } => {
Socks5ResponseContent::NetworkData(response) => {
self.controller_sender
.unbounded_send(ControllerCommand::new_send(content))
.unbounded_send(response.into())
.unwrap();
Ok(())
}
+142 -172
View File
@@ -1,21 +1,6 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::message::OrderedMessage;
use log::*;
use std::collections::BTreeMap;
use thiserror::Error;
#[derive(Debug, Error, PartialEq, Eq)]
pub enum OrderedMessageError {
#[error("received message with sequence number {received}, which is way higher than our current {current}")]
MessageSequenceTooLarge { current: u64, received: u64 },
#[error("received message with sequence number {received}, while we're already at {current}!")]
MessageAlreadyReconstructed { current: u64, received: u64 },
#[error("attempted to overwrite message at sequence {received}")]
AttemptedToOverwriteSequence { received: u64 },
}
use std::collections::HashMap;
/// Stores messages and emits them in order.
///
@@ -24,58 +9,36 @@ pub enum OrderedMessageError {
/// to fill up with the full sequence.
#[derive(Debug)]
pub struct OrderedMessageBuffer {
next_sequence: u64,
messages: BTreeMap<u64, Vec<u8>>,
next_index: u64,
messages: HashMap<u64, OrderedMessage>,
}
/// Data returned from `OrderedMessageBuffer` on a successful read of gapless ordered data.
#[derive(Debug, PartialEq, Eq)]
pub struct ReadContiguousData {
pub data: Vec<u8>,
pub last_sequence: u64,
pub last_index: u64,
}
const MAX_REASONABLE_OFFSET: u64 = 1000;
impl OrderedMessageBuffer {
pub fn new() -> OrderedMessageBuffer {
OrderedMessageBuffer {
next_sequence: 0,
messages: BTreeMap::new(),
next_index: 0,
messages: HashMap::new(),
}
}
/// Writes a message to the buffer. messages are sort on insertion, so
/// that later on multiple reads for incomplete sequences don't result in
/// useless sort work.
pub fn write(&mut self, sequence: u64, data: Vec<u8>) -> Result<(), OrderedMessageError> {
// reject messages that have clearly malformed sequence
if sequence > self.next_sequence + MAX_REASONABLE_OFFSET {
return Err(OrderedMessageError::MessageSequenceTooLarge {
current: self.next_sequence,
received: sequence,
});
}
if self.messages.contains_key(&sequence) {
return Err(OrderedMessageError::AttemptedToOverwriteSequence { received: sequence });
}
if sequence < self.next_sequence {
return Err(OrderedMessageError::MessageAlreadyReconstructed {
current: self.next_sequence,
received: sequence,
});
}
pub fn write(&mut self, message: OrderedMessage) {
trace!(
"Writing message index: {} length {} to OrderedMessageBuffer.",
sequence,
data.len()
"Writing message index: {} length {:?} to OrderedMessageBuffer.",
message.index,
message.data.len()
);
self.messages.insert(sequence, data);
Ok(())
self.messages.insert(message.index, message);
}
/// Returns `Option<Vec<u8>>` where it's `Some(bytes)` if there is gapless
@@ -86,31 +49,33 @@ impl OrderedMessageBuffer {
/// a read will return the bytes of messages 0, 1, 2. Subsequent reads will
/// return `None` until message 3 comes in, at which point 3, 4, and any
/// further contiguous messages which have arrived will be returned.
#[must_use]
pub fn read(&mut self) -> Option<ReadContiguousData> {
if !self.messages.contains_key(&self.next_sequence) {
if !self.messages.contains_key(&self.next_index) {
return None;
}
let mut contiguous_messages = Vec::new();
let mut seq = self.next_sequence;
let mut index = self.next_index;
while let Some(mut data) = self.messages.remove(&seq) {
contiguous_messages.append(&mut data);
seq += 1;
while let Some(ordered_message) = self.messages.remove(&index) {
contiguous_messages.push(ordered_message);
index += 1;
}
let high_water = seq;
self.next_sequence = high_water;
trace!("Next high water mark is: {high_water}");
let high_water = index;
self.next_index = high_water;
trace!("Next high water mark is: {}", high_water);
trace!(
"Returning {} bytes from ordered message buffer",
contiguous_messages.len()
);
// dig out the bytes from inside the struct
let data: Vec<u8> = contiguous_messages
.into_iter()
.flat_map(|message| message.data)
.collect();
trace!("Returning {} bytes from ordered message buffer", data.len());
Some(ReadContiguousData {
data: contiguous_messages,
last_sequence: seq,
data,
last_index: index,
})
}
}
@@ -125,64 +90,6 @@ impl Default for OrderedMessageBuffer {
mod test_chunking_and_reassembling {
use super::*;
#[test]
fn trying_to_write_unreasonable_high_sequence() {
let mut buffer = OrderedMessageBuffer::new();
let first_message = vec![1, 2, 3, 4];
let second_message = vec![5, 6, 7, 8];
buffer.write(0, first_message).unwrap();
buffer.write(1, second_message).unwrap();
assert_eq!(
Err(OrderedMessageError::MessageSequenceTooLarge {
current: 0,
received: 12345678
}),
buffer.write(12345678, b"foomp".to_vec())
)
}
#[test]
fn trying_to_overwrite_sequence() {
let mut buffer = OrderedMessageBuffer::new();
let message = vec![1, 2, 3, 4];
buffer.write(0, message.clone()).unwrap();
buffer.write(1, message.clone()).unwrap();
buffer.write(2, message.clone()).unwrap();
buffer.write(3, message.clone()).unwrap();
for seq in 0..=3 {
assert_eq!(
Err(OrderedMessageError::AttemptedToOverwriteSequence { received: seq }),
buffer.write(seq, message.clone())
)
}
}
#[test]
fn writing_past_data() {
let mut buffer = OrderedMessageBuffer::new();
let message = vec![1, 2, 3, 4];
buffer.write(0, message.clone()).unwrap();
buffer.write(1, message.clone()).unwrap();
buffer.write(2, message.clone()).unwrap();
buffer.write(3, message.clone()).unwrap();
let _ = buffer.read().unwrap();
for seq in 0..=3 {
assert_eq!(
Err(OrderedMessageError::MessageAlreadyReconstructed {
current: 4,
received: seq
}),
buffer.write(seq, message.clone())
)
}
}
#[cfg(test)]
mod reading_from_and_writing_to_the_buffer {
use super::*;
@@ -195,14 +102,20 @@ mod test_chunking_and_reassembling {
fn read_returns_ordered_bytes_and_resets_buffer() {
let mut buffer = OrderedMessageBuffer::new();
let first_message = vec![1, 2, 3, 4];
let second_message = vec![5, 6, 7, 8];
let first_message = OrderedMessage {
data: vec![1, 2, 3, 4],
index: 0,
};
let second_message = OrderedMessage {
data: vec![5, 6, 7, 8],
index: 1,
};
buffer.write(0, first_message).unwrap();
buffer.write(first_message);
let first_read = buffer.read().unwrap().data;
assert_eq!(vec![1, 2, 3, 4], first_read);
buffer.write(1, second_message).unwrap();
buffer.write(second_message);
let second_read = buffer.read().unwrap().data;
assert_eq!(vec![5, 6, 7, 8], second_read);
@@ -213,11 +126,17 @@ mod test_chunking_and_reassembling {
fn test_multiple_adds_stacks_up_bytes_in_the_buffer() {
let mut buffer = OrderedMessageBuffer::new();
let first_message = vec![1, 2, 3, 4];
let second_message = vec![5, 6, 7, 8];
let first_message = OrderedMessage {
data: vec![1, 2, 3, 4],
index: 0,
};
let second_message = OrderedMessage {
data: vec![5, 6, 7, 8],
index: 1,
};
buffer.write(0, first_message).unwrap();
buffer.write(1, second_message).unwrap();
buffer.write(first_message);
buffer.write(second_message);
let second_read = buffer.read();
assert_eq!(vec![1, 2, 3, 4, 5, 6, 7, 8], second_read.unwrap().data);
assert_eq!(None, buffer.read()); // second read on fully ordered result set is empty
@@ -227,11 +146,17 @@ mod test_chunking_and_reassembling {
fn out_of_order_adds_results_in_ordered_byte_vector() {
let mut buffer = OrderedMessageBuffer::new();
let first_message = vec![1, 2, 3, 4];
let second_message = vec![5, 6, 7, 8];
let first_message = OrderedMessage {
data: vec![1, 2, 3, 4],
index: 0,
};
let second_message = OrderedMessage {
data: vec![5, 6, 7, 8],
index: 1,
};
buffer.write(1, second_message).unwrap();
buffer.write(0, first_message).unwrap();
buffer.write(second_message);
buffer.write(first_message);
let read = buffer.read().unwrap().data;
assert_eq!(vec![1, 2, 3, 4, 5, 6, 7, 8], read);
assert_eq!(None, buffer.read()); // second read on fully ordered result set is empty
@@ -245,13 +170,23 @@ mod test_chunking_and_reassembling {
fn setup() -> OrderedMessageBuffer {
let mut buffer = OrderedMessageBuffer::new();
let zero_message = vec![0, 0, 0, 0];
let one_message = vec![1, 1, 1, 1];
let three_message = vec![3, 3, 3, 3];
let zero_message = OrderedMessage {
data: vec![0, 0, 0, 0],
index: 0,
};
let one_message = OrderedMessage {
data: vec![1, 1, 1, 1],
index: 1,
};
buffer.write(0, zero_message).unwrap();
buffer.write(1, one_message).unwrap();
buffer.write(3, three_message).unwrap();
let three_message = OrderedMessage {
data: vec![3, 3, 3, 3],
index: 3,
};
buffer.write(zero_message);
buffer.write(one_message);
buffer.write(three_message);
buffer
}
#[test]
@@ -264,31 +199,43 @@ mod test_chunking_and_reassembling {
assert_eq!(None, buffer.read());
// let's add another message, leaving a gap in place at index 2
let five_message = vec![5, 5, 5, 5];
buffer.write(5, five_message).unwrap();
let five_message = OrderedMessage {
data: vec![5, 5, 5, 5],
index: 5,
};
buffer.write(five_message);
assert_eq!(None, buffer.read());
}
#[test]
fn filling_the_gap_allows_us_to_get_everything() {
let mut buffer = setup();
let _ = buffer.read(); // that burns the first two. We still have a gap before the 3s.
buffer.read(); // that burns the first two. We still have a gap before the 3s.
let two_message = vec![2, 2, 2, 2];
buffer.write(2, two_message).unwrap();
let two_message = OrderedMessage {
data: vec![2, 2, 2, 2],
index: 2,
};
buffer.write(two_message);
let more_ordered_bytes = buffer.read().unwrap().data;
assert_eq!([2, 2, 2, 2, 3, 3, 3, 3].to_vec(), more_ordered_bytes);
// let's add another message
let five_message = vec![5, 5, 5, 5];
buffer.write(5, five_message).unwrap();
let five_message = OrderedMessage {
data: vec![5, 5, 5, 5],
index: 5,
};
buffer.write(five_message);
assert_eq!(None, buffer.read());
// let's fill in the gap of 4s now and read again
let four_message = vec![4, 4, 4, 4];
buffer.write(4, four_message).unwrap();
let four_message = OrderedMessage {
data: vec![4, 4, 4, 4],
index: 4,
};
buffer.write(four_message);
assert_eq!(
[4, 4, 4, 4, 5, 5, 5, 5].to_vec(),
@@ -302,47 +249,70 @@ mod test_chunking_and_reassembling {
#[test]
fn filling_the_gap_allows_us_to_get_everything_when_last_element_is_empty() {
let mut buffer = OrderedMessageBuffer::new();
let zero_message = vec![0, 0, 0, 0];
let one_message = vec![2, 2, 2, 2];
let two_message = vec![];
let zero_message = OrderedMessage {
data: vec![0, 0, 0, 0],
index: 0,
};
let one_message = OrderedMessage {
data: vec![2, 2, 2, 2],
index: 1,
};
let two_message = OrderedMessage {
data: vec![],
index: 2,
};
buffer.write(0, zero_message).unwrap();
buffer.write(zero_message);
assert!(buffer.read().is_some()); // burn the buffer
buffer.write(2, two_message).unwrap();
buffer.write(1, one_message).unwrap();
buffer.write(two_message);
buffer.write(one_message);
assert!(buffer.read().is_some());
assert_eq!(buffer.next_sequence, 3);
assert_eq!(buffer.next_index, 3);
}
#[test]
fn works_with_gaps_bigger_than_one() {
let mut buffer = OrderedMessageBuffer::new();
let zero_message = vec![0, 0, 0, 0];
let one_message = vec![2, 2, 2, 2];
let two_message = vec![2, 2, 2, 2];
let three_message = vec![2, 2, 2, 2];
let four_message = vec![2, 2, 2, 2];
buffer.write(0, zero_message).unwrap();
let zero_message = OrderedMessage {
data: vec![0, 0, 0, 0],
index: 0,
};
let one_message = OrderedMessage {
data: vec![2, 2, 2, 2],
index: 1,
};
let two_message = OrderedMessage {
data: vec![2, 2, 2, 2],
index: 2,
};
let three_message = OrderedMessage {
data: vec![2, 2, 2, 2],
index: 3,
};
let four_message = OrderedMessage {
data: vec![2, 2, 2, 2],
index: 4,
};
buffer.write(zero_message);
assert!(buffer.read().is_some());
assert_eq!(buffer.next_sequence, 1);
assert_eq!(buffer.next_index, 1);
buffer.write(4, four_message).unwrap();
buffer.write(four_message);
assert!(buffer.read().is_none());
assert_eq!(buffer.next_sequence, 1);
assert_eq!(buffer.next_index, 1);
buffer.write(3, three_message).unwrap();
buffer.write(three_message);
assert!(buffer.read().is_none());
assert_eq!(buffer.next_sequence, 1);
assert_eq!(buffer.next_index, 1);
buffer.write(2, two_message).unwrap();
buffer.write(two_message);
assert!(buffer.read().is_none());
assert_eq!(buffer.next_sequence, 1);
assert_eq!(buffer.next_index, 1);
buffer.write(1, one_message).unwrap();
buffer.write(one_message);
assert!(buffer.read().is_some());
assert_eq!(buffer.next_sequence, 5)
assert_eq!(buffer.next_index, 5)
}
}
}
+6 -1
View File
@@ -1,3 +1,8 @@
mod buffer;
mod message;
mod sender;
pub use buffer::{OrderedMessageBuffer, OrderedMessageError, ReadContiguousData};
pub use buffer::{OrderedMessageBuffer, ReadContiguousData};
pub use message::MessageError;
pub use message::OrderedMessage;
pub use sender::OrderedMessageSender;
+143
View File
@@ -0,0 +1,143 @@
// Copyright 2020-2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::cmp::Ordering;
use thiserror::Error;
#[derive(Error, Debug, PartialEq, Eq)]
pub enum MessageError {
#[error("the received message was empty")]
NoData,
#[error("could not extract message index. Received {received} bytes, but expected {expected}")]
IndexTooShort { received: usize, expected: usize },
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct OrderedMessage {
pub data: Vec<u8>,
pub index: u64,
}
impl OrderedMessage {
/// Serializes an `OrderedMessage` into bytes.
/// The output format is:
/// | 8 bytes index | data... |
pub fn into_bytes(self) -> Vec<u8> {
self.index
.to_be_bytes()
.iter()
.cloned()
.chain(self.data.into_iter())
.collect()
}
/// Attempts to deserialize an `OrderedMessage` from bytes.
pub fn try_from_bytes(data: Vec<u8>) -> Result<OrderedMessage, MessageError> {
if data.is_empty() {
return Err(MessageError::NoData);
}
if data.len() < 8 {
return Err(MessageError::IndexTooShort {
received: data.len(),
expected: 8,
});
}
let index = u64::from_be_bytes([
data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
]);
Ok(OrderedMessage {
data: data[8..].to_vec(),
index,
})
}
}
/// Order messages by their index only, ignoring their data
impl PartialOrd for OrderedMessage {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some((self.index).cmp(&(other.index)))
}
}
#[cfg(test)]
mod ordered_message_to_bytes {
use super::*;
#[test]
fn works() {
let message = OrderedMessage {
data: vec![123],
index: 1,
};
let bytes = message.into_bytes();
let expected = vec![0, 0, 0, 0, 0, 0, 0, 1, 123];
assert_eq!(expected, bytes);
}
}
#[cfg(test)]
mod ordered_message_from_bytes {
use super::*;
#[test]
fn fails_when_there_is_no_data() {
let result = OrderedMessage::try_from_bytes(Vec::new());
assert_eq!(Err(MessageError::NoData), result);
}
#[test]
fn fails_when_data_is_too_short() {
let result = OrderedMessage::try_from_bytes(vec![1, 2, 3]);
assert_eq!(
Err(MessageError::IndexTooShort {
received: 3,
expected: 8
}),
result
);
}
#[test]
fn works_when_there_is_enough_to_make_a_sequence_number_but_no_message_data() {
let expected = OrderedMessage {
data: Vec::new(),
index: 1,
};
let result = OrderedMessage::try_from_bytes(vec![0, 0, 0, 0, 0, 0, 0, 1]).unwrap();
assert_eq!(expected, result);
}
#[test]
fn works_when_there_is_seq_number_and_data() {
let expected = OrderedMessage {
data: vec![255, 255, 255],
index: 1,
};
let result =
OrderedMessage::try_from_bytes(vec![0, 0, 0, 0, 0, 0, 0, 1, 255, 255, 255]).unwrap();
assert_eq!(expected, result);
}
}
#[test]
fn empty_message_does_not_affect_ordering() {
let mut msg1 = OrderedMessage {
data: vec![255, 255, 255],
index: 1,
};
let mut msg2 = OrderedMessage {
data: vec![],
index: 2,
};
assert!(msg1 < msg2);
msg1.index = 2;
msg2.index = 1;
assert!(msg1 > msg2);
}
@@ -0,0 +1,57 @@
use crate::message::OrderedMessage;
/// Assigns sequence numbers to outbound byte vectors. These messages can then
/// be reassembled into an ordered sequence by the `OrderedMessageSender`.
#[derive(Debug)]
pub struct OrderedMessageSender {
next_index: u64,
}
impl OrderedMessageSender {
pub fn new() -> OrderedMessageSender {
OrderedMessageSender { next_index: 0 }
}
/// Turns raw bytes into an OrderedMessage containing the original bytes
/// and a sequence number;
pub fn wrap_message(&mut self, input: Vec<u8>) -> OrderedMessage {
let message = OrderedMessage {
data: input.to_vec(),
index: self.next_index,
};
self.next_index += 1;
message
}
}
impl Default for OrderedMessageSender {
fn default() -> Self {
OrderedMessageSender::new()
}
}
#[cfg(test)]
mod ordered_message_sender {
use super::*;
mod when_input_bytes_are_empty {}
#[cfg(test)]
mod sequence_index_numbers {
use super::*;
#[test]
fn increase_as_messages_are_sent() {
let mut sender = OrderedMessageSender::new();
let first_bytes = vec![1, 2, 3, 4];
let second_bytes = vec![5, 6, 7, 8];
let first_message = sender.wrap_message(first_bytes);
assert_eq!(first_message.index, 0);
let second_message = sender.wrap_message(second_bytes);
assert_eq!(second_message.index, 1);
}
}
}
@@ -1,11 +1,11 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use futures::channel::mpsc;
use futures::StreamExt;
use log::*;
use nym_ordered_buffer::{OrderedMessageBuffer, ReadContiguousData};
use nym_socks5_requests::{ConnectionId, SocketData};
use nym_ordered_buffer::{OrderedMessage, OrderedMessageBuffer, ReadContiguousData};
use nym_socks5_requests::{ConnectionId, NetworkData, SendRequest};
use nym_task::connections::{ConnectionCommand, ConnectionCommandSender};
use nym_task::TaskClient;
use std::collections::{HashMap, HashSet};
@@ -40,13 +40,29 @@ pub enum ControllerCommand {
connection_id: ConnectionId,
},
Send {
data: SocketData,
connection_id: ConnectionId,
data: Vec<u8>,
is_closed: bool,
},
}
impl ControllerCommand {
pub fn new_send(data: SocketData) -> Self {
ControllerCommand::Send { data }
impl From<NetworkData> for ControllerCommand {
fn from(value: NetworkData) -> Self {
ControllerCommand::Send {
connection_id: value.connection_id,
data: value.data,
is_closed: value.is_closed,
}
}
}
impl From<SendRequest> for ControllerCommand {
fn from(value: SendRequest) -> Self {
ControllerCommand::Send {
connection_id: value.conn_id,
data: value.data,
is_closed: value.local_closed,
}
}
}
@@ -58,13 +74,18 @@ struct ActiveConnection {
}
impl ActiveConnection {
fn write_to_buf(&mut self, seq: u64, payload: Vec<u8>, is_closed: bool) {
fn write_to_buf(&mut self, payload: Vec<u8>, is_closed: bool) {
let ordered_message = match OrderedMessage::try_from_bytes(payload) {
Ok(msg) => msg,
Err(err) => {
error!("Malformed ordered message - {err}");
return;
}
};
if is_closed {
self.closed_at_index = Some(seq);
}
if let Err(err) = self.ordered_buffer.write(seq, payload) {
error!("failed to write to the buffer: {err}")
self.closed_at_index = Some(ordered_message.index);
}
self.ordered_buffer.write(ordered_message);
}
fn read_from_buf(&mut self) -> Option<ReadContiguousData> {
@@ -96,7 +117,7 @@ pub struct Controller {
// buffer for messages received before connection was established due to mixnet being able to
// un-order messages. Note we don't ever expect to have more than 1-2 messages per connection here
pending_messages: HashMap<ConnectionId, Vec<SocketData>>,
pending_messages: HashMap<ConnectionId, Vec<(Vec<u8>, bool)>>,
shutdown: TaskClient,
}
@@ -133,8 +154,8 @@ impl Controller {
// check if there were any pending messages
if let Some(pending) = self.pending_messages.remove(&conn_id) {
debug!("There were some pending messages for {}", conn_id);
for data in pending {
self.send_to_connection(data)
for (payload, is_closed) in pending {
self.send_to_connection(conn_id, payload, is_closed)
}
}
}
@@ -163,25 +184,20 @@ impl Controller {
}
}
fn send_to_connection(&mut self, message: SocketData) {
let hdr = message.header;
if let Some(active_connection) = self.active_connections.get_mut(&hdr.connection_id) {
// always write to the buffer even if payload is empty (because it could have been the keep-alive message)
active_connection.write_to_buf(hdr.seq, message.data, hdr.local_socket_closed);
fn send_to_connection(&mut self, conn_id: ConnectionId, payload: Vec<u8>, is_closed: bool) {
if let Some(active_connection) = self.active_connections.get_mut(&conn_id) {
if !payload.is_empty() {
active_connection.write_to_buf(payload, is_closed);
} else if !is_closed {
error!("Tried to write an empty message to a not-closing connection. Please let us know if you see this message");
}
if let Some(payload) = active_connection.read_from_buf() {
if let Some(closed_at_index) = active_connection.closed_at_index {
if payload.last_sequence > closed_at_index {
if payload.last_index > closed_at_index {
active_connection.is_closed = true;
}
}
// however, don't send empty payload to the actual connection if it's not a close message
// TODO: or should we?
if payload.data.is_empty() && !active_connection.is_closed {
return;
}
if let Err(err) = active_connection
.connection_sender
.as_mut()
@@ -191,26 +207,34 @@ impl Controller {
socket_closed: active_connection.is_closed,
})
{
error!("failed to send on the active connection channel: {err}");
error!("WTF IS THIS: {err}");
}
// TODO: ABOVE UNWRAP CAUSED A CRASH IN A NORMAL USE!!!!
// TODO:
// TODO: surprisingly it only happened on socks client, never on nSP
// TODO:
// TODO:
// TODO:
// TODO:
}
} else if !self.recently_closed.contains(&hdr.connection_id) {
} else if !self.recently_closed.contains(&conn_id) {
debug!("Received a 'Send' before 'Connect' - going to buffer the data");
let pending = self
.pending_messages
.entry(hdr.connection_id)
.entry(conn_id)
.or_insert_with(Vec::new);
pending.push(message);
} else if !hdr.local_socket_closed {
pending.push((payload, is_closed));
} else if !is_closed {
error!(
"Tried to write to closed connection {} ({} bytes were 'lost)",
hdr.connection_id,
message.data.len()
conn_id,
payload.len()
);
} else {
debug!(
"Tried to write to closed connection {}, but remote is already closed",
hdr.connection_id
conn_id
)
}
}
@@ -219,8 +243,8 @@ impl Controller {
loop {
tokio::select! {
command = self.receiver.next() => match command {
Some(ControllerCommand::Send{data}) => {
self.send_to_connection(data)
Some(ControllerCommand::Send{connection_id, data, is_closed}) => {
self.send_to_connection(connection_id, data, is_closed)
}
Some(ControllerCommand::Insert{connection_id, connection_sender}) => {
self.insert_connection(connection_id, connection_sender)
-1
View File
@@ -3,5 +3,4 @@
pub mod available_reader;
pub mod connection_controller;
pub mod ordered_sender;
pub mod proxy_runner;
@@ -1,116 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::proxy_runner::MixProxySender;
use bytes::Bytes;
use log::{debug, error};
use nym_socks5_requests::{ConnectionId, SocketData};
use std::io;
pub(crate) struct OrderedMessageSender<F, S> {
connection_id: ConnectionId,
// addresses are provided for better logging
local_destination_address: String,
remote_source_address: String,
mixnet_sender: MixProxySender<S>,
next_message_seq: u64,
mix_message_adapter: F,
}
impl<F, S> OrderedMessageSender<F, S>
where
F: Fn(SocketData) -> S,
{
pub(crate) fn new(
local_destination_address: String,
remote_source_address: String,
connection_id: ConnectionId,
mixnet_sender: MixProxySender<S>,
mix_message_adapter: F,
) -> Self {
OrderedMessageSender {
local_destination_address,
remote_source_address,
connection_id,
mixnet_sender,
next_message_seq: 0,
mix_message_adapter,
}
}
fn sequence(&mut self) -> u64 {
let next = self.next_message_seq;
self.next_message_seq += 1;
next
}
fn construct_message(&mut self, data: Vec<u8>, local_socket_closed: bool) -> S {
let data = SocketData::new(
self.sequence(),
self.connection_id,
local_socket_closed,
data,
);
(self.mix_message_adapter)(data)
}
async fn send_message(&self, message: S) {
if self.mixnet_sender.send(message).await.is_err() {
panic!("BatchRealMessageReceiver has stopped receiving!")
}
}
pub(crate) async fn send_empty_close(&mut self) {
let message = self.construct_message(Vec::new(), true);
self.send_message(message).await
}
pub(crate) async fn send_empty_keepalive(&mut self) {
log::trace!("Sending keepalive for connection: {}", self.connection_id);
let message = self.construct_message(Vec::new(), false);
self.send_message(message).await
}
pub(crate) fn process_data(&self, read_data: Option<io::Result<Bytes>>) -> ProcessedData {
let (read_data, is_finished) = match read_data {
Some(data) => match data {
Ok(data) => (data, false),
Err(err) => {
error!(target: &*format!("({}) socks5 inbound", self.connection_id), "failed to read request from the socket - {err}");
(Default::default(), true)
}
},
None => (Default::default(), true),
};
ProcessedData {
data: read_data,
is_done: is_finished,
}
}
fn log_sent_message(&self, data: &ProcessedData) {
debug!(
target: &*format!("({}) socks5 inbound", self.connection_id),
"[{} bytes]\t{} → local → mixnet → remote → {}. Local closed: {}",
data.data.len(),
self.local_destination_address,
self.remote_source_address,
data.is_done
);
}
/// Send data read from local socket into the mixnet
pub(crate) async fn send_data(&mut self, data: ProcessedData) {
self.log_sent_message(&data);
let message = self.construct_message(data.data.into(), data.is_done);
self.send_message(message).await;
}
}
// helper wrapper to keep track of field meanings
pub(crate) struct ProcessedData {
data: Bytes,
pub(crate) is_done: bool,
}
@@ -1,22 +1,106 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::MixProxySender;
use super::SHUTDOWN_TIMEOUT;
use crate::available_reader::AvailableReader;
use crate::ordered_sender::OrderedMessageSender;
use crate::proxy_runner::KEEPALIVE_INTERVAL;
use bytes::Bytes;
use futures::FutureExt;
use futures::StreamExt;
use log::*;
use nym_socks5_requests::{ConnectionId, SocketData};
use nym_ordered_buffer::OrderedMessageSender;
use nym_socks5_requests::ConnectionId;
use nym_task::connections::LaneQueueLengths;
use nym_task::connections::TransmissionLane;
use nym_task::TaskClient;
use std::sync::Arc;
use std::fmt::Debug;
use std::time::Duration;
use std::{io, sync::Arc};
use tokio::select;
use tokio::{net::tcp::OwnedReadHalf, sync::Notify, time::sleep};
async fn send_empty_close<F, S>(
connection_id: ConnectionId,
message_sender: &mut OrderedMessageSender,
mix_sender: &MixProxySender<S>,
adapter_fn: F,
) where
F: Fn(ConnectionId, Vec<u8>, bool) -> S,
S: Debug,
{
let ordered_msg = message_sender.wrap_message(Vec::new()).into_bytes();
mix_sender
.send(adapter_fn(connection_id, ordered_msg, true))
.await
.expect("BatchRealMessageReceiver has stopped receiving!");
}
async fn send_empty_keepalive<F, S>(
connection_id: ConnectionId,
message_sender: &mut OrderedMessageSender,
mix_sender: &MixProxySender<S>,
adapter_fn: F,
) where
F: Fn(ConnectionId, Vec<u8>, bool) -> S,
S: Debug,
{
log::trace!("Sending keepalive for connection: {connection_id}");
let ordered_msg = message_sender.wrap_message(Vec::new()).into_bytes();
mix_sender
.send(adapter_fn(connection_id, ordered_msg, false))
.await
.expect("BatchRealMessageReceiver has stopped receiving!");
}
async fn deal_with_data<F, S>(
read_data: Option<io::Result<Bytes>>,
local_destination_address: &str,
remote_source_address: &str,
connection_id: ConnectionId,
message_sender: &mut OrderedMessageSender,
mix_sender: &MixProxySender<S>,
adapter_fn: F,
) -> bool
where
F: Fn(ConnectionId, Vec<u8>, bool) -> S,
S: Debug,
{
let (read_data, is_finished) = match read_data {
Some(data) => match data {
Ok(data) => (data, false),
Err(err) => {
error!(target: &*format!("({connection_id}) socks5 inbound"), "failed to read request from the socket - {err}");
(Default::default(), true)
}
},
None => (Default::default(), true),
};
debug!(
target: &*format!("({connection_id}) socks5 inbound"),
"[{} bytes]\t{} → local → mixnet → remote → {}. Local closed: {}",
read_data.len(),
local_destination_address,
remote_source_address,
is_finished
);
// if we're sending through the mixnet increase the sequence number...
let ordered_msg = message_sender.wrap_message(read_data.to_vec()).into_bytes();
log::trace!(
"pushing data down the input sender: size: {}",
ordered_msg.len()
);
mix_sender
.send(adapter_fn(connection_id, ordered_msg, is_finished))
.await
.expect("InputMessageReceiver has stopped receiving!");
is_finished
}
async fn wait_until_lane_empty(lane_queue_lengths: &Option<LaneQueueLengths>, connection_id: u64) {
if let Some(lane_queue_lengths) = lane_queue_lengths {
if tokio::time::timeout(
@@ -74,21 +158,27 @@ async fn wait_for_lane(
}
}
#[allow(clippy::too_many_arguments)]
pub(super) async fn run_inbound<F, S>(
mut reader: OwnedReadHalf,
mut message_sender: OrderedMessageSender<F, S>,
local_destination_address: String, // addresses are provided for better logging
remote_source_address: String,
connection_id: ConnectionId,
mix_sender: MixProxySender<S>,
available_plaintext_per_mix_packet: usize,
adapter_fn: F,
shutdown_notify: Arc<Notify>,
lane_queue_lengths: Option<LaneQueueLengths>,
mut shutdown_listener: TaskClient,
) -> OwnedReadHalf
where
F: Fn(SocketData) -> S + Send + 'static,
F: Fn(ConnectionId, Vec<u8>, bool) -> S + Send + 'static,
S: Debug,
{
// TODO: this multiplication by 4 is completely arbitrary here
let mut available_reader =
AvailableReader::new(&mut reader, Some(available_plaintext_per_mix_packet * 4));
let mut message_sender = OrderedMessageSender::new();
// Shutdown if outbound signled to shutdown
let shutdown_future = shutdown_notify.notified().then(|_| sleep(SHUTDOWN_TIMEOUT));
@@ -127,7 +217,7 @@ where
);
// inform remote just in case it was closed because of lack of heartbeat.
// worst case the remote will just have couple of false negatives
message_sender.send_empty_close().await;
send_empty_close(connection_id, &mut message_sender, &mix_sender, &adapter_fn).await;
break;
}
_ = shutdown_listener.recv() => {
@@ -143,7 +233,7 @@ where
break;
}
_ = keepalive_timer.tick() => {
message_sender.send_empty_keepalive().await;
send_empty_keepalive(connection_id, &mut message_sender, &mix_sender, &adapter_fn).await;
}
// Read the next data when there is space in the lane.
// The purpose of chaining the wait here is that it makes sure we can cancel the
@@ -151,12 +241,15 @@ where
read_data = wait_until_lane_almost_empty(&lane_queue_lengths, connection_id)
.then(|_| available_reader.next()), if !we_are_closed =>
{
let processed = message_sender.process_data(read_data);
let is_done = processed.is_done;
message_sender.send_data(processed).await;
if is_done {
if deal_with_data(
read_data,
&local_destination_address,
&remote_source_address,
connection_id,
&mut message_sender,
&mix_sender,
&adapter_fn,
).await {
// After reading the last data, notify the closing_future to wait until the
// lane is clear before exiting.
// We don't wait here since we want to be able to cancel the wait on close or
@@ -2,8 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::connection_controller::ConnectionReceiver;
use crate::ordered_sender::OrderedMessageSender;
use nym_socks5_requests::{ConnectionId, SocketData};
use nym_socks5_requests::ConnectionId;
use nym_task::connections::LaneQueueLengths;
use nym_task::TaskClient;
use std::fmt::Debug;
@@ -93,24 +92,20 @@ where
// request/response as required by entity running particular side of the proxy.
pub async fn run<F>(mut self, adapter_fn: F) -> Self
where
F: Fn(SocketData) -> S + Send + Sync + 'static,
F: Fn(ConnectionId, Vec<u8>, bool) -> S + Send + Sync + 'static,
{
let (read_half, write_half) = self.socket.take().unwrap().into_split();
let shutdown_notify = Arc::new(Notify::new());
// should run until either inbound closes or is notified from outbound
let ordered_sender = OrderedMessageSender::new(
let inbound_future = inbound::run_inbound(
read_half,
self.local_destination_address.clone(),
self.remote_source_address.clone(),
self.connection_id,
self.mix_sender.clone(),
adapter_fn,
);
let inbound_future = inbound::run_inbound(
read_half,
ordered_sender,
self.connection_id,
self.available_plaintext_per_mix_packet,
adapter_fn,
Arc::clone(&shutdown_notify),
self.lane_queue_lengths.clone(),
self.shutdown_listener.clone(),
+3 -281
View File
@@ -3,7 +3,6 @@
use nym_service_providers_common::interface;
use nym_service_providers_common::interface::ServiceProviderMessagingError;
use std::mem;
use thiserror::Error;
pub use request::*;
@@ -17,185 +16,6 @@ pub mod version;
pub type Socks5ProviderRequest = interface::Request<Socks5Request>;
pub type Socks5ProviderResponse = interface::Response<Socks5Request>;
#[derive(Debug, Error, PartialEq, Eq)]
#[error(
"didn't receive enough data to recover socket data. got {received}, but expected at least {expected}"
)]
pub struct InsufficientSocketDataError {
received: usize,
expected: usize,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct SocketDataHeader {
pub seq: u64,
pub connection_id: ConnectionId,
pub local_socket_closed: bool,
}
impl SocketDataHeader {
const SERIALIZED_LEN: usize = mem::size_of::<ConnectionId>() + 1 + mem::size_of::<u64>();
// we need to have two serialization methods for backwards compatibility,
// since we serialized those fields differently depending on whether it was ingress vs egress...
pub fn try_from_request_bytes(
b: &[u8],
) -> Result<SocketDataHeader, InsufficientSocketDataError> {
if b.len() != Self::SERIALIZED_LEN {
return Err(InsufficientSocketDataError {
received: b.len(),
expected: Self::SERIALIZED_LEN,
});
}
// the unwraps here are fine as we just ensured we have the exact amount of bytes we need
let connection_id = ConnectionId::from_be_bytes(b[0..8].try_into().unwrap());
let local_socket_closed = b[8] != 0;
let seq = u64::from_be_bytes(b[9..].try_into().unwrap());
Ok(SocketDataHeader {
seq,
connection_id,
local_socket_closed,
})
}
// the serialization of the header looks as follows:
// (it's vital it's not modified as we need this exact structure for backwards compatibility)
// CONNECTION_ID (8B) || SOCKET_CLOSED (1B) || SEQUENCE (8B)
pub fn into_request_bytes(self) -> Vec<u8> {
self.into_request_bytes_iter().collect()
}
pub fn into_request_bytes_iter(self) -> impl Iterator<Item = u8> {
self.connection_id
.to_be_bytes()
.into_iter()
.chain(std::iter::once(self.local_socket_closed as u8))
.chain(self.seq.to_be_bytes().into_iter())
}
pub fn try_from_response_bytes(
b: &[u8],
) -> Result<SocketDataHeader, InsufficientSocketDataError> {
if b.len() != Self::SERIALIZED_LEN {
return Err(InsufficientSocketDataError {
received: b.len(),
expected: Self::SERIALIZED_LEN,
});
}
// the unwraps here are fine as we just ensured we have the exact amount of bytes we need
let local_socket_closed = b[0] != 0;
let connection_id = ConnectionId::from_be_bytes(b[1..9].try_into().unwrap());
let seq = u64::from_be_bytes(b[9..].try_into().unwrap());
Ok(SocketDataHeader {
seq,
connection_id,
local_socket_closed,
})
}
// the serialization of the header looks as follows:
// (it's vital it's not modified as we need this exact structure for backwards compatibility)
// SOCKET_CLOSED (1B) || CONNECTION_ID (8B) || SEQUENCE (8B)
pub fn into_response_bytes(self) -> Vec<u8> {
self.into_response_bytes_iter().collect()
}
pub fn into_response_bytes_iter(self) -> impl Iterator<Item = u8> {
std::iter::once(self.local_socket_closed as u8)
.chain(self.connection_id.to_be_bytes().into_iter())
.chain(self.seq.to_be_bytes().into_iter())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SocketData {
pub header: SocketDataHeader,
pub data: Vec<u8>,
}
impl SocketData {
pub fn new(
seq: u64,
connection_id: ConnectionId,
local_socket_closed: bool,
data: Vec<u8>,
) -> Self {
SocketData {
header: SocketDataHeader {
seq,
connection_id,
local_socket_closed,
},
data,
}
}
fn verify_deserialization_len(b: &[u8]) -> Result<(), InsufficientSocketDataError> {
if b.is_empty() {
return Err(InsufficientSocketDataError {
received: 0,
expected: SocketDataHeader::SERIALIZED_LEN,
});
}
if b.len() < SocketDataHeader::SERIALIZED_LEN {
return Err(InsufficientSocketDataError {
received: b.len(),
expected: SocketDataHeader::SERIALIZED_LEN,
});
}
Ok(())
}
// we need to have two serialization methods for backwards compatibility,
// since we serialized those fields differently depending on whether it was ingress vs egress...
pub fn try_from_request_bytes(b: &[u8]) -> Result<SocketData, InsufficientSocketDataError> {
Self::verify_deserialization_len(b)?;
let header =
SocketDataHeader::try_from_request_bytes(&b[..SocketDataHeader::SERIALIZED_LEN])?;
let data = b[SocketDataHeader::SERIALIZED_LEN..].to_vec();
Ok(SocketData { header, data })
}
// the serialization of the socket data looks as follows:
// HEADER || DATA
pub fn into_request_bytes(self) -> Vec<u8> {
self.into_request_bytes_iter().collect()
}
pub fn into_request_bytes_iter(self) -> impl Iterator<Item = u8> {
self.header
.into_request_bytes_iter()
.chain(self.data.into_iter())
}
pub fn try_from_response_bytes(b: &[u8]) -> Result<SocketData, InsufficientSocketDataError> {
Self::verify_deserialization_len(b)?;
let header =
SocketDataHeader::try_from_response_bytes(&b[..SocketDataHeader::SERIALIZED_LEN])?;
let data = b[SocketDataHeader::SERIALIZED_LEN..].to_vec();
Ok(SocketData { header, data })
}
pub fn into_response_bytes(self) -> Vec<u8> {
self.into_response_bytes_iter().collect()
}
pub fn into_response_bytes_iter(self) -> impl Iterator<Item = u8> {
self.header
.into_response_bytes_iter()
.chain(self.data.into_iter())
}
}
#[derive(Debug, Error)]
pub enum Socks5RequestError {
#[error("failed to deserialize received request: {source}")]
@@ -231,103 +51,6 @@ mod tests {
use super::*;
use nym_service_providers_common::interface::RequestContent;
#[cfg(test)]
mod socket_data_serialization {
use super::*;
#[test]
fn for_requests() {
assert_eq!(
InsufficientSocketDataError {
received: 0,
expected: SocketDataHeader::SERIALIZED_LEN
},
SocketData::try_from_request_bytes(&[]).unwrap_err()
);
assert_eq!(
InsufficientSocketDataError {
received: 10,
expected: SocketDataHeader::SERIALIZED_LEN
},
SocketData::try_from_request_bytes(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]).unwrap_err()
);
let good_data = SocketData::new(42, 12345, false, vec![2, 3]);
let serialized = good_data.clone().into_request_bytes();
assert_eq!(
good_data,
SocketData::try_from_request_bytes(&serialized).unwrap()
);
assert_ne!(
good_data,
SocketData::try_from_response_bytes(&serialized).unwrap()
);
let raw_bytes = [
6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 1, 2, 3, 4, 5, 6, 7, 255, 255, 255,
];
assert_eq!(
SocketData {
header: SocketDataHeader {
seq: u64::from_be_bytes([0, 1, 2, 3, 4, 5, 6, 7]),
connection_id: ConnectionId::from_be_bytes([6, 6, 6, 6, 6, 6, 6, 6]),
local_socket_closed: false,
},
data: vec![255, 255, 255],
},
SocketData::try_from_request_bytes(&raw_bytes).unwrap()
)
}
#[test]
fn for_responses() {
assert_eq!(
InsufficientSocketDataError {
received: 0,
expected: SocketDataHeader::SERIALIZED_LEN
},
SocketData::try_from_response_bytes(&[]).unwrap_err()
);
assert_eq!(
InsufficientSocketDataError {
received: 10,
expected: SocketDataHeader::SERIALIZED_LEN
},
SocketData::try_from_response_bytes(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]).unwrap_err()
);
let good_data = SocketData::new(42, 12345, false, vec![2, 3]);
let serialized = good_data.clone().into_response_bytes();
assert_eq!(
good_data,
SocketData::try_from_response_bytes(&serialized).unwrap()
);
assert_ne!(
good_data,
SocketData::try_from_request_bytes(&serialized).unwrap()
);
let raw_bytes = [
0, 6, 6, 6, 6, 6, 6, 6, 6, 0, 1, 2, 3, 4, 5, 6, 7, 255, 255, 255,
];
assert_eq!(
SocketData {
header: SocketDataHeader {
seq: u64::from_be_bytes([0, 1, 2, 3, 4, 5, 6, 7]),
connection_id: ConnectionId::from_be_bytes([6, 6, 6, 6, 6, 6, 6, 6]),
local_socket_closed: false,
},
data: vec![255, 255, 255],
},
SocketData::try_from_response_bytes(&raw_bytes).unwrap()
)
}
}
#[cfg(test)]
mod interface_backwards_compatibility {
use super::*;
@@ -376,10 +99,9 @@ mod tests {
match new_deserialized.content {
RequestContent::ProviderData(req) => match req.content {
Socks5RequestContent::Send(send_req) => {
assert_eq!(send_req.data.header.connection_id, 7810961472501196273);
assert_eq!(send_req.data.header.seq, 0);
assert_eq!(send_req.data.data.len(), 103);
assert!(!send_req.data.header.local_socket_closed);
assert_eq!(send_req.conn_id, 7810961472501196273);
assert_eq!(send_req.data.len(), 111);
assert!(!send_req.local_closed);
}
_ => panic!("unexpected request"),
},
+67 -77
View File
@@ -1,10 +1,7 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{
make_bincode_serializer, InsufficientSocketDataError, SocketData, Socks5ProtocolVersion,
Socks5RequestError, Socks5Response,
};
use crate::{make_bincode_serializer, Socks5ProtocolVersion, Socks5RequestError, Socks5Response};
use nym_service_providers_common::interface::{Serializable, ServiceProviderRequest};
use nym_sphinx_addressing::clients::{Recipient, RecipientFormattingError};
use serde::{Deserialize, Serialize};
@@ -64,9 +61,6 @@ pub enum RequestDeserializationError {
#[from]
source: bincode::Error,
},
#[error(transparent)]
InvalidSocketData(#[from] InsufficientSocketDataError),
}
impl RequestDeserializationError {
@@ -85,9 +79,13 @@ pub struct ConnectRequest {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SendRequest {
pub data: SocketData,
pub conn_id: ConnectionId,
pub data: Vec<u8>,
pub local_closed: bool,
}
pub type QueryRequestId = u64;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum QueryRequest {
OpenProxy,
@@ -182,20 +180,26 @@ impl Socks5Request {
}
}
pub fn new_send(protocol_version: Socks5ProtocolVersion, data: SocketData) -> Socks5Request {
pub fn new_send(
protocol_version: Socks5ProtocolVersion,
conn_id: ConnectionId,
data: Vec<u8>,
local_closed: bool,
) -> Socks5Request {
Socks5Request {
protocol_version,
content: Socks5RequestContent::new_send(data),
content: Socks5RequestContent::new_send(conn_id, data, local_closed),
}
}
pub fn new_query(
protocol_version: Socks5ProtocolVersion,
req_id: u64,
query: QueryRequest,
) -> Socks5Request {
Socks5Request {
protocol_version,
content: Socks5RequestContent::Query(query),
content: Socks5RequestContent::Query((req_id, query)),
}
}
}
@@ -212,7 +216,7 @@ pub enum Socks5RequestContent {
/// Re-use an existing TCP connection, sending more request data up it.
Send(SendRequest),
Query(QueryRequest),
Query((QueryRequestId, QueryRequest)),
}
impl Socks5RequestContent {
@@ -230,8 +234,16 @@ impl Socks5RequestContent {
}
/// Construct a new Request::Send instance
pub fn new_send(data: SocketData) -> Socks5RequestContent {
Socks5RequestContent::Send(SendRequest { data })
pub fn new_send(
conn_id: ConnectionId,
data: Vec<u8>,
local_closed: bool,
) -> Socks5RequestContent {
Socks5RequestContent::Send(SendRequest {
conn_id,
data,
local_closed,
})
}
/// Deserialize the request type, connection id, destination address and port,
@@ -248,14 +260,6 @@ impl Socks5RequestContent {
/// The request_flag tells us whether this is a new connection request (`new_connect`),
/// an already-established connection we should send up (`new_send`), or
/// a request to close an established connection (`new_close`).
// connect:
// RequestFlag::Connect || CONN_ID || ADDR_LEN || ADDR || <RETURN_ADDR>
//
// send:
// RequestFlag::Send || CONN_ID || LOCAL_CLOSED || DATA
// where DATA: SEQ || TRUE_DATA
pub fn try_from_bytes(b: &[u8]) -> Result<Socks5RequestContent, RequestDeserializationError> {
// each request needs to at least contain flag and ConnectionId
if b.is_empty() {
@@ -313,9 +317,21 @@ impl Socks5RequestContent {
return_address,
))
}
RequestFlag::Send => Ok(Socks5RequestContent::Send(SendRequest {
data: SocketData::try_from_request_bytes(&b[1..])?,
})),
RequestFlag::Send => {
if b.len() < 9 {
return Err(RequestDeserializationError::ConnectionIdTooShort);
}
let conn_id = u64::from_be_bytes([b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8]]);
let local_closed = b[9] != 0;
let data = b[10..].to_vec();
Ok(Socks5RequestContent::Send(SendRequest {
conn_id,
data,
local_closed,
}))
}
RequestFlag::Query => {
use bincode::Options;
let query = make_bincode_serializer().deserialize(&b[1..])?;
@@ -346,7 +362,9 @@ impl Socks5RequestContent {
}
}
Socks5RequestContent::Send(req) => std::iter::once(RequestFlag::Send as u8)
.chain(req.data.into_request_bytes_iter())
.chain(req.conn_id.to_be_bytes().into_iter())
.chain(std::iter::once(req.local_closed as u8))
.chain(req.data.into_iter())
.collect(),
Socks5RequestContent::Query(query) => {
@@ -603,38 +621,18 @@ mod request_deserialization_tests {
#[test]
fn works_when_request_is_sized_properly_even_without_data() {
// correct 8 bytes of connection_id, 1 byte of local_closed, 8 bytes of sequence and 0 bytes request data
let request_bytes = [
RequestFlag::Send as u8,
1,
2,
3,
4,
5,
6,
7,
8,
0,
0,
0,
0,
0,
0,
0,
0,
1,
]
.to_vec();
// correct 8 bytes of connection_id, 1 byte of local_closed and 0 bytes request data
let request_bytes = [RequestFlag::Send as u8, 1, 2, 3, 4, 5, 6, 7, 8, 0].to_vec();
let request = Socks5RequestContent::try_from_bytes(&request_bytes).unwrap();
match request {
Socks5RequestContent::Send(SendRequest { data }) => {
assert_eq!(
u64::from_be_bytes([1, 2, 3, 4, 5, 6, 7, 8]),
data.header.connection_id
);
assert!(!data.header.local_socket_closed);
assert_eq!(1, data.header.seq);
assert!(data.data.is_empty());
Socks5RequestContent::Send(SendRequest {
conn_id,
data,
local_closed,
}) => {
assert_eq!(u64::from_be_bytes([1, 2, 3, 4, 5, 6, 7, 8]), conn_id);
assert_eq!(Vec::<u8>::new(), data);
assert!(!local_closed)
}
_ => unreachable!(),
}
@@ -642,7 +640,7 @@ mod request_deserialization_tests {
#[test]
fn works_when_request_is_sized_properly_and_has_data() {
// correct 8 bytes of connection_id, 1 byte of local_closed, 8 bytes of sequence and 3 bytes request data (all 255)
// correct 8 bytes of connection_id, 1 byte of local_closed and 3 bytes request data (all 255)
let request_bytes = [
RequestFlag::Send as u8,
1,
@@ -653,15 +651,7 @@ mod request_deserialization_tests {
6,
7,
8,
1,
0,
0,
0,
0,
0,
0,
0,
1,
255,
255,
255,
@@ -670,14 +660,14 @@ mod request_deserialization_tests {
let request = Socks5RequestContent::try_from_bytes(&request_bytes).unwrap();
match request {
Socks5RequestContent::Send(SendRequest { data }) => {
assert_eq!(
u64::from_be_bytes([1, 2, 3, 4, 5, 6, 7, 8]),
data.header.connection_id
);
assert!(data.header.local_socket_closed);
assert_eq!(1, data.header.seq);
assert_eq!(vec![255, 255, 255], data.data);
Socks5RequestContent::Send(SendRequest {
conn_id,
data,
local_closed,
}) => {
assert_eq!(u64::from_be_bytes([1, 2, 3, 4, 5, 6, 7, 8]), conn_id);
assert_eq!(vec![255, 255, 255], data);
assert!(!local_closed)
}
_ => unreachable!(),
}
@@ -690,13 +680,13 @@ mod request_deserialization_tests {
#[test]
fn serialize_there_and_back() {
let open_proxy = Socks5RequestContent::Query(QueryRequest::OpenProxy);
let open_proxy = Socks5RequestContent::Query((42, QueryRequest::OpenProxy));
let bytes_open_proxy = open_proxy.clone().into_bytes();
assert_eq!(bytes_open_proxy, vec![2, 0]);
assert_eq!(bytes_open_proxy, vec![42, 2, 0]);
let description = Socks5RequestContent::Query(QueryRequest::Description);
let description = Socks5RequestContent::Query((8723647823, QueryRequest::Description));
let bytes_description = description.clone().into_bytes();
assert_eq!(bytes_description, vec![2, 1]);
assert_eq!(bytes_description, vec![23, 2, 1]);
let open_proxy2 = Socks5RequestContent::try_from_bytes(&bytes_open_proxy).unwrap();
let description2 = Socks5RequestContent::try_from_bytes(&bytes_description).unwrap();
+146 -26
View File
@@ -1,10 +1,7 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{
make_bincode_serializer, ConnectionId, InsufficientSocketDataError, SocketData,
Socks5ProtocolVersion, Socks5RequestError,
};
use crate::{make_bincode_serializer, ConnectionId, Socks5ProtocolVersion, Socks5RequestError, QueryRequestId};
use nym_service_providers_common::interface::{Serializable, ServiceProviderResponse};
use serde::{Deserialize, Serialize};
use tap::TapFallible;
@@ -36,12 +33,6 @@ impl TryFrom<u8> for ResponseFlag {
#[derive(Debug, Error)]
pub enum ResponseDeserializationError {
#[error("the network data was malformed: {source}")]
MalformedNetworkData {
#[from]
source: InsufficientSocketDataError,
},
#[error("not enough bytes to recover the connection id")]
ConnectionIdTooShort,
@@ -124,14 +115,23 @@ impl Socks5Response {
pub fn new_network_data(
protocol_version: Socks5ProtocolVersion,
seq: u64,
connection_id: ConnectionId,
data: Vec<u8>,
is_closed: bool,
) -> Socks5Response {
Socks5Response {
protocol_version,
content: Socks5ResponseContent::new_network_data(seq, connection_id, data, is_closed),
content: Socks5ResponseContent::new_network_data(connection_id, data, is_closed),
}
}
pub fn new_closed_empty(
protocol_version: Socks5ProtocolVersion,
connection_id: ConnectionId,
) -> Socks5Response {
Socks5Response {
protocol_version,
content: Socks5ResponseContent::new_closed_empty(connection_id),
}
}
@@ -148,32 +148,34 @@ impl Socks5Response {
pub fn new_query(
protocol_version: Socks5ProtocolVersion,
query_request_id: QueryRequestId,
query_response: QueryResponse,
) -> Socks5Response {
Socks5Response {
protocol_version,
content: Socks5ResponseContent::Query(query_response),
content: Socks5ResponseContent::Query((query_request_id, query_response)),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Socks5ResponseContent {
NetworkData { content: SocketData },
NetworkData(NetworkData),
ConnectionError(ConnectionError),
Query(QueryResponse),
Query((QueryRequestId, QueryResponse)),
}
impl Socks5ResponseContent {
pub fn new_network_data(
seq: u64,
connection_id: ConnectionId,
data: Vec<u8>,
is_closed: bool,
) -> Socks5ResponseContent {
Socks5ResponseContent::NetworkData {
content: SocketData::new(seq, connection_id, is_closed, data),
}
Socks5ResponseContent::NetworkData(NetworkData::new(connection_id, data, is_closed))
}
pub fn new_closed_empty(connection_id: ConnectionId) -> Socks5ResponseContent {
Socks5ResponseContent::NetworkData(NetworkData::new_closed_empty(connection_id))
}
pub fn new_connection_error(
@@ -185,9 +187,9 @@ impl Socks5ResponseContent {
pub fn into_bytes(self) -> Vec<u8> {
match self {
Socks5ResponseContent::NetworkData { content } => {
Socks5ResponseContent::NetworkData(res) => {
std::iter::once(ResponseFlag::NetworkData as u8)
.chain(content.into_response_bytes_iter())
.chain(res.into_bytes().into_iter())
.collect()
}
Socks5ResponseContent::ConnectionError(res) => {
@@ -219,9 +221,9 @@ impl Socks5ResponseContent {
let response_flag = ResponseFlag::try_from(b[0])?;
match response_flag {
ResponseFlag::NetworkData => Ok(Socks5ResponseContent::NetworkData {
content: SocketData::try_from_response_bytes(&b[1..])?,
}),
ResponseFlag::NetworkData => Ok(Socks5ResponseContent::NetworkData(
NetworkData::try_from_bytes(&b[1..])?,
)),
ResponseFlag::ConnectionError => Ok(Socks5ResponseContent::ConnectionError(
ConnectionError::try_from_bytes(&b[1..])?,
)),
@@ -233,14 +235,81 @@ impl Socks5ResponseContent {
}
}
pub fn as_query(&self) -> Option<&QueryResponse> {
pub fn as_query(&self) -> Option<(&QueryRequestId, &QueryResponse)> {
match self {
Socks5ResponseContent::Query(query) => Some(query),
Socks5ResponseContent::Query((query_id, query)) => Some((query_id, query)),
_ => None,
}
}
}
/// A remote network network data response retrieved by the Socks5 service provider. This
/// can be serialized and sent back through the mixnet to the requesting
/// application.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct NetworkData {
pub data: Vec<u8>,
pub connection_id: ConnectionId,
pub is_closed: bool,
}
impl NetworkData {
/// Constructor for responses
pub fn new(connection_id: ConnectionId, data: Vec<u8>, is_closed: bool) -> Self {
NetworkData {
data,
connection_id,
is_closed,
}
}
pub fn new_closed_empty(connection_id: ConnectionId) -> Self {
NetworkData {
data: vec![],
connection_id,
is_closed: false,
}
}
pub fn try_from_bytes(b: &[u8]) -> Result<NetworkData, ResponseDeserializationError> {
if b.is_empty() {
return Err(ResponseDeserializationError::NoData);
}
let is_closed = b[0] != 0;
if b.len() < 9 {
return Err(ResponseDeserializationError::ConnectionIdTooShort);
}
let mut connection_id_bytes = b.to_vec();
let data = connection_id_bytes.split_off(9);
let connection_id = u64::from_be_bytes([
connection_id_bytes[1],
connection_id_bytes[2],
connection_id_bytes[3],
connection_id_bytes[4],
connection_id_bytes[5],
connection_id_bytes[6],
connection_id_bytes[7],
connection_id_bytes[8],
]);
let response = NetworkData::new(connection_id, data, is_closed);
Ok(response)
}
/// Serializes the response into bytes so that it can be sent back through
/// the mixnet to the requesting application.
pub fn into_bytes(self) -> Vec<u8> {
std::iter::once(self.is_closed as u8)
.chain(self.connection_id.to_be_bytes().iter().cloned())
.chain(self.data.into_iter())
.collect()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ConnectionError {
pub connection_id: ConnectionId,
@@ -305,6 +374,57 @@ pub enum QueryResponse {
mod tests {
use super::*;
#[cfg(test)]
mod constructing_socks5_data_responses_from_bytes {
use super::*;
#[test]
fn fails_when_zero_bytes_are_supplied() {
let response_bytes = Vec::new();
let err = NetworkData::try_from_bytes(&response_bytes).unwrap_err();
assert!(matches!(err, ResponseDeserializationError::NoData));
}
#[test]
fn fails_when_connection_id_bytes_are_too_short() {
let response_bytes = vec![0, 1, 2, 3, 4, 5, 6];
let err = NetworkData::try_from_bytes(&response_bytes).unwrap_err();
assert!(matches!(
err,
ResponseDeserializationError::ConnectionIdTooShort,
));
}
#[test]
fn works_when_there_is_no_data() {
let response_bytes = vec![0, 0, 1, 2, 3, 4, 5, 6, 7];
let expected = NetworkData::new(
u64::from_be_bytes([0, 1, 2, 3, 4, 5, 6, 7]),
Vec::new(),
false,
);
let actual = NetworkData::try_from_bytes(&response_bytes).unwrap();
assert_eq!(expected.connection_id, actual.connection_id);
assert_eq!(expected.data, actual.data);
assert_eq!(expected.is_closed, actual.is_closed);
}
#[test]
fn works_when_there_is_data() {
let response_bytes = vec![0, 0, 1, 2, 3, 4, 5, 6, 7, 255, 255, 255];
let expected = NetworkData::new(
u64::from_be_bytes([0, 1, 2, 3, 4, 5, 6, 7]),
vec![255, 255, 255],
false,
);
let actual = NetworkData::try_from_bytes(&response_bytes).unwrap();
assert_eq!(expected.connection_id, actual.connection_id);
assert_eq!(expected.data, actual.data);
assert_eq!(expected.is_closed, actual.is_closed);
}
}
#[cfg(test)]
mod connection_error_response_serde_tests {
use super::*;
+7 -7
View File
@@ -31,7 +31,7 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [
"getrandom 0.2.10",
"getrandom 0.2.8",
"once_cell",
"version_check",
]
@@ -936,9 +936,9 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.10"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
dependencies = [
"cfg-if",
"js-sys",
@@ -1148,9 +1148,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.146"
version = "0.2.142"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b"
checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317"
[[package]]
name = "libgit2-sys"
@@ -1444,7 +1444,7 @@ dependencies = [
"chacha20",
"chacha20poly1305",
"curve25519-dalek",
"getrandom 0.2.10",
"getrandom 0.2.8",
"log",
"rand 0.7.3",
"rayon",
@@ -1750,7 +1750,7 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom 0.2.10",
"getrandom 0.2.8",
]
[[package]]
+1 -1
View File
@@ -50,7 +50,7 @@ assets_version = "2.0.0" # do not edit: managed by `mdbook-admonish install`
# code prerequisites versions
minimum_rust_version = "1.66"
# TODO remove this in place of develop in next release
platform_release_version = "v1.1.21"
platform_release_version = "v1.1.20"
[preprocessor.last-changed]
command = "mdbook-last-changed"
+1 -1
View File
@@ -50,7 +50,7 @@ assets_version = "2.0.0" # do not edit: managed by `mdbook-admonish install`
minimum_rust_version = "1.66"
# old variables - this is still needed for links.. TODO change to develop?
platform_release_version = "v1.1.21"
platform_release_version = "v1.1.20"
[preprocessor.last-changed]
command = "mdbook-last-changed"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "explorer-api"
version = "1.1.21"
version = "1.1.20"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+10 -77
View File
@@ -1,93 +1,26 @@
use crate::service_providers::models::{
DirectoryService, DirectorySpDetailed, HarbourMasterService, PagedResult,
};
use crate::service_providers::models::DirectoryService;
use okapi::openapi3::OpenApi;
use reqwest::{Client, Error as ReqwestError};
use rocket::{http::Status, serde::json::Json, Route};
use reqwest::Error as ReqwestError;
use rocket::{serde::json::Json, Route};
use rocket_okapi::settings::OpenApiSettings;
const SERVICE_PROVIDER_WELLKNOWN_URL: &str =
static SERVICE_PROVIDER_WELLKNOWN_URL: &str =
"https://nymtech.net/.wellknown/connect/service-providers.json";
const HARBOUR_MASTER_URL: &str = "https://harbourmaster.nymtech.net/v1/services";
const HM_SINCE_MIN: u32 = 120;
const HM_SIZE: u8 = 100;
#[derive(Debug)]
pub enum GetSpError {
ReqwestError(ReqwestError),
Error(String),
}
impl From<ReqwestError> for GetSpError {
fn from(error: ReqwestError) -> Self {
GetSpError::ReqwestError(error)
}
}
impl From<&str> for GetSpError {
fn from(error: &str) -> Self {
GetSpError::Error(String::from(error))
}
}
pub fn service_providers_make_default_routes(settings: &OpenApiSettings) -> (Vec<Route>, OpenApi) {
openapi_get_routes_spec![settings: get_service_providers]
}
pub async fn get_services() -> Result<Vec<DirectorySpDetailed>, GetSpError> {
let reqw = Client::new();
let services_res = reqw
.get(SERVICE_PROVIDER_WELLKNOWN_URL)
.send()
pub async fn get_services() -> Result<Vec<DirectoryService>, ReqwestError> {
reqwest::get(SERVICE_PROVIDER_WELLKNOWN_URL)
.await?
.json::<Vec<DirectoryService>>()
.await?;
let directory_sp = services_res
.iter()
.find(|item| item.id == "all")
.ok_or("NymConnect network requesters data not found in response")?;
let hm_services = reqw
.get(format!(
"{HARBOUR_MASTER_URL}?since_min={HM_SINCE_MIN}&size={HM_SIZE}"
))
.send()
.await?
.json::<PagedResult<HarbourMasterService>>()
.await?;
let sp_list: Vec<_> = directory_sp
.items
.iter()
.map(|sp| {
let directory_sp = hm_services
.items
.iter()
.find(|item| item.service_provider_client_id == sp.address);
DirectorySpDetailed {
id: sp.id.clone(),
description: sp.description.clone(),
address: sp.address.clone(),
routing_score: directory_sp.map(|sp| sp.routing_score),
service_type: "Network requester".into(),
}
})
.collect();
Ok(sp_list)
.await
}
#[openapi(tag = "service_providers")]
#[get("/")]
pub(crate) async fn get_service_providers() -> Result<Json<Vec<DirectorySpDetailed>>, Status> {
match get_services().await {
Ok(res) => Ok(Json(res)),
Err(err) => {
log::error!("{:?}", err);
Err(Status::InternalServerError)
}
}
pub(crate) async fn get_service_providers() -> Json<Vec<DirectoryService>> {
let result = get_services().await.unwrap();
Json(result)
}
@@ -8,36 +8,9 @@ pub struct DirectoryServiceProvider {
pub gateway: String,
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct DirectorySpDetailed {
pub id: String,
pub description: String,
pub address: String,
pub routing_score: Option<f32>,
pub service_type: String,
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct DirectoryService {
pub id: String,
pub description: String,
pub items: Vec<DirectoryServiceProvider>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct HarbourMasterService {
pub service_provider_client_id: String,
pub gateway_identity_key: String,
pub ip_address: String,
pub last_successful_ping_utc: String,
pub last_updated_utc: String,
pub routing_score: f32,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct PagedResult<T> {
pub page: u32,
pub size: u32,
pub total: i32,
pub items: Vec<T>,
}
+2 -2
View File
@@ -31,7 +31,7 @@ import {
Environment,
GatewayBondAnnotated,
GatewayBond,
DirectoryServiceProvider,
DirectoryService,
} from '../typeDefs/explorer-api';
function getFromCache(key: string) {
@@ -153,7 +153,7 @@ export class Api {
static fetchUptimeStoryById = async (id: string): Promise<UptimeStoryResponse> =>
(await fetch(`${UPTIME_STORY_API}/${id}/history`)).json();
static fetchServiceProviders = async (): Promise<DirectoryServiceProvider[]> => {
static fetchServiceProviders = async (): Promise<DirectoryService[]> => {
const res = await fetch(SERVICE_PROVIDERS);
const json = await res.json();
return json;
+38 -48
View File
@@ -4,7 +4,6 @@ import { DataGrid, GridColDef, useGridApiContext, useGridState } from '@mui/x-da
import Pagination from '@mui/material/Pagination';
import { SxProps } from '@mui/system';
import { LinearProgress } from '@mui/material';
import { GridInitialStateCommunity } from '@mui/x-data-grid/models/gridStateCommunity';
const useStyles = makeStyles({
root: {
@@ -50,55 +49,46 @@ type DataGridProps = {
pageSize?: string | undefined;
rows: any;
loading?: boolean;
initialState?: GridInitialStateCommunity;
};
export const UniversalDataGrid: FCWithChildren<DataGridProps> = ({
rows,
columns,
loading,
pagination,
pageSize,
initialState,
}) => {
export const UniversalDataGrid: FCWithChildren<DataGridProps> = ({ rows, columns, loading, pagination, pageSize }) => {
if (loading) return <LinearProgress />;
return (
<DataGrid
pagination={pagination}
rows={rows}
components={{
Pagination: CustomPagination,
}}
columns={columns}
pageSize={Number(pageSize)}
disableSelectionOnClick
autoHeight
hideFooter={!pagination}
initialState={initialState}
style={{
width: '100%',
border: 'none',
}}
sx={{
'*::-webkit-scrollbar': {
width: '1em',
},
'*::-webkit-scrollbar-track': {
background: (t) => t.palette.nym.networkExplorer.scroll.backgroud,
outline: (t) => `1px solid ${t.palette.nym.networkExplorer.scroll.border}`,
boxShadow: 'auto',
borderRadius: 'auto',
},
'*::-webkit-scrollbar-thumb': {
backgroundColor: (t) => t.palette.nym.networkExplorer.scroll.color,
borderRadius: '20px',
width: '.4em',
border: (t) => `3px solid ${t.palette.nym.networkExplorer.scroll.backgroud}`,
shadow: 'auto',
},
}}
/>
);
if (!loading)
return (
<DataGrid
pagination={pagination}
rows={rows}
components={{
Pagination: CustomPagination,
}}
columns={columns}
pageSize={Number(pageSize)}
disableSelectionOnClick
autoHeight
hideFooter={!pagination}
style={{
width: '100%',
border: 'none',
}}
sx={{
'*::-webkit-scrollbar': {
width: '1em',
},
'*::-webkit-scrollbar-track': {
background: (t) => t.palette.nym.networkExplorer.scroll.backgroud,
outline: (t) => `1px solid ${t.palette.nym.networkExplorer.scroll.border}`,
boxShadow: 'auto',
borderRadius: 'auto',
},
'*::-webkit-scrollbar-thumb': {
backgroundColor: (t) => t.palette.nym.networkExplorer.scroll.color,
borderRadius: '20px',
width: '.4em',
border: (t) => `3px solid ${t.palette.nym.networkExplorer.scroll.backgroud}`,
shadow: 'auto',
},
}}
/>
);
return null;
};
+5 -12
View File
@@ -4,18 +4,17 @@ import {
ApiState,
BlockResponse,
CountryDataResponse,
DirectoryService,
GatewayResponse,
MixNodeResponse,
MixnodeStatus,
SummaryOverviewResponse,
ValidatorsResponse,
Environment,
DirectoryServiceProvider,
} from '../typeDefs/explorer-api';
import { EnumFilterKey } from '../typeDefs/filters';
import { Api, getEnvironment } from '../api';
import { NavOptionType, originalNavOptions } from './nav';
import { toPercentIntegerString } from '../utils';
interface StateData {
summaryOverview?: ApiState<SummaryOverviewResponse>;
@@ -28,7 +27,7 @@ interface StateData {
navState: NavOptionType[];
validators?: ApiState<ValidatorsResponse>;
environment?: Environment;
serviceProviders?: ApiState<DirectoryServiceProvider[]>;
serviceProviders?: ApiState<DirectoryService>;
}
interface StateApi {
@@ -71,7 +70,7 @@ export const MainContextProvider: FCWithChildren = ({ children }) => {
const [validators, setValidators] = React.useState<ApiState<ValidatorsResponse>>();
const [block, setBlock] = React.useState<ApiState<BlockResponse>>();
const [countryData, setCountryData] = React.useState<ApiState<CountryDataResponse>>();
const [serviceProviders, setServiceProviders] = React.useState<ApiState<DirectoryServiceProvider[]>>();
const [serviceProviders, setServiceProviders] = React.useState<ApiState<DirectoryService>>();
const toggleMode = () => setMode((m) => (m !== 'light' ? 'light' : 'dark'));
@@ -169,14 +168,8 @@ export const MainContextProvider: FCWithChildren = ({ children }) => {
const fetchServiceProviders = async () => {
setServiceProviders({ data: undefined, isLoading: true });
try {
const res = await Api.fetchServiceProviders();
const resWithRoutingScorePercentage = res.map((item) => ({
...item,
routing_score: item.routing_score
? `${toPercentIntegerString(item.routing_score.toString())}%`
: item.routing_score,
}));
setServiceProviders({ data: resWithRoutingScorePercentage, isLoading: false });
const [res] = await Api.fetchServiceProviders();
setServiceProviders({ data: res, isLoading: false });
} catch (error) {
setServiceProviders({
error: error instanceof Error ? error : new Error('Service provider api fail'),
+1 -1
View File
@@ -78,7 +78,7 @@ export const PageOverview: FCWithChildren = () => {
onClick={() => navigate('/network-components/service-providers')}
title="Service providers"
icon={<PeopleAlt />}
count={serviceProviders.data?.length}
count={serviceProviders.data?.items.length}
errorMsg={summaryOverview?.error}
/>
</Grid>
+3 -46
View File
@@ -1,50 +1,17 @@
import React from 'react';
import { Button, Card, FormControl, Grid, ListItem, Menu, SelectChangeEvent, Typography } from '@mui/material';
import { GridColDef, GridRenderCellParams } from '@mui/x-data-grid';
import { TableToolbar } from '../../components/TableToolbar';
import { Title } from '../../components/Title';
import { UniversalDataGrid } from '../../components/Universal-DataGrid';
import { useMainContext } from '../../context/main';
import { CustomColumnHeading } from '../../components/CustomColumnHeading';
const columns: GridColDef[] = [
const columns = [
{
headerName: 'Client ID',
field: 'address',
disableColumnMenu: true,
flex: 3,
},
{
headerName: 'Type',
field: 'service_type',
disableColumnMenu: true,
flex: 1,
},
{
headerName: 'Routing score',
field: 'routing_score',
disableColumnMenu: true,
flex: 2,
sortingOrder: ['asc', 'desc'],
sortComparator: (a?: string, b?: string) => {
if (!a) return -1; // Place undefined values at the end
if (!b) return 1; // Place undefined values at the end
const aToNum = parseInt(a, 10);
const bToNum = parseInt(b, 10);
if (aToNum > bToNum) return 1;
return -1; // Sort numbers in ascending order
},
renderCell: (params: GridRenderCellParams) => (!params.value ? '-' : params.value),
renderHeader: () => (
<CustomColumnHeading
headingTitle="Routing score"
tooltipInfo="Routing score is only displayed for the service providers that had a successful ping within the last two hours"
/>
),
},
];
const SupportedApps = () => {
@@ -70,7 +37,7 @@ const SupportedApps = () => {
>
Supported Apps
</Button>
<Menu anchorEl={anchorEl} open={open} onClose={handleClose}>
<Menu id="basic-menu" anchorEl={anchorEl} open={open} onClose={handleClose}>
<ListItem>Keybase</ListItem>
<ListItem>Telegram</ListItem>
<ListItem>Electrum</ListItem>
@@ -107,19 +74,9 @@ export const ServiceProviders = () => {
/>
<UniversalDataGrid
pagination
rows={serviceProviders.data}
rows={serviceProviders?.data?.items}
columns={columns}
pageSize={pageSize}
initialState={{
sorting: {
sortModel: [
{
field: 'routing_score',
sort: 'desc',
},
],
},
}}
/>
</>
) : (
-4
View File
@@ -241,15 +241,11 @@ export type MixNodeEconomicDynamicsStatsResponse = {
export type Environment = 'mainnet' | 'sandbox' | 'qa';
export type ServiceProviderType = 'Network Requester';
export type DirectoryServiceProvider = {
id: string;
description: string;
address: string;
gateway: string;
routing_score: string | null;
service_type: ServiceProviderType;
};
export type DirectoryService = {
+1 -1
View File
@@ -3,7 +3,7 @@
[package]
name = "nym-gateway"
version = "1.1.21"
version = "1.1.20"
authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
"Jędrzej Stuczyński <andrew@nymtech.net>",
+1 -1
View File
@@ -3,7 +3,7 @@
[package]
name = "nym-mixnode"
version = "1.1.22"
version = "1.1.21"
authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
"Jędrzej Stuczyński <andrew@nymtech.net>",
+1 -1
View File
@@ -3,7 +3,7 @@
[package]
name = "nym-api"
version = "1.1.22"
version = "1.1.21"
authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
"Jędrzej Stuczyński <andrew@nymtech.net>",
+14 -14
View File
@@ -54,7 +54,7 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [
"getrandom 0.2.10",
"getrandom 0.2.8",
"once_cell",
"version_check",
]
@@ -783,7 +783,7 @@ dependencies = [
"cosmos-sdk-proto",
"ecdsa",
"eyre",
"getrandom 0.2.10",
"getrandom 0.2.8",
"k256",
"prost",
"prost-types",
@@ -1894,9 +1894,9 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.10"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
dependencies = [
"cfg-if",
"js-sys",
@@ -2773,9 +2773,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.146"
version = "0.2.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b"
checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c"
[[package]]
name = "libdbus-sys"
@@ -3286,7 +3286,7 @@ dependencies = [
"bs58",
"digest 0.9.0",
"ff 0.11.1",
"getrandom 0.2.10",
"getrandom 0.2.8",
"group 0.11.0",
"itertools",
"nym-dkg",
@@ -3472,7 +3472,7 @@ name = "nym-gateway-client"
version = "0.1.0"
dependencies = [
"futures",
"getrandom 0.2.10",
"getrandom 0.2.8",
"log",
"nym-bandwidth-controller",
"nym-coconut-interface",
@@ -3608,7 +3608,7 @@ dependencies = [
"chacha20",
"chacha20poly1305",
"curve25519-dalek",
"getrandom 0.2.10",
"getrandom 0.2.8",
"log",
"rand 0.7.3",
"rayon",
@@ -4669,7 +4669,7 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom 0.2.10",
"getrandom 0.2.8",
]
[[package]]
@@ -4743,7 +4743,7 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
dependencies = [
"getrandom 0.2.10",
"getrandom 0.2.8",
"redox_syscall",
"thiserror",
]
@@ -6202,7 +6202,7 @@ dependencies = [
"bytes",
"flex-error",
"futures",
"getrandom 0.2.10",
"getrandom 0.2.8",
"http",
"hyper",
"hyper-proxy",
@@ -6724,7 +6724,7 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79"
dependencies = [
"getrandom 0.2.10",
"getrandom 0.2.8",
]
[[package]]
@@ -6912,7 +6912,7 @@ name = "wasm-utils"
version = "0.1.0"
dependencies = [
"futures",
"getrandom 0.2.10",
"getrandom 0.2.8",
"js-sys",
"tungstenite",
"wasm-bindgen",
+1 -3
View File
@@ -28,13 +28,11 @@
"@emotion/styled": "^11.6.0",
"@hookform/resolvers": "^2.8.0",
"@mui/icons-material": "^5.2.0",
"@mui/lab": "^5.0.0-alpha.72",
"@mui/material": "^5.2.2",
"@mui/styles": "^5.2.2",
"@mui/system": ">= 5",
"@mui/lab": "^5.0.0-alpha.72",
"@nymproject/react": "^1.0.0",
"@sentry/integrations": "^7.54.0",
"@sentry/react": "^7.54.0",
"@tauri-apps/api": "^1.2.0",
"@tauri-apps/tauri-forage": "^1.0.0-beta.2",
"clsx": "^1.1.1",
@@ -73,11 +73,7 @@ pub fn socks5_config_id_appended_with(gateway_id: &str) -> String {
impl Config {
pub fn new<S: AsRef<str>>(id: S, provider_mix_address: S) -> Self {
Config {
core: Socks5CoreConfig::new(
id.as_ref(),
env!("CARGO_PKG_VERSION"),
provider_mix_address.as_ref(),
),
core: Socks5CoreConfig::new(id.as_ref(), provider_mix_address.as_ref()),
storage_paths: NymConnectPaths::new_default(default_data_directory(id.as_ref())),
}
}
@@ -40,7 +40,6 @@ fn main() {
.invoke_handler(tauri::generate_handler![
crate::operations::config::get_config_file_location,
crate::operations::config::get_config_id,
crate::operations::common::get_env,
crate::operations::connection::connect::get_gateway,
crate::operations::connection::connect::get_service_provider,
crate::operations::connection::connect::set_gateway,
@@ -1,9 +0,0 @@
use std::env;
#[tauri::command]
pub async fn get_env(variable: String) -> Option<String> {
let var = env::var(&variable).ok();
log::trace!("get_env {variable} {:?}", var);
var
}
@@ -1,4 +1,3 @@
pub mod common;
pub mod config;
pub mod connection;
pub mod directory;
+2 -31
View File
@@ -9,11 +9,9 @@ import { getItemFromStorage, setItemInStorage } from 'src/utils';
import { ConnectionStatusKind, GatewayPerformance } from '../types';
import { ConnectionStatsItem } from '../components/ConnectionStats';
import { ServiceProvider } from '../types/directory';
import initSentry from '../sentry';
const FORAGE_GATEWAY_KEY = 'nym-connect-user-gateway';
const FORAGE_SP_KEY = 'nym-connect-user-sp';
const FORAGE_MONITORING_ENABLED = 'nym-connect-monitoring-enabled';
type ModeType = 'light' | 'dark';
@@ -32,7 +30,6 @@ export type TClientContext = {
serviceProviders?: ServiceProvider[];
setMode: (mode: ModeType) => void;
clearError: () => void;
monitoringEnabled: boolean;
setConnectionStatus: (connectionStatus: ConnectionStatusKind) => void;
setConnectionStats: (connectionStats: ConnectionStatsItem[] | undefined) => void;
setConnectedSince: (connectedSince: DateTime | undefined) => void;
@@ -42,7 +39,6 @@ export type TClientContext = {
startDisconnecting: () => Promise<void>;
setUserDefinedGateway: React.Dispatch<React.SetStateAction<UserDefinedGateway>>;
setUserDefinedSPAddress: React.Dispatch<React.SetStateAction<UserDefinedSPAddress>>;
setMonitoring: (value: boolean) => Promise<void>;
};
export const ClientContext = createContext({} as TClientContext);
@@ -66,25 +62,12 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
isActive: false,
address: undefined,
});
const [monitoringEnabled, setMonitoringEnabled] = useState(false);
const getAppVersion = async () => {
const version = await getVersion();
return version;
};
useEffect(() => {
const initSentryClient = async () => {
const monitoring = await getItemFromStorage({ key: FORAGE_MONITORING_ENABLED });
setMonitoringEnabled(Boolean(monitoring));
if (monitoring === true) {
await initSentry();
}
};
initSentryClient();
}, []);
useEffect(() => {
setItemInStorage({ key: FORAGE_GATEWAY_KEY, value: userDefinedGateway });
}, [userDefinedGateway]);
@@ -102,12 +85,8 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
setAppVersion(AppVersion);
setServiceProviders(services as ServiceProvider[]);
if (storedUserDefinedGateway) {
setUserDefinedGateway(storedUserDefinedGateway);
}
if (storedUserDefinedSP) {
setUserDefinedSPAddress(storedUserDefinedSP);
}
if (storedUserDefinedGateway) setUserDefinedGateway(storedUserDefinedGateway);
if (storedUserDefinedSP) setUserDefinedSPAddress(storedUserDefinedSP);
};
useEvents({
@@ -183,11 +162,6 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
const clearError = () => setError(undefined);
const setMonitoring = async (value: boolean) => {
setMonitoringEnabled(value);
await setItemInStorage({ key: FORAGE_MONITORING_ENABLED, value });
};
const contextValue = useMemo(
() => ({
mode,
@@ -203,7 +177,6 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
selectedProvider,
serviceProviders,
connectedSince,
monitoringEnabled,
setConnectedSince,
setSerivceProvider,
startConnecting,
@@ -214,7 +187,6 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
userDefinedGateway,
setUserDefinedGateway,
setUserDefinedSPAddress,
setMonitoring,
}),
[
mode,
@@ -230,7 +202,6 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
selectedProvider,
userDefinedGateway,
userDefinedSPAddress,
monitoringEnabled,
],
);
@@ -11,7 +11,6 @@ const mockValues: TClientContext = {
showInfoModal: false,
userDefinedGateway: { isActive: false, gateway: '' },
userDefinedSPAddress: { isActive: false, address: '' },
monitoringEnabled: false,
setShowInfoModal: () => {},
setMode: () => {},
clearError: () => {},
@@ -23,7 +22,6 @@ const mockValues: TClientContext = {
setSerivceProvider: () => {},
setUserDefinedGateway: () => {},
setUserDefinedSPAddress: () => {},
setMonitoring: async () => {},
};
export const MockProvider: FCWithChildren<{
@@ -1,52 +0,0 @@
import React, { ChangeEvent, useState } from 'react';
import { Warning as WarningIcon } from '@mui/icons-material';
import { Box, FormControl, FormControlLabel, FormHelperText, Stack, Switch, Typography } from '@mui/material';
import { useClientContext } from 'src/context/main';
export const MonitoringSettings = () => {
const { monitoringEnabled, setMonitoring } = useClientContext();
const [enabled, setEnabled] = useState(monitoringEnabled);
const [loading, setLoading] = useState(false);
const handleChange = async (e: ChangeEvent<HTMLInputElement>) => {
setLoading(true);
setEnabled(e.target.checked);
await setMonitoring(e.target.checked);
setLoading(false);
};
return (
<Box height="100%">
<Stack justifyContent="space-between" height="100%">
<Box>
<Typography fontWeight="bold" variant="body2" mb={2}>
Error reporting and performance monitoring
</Typography>
<FormControl fullWidth>
<FormControlLabel
control={
<Switch checked={enabled} onChange={handleChange} disabled={loading} size="small" sx={{ ml: 1 }} />
}
label="Enable"
/>
<FormHelperText sx={{ m: 0, my: 2 }}>
Help Nym developers to fix errors, crashes and improve the application by enabling this option. If errors
occur or if the app crashes, it will automatically send a report. Also it tracks various performance
metrics. We use sentry.io service to handle this.
</FormHelperText>
<FormHelperText sx={{ m: 0, mb: 2 }}>
Note: A report can include your external IP, this can be useful to catch issues related to IP location.
All recorded data is used by Nym developers and for app development purposes only.
</FormHelperText>
</FormControl>
<Stack direction="row" gap={1} alignItems="center">
<WarningIcon color="warning" fontSize="small" />
<Typography variant="caption" color="warning.main">
You must restart the application for the change to take effect.
</Typography>
</Stack>
</Box>
</Stack>
</Box>
);
};
@@ -7,7 +7,6 @@ import { toggleLogViewer } from 'src/utils';
const menuSchema = [
{ title: 'Select your gateway', path: 'gateway' },
{ title: 'Select a service provider', path: 'service-provider' },
{ title: 'Help improve the app', path: 'monitoring' },
];
export const SettingsMenu = () => (
+14 -26
View File
@@ -1,6 +1,5 @@
import React from 'react';
import { Routes, Route } from 'react-router-dom';
import * as Sentry from '@sentry/react';
import { ConnectionPage } from 'src/pages/connection';
import { Menu } from 'src/pages/menu';
import { CompatibleApps } from 'src/pages/menu/Apps';
@@ -8,30 +7,19 @@ import { HelpGuide } from 'src/pages/menu/Guide';
import { SettingsMenu } from 'src/pages/menu/settings';
import { GatewaySettings } from 'src/pages/menu/settings/GatewaySettings';
import { ServiceProviderSettings } from 'src/pages/menu/settings/ServiceProviderSettings';
import { MonitoringSettings } from '../pages/menu/settings/MonitoringSettings';
import { useClientContext } from '../context/main';
const SentryRoutes = Sentry.withSentryReactRouterV6Routing(Routes);
export const AppRoutes = () => {
const { monitoringEnabled } = useClientContext();
const RoutesContainer = monitoringEnabled ? SentryRoutes : Routes;
return (
<RoutesContainer>
<Route index path="/" element={<ConnectionPage />} />
<Route path="menu">
<Route index element={<Menu />} />
<Route path="apps" element={<CompatibleApps />} />
<Route path="guide" element={<HelpGuide />} />
<Route path="settings">
<Route index element={<SettingsMenu />} />
<Route path="gateway" element={<GatewaySettings />} />
<Route path="service-provider" element={<ServiceProviderSettings />} />
<Route path="monitoring" element={<MonitoringSettings />} />
</Route>
export const AppRoutes = () => (
<Routes>
<Route index path="/" element={<ConnectionPage />} />
<Route path="menu">
<Route index element={<Menu />} />
<Route path="apps" element={<CompatibleApps />} />
<Route path="guide" element={<HelpGuide />} />
<Route path="settings">
<Route index element={<SettingsMenu />} />
<Route path="gateway" element={<GatewaySettings />} />
<Route path="service-provider" element={<ServiceProviderSettings />} />
</Route>
</RoutesContainer>
);
};
</Route>
</Routes>
);
-48
View File
@@ -1,48 +0,0 @@
import React from 'react';
import { createRoutesFromChildren, matchRoutes, useLocation, useNavigationType } from 'react-router-dom';
import * as Sentry from '@sentry/react';
import { CaptureConsole } from '@sentry/integrations';
import { getVersion } from '@tauri-apps/api/app';
const SENTRY_DSN = 'https://625e2658da4945a7a253f3ee04413a31@o967446.ingest.sentry.io/4505306292289536';
async function initSentry() {
console.log('⚠ performance monitoring and error reporting enabled');
console.log('initializing sentry');
Sentry.init({
dsn: SENTRY_DSN,
integrations: [
new Sentry.BrowserTracing({
// Set `tracePropagationTargets` to control for which URLs distributed tracing should be enabled
tracePropagationTargets: ['localhost'],
routingInstrumentation: Sentry.reactRouterV6Instrumentation(
React.useEffect,
useLocation,
useNavigationType,
createRoutesFromChildren,
matchRoutes,
),
}),
new Sentry.Replay(),
// captures Console API calls
new CaptureConsole({ levels: ['error', 'warn'] }),
],
// TODO adjust this in the future, 100% is not recommended for production
tracesSampleRate: 1.0,
// Capture Replay for 10% of all sessions,
// plus for 100% of sessions with an error
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
environment: process.env.NODE_ENV,
});
getVersion().then((version) => {
Sentry.setTag('app_version', version);
});
}
export default initSentry;
+1 -1
View File
@@ -121,4 +121,4 @@ dependencies {
debugImplementation 'androidx.compose.ui:ui-tooling'
debugImplementation 'androidx.compose.ui:ui-test-manifest'
implementation 'com.github.kittinunf.fuel:fuel:3.0.0-alpha1'
}
}
@@ -36,4 +36,4 @@
tools:node="remove" />
</application>
</manifest>
</manifest>
+3 -6
View File
@@ -1,11 +1,7 @@
[package]
name = "nym-outfox"
version = "0.1.0"
description = "Outfox package format"
edition = { workspace = true }
authors = { workspace = true }
license = { workspace = true }
repository = { workspace = true }
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -16,7 +12,8 @@ zeroize = "1.5"
chacha20 = "0.9.0"
curve25519-dalek = "3.2"
chacha20poly1305 = "0.10.1"
getrandom = { workspace = true, features = ["js"] }
# Need this star over here to pull in js into getrandom
getrandom = { version = "*", features = ["js"] }
thiserror = "1"
sphinx-packet = "0.1.0"
rand = "0.7.3"
+13 -13
View File
@@ -68,7 +68,7 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [
"getrandom 0.2.10",
"getrandom 0.2.8",
"once_cell",
"version_check",
]
@@ -764,7 +764,7 @@ dependencies = [
"cosmos-sdk-proto",
"ecdsa",
"eyre",
"getrandom 0.2.10",
"getrandom 0.2.8",
"k256",
"prost",
"prost-types",
@@ -1763,9 +1763,9 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.10"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
dependencies = [
"cfg-if",
"js-sys",
@@ -2543,9 +2543,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.146"
version = "0.2.139"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b"
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
[[package]]
name = "libgit2-sys"
@@ -2936,7 +2936,7 @@ dependencies = [
"bs58",
"digest 0.9.0",
"ff",
"getrandom 0.2.10",
"getrandom 0.2.8",
"group",
"itertools",
"nym-dkg",
@@ -3111,7 +3111,7 @@ dependencies = [
"chacha20",
"chacha20poly1305",
"curve25519-dalek",
"getrandom 0.2.10",
"getrandom 0.2.8",
"log",
"rand 0.7.3",
"rayon",
@@ -3156,7 +3156,7 @@ dependencies = [
"aes-gcm",
"argon2",
"generic-array 0.14.7",
"getrandom 0.2.10",
"getrandom 0.2.8",
"rand 0.8.5",
"serde",
"serde_json",
@@ -4051,7 +4051,7 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom 0.2.10",
"getrandom 0.2.8",
]
[[package]]
@@ -4128,7 +4128,7 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
dependencies = [
"getrandom 0.2.10",
"getrandom 0.2.8",
"redox_syscall",
"thiserror",
]
@@ -5292,7 +5292,7 @@ dependencies = [
"bytes",
"flex-error",
"futures",
"getrandom 0.2.10",
"getrandom 0.2.8",
"http",
"hyper",
"hyper-proxy",
@@ -5750,7 +5750,7 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79"
dependencies = [
"getrandom 0.2.10",
"getrandom 0.2.8",
]
[[package]]
+1 -1
View File
@@ -122,4 +122,4 @@
"webpack-favicons": "^1.3.8",
"webpack-merge": "^5.8.0"
}
}
}
+1 -5
View File
@@ -67,11 +67,7 @@ impl Config {
S: AsRef<str>,
{
Config {
core: CoreConfig::new(
id.as_ref(),
env!("CARGO_PKG_VERSION"),
provider_mix_address.as_ref(),
),
core: CoreConfig::new(id.as_ref(), provider_mix_address.as_ref()),
storage_paths: storage_root.map(|storage_root| {
MobileSocksClientPaths::new_default(data_directory_from_root(
storage_root,
+1 -3
View File
@@ -8,7 +8,6 @@ use anyhow::{anyhow, Result};
use lazy_static::lazy_static;
use log::{info, warn};
use nym_bin_common::logging::setup_logging;
use nym_client_core::init::GatewaySetup;
use nym_config_common::defaults::setup_env;
use nym_socks5_client_core::NymClient as Socks5NymClient;
use safer_ffi::char_p::char_p_boxed;
@@ -208,8 +207,7 @@ where
let config = load_or_generate_base_config(storage_dir, client_id, service_provider).await?;
let storage = MobileClientStorage::new(&config);
let socks5_client = Socks5NymClient::new(config.core, storage)
.with_gateway_setup(GatewaySetup::New { by_latency: false });
let socks5_client = Socks5NymClient::new(config.core, storage);
eprintln!("starting the socks5 client");
let mut started_client = socks5_client.start().await?;
@@ -3,7 +3,7 @@
[package]
name = "nym-network-requester"
version = "1.1.21"
version = "1.1.20"
authors.workspace = true
edition.workspace = true
rust-version = "1.65"
@@ -82,7 +82,7 @@ impl NymConfigTemplate for Config {
impl Config {
pub fn new<S: AsRef<str>>(id: S) -> Self {
Config {
base: BaseClientConfig::new(id.as_ref(), env!("CARGO_PKG_VERSION")),
base: BaseClientConfig::new(id.as_ref()),
network_requester: Default::default(),
storage_paths: NetworkRequesterPaths::new_default(default_data_directory(id.as_ref())),
network_requester_debug: Default::default(),
@@ -25,8 +25,8 @@ use nym_socks5_proxy_helpers::connection_controller::{
};
use nym_socks5_proxy_helpers::proxy_runner::{MixProxyReader, MixProxySender};
use nym_socks5_requests::{
ConnectRequest, ConnectionId, QueryRequest, QueryResponse, SendRequest, SocketData,
Socks5ProtocolVersion, Socks5ProviderRequest, Socks5Request, Socks5RequestContent,
ConnectRequest, ConnectionId, NetworkData, QueryRequest, QueryRequestId, QueryResponse,
SendRequest, Socks5ProtocolVersion, Socks5ProviderRequest, Socks5Request, Socks5RequestContent,
Socks5Response,
};
use nym_sphinx::addressing::clients::Recipient;
@@ -138,18 +138,20 @@ impl ServiceProvider<Socks5Request> for NRServiceProvider {
.connected_services
.read()
.await
.get(&req.data.header.connection_id)
.get(&req.conn_id)
{
stats_collector
.request_stats_data
.write()
.await
.processed(remote_addr, req.data.data.len() as u32);
.processed(remote_addr, req.data.len() as u32);
}
}
self.handle_proxy_send(req)
}
Socks5RequestContent::Query(query) => return self.handle_query(query),
Socks5RequestContent::Query((query_id, query)) => {
return self.handle_query(query_id, query)
}
}
Ok(None)
@@ -363,7 +365,7 @@ impl NRServiceProvider {
return_address,
remote_version,
connection_id,
SocketData::new(0, connection_id, true, Vec::new()),
NetworkData::new_closed_empty(connection_id),
);
mix_input_sender
@@ -475,23 +477,24 @@ impl NRServiceProvider {
}
fn handle_proxy_send(&mut self, req: SendRequest) {
self.controller_sender
.unbounded_send(ControllerCommand::new_send(req.data))
.unwrap()
self.controller_sender.unbounded_send(req.into()).unwrap()
}
fn handle_query(
&self,
query_id: QueryRequestId,
query: QueryRequest,
) -> Result<Option<Socks5Response>, NetworkRequesterError> {
let protocol_version = Socks5ProtocolVersion::default();
let response = match query {
QueryRequest::OpenProxy => Socks5Response::new_query(
protocol_version,
query_id,
QueryResponse::OpenProxy(self.open_proxy),
),
QueryRequest::Description => Socks5Response::new_query(
protocol_version,
query_id,
QueryResponse::Description("Description (placeholder)".to_string()),
),
};
@@ -6,7 +6,7 @@ use nym_service_providers_common::interface::{
ControlRequest, ControlResponse, ProviderInterfaceVersion, RequestVersion,
};
use nym_socks5_requests::{
ConnectionId, SocketData, Socks5ProviderRequest, Socks5ProviderResponse, Socks5Request,
ConnectionId, NetworkData, Socks5ProviderRequest, Socks5ProviderResponse, Socks5Request,
Socks5RequestContent, Socks5Response, Socks5ResponseContent,
};
use nym_sphinx::addressing::clients::Recipient;
@@ -78,12 +78,12 @@ impl MixnetMessage {
address: MixnetAddress,
request_version: RequestVersion<Socks5Request>,
connection_id: ConnectionId,
content: SocketData,
content: NetworkData,
) -> Self {
// TODO: simplify by providing better constructor for `PlaceholderResponse`
let res = Socks5Response::new(
request_version.provider_protocol,
Socks5ResponseContent::NetworkData { content },
Socks5ResponseContent::NetworkData(content),
);
let msg =
Socks5ProviderResponse::new_provider_data(request_version.provider_interface, res);
@@ -135,15 +135,11 @@ impl MixnetMessage {
pub(crate) fn new_network_data_response_content(
address: MixnetAddress,
request_version: RequestVersion<Socks5Request>,
seq: u64,
connection_id: ConnectionId,
data: Vec<u8>,
closed_socket: bool,
) -> Self {
if seq == 0 && data.is_empty() {
println!("new empty response with 0 seq")
}
let response_content = SocketData::new(seq, connection_id, closed_socket, data);
let response_content = NetworkData::new(connection_id, data, closed_socket);
Self::new_network_data_response(address, request_version, connection_id, response_content)
}
@@ -66,14 +66,13 @@ impl Connection {
Some(lane_queue_lengths),
shutdown,
)
.run(move |socket_data| {
.run(move |conn_id, read_data, socket_closed| {
MixnetMessage::new_network_data_response_content(
return_address.clone(),
remote_version.clone(),
socket_data.header.seq,
socket_data.header.connection_id,
socket_data.data,
socket_data.header.local_socket_closed,
conn_id,
read_data,
socket_closed,
)
})
.await
@@ -6,11 +6,10 @@ use crate::core::new_legacy_request_version;
use crate::reply::MixnetMessage;
use async_trait::async_trait;
use log::*;
use nym_ordered_buffer::OrderedMessageSender;
use nym_service_providers_common::interface::RequestVersion;
use nym_socks5_proxy_helpers::proxy_runner::MixProxySender;
use nym_socks5_requests::{
ConnectionId, RemoteAddress, SocketData, Socks5Request, Socks5RequestContent,
};
use nym_socks5_requests::{ConnectionId, RemoteAddress, Socks5Request, Socks5RequestContent};
use nym_sphinx::addressing::clients::Recipient;
use nym_statistics_common::api::{
build_statistics_request_bytes, DEFAULT_STATISTICS_SERVICE_ADDRESS,
@@ -191,9 +190,9 @@ impl StatisticsCollector for ServiceStatisticsCollector {
.expect("MixProxyReader has stopped receiving!");
trace!("Sending data to statistics service");
let message = SocketData::new(0, conn_id, true, msg);
let send_req = Socks5RequestContent::new_send(message);
let mut message_sender = OrderedMessageSender::new();
let ordered_msg = message_sender.wrap_message(msg).into_bytes();
let send_req = Socks5RequestContent::new_send(conn_id, ordered_msg, true);
let mixnet_message = MixnetMessage::new_network_data_request(
self.stats_provider_addr,
@@ -1,6 +1,6 @@
[package]
name = "nym-network-statistics"
version = "1.1.21"
version = "1.1.20"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-cli"
version = "1.1.21"
version = "1.1.20"
authors.workspace = true
edition = "2021"
+1
View File
@@ -14,5 +14,6 @@ nym-network-defaults = { path = "../../common/network-defaults" }
nym-sdk = { path = "../../sdk/rust/nym-sdk" }
nym-service-providers-common = { path = "../../service-providers/common" }
nym-socks5-requests = { path = "../../common/socks5/requests" }
rand = "0.8"
serde = { workspace = true }
tokio = { workspace = true, features = [ "net", "rt-multi-thread", "macros" ] }
+18 -7
View File
@@ -7,13 +7,20 @@ use nym_service_providers_common::interface::{
ControlRequest, ControlResponse, ProviderInterfaceVersion, Request, Response, ResponseContent,
};
use nym_socks5_requests::{
QueryRequest, QueryResponse, Socks5ProtocolVersion, Socks5Request, Socks5Response,
QueryRequest, QueryRequestId, QueryResponse, Socks5ProtocolVersion, Socks5Request,
Socks5Response,
};
use rand::RngCore;
use serde::Serialize;
use tokio::time::{timeout, Duration};
const RESPONSE_TIMEOUT: Duration = Duration::from_secs(10);
fn generate_random() -> u64 {
let mut rng = rand::rngs::OsRng;
rng.next_u64()
}
#[derive(Parser)]
#[command(version, about, long_about = None)]
struct Cli {
@@ -128,9 +135,10 @@ fn new_supported_request_versions_request() -> Request {
Request::new_control(ProviderInterfaceVersion::new_current(), request_versions)
}
fn new_open_proxy_request() -> Request<Socks5Request> {
fn new_open_proxy_request(query_id: QueryRequestId) -> Request<Socks5Request> {
let request_open_proxy = Socks5Request::new_query(
Socks5ProtocolVersion::new_current(),
query_id,
QueryRequest::OpenProxy,
);
Request::new_provider_data(ProviderInterfaceVersion::new_current(), request_open_proxy)
@@ -175,19 +183,22 @@ impl QueryClient {
}
async fn query_open_proxy(&mut self) -> QueryResponse {
let query_id: QueryRequestId = generate_random();
self.client
.send_bytes(
self.provider,
new_open_proxy_request().into_bytes(),
new_open_proxy_request(query_id).into_bytes(),
IncludedSurbs::new(10),
)
.await;
let response = wait_for_socks5_response(&mut self.client).await;
response
let query_response = response
.content
.as_query()
.expect("Unexpected response type!")
.clone()
.expect("Unexpected response type!");
// TODO: we should just drop unexpected messages
assert_eq!(&query_id, query_response.0);
query_response.1.clone()
}
async fn ping(&mut self) -> PingResponse {
@@ -196,7 +207,7 @@ impl QueryClient {
.send_bytes(
self.provider,
new_ping_request().into_bytes(),
IncludedSurbs::new(5),
IncludedSurbs::new(10),
)
.await;
let response = wait_for_control_response(&mut self.client).await;
+66 -75
View File
@@ -3042,80 +3042,6 @@
resolved "https://registry.yarnpkg.com/@sapphire/utilities/-/utilities-3.11.1.tgz#75d068fe473d9cf8483156ae731bce92fa39a2ef"
integrity sha512-SO7WvlPc0QEB+bCT6Z/NGpYTpOtYAJtxpxKV4YU+agEGlMZ5hM8J/xkco4/yFWH7X2gAcG4Ckoi2shFr7CMS/A==
"@sentry-internal/tracing@7.54.0":
version "7.54.0"
resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.54.0.tgz#eeb10ee72426d08669a7706faa4264f1ec02c71d"
integrity sha512-JsyhZ0wWZ+VqbHJg+azqRGdYJDkcI5R9+pnkO6SzbzxrRewqMAIwzkpPee3oI7vG99uhMEkOkMjHu0nQGwkOQw==
dependencies:
"@sentry/core" "7.54.0"
"@sentry/types" "7.54.0"
"@sentry/utils" "7.54.0"
tslib "^1.9.3"
"@sentry/browser@7.54.0":
version "7.54.0"
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.54.0.tgz#7fe331c776d02b5c902733aa41dcbfac7bef1ae6"
integrity sha512-EvLAw03N9WE2m1CMl2/1YMeIs1icw9IEOVJhWmf3uJEysNJOFWXu6ZzdtHEz1E6DiJYhc1HzDya0ExZeJxNARA==
dependencies:
"@sentry-internal/tracing" "7.54.0"
"@sentry/core" "7.54.0"
"@sentry/replay" "7.54.0"
"@sentry/types" "7.54.0"
"@sentry/utils" "7.54.0"
tslib "^1.9.3"
"@sentry/core@7.54.0":
version "7.54.0"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.54.0.tgz#8c4cb8800f8df708b3f3f6483026bb9a02820014"
integrity sha512-MAn0E2EwgNn1pFQn4qxhU+1kz6edullWg6VE5wCmtpXWOVw6sILBUsQpeIG5djBKMcneJCdOlz5jeqcKPrLvZQ==
dependencies:
"@sentry/types" "7.54.0"
"@sentry/utils" "7.54.0"
tslib "^1.9.3"
"@sentry/integrations@^7.54.0":
version "7.54.0"
resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-7.54.0.tgz#62c73013ca6040d0c9b045809fc5d6ecefda3339"
integrity sha512-RolGsQzJChJzjHTJcCKSZ1HanmY33floc5o13WgU9NoDqJbLGLNcOIrAu+WynqPe8P5VTVrVb8NiwhLqWrKp4g==
dependencies:
"@sentry/types" "7.54.0"
"@sentry/utils" "7.54.0"
localforage "^1.8.1"
tslib "^1.9.3"
"@sentry/react@^7.54.0":
version "7.54.0"
resolved "https://registry.yarnpkg.com/@sentry/react/-/react-7.54.0.tgz#0d9e1b902fd9ded713ac46a623f6a490e4aa2c8a"
integrity sha512-qUbwmRRpTh05m2rbC8A2zAFQYsoHhwIpxT5UXxh0P64ZlA3cSg1/DmTTgwnd1l+7gzKrc31UikXQ4y0YDbMNKg==
dependencies:
"@sentry/browser" "7.54.0"
"@sentry/types" "7.54.0"
"@sentry/utils" "7.54.0"
hoist-non-react-statics "^3.3.2"
tslib "^1.9.3"
"@sentry/replay@7.54.0":
version "7.54.0"
resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-7.54.0.tgz#f0f44f9413ceefd1809bf1665e82315927ae08db"
integrity sha512-C0F0568ybphzGmKGe23duB6n5wJcgM7WLYhoeqW3o2bHeqpj1dGPSka/K3s9KzGaAgzn1zeOUYXJsOs+T/XdsA==
dependencies:
"@sentry/core" "7.54.0"
"@sentry/types" "7.54.0"
"@sentry/utils" "7.54.0"
"@sentry/types@7.54.0":
version "7.54.0"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.54.0.tgz#bfee18107a78e290e6c8ad41646e2b9d9dd95234"
integrity sha512-D+i9xogBeawvQi2r0NOrM7zYcUaPuijeME4O9eOTrDF20tj71hWtJLilK+KTGLYFtpGg1h+9bPaz7OHEIyVopg==
"@sentry/utils@7.54.0":
version "7.54.0"
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.54.0.tgz#a3acb5e25a1409cbca7b46d6356d7417a253ea9a"
integrity sha512-3Yf5KlKjIcYLddOexSt2ovu2TWlR4Fi7M+aCK8yUTzwNzf/xwFSWOstHlD/WiDy9HvfhWAOB/ukNTuAeJmtasw==
dependencies:
"@sentry/types" "7.54.0"
tslib "^1.9.3"
"@sigstore/protobuf-specs@^0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@sigstore/protobuf-specs/-/protobuf-specs-0.1.0.tgz#957cb64ea2f5ce527cc9cf02a096baeb0d2b99b4"
@@ -4284,51 +4210,101 @@
resolved "https://registry.yarnpkg.com/@tauri-apps/api/-/api-1.3.0.tgz#d0c853ab2cc7506bd826c5f7f260c67c7c15def5"
integrity sha512-AH+3FonkKZNtfRtGrObY38PrzEj4d+1emCbwNGu0V2ENbXjlLHMZQlUh+Bhu/CRmjaIwZMGJ3yFvWaZZgTHoog==
"@tauri-apps/api@^2.0.0-alpha.0":
version "2.0.0-alpha.3"
resolved "https://registry.yarnpkg.com/@tauri-apps/api/-/api-2.0.0-alpha.3.tgz#7f467c188305944d67b5b6e82be0041b8b4dbbc9"
integrity sha512-F6seMDlcaxeCPy4gS0zJdp6Tet+0rd1qJi/fbKrOrhLM6Y5UtkiG1aSDnMPi+1udThSfadjhUwrLHINvfMCjzQ==
"@tauri-apps/cli-darwin-arm64@1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-1.3.0.tgz#6c738af14f494546e0fbe64f6befa0eb08959150"
integrity sha512-uuhx3/LaqFyHkoGOnOltBLKWGOzC6WzdXu+/Qv3NmNnyQWkY7O34z5V0oP6ibfuiOBZufKjOuBR+8YAIR8Qh9Q==
"@tauri-apps/cli-darwin-arm64@2.0.0-alpha.8":
version "2.0.0-alpha.8"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.0.0-alpha.8.tgz#399bec52290ea85f74470bc329376142e17e9878"
integrity sha512-ZF9nkkYCDiAEKZFwjEbuqTcFVp+DBgem3edKjsZDYPQpWg0VcZOSYr0o3/RPC81T1/FAy1lq478mkcMe0efvEw==
"@tauri-apps/cli-darwin-x64@1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-1.3.0.tgz#d3fda368b555663723337a70abe4b470592d9bda"
integrity sha512-fj0VXHMDvb/H1CjaS/JoYd7xcourxndJn1IyM4afYbpXibT/fpmM6uZflDI6rRa220NfnBtQvy+asgwC9wuyLA==
"@tauri-apps/cli-darwin-x64@2.0.0-alpha.8":
version "2.0.0-alpha.8"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.0.0-alpha.8.tgz#4935823acf880423f6e2c70097ab6584079321b3"
integrity sha512-N5V+tbP3qeAoXrrTZXvaLIeEWKCq11tqXoNFTkIZNGNC5yQdNpZSX0LqFqzmxVR1SHiOymebvcUlx+ADIpuXGw==
"@tauri-apps/cli-linux-arm-gnueabihf@1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-1.3.0.tgz#165ed5a299a944a8c0fc1ed5fc0d609db78a142b"
integrity sha512-f80DmFPnH5ZskG61KIlAyMVk9YkrTq0XM2uiQjOo5gToIdJidSwhPQVeBLv+7UxhqaRBx082Dg2fOkWlO3LiOQ==
"@tauri-apps/cli-linux-arm-gnueabihf@2.0.0-alpha.8":
version "2.0.0-alpha.8"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.0.0-alpha.8.tgz#5b46f8f38be9b0c6fe8c343e7e589c958a225775"
integrity sha512-7LHfZA99ncMDUO/wCGMtrqmDHt1uEKZiNmuzPljWLUwVvbAn0pNWNygnvPNVLEOyav5NnZSGPqT1Zmn+L6Fpyg==
"@tauri-apps/cli-linux-arm64-gnu@1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-1.3.0.tgz#a68f2efc78e03d1ae59804e61ad9ee6bd78b08c0"
integrity sha512-s6/OByuGoppoUSnOXv/b6Oe6cVFk2w/KHs19aJJpo9ov/dUAA1w9wXlXu2l6sOFGsu/plaVomF2cw3iAQmaUCQ==
"@tauri-apps/cli-linux-arm64-gnu@2.0.0-alpha.8":
version "2.0.0-alpha.8"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.0.0-alpha.8.tgz#56bfa1ec613bfff0aaecd8d38aaab470ae117d58"
integrity sha512-imq2MdhWdREvL2sqbU26mzH9sgSvfNWP0uvCPvPxUhK157xqdtGw+Gqm00hwnhTuT5bOFlsUNfnG2U19k1qMpA==
"@tauri-apps/cli-linux-arm64-musl@1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.3.0.tgz#eff2221408d1040d6f4cbf98a851c16e9eeb11e5"
integrity sha512-yZfZAW4BG92cynL/D4wdrwBAl2oekRwiZnU5CM8k5yncalVEL0tyzuxQjjqbqrtDcw0rdkoBWrhFd+EB89vQaQ==
"@tauri-apps/cli-linux-arm64-musl@2.0.0-alpha.8":
version "2.0.0-alpha.8"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.0.0-alpha.8.tgz#6a0e2a3f1a494481ce391d379284f688c59ba2aa"
integrity sha512-7hXEyvCosBHIN6ahkbFOI5JoyWZAulc0sYd3hWh9V/MBfU+LlPiapsJi6fdde0zew5nnzwcCtfEKkoR737tAig==
"@tauri-apps/cli-linux-x64-gnu@1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-1.3.0.tgz#2faaea4272278837df09edae90a9907c4dfe99c4"
integrity sha512-K3KRWSGKh7DTBr/ZKgWzeNX1Vdgx1ZBlUJXsm72R0Hb+93fDEp3TWgiwVkxqecB4aNWJhJsDcvRHuxw1G8xPlA==
"@tauri-apps/cli-linux-x64-gnu@2.0.0-alpha.8":
version "2.0.0-alpha.8"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.0.0-alpha.8.tgz#0ae1f0c19ab8fb9878161db4d40c3cbd581e37f3"
integrity sha512-S9T/trKpXcLc7hVIv7xFrRBlaivHD/HLUz0nYAkI2izNGFmbP3QpkqQDdwpWN/fxneodNgI2/mDFC3NULClj+A==
"@tauri-apps/cli-linux-x64-musl@1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-1.3.0.tgz#3f678ab7dc441da5582909230934204e23f85f6b"
integrity sha512-99bVHqL1EtF7oESrlmEb5BWJsMNQ2ha70gesZhaVO2qI9Vg089XvrFZWC+aGiUsXNFrOw270+D9DKn03xO5+Zg==
"@tauri-apps/cli-linux-x64-musl@2.0.0-alpha.8":
version "2.0.0-alpha.8"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.0.0-alpha.8.tgz#dbb668569ee71f041b38b05470697cc0ba761ff3"
integrity sha512-90mx6vSFNBbctaPaKhkH+um51gOoRJwLFOadOkklHS5Q6S6GjtSa1lmBEFyKUTAfFPtmiacuNYtoKx7nqm+z1Q==
"@tauri-apps/cli-win32-ia32-msvc@1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-1.3.0.tgz#eeaaa3e59cb1f26f5e7d7ce5aa101d5821a5355b"
integrity sha512-ckBUTqXXdnCiYyf2xvxiuqiKZurg7ET++f6yzfvYa+gofd5dagQJkGLlkIg2pJ2c8mhEG1Cfk1vxWPqqGfN2GQ==
"@tauri-apps/cli-win32-ia32-msvc@2.0.0-alpha.8":
version "2.0.0-alpha.8"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.0.0-alpha.8.tgz#f87db96860ca883efa7a7d96095583cb69a4dc5f"
integrity sha512-VAMsLJYfp6iVI7oJ+uIkfe8DKPRMtWDiSEkfFqvDyFX0WMTQl23B0AzYyapVwZc+WkTkLuoMLpIWMQCgAoQWfQ==
"@tauri-apps/cli-win32-x64-msvc@1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-1.3.0.tgz#8d67ab0e9bd3ae9e3a9b6e54845e0ca34ab1526c"
integrity sha512-vQ4wqRE0aziyRxgHIOLudGuxx4wETvFnmMvDBaNJRRrZQPlkOKnRxrvj1rNnI1845BdzSbDF4p7JDcFzToAfXA==
"@tauri-apps/cli-win32-x64-msvc@2.0.0-alpha.8":
version "2.0.0-alpha.8"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.0.0-alpha.8.tgz#d438b0231aaac35223d666d207b50c2794fc4ea6"
integrity sha512-zvWd13hRfRM0AEJJZ4t4CeB/cyru8hvbB6c+sxYDS9GPRWfHSH5dIeKoHhnMwP5fEOPZLN7VaeEP6tC88trD6g==
"@tauri-apps/cli@^1.0.5", "@tauri-apps/cli@^1.2.2":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli/-/cli-1.3.0.tgz#587b7d6ed34dab04f56f29925626ba1a248e298c"
@@ -4344,6 +4320,21 @@
"@tauri-apps/cli-win32-ia32-msvc" "1.3.0"
"@tauri-apps/cli-win32-x64-msvc" "1.3.0"
"@tauri-apps/cli@^2.0.0-alpha.2":
version "2.0.0-alpha.8"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli/-/cli-2.0.0-alpha.8.tgz#84772a16889b38dcd6239fe159bb5a5f2b1e1cc9"
integrity sha512-cXt6pxh7oiV8Htz7eTPor7if4aN9f9emn10+5h2Y82YzST7I7wKXsrjuk0HIyzUiqiQjUgl3iT9gh791zgtI3w==
optionalDependencies:
"@tauri-apps/cli-darwin-arm64" "2.0.0-alpha.8"
"@tauri-apps/cli-darwin-x64" "2.0.0-alpha.8"
"@tauri-apps/cli-linux-arm-gnueabihf" "2.0.0-alpha.8"
"@tauri-apps/cli-linux-arm64-gnu" "2.0.0-alpha.8"
"@tauri-apps/cli-linux-arm64-musl" "2.0.0-alpha.8"
"@tauri-apps/cli-linux-x64-gnu" "2.0.0-alpha.8"
"@tauri-apps/cli-linux-x64-musl" "2.0.0-alpha.8"
"@tauri-apps/cli-win32-ia32-msvc" "2.0.0-alpha.8"
"@tauri-apps/cli-win32-x64-msvc" "2.0.0-alpha.8"
"@tauri-apps/tauri-forage@^1.0.0-beta.2":
version "1.0.0-beta.2"
resolved "https://registry.yarnpkg.com/@tauri-apps/tauri-forage/-/tauri-forage-1.0.0-beta.2.tgz#c11b438ba32731dc7b4c8689d9fda4130baadc07"
@@ -13061,7 +13052,7 @@ loader-utils@^2.0.0, loader-utils@^2.0.4:
emojis-list "^3.0.0"
json5 "^2.1.2"
localforage@^1.7.3, localforage@^1.8.1:
localforage@^1.7.3:
version "1.10.0"
resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.10.0.tgz#5c465dc5f62b2807c3a84c0c6a1b1b3212781dd4"
integrity sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==