Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6f041f7b38 |
@@ -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
@@ -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
@@ -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,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"
|
||||
|
||||
@@ -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,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"
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
|
||||
Generated
+13
-13
@@ -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",
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,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"),
|
||||
},
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
Generated
+7
-7
@@ -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]]
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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>,
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
],
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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>",
|
||||
|
||||
Generated
+14
-14
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 = () => (
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
|
||||
Generated
+13
-13
@@ -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]]
|
||||
|
||||
@@ -122,4 +122,4 @@
|
||||
"webpack-favicons": "^1.3.8",
|
||||
"webpack-merge": "^5.8.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-cli"
|
||||
version = "1.1.21"
|
||||
version = "1.1.20"
|
||||
authors.workspace = true
|
||||
edition = "2021"
|
||||
|
||||
|
||||
@@ -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" ] }
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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==
|
||||
|
||||
Reference in New Issue
Block a user