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