Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d1a8dd45f4 |
@@ -79,6 +79,7 @@ jobs:
|
||||
target/release/nym-socks5-client
|
||||
target/release/nym-api
|
||||
target/release/nym-network-requester
|
||||
target/release/nym-data-observatory
|
||||
target/release/nym-cli
|
||||
target/release/nymvisor
|
||||
target/release/nym-node
|
||||
@@ -96,6 +97,7 @@ jobs:
|
||||
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-data-observatory $OUTPUT_DIR
|
||||
cp target/release/nymvisor $OUTPUT_DIR
|
||||
cp target/release/nym-node $OUTPUT_DIR
|
||||
cp target/release/nym-cli $OUTPUT_DIR
|
||||
|
||||
@@ -8,18 +8,17 @@ on:
|
||||
- 'explorer-api/**'
|
||||
- 'gateway/**'
|
||||
- 'integrations/**'
|
||||
- 'mixnode/**'
|
||||
- 'nym-api/**'
|
||||
- 'nym-credential-proxy/**'
|
||||
- 'nym-data-observatory/**'
|
||||
- 'nym-network-monitor/**'
|
||||
- 'nym-node/**'
|
||||
- 'nym-node-status-api/**'
|
||||
- 'nym-outfox/**'
|
||||
- 'nym-validator-rewarder/**'
|
||||
- 'nyx-chain-watcher/**'
|
||||
- 'sdk/ffi/**'
|
||||
- 'sdk/lib/**'
|
||||
- 'sdk/rust/**'
|
||||
- 'service-providers/**'
|
||||
- 'nym-browser-extension/storage/**'
|
||||
- 'tools/**'
|
||||
- 'wasm/**'
|
||||
- 'Cargo.toml'
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
name: ci-sdk-wasm
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'wasm/**'
|
||||
|
||||
-116
@@ -4,122 +4,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [2025.3-ruta] (2025-02-10)
|
||||
|
||||
- Push down forget me to client configs ([#5431])
|
||||
- Fix statistics shutdown ([#5426])
|
||||
- Make wait_for_graceful_shutdown to be pub ([#5424])
|
||||
- Upgrade to thiserror 2.0 ([#5414])
|
||||
- build(deps): bump the patch-updates group across 1 directory with 9 updates ([#5406])
|
||||
- Relocate a validator api function ([#5401])
|
||||
- Send shutdown instead of panic when reaching max fail ([#5398])
|
||||
- Change Explorer URL to new smooshed nodes ([#5396])
|
||||
- reduce log severity for checking topology validity ([#5395])
|
||||
- MixnetClient can send ClientRequests ([#5381])
|
||||
- Fix missing path triggers for CI ([#5380])
|
||||
- Uncouple storage reference for bandwidth client ([#5372])
|
||||
- build(deps): bump tokio from 1.40.0 to 1.43.0 ([#5370])
|
||||
- DNS resolver configuration for internal HTTP client lookups ([#5355])
|
||||
- Update README.md ([#5328])
|
||||
- Update README.md ([#5327])
|
||||
|
||||
[#5431]: https://github.com/nymtech/nym/pull/5431
|
||||
[#5426]: https://github.com/nymtech/nym/pull/5426
|
||||
[#5424]: https://github.com/nymtech/nym/pull/5424
|
||||
[#5414]: https://github.com/nymtech/nym/pull/5414
|
||||
[#5406]: https://github.com/nymtech/nym/pull/5406
|
||||
[#5401]: https://github.com/nymtech/nym/pull/5401
|
||||
[#5398]: https://github.com/nymtech/nym/pull/5398
|
||||
[#5396]: https://github.com/nymtech/nym/pull/5396
|
||||
[#5395]: https://github.com/nymtech/nym/pull/5395
|
||||
[#5381]: https://github.com/nymtech/nym/pull/5381
|
||||
[#5380]: https://github.com/nymtech/nym/pull/5380
|
||||
[#5372]: https://github.com/nymtech/nym/pull/5372
|
||||
[#5370]: https://github.com/nymtech/nym/pull/5370
|
||||
[#5355]: https://github.com/nymtech/nym/pull/5355
|
||||
[#5328]: https://github.com/nymtech/nym/pull/5328
|
||||
[#5327]: https://github.com/nymtech/nym/pull/5327
|
||||
|
||||
## [2025.2-hu] (2025-02-04)
|
||||
|
||||
- Feature/remove double spending bloomfilter ([#5417])
|
||||
- HU - Downgrade harmless log message from info to debug ([#5405])
|
||||
- lower default ticket verification quorum to 0.7 ([#5404])
|
||||
- Downgrade harmless log message from info to debug ([#5403])
|
||||
- Redirect from mixnode page to nodes page ([#5397])
|
||||
- chore :update version of chain watcher and validator rewarder ([#5394])
|
||||
- bugfix: correctly handle ingore epoch roles flag ([#5390])
|
||||
- bugfix: terminate mixnet socket listener on shutdown ([#5389])
|
||||
- feat: make client ignore dual mode nodes by default ([#5388])
|
||||
- Handle ecash network errors differently ([#5378])
|
||||
- Remove empty ephemeral keys ([#5376])
|
||||
- fixed sql migration for adding default message timestamp ([#5374])
|
||||
- Bind to [::] on nym-node for both IP versions ([#5361])
|
||||
- exposed NymApiClient method for obtaining node performance history ([#5360])
|
||||
- Client gateway selection ([#5358])
|
||||
- chore: refresh wasm sdk ([#5353])
|
||||
- chore: update indexed_db_futures ([#5347])
|
||||
- build(deps): bump mikefarah/yq from 4.44.6 to 4.45.1 ([#5342])
|
||||
- updated cosmrs and tendermint-rpc to their most recent versions ([#5339])
|
||||
- build(deps): bump ts-rs from 10.0.0 to 10.1.0 ([#5338])
|
||||
- build(deps): bump tempfile from 3.14.0 to 3.15.0 ([#5337])
|
||||
- build(deps): bump the patch-updates group with 8 updates ([#5336])
|
||||
- feature: introduce /load endpoint for self-reported quantised NymNode load ([#5326])
|
||||
- feature: `CancellationToken`-based shutdowns ([#5325])
|
||||
- Use expect in geodata test to give error message on failure ([#5314])
|
||||
- feature: periodically remove stale gateway messages ([#5312])
|
||||
- build(deps): bump the patch-updates group across 1 directory with 35 updates ([#5310])
|
||||
- Add dependabot assignes for the root cargo ecosystem ([#5297])
|
||||
- Move tun constants to network defaults ([#5286])
|
||||
- Include IPINFO_API_TOKEN in nightly CI ([#5285])
|
||||
- Nyx Chain Watcher ([#5274])
|
||||
- bugfix: remove unnecessary arguments for nym-api swagger endpoints ([#5272])
|
||||
- feature: nym topology revamp ([#5271])
|
||||
- Add windows to CI builds ([#5269])
|
||||
- http-api-client: deduplicate code ([#5267])
|
||||
- build(deps): bump http from 1.1.0 to 1.2.0 ([#5228])
|
||||
- NS API: add mixnet scraper ([#5200])
|
||||
- build(deps): bump criterion from 0.4.0 to 0.5.1 ([#4911])
|
||||
|
||||
[#5417]: https://github.com/nymtech/nym/pull/5417
|
||||
[#5405]: https://github.com/nymtech/nym/pull/5405
|
||||
[#5404]: https://github.com/nymtech/nym/pull/5404
|
||||
[#5403]: https://github.com/nymtech/nym/pull/5403
|
||||
[#5397]: https://github.com/nymtech/nym/pull/5397
|
||||
[#5394]: https://github.com/nymtech/nym/pull/5394
|
||||
[#5390]: https://github.com/nymtech/nym/pull/5390
|
||||
[#5389]: https://github.com/nymtech/nym/pull/5389
|
||||
[#5388]: https://github.com/nymtech/nym/pull/5388
|
||||
[#5378]: https://github.com/nymtech/nym/pull/5378
|
||||
[#5376]: https://github.com/nymtech/nym/pull/5376
|
||||
[#5374]: https://github.com/nymtech/nym/pull/5374
|
||||
[#5361]: https://github.com/nymtech/nym/pull/5361
|
||||
[#5360]: https://github.com/nymtech/nym/pull/5360
|
||||
[#5358]: https://github.com/nymtech/nym/pull/5358
|
||||
[#5353]: https://github.com/nymtech/nym/pull/5353
|
||||
[#5347]: https://github.com/nymtech/nym/pull/5347
|
||||
[#5342]: https://github.com/nymtech/nym/pull/5342
|
||||
[#5339]: https://github.com/nymtech/nym/pull/5339
|
||||
[#5338]: https://github.com/nymtech/nym/pull/5338
|
||||
[#5337]: https://github.com/nymtech/nym/pull/5337
|
||||
[#5336]: https://github.com/nymtech/nym/pull/5336
|
||||
[#5326]: https://github.com/nymtech/nym/pull/5326
|
||||
[#5325]: https://github.com/nymtech/nym/pull/5325
|
||||
[#5314]: https://github.com/nymtech/nym/pull/5314
|
||||
[#5312]: https://github.com/nymtech/nym/pull/5312
|
||||
[#5310]: https://github.com/nymtech/nym/pull/5310
|
||||
[#5297]: https://github.com/nymtech/nym/pull/5297
|
||||
[#5286]: https://github.com/nymtech/nym/pull/5286
|
||||
[#5285]: https://github.com/nymtech/nym/pull/5285
|
||||
[#5274]: https://github.com/nymtech/nym/pull/5274
|
||||
[#5272]: https://github.com/nymtech/nym/pull/5272
|
||||
[#5271]: https://github.com/nymtech/nym/pull/5271
|
||||
[#5269]: https://github.com/nymtech/nym/pull/5269
|
||||
[#5267]: https://github.com/nymtech/nym/pull/5267
|
||||
[#5228]: https://github.com/nymtech/nym/pull/5228
|
||||
[#5200]: https://github.com/nymtech/nym/pull/5200
|
||||
[#4911]: https://github.com/nymtech/nym/pull/4911
|
||||
|
||||
## [2025.1-reeses] (2025-01-15)
|
||||
|
||||
- Feture/legacy alert ([#5346])
|
||||
|
||||
Generated
+261
-511
File diff suppressed because it is too large
Load Diff
+45
-42
@@ -48,12 +48,13 @@ members = [
|
||||
"common/credentials-interface",
|
||||
"common/crypto",
|
||||
"common/dkg",
|
||||
"common/ecash-double-spending",
|
||||
"common/ecash-time",
|
||||
"common/execute",
|
||||
"common/exit-policy",
|
||||
"common/gateway-requests",
|
||||
"common/gateway-stats-storage",
|
||||
"common/gateway-storage",
|
||||
"common/gateway-stats-storage",
|
||||
"common/http-api-client",
|
||||
"common/http-api-common",
|
||||
"common/inclusion-probability",
|
||||
@@ -92,7 +93,6 @@ members = [
|
||||
"common/topology",
|
||||
"common/tun",
|
||||
"common/types",
|
||||
"common/verloc",
|
||||
"common/wasm/client-core",
|
||||
"common/wasm/storage",
|
||||
"common/wasm/utils",
|
||||
@@ -104,22 +104,6 @@ members = [
|
||||
"explorer-api/explorer-client",
|
||||
"gateway",
|
||||
"integrations/bity",
|
||||
"nym-api",
|
||||
"nym-api/nym-api-requests",
|
||||
"nym-browser-extension/storage",
|
||||
"nym-credential-proxy/nym-credential-proxy",
|
||||
"nym-credential-proxy/nym-credential-proxy-requests",
|
||||
"nym-credential-proxy/vpn-api-lib-wasm",
|
||||
"nym-network-monitor",
|
||||
"nym-node",
|
||||
"nym-node-status-api/nym-node-status-agent",
|
||||
"nym-node-status-api/nym-node-status-api",
|
||||
"nym-node-status-api/nym-node-status-client",
|
||||
"nym-node/nym-node-metrics",
|
||||
"nym-node/nym-node-requests",
|
||||
"nym-outfox",
|
||||
"nym-validator-rewarder",
|
||||
"nyx-chain-watcher",
|
||||
"sdk/ffi/cpp",
|
||||
"sdk/ffi/go",
|
||||
"sdk/ffi/shared",
|
||||
@@ -128,16 +112,26 @@ members = [
|
||||
"service-providers/common",
|
||||
"service-providers/ip-packet-router",
|
||||
"service-providers/network-requester",
|
||||
"nym-api",
|
||||
"nym-api/nym-api-requests",
|
||||
"nym-browser-extension/storage",
|
||||
"nym-credential-proxy/nym-credential-proxy",
|
||||
"nym-credential-proxy/nym-credential-proxy-requests",
|
||||
"nym-credential-proxy/vpn-api-lib-wasm",
|
||||
"nym-network-monitor",
|
||||
"nyx-chain-watcher",
|
||||
"nym-node",
|
||||
"nym-node/nym-node-requests",
|
||||
"nym-node/nym-node-metrics",
|
||||
"nym-node-status-api/nym-node-status-agent",
|
||||
"nym-node-status-api/nym-node-status-api",
|
||||
"nym-node-status-api/nym-node-status-client",
|
||||
"nym-outfox",
|
||||
"nym-validator-rewarder",
|
||||
"tools/echo-server",
|
||||
"tools/echo-server",
|
||||
"tools/internal/contract-state-importer/importer-cli",
|
||||
"tools/internal/contract-state-importer/importer-contract",
|
||||
"tools/internal/mixnet-connectivity-check",
|
||||
# "tools/internal/sdk-version-bump",
|
||||
"tools/internal/ssl-inject",
|
||||
# "tools/internal/sdk-version-bump",
|
||||
"tools/internal/testnet-manager",
|
||||
"tools/internal/testnet-manager",
|
||||
"tools/internal/testnet-manager/dkg-bypass-contract",
|
||||
"tools/internal/testnet-manager/dkg-bypass-contract",
|
||||
"tools/nym-cli",
|
||||
"tools/nym-id-cli",
|
||||
@@ -149,6 +143,13 @@ members = [
|
||||
"wasm/mix-fetch",
|
||||
"wasm/node-tester",
|
||||
"wasm/zknym-lib",
|
||||
"tools/echo-server",
|
||||
"tools/internal/contract-state-importer/importer-cli",
|
||||
"tools/internal/contract-state-importer/importer-contract",
|
||||
"tools/internal/testnet-manager",
|
||||
"tools/internal/testnet-manager/dkg-bypass-contract",
|
||||
"common/verloc",
|
||||
"tools/internal/mixnet-connectivity-check",
|
||||
]
|
||||
|
||||
default-members = [
|
||||
@@ -172,6 +173,7 @@ exclude = [
|
||||
"explorer",
|
||||
"contracts",
|
||||
"nym-wallet",
|
||||
"nym-vpn/ui/src-tauri",
|
||||
"cpu-cycles",
|
||||
]
|
||||
|
||||
@@ -192,7 +194,6 @@ aes-gcm = "0.10.1"
|
||||
aes-gcm-siv = "0.11.1"
|
||||
aead = "0.5.2"
|
||||
anyhow = "1.0.95"
|
||||
arc-swap = "1.7.1"
|
||||
argon2 = "0.5.0"
|
||||
async-trait = "0.1.85"
|
||||
axum-client-ip = "0.6.1"
|
||||
@@ -215,7 +216,7 @@ chacha20 = "0.9.0"
|
||||
chacha20poly1305 = "0.10.1"
|
||||
chrono = "0.4.39"
|
||||
cipher = "0.4.3"
|
||||
clap = "4.5.27"
|
||||
clap = "4.5.26"
|
||||
clap_complete = "4.5"
|
||||
clap_complete_fig = "4.5"
|
||||
colored = "2.0"
|
||||
@@ -248,12 +249,11 @@ futures = "0.3.31"
|
||||
futures-util = "0.3"
|
||||
generic-array = "0.14.7"
|
||||
getrandom = "0.2.10"
|
||||
getset = "0.1.4"
|
||||
getset = "0.1.3"
|
||||
handlebars = "3.5.5"
|
||||
headers = "0.4.0"
|
||||
hex = "0.4.3"
|
||||
hex-literal = "0.3.3"
|
||||
hickory-resolver = "0.24.2"
|
||||
hkdf = "0.12.3"
|
||||
hmac = "0.12.1"
|
||||
http = "1"
|
||||
@@ -264,7 +264,7 @@ humantime-serde = "1.1.1"
|
||||
human-repr = "1.1.0"
|
||||
hyper = "1.4.1"
|
||||
hyper-util = "0.1"
|
||||
indicatif = "0.17.11"
|
||||
indicatif = "0.17.9"
|
||||
inquire = "0.6.2"
|
||||
ip_network = "0.4.1"
|
||||
ipnetwork = "0.20"
|
||||
@@ -307,11 +307,11 @@ rocket_cors = "0.6.0"
|
||||
rocket_okapi = "0.8.0"
|
||||
safer-ffi = "0.1.13"
|
||||
schemars = "0.8.21"
|
||||
semver = "1.0.25"
|
||||
semver = "1.0.24"
|
||||
serde = "1.0.217"
|
||||
serde_bytes = "0.11.15"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0.138"
|
||||
serde_json = "1.0.135"
|
||||
serde_json_path = "0.7.1"
|
||||
serde_repr = "0.1"
|
||||
serde_with = "3.9.0"
|
||||
@@ -324,13 +324,13 @@ strum = "0.26"
|
||||
strum_macros = "0.26"
|
||||
subtle-encoding = "0.5"
|
||||
syn = "1"
|
||||
sysinfo = "0.33.0"
|
||||
sysinfo = "0.30.13"
|
||||
tap = "1.0.1"
|
||||
tar = "0.4.43"
|
||||
tempfile = "3.15"
|
||||
thiserror = "2.0"
|
||||
thiserror = "1.0.64"
|
||||
time = "0.3.37"
|
||||
tokio = "1.43"
|
||||
tokio = "1.39"
|
||||
tokio-stream = "0.1.17"
|
||||
tokio-test = "0.4.4"
|
||||
tokio-tun = "0.11.5"
|
||||
@@ -384,19 +384,22 @@ cw4 = { version = "=1.1.2" }
|
||||
cw-controllers = { version = "=1.1.0" }
|
||||
|
||||
# cosmrs-related
|
||||
bip32 = { version = "0.5.3", default-features = false }
|
||||
bip32 = { version = "0.5.2", default-features = false }
|
||||
|
||||
|
||||
cosmrs = { version = "0.21.0" }
|
||||
tendermint = "0.40.0"
|
||||
tendermint-rpc = "0.40.0"
|
||||
prost = { version = "0.13", default-features = false }
|
||||
# temporarily using a fork again (yay.) because we need staking and slashing support (which are already on main but not released)
|
||||
# plus response message parsing (which is, as of the time of writing this message, waiting to get merged)
|
||||
#cosmrs = { path = "../cosmos-rust-fork/cosmos-rust/cosmrs" }
|
||||
cosmrs = { git = "https://github.com/cosmos/cosmos-rust", rev = "4b1332e6d8258ac845cef71589c8d362a669675a" } # unfortuntely we need a fork by yours truly to get the staking support
|
||||
tendermint = "0.37.0" # same version as used by cosmrs
|
||||
tendermint-rpc = "0.37.0" # same version as used by cosmrs
|
||||
prost = { version = "0.12", default-features = false }
|
||||
|
||||
# wasm-related dependencies
|
||||
gloo-utils = "0.2.0"
|
||||
gloo-net = "0.6.0"
|
||||
|
||||
indexed_db_futures = "0.6.0"
|
||||
# TODO: migrate to 0.6+
|
||||
indexed_db_futures = "0.4.2"
|
||||
js-sys = "0.3.76"
|
||||
serde-wasm-bindgen = "0.6.5"
|
||||
tsify = "0.4.5"
|
||||
|
||||
@@ -13,7 +13,7 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
|
||||
* `nym-client` - an executable which you can build into your own applications. Use it for interacting with Nym nodes.
|
||||
* `nym-socks5-client` - a Socks5 proxy you can run on your machine and use with existing applications.
|
||||
* `nym-explorer` - a (projected) block explorer and (existing) mixnet viewer.
|
||||
* `nym-wallet` - a desktop wallet implemented using the [Tauri](https://tauri.app)) framework.
|
||||
* `nym-wallet` - a desktop wallet implemented using the [Tauri](https://tauri.studio/en/docs/about/intro) framework.
|
||||
* `nym-cli` - a tool for interacting with the network from the CLI.
|
||||
<!-- coming soon
|
||||
* `nym-network-monitor` - sends packets through the full system to check that they are working as expected, and stores node uptime histories as the basis of a rewards system ("mixmining" or "proof-of-mixing").
|
||||
@@ -66,4 +66,4 @@ As a general approach, licensing is as follows this pattern:
|
||||
- libraries and components are Apache 2.0 or MIT
|
||||
- documentation is Apache 2.0 or CC0-1.0
|
||||
|
||||
Nym Node Operators and Validators Terms and Conditions can be found [here](https://nym.com/operators-validators-terms).
|
||||
Nym Node Operators and Validators Temrs and Conditions can be found [here](https://nym.com/terms-and-conditions/operators/v1.0.0).
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.1.48"
|
||||
version = "1.1.46"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Client"
|
||||
edition = "2021"
|
||||
|
||||
@@ -56,7 +56,7 @@ pub fn default_data_directory<P: AsRef<Path>>(id: P) -> PathBuf {
|
||||
.join(DEFAULT_DATA_DIR)
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize, Clone)]
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
pub struct Config {
|
||||
#[serde(flatten)]
|
||||
pub base: BaseClientConfig,
|
||||
@@ -94,10 +94,6 @@ impl CliClientConfig for Config {
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn base(&self) -> BaseClientConfig {
|
||||
self.base.clone()
|
||||
}
|
||||
|
||||
pub fn new<S: AsRef<str>>(id: S) -> Self {
|
||||
Config {
|
||||
base: BaseClientConfig::new(id.as_ref(), env!("CARGO_PKG_VERSION")),
|
||||
@@ -213,7 +209,7 @@ impl SocketType {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone)]
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct Socket {
|
||||
pub socket_type: SocketType,
|
||||
|
||||
@@ -107,8 +107,5 @@ enabled = {{ debug.stats_reporting.enabled }}
|
||||
provider_address = '{{ debug.stats_reporting.provider_address }}'
|
||||
reporting_interval = '{{ debug.stats_reporting.reporting_interval }}'
|
||||
|
||||
[debug.forget_me]
|
||||
client = {{ debug.forget_me.client }}
|
||||
stats = {{ debug.forget_me.stats }}
|
||||
|
||||
"#;
|
||||
|
||||
@@ -20,7 +20,7 @@ pub use nym_sphinx::addressing::clients::Recipient;
|
||||
|
||||
pub mod config;
|
||||
|
||||
type NativeClientBuilder = BaseClientBuilder<QueryHttpRpcNyxdClient, OnDiskPersistent>;
|
||||
type NativeClientBuilder<'a> = BaseClientBuilder<'a, QueryHttpRpcNyxdClient, OnDiskPersistent>;
|
||||
|
||||
pub struct SocketClient {
|
||||
/// Client configuration options, including, among other things, packet sending rates,
|
||||
@@ -32,10 +32,6 @@ pub struct SocketClient {
|
||||
}
|
||||
|
||||
impl SocketClient {
|
||||
pub fn config(&self) -> Config {
|
||||
self.config.clone()
|
||||
}
|
||||
|
||||
pub fn new(config: Config, custom_mixnet: Option<PathBuf>) -> Self {
|
||||
SocketClient {
|
||||
config,
|
||||
@@ -112,9 +108,8 @@ impl SocketClient {
|
||||
let storage = self.initialise_storage().await?;
|
||||
let user_agent = nym_bin_common::bin_info!().into();
|
||||
|
||||
let mut base_client =
|
||||
BaseClientBuilder::new(self.config().base(), storage, dkg_query_client)
|
||||
.with_user_agent(user_agent);
|
||||
let mut base_client = BaseClientBuilder::new(&self.config.base, storage, dkg_query_client)
|
||||
.with_user_agent(user_agent);
|
||||
|
||||
if let Some(custom_mixnet) = &self.custom_mixnet {
|
||||
base_client = base_client.with_stored_topology(custom_mixnet)?;
|
||||
|
||||
@@ -82,7 +82,6 @@ impl From<Init> for OverrideConfig {
|
||||
nyxd_urls: init_config.common_args.nyxd_urls,
|
||||
enabled_credentials_mode: init_config.common_args.enabled_credentials_mode,
|
||||
stats_reporting_address: init_config.common_args.stats_reporting_address,
|
||||
forget_me: init_config.common_args.forget_me.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ use nym_bin_common::completions::{fig_generate, ArgShell};
|
||||
use nym_client::client::Recipient;
|
||||
use nym_client_core::cli_helpers::CliClient;
|
||||
use nym_client_core::client::base_client::storage::migration_helpers::v1_1_33;
|
||||
use nym_client_core::config::ForgetMe;
|
||||
use nym_config::OptionalSet;
|
||||
use std::error::Error;
|
||||
use std::net::IpAddr;
|
||||
@@ -107,7 +106,6 @@ pub(crate) struct OverrideConfig {
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
stats_reporting_address: Option<Recipient>,
|
||||
forget_me: ForgetMe,
|
||||
}
|
||||
|
||||
pub(crate) async fn execute(args: Cli) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
@@ -135,7 +133,6 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
|
||||
args.fastmode,
|
||||
)
|
||||
.with_base(BaseClientConfig::with_disabled_cover_traffic, args.no_cover)
|
||||
.with_base(BaseClientConfig::with_forget_me, args.forget_me)
|
||||
.with_optional(Config::with_port, args.port)
|
||||
.with_optional(Config::with_host, args.host)
|
||||
.with_optional_custom_env_ext(
|
||||
|
||||
@@ -41,7 +41,6 @@ impl From<Run> for OverrideConfig {
|
||||
nyxd_urls: run_config.common_args.nyxd_urls,
|
||||
enabled_credentials_mode: run_config.common_args.enabled_credentials_mode,
|
||||
stats_reporting_address: run_config.common_args.stats_reporting_address,
|
||||
forget_me: run_config.common_args.forget_me.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.48"
|
||||
version = "1.1.46"
|
||||
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"
|
||||
|
||||
@@ -93,7 +93,6 @@ impl From<Init> for OverrideConfig {
|
||||
enabled_credentials_mode: init_config.common_args.enabled_credentials_mode,
|
||||
outfox: false,
|
||||
stats_reporting_address: init_config.common_args.stats_reporting_address,
|
||||
forget_me: init_config.common_args.forget_me.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ use nym_bin_common::completions::{fig_generate, ArgShell};
|
||||
use nym_client_core::cli_helpers::CliClient;
|
||||
use nym_client_core::client::base_client::storage::migration_helpers::v1_1_33;
|
||||
use nym_client_core::client::topology_control::geo_aware_provider::CountryGroup;
|
||||
use nym_client_core::config::{ForgetMe, GroupBy, TopologyStructure};
|
||||
use nym_client_core::config::{GroupBy, TopologyStructure};
|
||||
use nym_config::OptionalSet;
|
||||
use nym_sphinx::addressing::Recipient;
|
||||
use nym_sphinx::params::{PacketSize, PacketType};
|
||||
@@ -113,7 +113,6 @@ pub(crate) struct OverrideConfig {
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
outfox: bool,
|
||||
stats_reporting_address: Option<Recipient>,
|
||||
forget_me: ForgetMe,
|
||||
}
|
||||
|
||||
pub(crate) async fn execute(args: Cli) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
@@ -180,7 +179,6 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
|
||||
BaseClientConfig::with_topology_structure,
|
||||
topology_structure,
|
||||
)
|
||||
.with_base(BaseClientConfig::with_forget_me, args.forget_me)
|
||||
.with_optional(Config::with_anonymous_replies, args.use_anonymous_replies)
|
||||
.with_optional(Config::with_port, args.port)
|
||||
.with_optional(Config::with_ip, args.ip)
|
||||
|
||||
@@ -65,7 +65,6 @@ impl From<Run> for OverrideConfig {
|
||||
enabled_credentials_mode: run_config.common_args.enabled_credentials_mode,
|
||||
outfox: run_config.outfox,
|
||||
stats_reporting_address: run_config.common_args.stats_reporting_address,
|
||||
forget_me: run_config.common_args.forget_me.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,8 +113,4 @@ enabled = {{ core.debug.stats_reporting.enabled }}
|
||||
provider_address = '{{ core.debug.stats_reporting.provider_address }}'
|
||||
reporting_interval = '{{ core.debug.stats_reporting.reporting_interval }}'
|
||||
|
||||
[core.debug.forget_me]
|
||||
client = {{ core.debug.forget_me.client }}
|
||||
stats = {{ core.debug.forget_me.stats }}
|
||||
|
||||
"#;
|
||||
|
||||
@@ -40,7 +40,6 @@ nym-crypto = { path = "../crypto" }
|
||||
nym-explorer-client = { path = "../../explorer-api/explorer-client" }
|
||||
nym-gateway-client = { path = "../client-libs/gateway-client" }
|
||||
nym-gateway-requests = { path = "../gateway-requests" }
|
||||
nym-http-api-client = { path = "../http-api-client" }
|
||||
nym-metrics = { path = "../nym-metrics" }
|
||||
nym-nonexhaustive-delayqueue = { path = "../nonexhaustive-delayqueue" }
|
||||
nym-sphinx = { path = "../nymsphinx" }
|
||||
|
||||
@@ -145,11 +145,6 @@ impl Config {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_forget_me(mut self, forget_me: ForgetMe) -> Self {
|
||||
self.debug.forget_me = forget_me;
|
||||
self
|
||||
}
|
||||
|
||||
// TODO: this should be refactored properly
|
||||
// as of 12.09.23 the below is true (not sure how this comment will rot in the future)
|
||||
// medium_toggle:
|
||||
@@ -522,7 +517,7 @@ impl Default for Acknowledgements {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct Topology {
|
||||
/// The uniform delay every which clients are querying the directory server
|
||||
/// to try to obtain a compatible network topology to send sphinx packets through.
|
||||
@@ -558,15 +553,12 @@ pub struct Topology {
|
||||
|
||||
/// Specifies whether this client should attempt to retrieve all available network nodes
|
||||
/// as opposed to just active mixnodes/gateways.
|
||||
/// Useless without `ignore_epoch_roles = true`
|
||||
pub use_extended_topology: bool,
|
||||
|
||||
/// Specifies whether this client should ignore the current epoch role of the target egress node
|
||||
/// when constructing the final hop packets.
|
||||
pub ignore_egress_epoch_role: bool,
|
||||
|
||||
/// Specifies whether this client should ignore the current epoch role of the ingress node
|
||||
/// when attempting to establish new connection
|
||||
pub ignore_ingress_epoch_role: bool,
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
@@ -604,9 +596,7 @@ impl Default for Topology {
|
||||
minimum_mixnode_performance: DEFAULT_MIN_MIXNODE_PERFORMANCE,
|
||||
minimum_gateway_performance: DEFAULT_MIN_GATEWAY_PERFORMANCE,
|
||||
use_extended_topology: false,
|
||||
|
||||
ignore_egress_epoch_role: true,
|
||||
ignore_ingress_epoch_role: true,
|
||||
ignore_egress_epoch_role: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -724,9 +714,6 @@ pub struct DebugConfig {
|
||||
|
||||
/// Defines all configuration options related to stats reporting.
|
||||
pub stats_reporting: StatsReporting,
|
||||
|
||||
/// Defines all configuration options related to the forget me flag.
|
||||
pub forget_me: ForgetMe,
|
||||
}
|
||||
|
||||
impl DebugConfig {
|
||||
@@ -749,69 +736,6 @@ impl Default for DebugConfig {
|
||||
topology: Default::default(),
|
||||
reply_surbs: Default::default(),
|
||||
stats_reporting: Default::default(),
|
||||
forget_me: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug, Deserialize, PartialEq, Serialize, Copy)]
|
||||
pub struct ForgetMe {
|
||||
client: bool,
|
||||
stats: bool,
|
||||
}
|
||||
|
||||
impl From<bool> for ForgetMe {
|
||||
fn from(value: bool) -> Self {
|
||||
if value {
|
||||
Self::new_all()
|
||||
} else {
|
||||
Self::new_none()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ForgetMe {
|
||||
pub fn new_all() -> Self {
|
||||
Self {
|
||||
client: true,
|
||||
stats: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_client() -> Self {
|
||||
Self {
|
||||
client: true,
|
||||
stats: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_stats() -> Self {
|
||||
Self {
|
||||
client: false,
|
||||
stats: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(client: bool, stats: bool) -> Self {
|
||||
Self { client, stats }
|
||||
}
|
||||
|
||||
pub fn any(&self) -> bool {
|
||||
self.client || self.stats
|
||||
}
|
||||
|
||||
pub fn client(&self) -> bool {
|
||||
self.client
|
||||
}
|
||||
|
||||
pub fn stats(&self) -> bool {
|
||||
self.stats
|
||||
}
|
||||
|
||||
pub fn new_none() -> Self {
|
||||
Self {
|
||||
client: false,
|
||||
stats: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ impl From<ConfigV5> for Config {
|
||||
maximum_reply_key_age: value.debug.reply_surbs.maximum_reply_key_age,
|
||||
surb_mix_hops: value.debug.reply_surbs.surb_mix_hops,
|
||||
},
|
||||
..Default::default()
|
||||
stats_reporting: Default::default(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ pub mod error;
|
||||
mod manager;
|
||||
mod models;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct OnDiskGatewaysDetails {
|
||||
manager: StorageManager,
|
||||
}
|
||||
|
||||
@@ -20,12 +20,12 @@ pub enum InMemStorageError {
|
||||
MalformedGateway(#[from] BadGateway),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct InMemGatewaysDetails {
|
||||
inner: Arc<RwLock<InMemStorageInner>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
#[derive(Debug, Default)]
|
||||
struct InMemStorageInner {
|
||||
active_gateway: Option<String>,
|
||||
gateways: HashMap<String, GatewayRegistration>,
|
||||
|
||||
@@ -115,12 +115,11 @@ where
|
||||
hardcoded_topology.entry_capable_nodes().cloned().collect()
|
||||
} else {
|
||||
let mut rng = rand::thread_rng();
|
||||
crate::init::helpers::gateways_for_init(
|
||||
crate::init::helpers::current_gateways(
|
||||
&mut rng,
|
||||
&core.client.nym_api_urls,
|
||||
user_agent,
|
||||
core.debug.topology.minimum_gateway_performance,
|
||||
core.debug.topology.ignore_ingress_epoch_role,
|
||||
)
|
||||
.await?
|
||||
};
|
||||
|
||||
@@ -93,10 +93,6 @@ pub struct CommonClientInitArgs {
|
||||
/// Sets the address to report statistics
|
||||
#[cfg_attr(feature = "cli", clap(long, hide = true))]
|
||||
pub stats_reporting_address: Option<Recipient>,
|
||||
|
||||
/// Sets the forget me flag
|
||||
#[cfg_attr(feature = "cli", clap(long, hide = true, default_value_t = false))]
|
||||
pub forget_me: bool,
|
||||
}
|
||||
|
||||
pub struct InitResultsWithConfig<T> {
|
||||
@@ -174,12 +170,11 @@ where
|
||||
hardcoded_topology.entry_capable_nodes().cloned().collect()
|
||||
} else {
|
||||
let mut rng = rand::thread_rng();
|
||||
crate::init::helpers::gateways_for_init(
|
||||
crate::init::helpers::current_gateways(
|
||||
&mut rng,
|
||||
&core.client.nym_api_urls,
|
||||
user_agent,
|
||||
core.debug.topology.minimum_gateway_performance,
|
||||
core.debug.topology.ignore_ingress_epoch_role,
|
||||
)
|
||||
.await?
|
||||
};
|
||||
|
||||
@@ -61,8 +61,4 @@ pub struct CommonClientRunArgs {
|
||||
/// Sets the address to report statistics
|
||||
#[cfg_attr(feature = "cli", clap(long, hide = true))]
|
||||
pub stats_reporting_address: Option<Recipient>,
|
||||
|
||||
/// Sets the forget me flag
|
||||
#[cfg_attr(feature = "cli", clap(long, hide = true, default_value_t = false))]
|
||||
pub forget_me: bool,
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::mix_traffic::ClientRequestSender;
|
||||
use super::received_buffer::ReceivedBufferMessage;
|
||||
use super::statistics_control::StatisticsControl;
|
||||
use crate::client::base_client::storage::helpers::store_client_keys;
|
||||
@@ -32,11 +31,10 @@ use crate::init::{
|
||||
setup_gateway,
|
||||
types::{GatewaySetup, InitialisationResult},
|
||||
};
|
||||
use crate::{config, spawn_future};
|
||||
use crate::{config, spawn_future, ForgetMe};
|
||||
use futures::channel::mpsc;
|
||||
use log::*;
|
||||
use nym_bandwidth_controller::BandwidthController;
|
||||
use nym_client_core_config_types::ForgetMe;
|
||||
use nym_client_core_gateways_storage::{GatewayDetails, GatewaysDetailsStore};
|
||||
use nym_credential_storage::storage::Storage as CredentialStorage;
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
@@ -177,8 +175,8 @@ impl From<bool> for CredentialsToggle {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BaseClientBuilder<C, S: MixnetClientStorage> {
|
||||
config: Config,
|
||||
pub struct BaseClientBuilder<'a, C, S: MixnetClientStorage> {
|
||||
config: &'a Config,
|
||||
client_store: S,
|
||||
dkg_query_client: Option<C>,
|
||||
|
||||
@@ -192,18 +190,20 @@ pub struct BaseClientBuilder<C, S: MixnetClientStorage> {
|
||||
|
||||
#[cfg(unix)]
|
||||
connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
|
||||
|
||||
forget_me: ForgetMe,
|
||||
}
|
||||
|
||||
impl<C, S> BaseClientBuilder<C, S>
|
||||
impl<'a, C, S> BaseClientBuilder<'a, C, S>
|
||||
where
|
||||
S: MixnetClientStorage + 'static,
|
||||
C: DkgQueryClient + Send + Sync + 'static,
|
||||
{
|
||||
pub fn new(
|
||||
base_config: Config,
|
||||
base_config: &'a Config,
|
||||
client_store: S,
|
||||
dkg_query_client: Option<C>,
|
||||
) -> BaseClientBuilder<C, S> {
|
||||
) -> BaseClientBuilder<'a, C, S> {
|
||||
BaseClientBuilder {
|
||||
config: base_config,
|
||||
client_store,
|
||||
@@ -216,12 +216,13 @@ where
|
||||
setup_method: GatewaySetup::MustLoad { gateway_id: None },
|
||||
#[cfg(unix)]
|
||||
connection_fd_callback: None,
|
||||
forget_me: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_forget_me(mut self, forget_me: &ForgetMe) -> Self {
|
||||
self.config.debug.forget_me = *forget_me;
|
||||
self.forget_me = forget_me.clone();
|
||||
self
|
||||
}
|
||||
|
||||
@@ -644,12 +645,13 @@ where
|
||||
fn start_mix_traffic_controller(
|
||||
gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
|
||||
shutdown: TaskClient,
|
||||
) -> (BatchMixMessageSender, ClientRequestSender) {
|
||||
forget_me: ForgetMe,
|
||||
) -> BatchMixMessageSender {
|
||||
info!("Starting mix traffic controller...");
|
||||
let (mix_traffic_controller, mix_tx, client_tx) =
|
||||
MixTrafficController::new(gateway_transceiver);
|
||||
let (mix_traffic_controller, mix_tx) =
|
||||
MixTrafficController::new(gateway_transceiver, forget_me);
|
||||
mix_traffic_controller.start_with_shutdown(shutdown);
|
||||
(mix_tx, client_tx)
|
||||
mix_tx
|
||||
}
|
||||
|
||||
// TODO: rename it as it implies the data is persistent whilst one can use InMemBackend
|
||||
@@ -771,7 +773,7 @@ where
|
||||
);
|
||||
|
||||
let stats_reporter = Self::start_statistics_control(
|
||||
&self.config,
|
||||
self.config,
|
||||
self.user_agent.clone(),
|
||||
generate_client_stats_id(*self_address.identity()),
|
||||
input_sender.clone(),
|
||||
@@ -797,7 +799,7 @@ where
|
||||
|
||||
let gateway_transceiver = Self::setup_gateway_transceiver(
|
||||
self.custom_gateway_transceiver,
|
||||
&self.config,
|
||||
self.config,
|
||||
init_res,
|
||||
bandwidth_controller,
|
||||
&details_store,
|
||||
@@ -831,9 +833,10 @@ where
|
||||
// traffic stream.
|
||||
// The MixTrafficController then sends the actual traffic
|
||||
|
||||
let (message_sender, client_request_sender) = Self::start_mix_traffic_controller(
|
||||
let message_sender = Self::start_mix_traffic_controller(
|
||||
gateway_transceiver,
|
||||
shutdown.fork("mix_traffic_controller"),
|
||||
self.forget_me,
|
||||
);
|
||||
|
||||
// Channels that the websocket listener can use to signal downstream to the real traffic
|
||||
@@ -908,8 +911,6 @@ where
|
||||
},
|
||||
stats_reporter,
|
||||
task_handle: shutdown,
|
||||
client_request_sender,
|
||||
forget_me: self.config.debug.forget_me,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -921,7 +922,6 @@ pub struct BaseClient {
|
||||
pub client_output: ClientOutputStatus,
|
||||
pub client_state: ClientState,
|
||||
pub stats_reporter: ClientStatsSender,
|
||||
pub client_request_sender: ClientRequestSender,
|
||||
|
||||
pub task_handle: TaskHandle,
|
||||
pub forget_me: ForgetMe,
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
// TODO: combine those more closely. Perhaps into a single underlying store.
|
||||
// Like for persistent, on-disk, storage, what's the point of having 3 different databases?
|
||||
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
use crate::client::key_manager::persistence::{InMemEphemeralKeys, KeyStore};
|
||||
use crate::client::replies::reply_storage;
|
||||
use crate::client::replies::reply_storage::ReplyStorageBackend;
|
||||
@@ -65,7 +63,7 @@ pub trait MixnetClientStorage {
|
||||
fn gateway_details_store(&self) -> &Self::GatewaysDetailsStore;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Default)]
|
||||
pub struct Ephemeral {
|
||||
key_store: InMemEphemeralKeys,
|
||||
reply_store: reply_storage::Empty,
|
||||
@@ -73,14 +71,9 @@ pub struct Ephemeral {
|
||||
gateway_details_store: InMemGatewaysDetails,
|
||||
}
|
||||
|
||||
impl Default for Ephemeral {
|
||||
fn default() -> Self {
|
||||
Ephemeral {
|
||||
key_store: InMemEphemeralKeys::new(&mut OsRng),
|
||||
reply_store: Default::default(),
|
||||
credential_store: Default::default(),
|
||||
gateway_details_store: Default::default(),
|
||||
}
|
||||
impl Ephemeral {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +114,6 @@ impl MixnetClientStorage for Ephemeral {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[cfg(all(
|
||||
not(target_arch = "wasm32"),
|
||||
feature = "fs-surb-storage",
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
|
||||
use crate::client::key_manager::ClientKeys;
|
||||
use async_trait::async_trait;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use std::error::Error;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
@@ -66,7 +64,6 @@ pub enum OnDiskKeysError {
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub struct OnDiskKeys {
|
||||
paths: ClientKeysPaths,
|
||||
@@ -196,20 +193,9 @@ impl KeyStore for OnDiskKeys {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Default)]
|
||||
pub struct InMemEphemeralKeys {
|
||||
keys: Arc<Mutex<ClientKeys>>,
|
||||
}
|
||||
|
||||
impl InMemEphemeralKeys {
|
||||
pub fn new<R>(rng: &mut R) -> Self
|
||||
where
|
||||
R: RngCore + CryptoRng,
|
||||
{
|
||||
InMemEphemeralKeys {
|
||||
keys: Arc::new(Mutex::new(ClientKeys::generate_new(rng))),
|
||||
}
|
||||
}
|
||||
keys: Mutex<Option<ClientKeys>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
@@ -222,11 +208,11 @@ impl KeyStore for InMemEphemeralKeys {
|
||||
type StorageError = EphemeralKeysError;
|
||||
|
||||
async fn load_keys(&self) -> Result<ClientKeys, Self::StorageError> {
|
||||
Ok(self.keys.lock().await.clone())
|
||||
self.keys.lock().await.clone().ok_or(EphemeralKeysError)
|
||||
}
|
||||
|
||||
async fn store_keys(&self, keys: &ClientKeys) -> Result<(), Self::StorageError> {
|
||||
*self.keys.lock().await = keys.clone();
|
||||
*self.keys.lock().await = Some(keys.clone());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,17 +2,13 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::mix_traffic::transceiver::GatewayTransceiver;
|
||||
use crate::error::ClientCoreError;
|
||||
use crate::spawn_future;
|
||||
use crate::{spawn_future, ForgetMe};
|
||||
use log::*;
|
||||
use nym_gateway_requests::ClientRequest;
|
||||
use nym_sphinx::forwarding::packet::MixPacket;
|
||||
use transceiver::ErasedGatewayError;
|
||||
|
||||
pub type BatchMixMessageSender = tokio::sync::mpsc::Sender<Vec<MixPacket>>;
|
||||
pub type BatchMixMessageReceiver = tokio::sync::mpsc::Receiver<Vec<MixPacket>>;
|
||||
pub type ClientRequestReceiver = tokio::sync::mpsc::Receiver<ClientRequest>;
|
||||
pub type ClientRequestSender = tokio::sync::mpsc::Sender<ClientRequest>;
|
||||
|
||||
pub mod transceiver;
|
||||
|
||||
@@ -27,67 +23,52 @@ pub struct MixTrafficController {
|
||||
gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
|
||||
|
||||
mix_rx: BatchMixMessageReceiver,
|
||||
client_rx: ClientRequestReceiver,
|
||||
|
||||
// TODO: this is temporary work-around.
|
||||
// in long run `gateway_client` will be moved away from `MixTrafficController` anyway.
|
||||
consecutive_gateway_failure_count: usize,
|
||||
forget_me: ForgetMe,
|
||||
}
|
||||
|
||||
impl MixTrafficController {
|
||||
pub fn new<T>(
|
||||
gateway_transceiver: T,
|
||||
) -> (
|
||||
MixTrafficController,
|
||||
BatchMixMessageSender,
|
||||
ClientRequestSender,
|
||||
)
|
||||
forget_me: ForgetMe,
|
||||
) -> (MixTrafficController, BatchMixMessageSender)
|
||||
where
|
||||
T: GatewayTransceiver + Send + 'static,
|
||||
{
|
||||
let (message_sender, message_receiver) =
|
||||
tokio::sync::mpsc::channel(MIX_MESSAGE_RECEIVER_BUFFER_SIZE);
|
||||
|
||||
let (client_sender, client_receiver) = tokio::sync::mpsc::channel(1);
|
||||
|
||||
(
|
||||
MixTrafficController {
|
||||
gateway_transceiver: Box::new(gateway_transceiver),
|
||||
mix_rx: message_receiver,
|
||||
client_rx: client_receiver,
|
||||
consecutive_gateway_failure_count: 0,
|
||||
forget_me,
|
||||
},
|
||||
message_sender,
|
||||
client_sender,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_dynamic(
|
||||
gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
|
||||
) -> (
|
||||
MixTrafficController,
|
||||
BatchMixMessageSender,
|
||||
ClientRequestSender,
|
||||
) {
|
||||
forget_me: ForgetMe,
|
||||
) -> (MixTrafficController, BatchMixMessageSender) {
|
||||
let (message_sender, message_receiver) =
|
||||
tokio::sync::mpsc::channel(MIX_MESSAGE_RECEIVER_BUFFER_SIZE);
|
||||
let (client_sender, client_receiver) = tokio::sync::mpsc::channel(1);
|
||||
(
|
||||
MixTrafficController {
|
||||
gateway_transceiver,
|
||||
mix_rx: message_receiver,
|
||||
client_rx: client_receiver,
|
||||
consecutive_gateway_failure_count: 0,
|
||||
forget_me,
|
||||
},
|
||||
message_sender,
|
||||
client_sender,
|
||||
)
|
||||
}
|
||||
|
||||
async fn on_messages(
|
||||
&mut self,
|
||||
mut mix_packets: Vec<MixPacket>,
|
||||
) -> Result<(), ErasedGatewayError> {
|
||||
async fn on_messages(&mut self, mut mix_packets: Vec<MixPacket>) {
|
||||
debug_assert!(!mix_packets.is_empty());
|
||||
|
||||
let result = if mix_packets.len() == 1 {
|
||||
@@ -99,14 +80,21 @@ impl MixTrafficController {
|
||||
.await
|
||||
};
|
||||
|
||||
if result.is_err() {
|
||||
self.consecutive_gateway_failure_count += 1;
|
||||
} else {
|
||||
trace!("We *might* have managed to forward sphinx packet(s) to the gateway!");
|
||||
self.consecutive_gateway_failure_count = 0;
|
||||
match result {
|
||||
Err(err) => {
|
||||
error!("Failed to send sphinx packet(s) to the gateway: {err}");
|
||||
self.consecutive_gateway_failure_count += 1;
|
||||
if self.consecutive_gateway_failure_count == MAX_FAILURE_COUNT {
|
||||
// todo: in the future this should initiate a 'graceful' shutdown or try
|
||||
// to reconnect?
|
||||
panic!("failed to send sphinx packet to the gateway {MAX_FAILURE_COUNT} times in a row - assuming the gateway is dead. Can't do anything about it yet :(")
|
||||
}
|
||||
}
|
||||
Ok(_) => {
|
||||
trace!("We *might* have managed to forward sphinx packet(s) to the gateway!");
|
||||
self.consecutive_gateway_failure_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn start_with_shutdown(mut self, mut shutdown: nym_task::TaskClient) {
|
||||
@@ -117,35 +105,13 @@ impl MixTrafficController {
|
||||
tokio::select! {
|
||||
mix_packets = self.mix_rx.recv() => match mix_packets {
|
||||
Some(mix_packets) => {
|
||||
if let Err(err) = self.on_messages(mix_packets).await {
|
||||
error!("Failed to send sphinx packet(s) to the gateway: {err}");
|
||||
if self.consecutive_gateway_failure_count == MAX_FAILURE_COUNT {
|
||||
// Disconnect from the gateway. If we should try to re-connect
|
||||
// is handled at a higher layer.
|
||||
error!("Failed to send sphinx packet to the gateway {MAX_FAILURE_COUNT} times in a row - assuming the gateway is dead");
|
||||
// Do we need to handle the embedded mixnet client case
|
||||
// separately?
|
||||
shutdown.send_we_stopped(Box::new(ClientCoreError::GatewayFailedToForwardMessages));
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.on_messages(mix_packets).await;
|
||||
},
|
||||
None => {
|
||||
log::trace!("MixTrafficController: Stopping since channel closed");
|
||||
break;
|
||||
}
|
||||
},
|
||||
client_request = self.client_rx.recv() => match client_request {
|
||||
Some(client_request) => {
|
||||
match self.gateway_transceiver.send_client_request(client_request).await {
|
||||
Ok(_) => (),
|
||||
Err(e) => error!("Failed to send client request: {}", e),
|
||||
};
|
||||
},
|
||||
None => {
|
||||
log::trace!("MixTrafficController, client request channel closed");
|
||||
}
|
||||
},
|
||||
_ = shutdown.recv_with_delay() => {
|
||||
log::trace!("MixTrafficController: Received shutdown");
|
||||
break;
|
||||
@@ -154,6 +120,25 @@ impl MixTrafficController {
|
||||
}
|
||||
shutdown.recv_timeout().await;
|
||||
|
||||
if self.forget_me.any() {
|
||||
log::info!("Sending forget me request to the gateway");
|
||||
match self
|
||||
.gateway_transceiver
|
||||
.send_client_request(ClientRequest::ForgetMe {
|
||||
client: self.forget_me.client(),
|
||||
stats: self.forget_me.stats(),
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
log::info!("Successfully sent forget me request to the gateway");
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!("Failed to send forget me request to the gateway: {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log::debug!("MixTrafficController: Exiting");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -86,9 +86,7 @@ impl<G: GatewayTransceiver + ?Sized + Send> GatewayTransceiver for Box<G> {
|
||||
&mut self,
|
||||
message: ClientRequest,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
let _ = (**self).send_client_request(message.clone()).await?;
|
||||
log::debug!("Sent client request: {:?}", message);
|
||||
Ok(())
|
||||
(**self).send_client_request(message).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,7 +143,14 @@ where
|
||||
&mut self,
|
||||
message: ClientRequest,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
self.gateway_client.send_client_request(message).await
|
||||
if let Some(shared_key) = self.gateway_client.shared_key() {
|
||||
self.gateway_client
|
||||
.send_websocket_message(message.encrypt(&*shared_key)?)
|
||||
.await?;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(GatewayClientError::ConnectionInInvalidState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -626,14 +626,9 @@ where
|
||||
messages: Vec<RealMessage>,
|
||||
transmission_lane: TransmissionLane,
|
||||
) {
|
||||
if let Err(err) = self
|
||||
.real_message_sender
|
||||
self.real_message_sender
|
||||
.send((messages, transmission_lane))
|
||||
.await
|
||||
{
|
||||
error!(
|
||||
"Failed to forward messages to the real message sender (OutQueueControl): {err}"
|
||||
);
|
||||
}
|
||||
.expect("real message receiver task (OutQueueControl) has died");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -545,7 +545,7 @@ where
|
||||
loop {
|
||||
tokio::select! {
|
||||
biased;
|
||||
_ = shutdown.recv() => {
|
||||
_ = shutdown.recv_with_delay() => {
|
||||
log::trace!("OutQueueControl: Received shutdown");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -123,11 +123,6 @@ impl StatisticsControl {
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
biased;
|
||||
_ = task_client.recv() => {
|
||||
log::trace!("StatisticsControl: Received shutdown");
|
||||
break;
|
||||
},
|
||||
stats_event = self.stats_rx.recv() => match stats_event {
|
||||
Some(stats_event) => self.stats.handle_event(stats_event),
|
||||
None => {
|
||||
@@ -151,8 +146,13 @@ impl StatisticsControl {
|
||||
_ = local_report_interval.next() => {
|
||||
self.stats.local_report(&mut task_client);
|
||||
}
|
||||
_ = task_client.recv_with_delay() => {
|
||||
log::trace!("StatisticsControl: Received shutdown");
|
||||
break;
|
||||
},
|
||||
}
|
||||
}
|
||||
task_client.recv_timeout().await;
|
||||
log::debug!("StatisticsControl: Exiting");
|
||||
}
|
||||
|
||||
|
||||
@@ -36,13 +36,6 @@ pub enum ClientCoreError {
|
||||
#[error("no gateway with id: {0}")]
|
||||
NoGatewayWithId(String),
|
||||
|
||||
#[error("Invalid URL: {0}")]
|
||||
InvalidUrl(String),
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[error("resolution failed: {0}")]
|
||||
ResolutionFailed(#[from] nym_http_api_client::HickoryDnsError),
|
||||
|
||||
#[error("no gateways on network")]
|
||||
NoGatewaysOnNetwork,
|
||||
|
||||
@@ -103,9 +96,6 @@ pub enum ClientCoreError {
|
||||
#[error("timed out while trying to establish gateway connection")]
|
||||
GatewayConnectionTimeout,
|
||||
|
||||
#[error("failed to forward mix messages to gateway")]
|
||||
GatewayFailedToForwardMessages,
|
||||
|
||||
#[error("no ping measurements for the gateway ({identity}) performed")]
|
||||
NoGatewayMeasurements { identity: String },
|
||||
|
||||
|
||||
@@ -15,9 +15,6 @@ use std::{sync::Arc, time::Duration};
|
||||
use tungstenite::Message;
|
||||
use url::Url;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use crate::init::websockets::connect_async;
|
||||
|
||||
use nym_topology::NodeId;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio::net::TcpStream;
|
||||
@@ -26,6 +23,8 @@ use tokio::time::sleep;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio::time::Instant;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio_tungstenite::connect_async;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_utils::websocket::JSWebsocket;
|
||||
@@ -87,12 +86,11 @@ impl<'a, G: ConnectableGateway> GatewayWithLatency<'a, G> {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn gateways_for_init<R: Rng>(
|
||||
pub async fn current_gateways<R: Rng>(
|
||||
rng: &mut R,
|
||||
nym_apis: &[Url],
|
||||
user_agent: Option<UserAgent>,
|
||||
minimum_performance: u8,
|
||||
ignore_epoch_roles: bool,
|
||||
) -> Result<Vec<RoutingNode>, ClientCoreError> {
|
||||
let nym_api = nym_apis
|
||||
.choose(rng)
|
||||
@@ -110,11 +108,8 @@ pub async fn gateways_for_init<R: Rng>(
|
||||
|
||||
log::trace!("Gateways: {:#?}", gateways);
|
||||
|
||||
// filter out gateways below minimum performance and ones that could operate as a mixnode
|
||||
// (we don't want instability)
|
||||
let valid_gateways = gateways
|
||||
.iter()
|
||||
.filter(|g| ignore_epoch_roles || !g.supported_roles.mixnode)
|
||||
.filter(|g| g.performance.round_to_integer() >= minimum_performance)
|
||||
.filter_map(|gateway| gateway.try_into().ok())
|
||||
.collect::<Vec<_>>();
|
||||
@@ -133,7 +128,7 @@ pub async fn gateways_for_init<R: Rng>(
|
||||
async fn connect(endpoint: &str) -> Result<WsConn, ClientCoreError> {
|
||||
match tokio::time::timeout(CONN_TIMEOUT, connect_async(endpoint)).await {
|
||||
Err(_elapsed) => Err(ClientCoreError::GatewayConnectionTimeout),
|
||||
Ok(Err(conn_failure)) => Err(conn_failure),
|
||||
Ok(Err(conn_failure)) => Err(conn_failure.into()),
|
||||
Ok(Ok((stream, _))) => Ok(stream),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,8 +26,6 @@ use serde::Serialize;
|
||||
|
||||
pub mod helpers;
|
||||
pub mod types;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(crate) mod websockets;
|
||||
|
||||
// helpers for error wrapping
|
||||
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
use crate::error::ClientCoreError;
|
||||
|
||||
use nym_http_api_client::HickoryDnsResolver;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
|
||||
use tungstenite::handshake::client::Response;
|
||||
use url::{Host, Url};
|
||||
|
||||
use std::net::SocketAddr;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(crate) async fn connect_async(
|
||||
endpoint: &str,
|
||||
) -> Result<(WebSocketStream<MaybeTlsStream<TcpStream>>, Response), ClientCoreError> {
|
||||
let resolver = HickoryDnsResolver::default();
|
||||
let uri = Url::parse(endpoint).map_err(|_| ClientCoreError::InvalidUrl(endpoint.to_owned()))?;
|
||||
let port: u16 = uri.port_or_known_default().unwrap_or(443);
|
||||
|
||||
let host = uri
|
||||
.host()
|
||||
.ok_or(ClientCoreError::InvalidUrl(endpoint.to_owned()))?;
|
||||
|
||||
// Get address for tcp connection, if a domain is provided use our preferred resolver rather than
|
||||
// the default std resolve
|
||||
let sock_addrs: Vec<SocketAddr> = match host {
|
||||
Host::Ipv4(addr) => vec![SocketAddr::new(addr.into(), port)],
|
||||
Host::Ipv6(addr) => vec![SocketAddr::new(addr.into(), port)],
|
||||
Host::Domain(domain) => {
|
||||
// Do a DNS lookup for the domain using our custom DNS resolver
|
||||
resolver
|
||||
.resolve_str(domain)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|a| SocketAddr::new(a, port))
|
||||
.collect()
|
||||
}
|
||||
};
|
||||
|
||||
let stream = TcpStream::connect(&sock_addrs[..]).await?;
|
||||
|
||||
tokio_tungstenite::client_async_tls(endpoint, stream)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
@@ -33,3 +33,48 @@ where
|
||||
{
|
||||
tokio::spawn(future);
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub struct ForgetMe {
|
||||
client: bool,
|
||||
stats: bool,
|
||||
}
|
||||
|
||||
impl ForgetMe {
|
||||
pub fn new_all() -> Self {
|
||||
Self {
|
||||
client: true,
|
||||
stats: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_client() -> Self {
|
||||
Self {
|
||||
client: true,
|
||||
stats: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_stats() -> Self {
|
||||
Self {
|
||||
client: false,
|
||||
stats: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(client: bool, stats: bool) -> Self {
|
||||
Self { client, stats }
|
||||
}
|
||||
|
||||
pub fn any(&self) -> bool {
|
||||
self.client || self.stats
|
||||
}
|
||||
|
||||
pub fn client(&self) -> bool {
|
||||
self.client
|
||||
}
|
||||
|
||||
pub fn stats(&self) -> bool {
|
||||
self.stats
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ mod error;
|
||||
mod manager;
|
||||
mod models;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Debug)]
|
||||
pub struct Backend {
|
||||
temporary_old_path: Option<PathBuf>,
|
||||
database_path: PathBuf,
|
||||
|
||||
@@ -19,7 +19,7 @@ pub mod fs_backend;
|
||||
#[error("no information provided")]
|
||||
pub struct UndefinedError;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Debug)]
|
||||
pub struct Empty {
|
||||
// we need to keep 'basic' metadata here to "load" the CombinedReplyStorage
|
||||
pub min_surb_threshold: usize,
|
||||
|
||||
@@ -27,7 +27,6 @@ nym-credential-storage = { path = "../../credential-storage" }
|
||||
nym-credentials-interface = { path = "../../credentials-interface" }
|
||||
nym-crypto = { path = "../../crypto" }
|
||||
nym-gateway-requests = { path = "../../gateway-requests" }
|
||||
nym-http-api-client = { path = "../../http-api-client" }
|
||||
nym-network-defaults = { path = "../../network-defaults" }
|
||||
nym-sphinx = { path = "../../nymsphinx" }
|
||||
nym-statistics-common = { path = "../../statistics" }
|
||||
|
||||
@@ -40,6 +40,8 @@ use url::Url;
|
||||
use std::os::fd::RawFd;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio::time::sleep;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio_tungstenite::connect_async;
|
||||
|
||||
#[cfg(not(unix))]
|
||||
use std::os::raw::c_int as RawFd;
|
||||
@@ -51,11 +53,6 @@ use zeroize::Zeroizing;
|
||||
|
||||
pub mod config;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(crate) mod websockets;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use websockets::connect_async;
|
||||
|
||||
pub struct GatewayConfig {
|
||||
pub gateway_identity: identity::PublicKey,
|
||||
|
||||
@@ -204,7 +201,15 @@ impl<C, St> GatewayClient<C, St> {
|
||||
"Attemting to establish connection to gateway at: {}",
|
||||
self.gateway_address
|
||||
);
|
||||
let (ws_stream, _) = connect_async(&self.gateway_address).await?;
|
||||
let ws_stream = match connect_async(&self.gateway_address).await {
|
||||
Ok((ws_stream, _)) => ws_stream,
|
||||
Err(error) => {
|
||||
return Err(GatewayClientError::NetworkConnectionFailed {
|
||||
address: self.gateway_address.clone(),
|
||||
source: error,
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
self.connection = SocketState::Available(Box::new(ws_stream));
|
||||
|
||||
@@ -266,19 +271,6 @@ impl<C, St> GatewayClient<C, St> {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send_client_request(
|
||||
&mut self,
|
||||
message: ClientRequest,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
if let Some(shared_key) = self.shared_key() {
|
||||
let encrypted = message.encrypt(&*shared_key)?;
|
||||
Box::pin(self.send_websocket_message(encrypted)).await?;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(GatewayClientError::ConnectionInInvalidState)
|
||||
}
|
||||
}
|
||||
|
||||
async fn read_control_response(&mut self) -> Result<ServerResponse, GatewayClientError> {
|
||||
// we use the fact that all request responses are Message::Text and only pushed
|
||||
// sphinx packets are Message::Binary
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
use crate::error::GatewayClientError;
|
||||
|
||||
use nym_http_api_client::HickoryDnsResolver;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
|
||||
use tungstenite::handshake::client::Response;
|
||||
use url::{Host, Url};
|
||||
|
||||
use std::net::SocketAddr;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(crate) async fn connect_async(
|
||||
endpoint: &str,
|
||||
) -> Result<(WebSocketStream<MaybeTlsStream<TcpStream>>, Response), GatewayClientError> {
|
||||
let resolver = HickoryDnsResolver::default();
|
||||
let uri =
|
||||
Url::parse(endpoint).map_err(|_| GatewayClientError::InvalidUrl(endpoint.to_owned()))?;
|
||||
let port: u16 = uri.port_or_known_default().unwrap_or(443);
|
||||
|
||||
let host = uri
|
||||
.host()
|
||||
.ok_or(GatewayClientError::InvalidUrl(endpoint.to_owned()))?;
|
||||
|
||||
// Get address for tcp connection, if a domain is provided use our preferred resolver rather than
|
||||
// the default std resolve
|
||||
let sock_addrs: Vec<SocketAddr> = match host {
|
||||
Host::Ipv4(addr) => vec![SocketAddr::new(addr.into(), port)],
|
||||
Host::Ipv6(addr) => vec![SocketAddr::new(addr.into(), port)],
|
||||
Host::Domain(domain) => {
|
||||
// Do a DNS lookup for the domain using our custom DNS resolver
|
||||
resolver
|
||||
.resolve_str(domain)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|a| SocketAddr::new(a, port))
|
||||
.collect()
|
||||
}
|
||||
};
|
||||
|
||||
let stream = TcpStream::connect(&sock_addrs[..]).await.map_err(|error| {
|
||||
GatewayClientError::NetworkConnectionFailed {
|
||||
address: endpoint.to_owned(),
|
||||
source: error.into(),
|
||||
}
|
||||
})?;
|
||||
|
||||
tokio_tungstenite::client_async_tls(endpoint, stream)
|
||||
.await
|
||||
.map_err(|error| GatewayClientError::NetworkConnectionFailed {
|
||||
address: endpoint.to_owned(),
|
||||
source: error,
|
||||
})
|
||||
}
|
||||
@@ -44,11 +44,7 @@ pub enum GatewayClientError {
|
||||
NetworkConnectionFailed { address: String, source: WsError },
|
||||
|
||||
#[error("Invalid URL: {0}")]
|
||||
InvalidUrl(String),
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[error("resolution failed: {0}")]
|
||||
ResolutionFailed(#[from] nym_http_api_client::HickoryDnsError),
|
||||
InvalidURL(String),
|
||||
|
||||
#[error("No shared key was provided or obtained")]
|
||||
NoSharedKeyAvailable,
|
||||
|
||||
@@ -11,16 +11,16 @@ use crate::{
|
||||
use nym_api_requests::ecash::models::{
|
||||
AggregatedCoinIndicesSignatureResponse, AggregatedExpirationDateSignatureResponse,
|
||||
BatchRedeemTicketsBody, EcashBatchTicketRedemptionResponse, EcashTicketVerificationResponse,
|
||||
IssuedTicketbooksChallengeResponse, IssuedTicketbooksForResponse, VerifyEcashTicketBody,
|
||||
IssuedTicketbooksChallengeResponse, IssuedTicketbooksForResponse, SpentCredentialsResponse,
|
||||
VerifyEcashTicketBody,
|
||||
};
|
||||
use nym_api_requests::ecash::{
|
||||
BlindSignRequestBody, BlindedSignatureResponse, PartialCoinIndicesSignatureResponse,
|
||||
PartialExpirationDateSignatureResponse, VerificationKeyResponse,
|
||||
};
|
||||
use nym_api_requests::models::{
|
||||
ApiHealthResponse, GatewayBondAnnotated, GatewayCoreStatusResponse,
|
||||
HistoricalPerformanceResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse,
|
||||
NymNodeDescription, RewardEstimationResponse, StakeSaturationResponse,
|
||||
ApiHealthResponse, GatewayBondAnnotated, GatewayCoreStatusResponse, MixnodeCoreStatusResponse,
|
||||
MixnodeStatusResponse, NymNodeDescription, RewardEstimationResponse, StakeSaturationResponse,
|
||||
};
|
||||
use nym_api_requests::models::{LegacyDescribedGateway, MixNodeBondAnnotated};
|
||||
use nym_api_requests::nym_nodes::SkimmedNode;
|
||||
@@ -264,31 +264,6 @@ impl<C, S> Client<C, S> {
|
||||
Ok(self.nym_api.get_gateways_detailed_unfiltered().await?)
|
||||
}
|
||||
|
||||
pub async fn get_full_node_performance_history(
|
||||
&self,
|
||||
node_id: NodeId,
|
||||
) -> Result<Vec<HistoricalPerformanceResponse>, ValidatorClientError> {
|
||||
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
|
||||
let mut page = 0;
|
||||
let mut history = Vec::new();
|
||||
|
||||
loop {
|
||||
let mut res = self
|
||||
.nym_api
|
||||
.get_node_performance_history(node_id, Some(page), None)
|
||||
.await?;
|
||||
|
||||
history.append(&mut res.history.data);
|
||||
if history.len() < res.history.pagination.total {
|
||||
page += 1
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(history)
|
||||
}
|
||||
|
||||
// TODO: combine with NymApiClient...
|
||||
pub async fn get_all_cached_described_nodes(
|
||||
&self,
|
||||
@@ -646,6 +621,13 @@ impl NymApiClient {
|
||||
.await?)
|
||||
}
|
||||
|
||||
#[deprecated]
|
||||
pub async fn spent_credentials_filter(
|
||||
&self,
|
||||
) -> Result<SpentCredentialsResponse, ValidatorClientError> {
|
||||
Ok(self.nym_api.double_spending_filter_v1().await?)
|
||||
}
|
||||
|
||||
pub async fn partial_expiration_date_signatures(
|
||||
&self,
|
||||
expiration_date: Option<Date>,
|
||||
|
||||
@@ -65,12 +65,6 @@ pub enum EcashApiError {
|
||||
#[from]
|
||||
source: cosmrs::ErrorReport,
|
||||
},
|
||||
|
||||
#[error("nym api error")]
|
||||
NymApi {
|
||||
#[from]
|
||||
source: crate::ValidatorClientError,
|
||||
},
|
||||
}
|
||||
|
||||
impl TryFrom<ContractVKShare> for EcashApiClient {
|
||||
|
||||
@@ -13,7 +13,7 @@ use nym_api_requests::ecash::models::{
|
||||
use nym_api_requests::ecash::VerificationKeyResponse;
|
||||
use nym_api_requests::models::{
|
||||
AnnotationResponse, ApiHealthResponse, LegacyDescribedMixNode, NodePerformanceResponse,
|
||||
NodeRefreshBody, NymNodeDescription, PerformanceHistoryResponse, RewardedSetResponse,
|
||||
NodeRefreshBody, NymNodeDescription, RewardedSetResponse,
|
||||
};
|
||||
use nym_api_requests::nym_nodes::PaginatedCachedNodesResponse;
|
||||
use nym_api_requests::pagination::PaginatedResponse;
|
||||
@@ -31,7 +31,6 @@ pub use nym_api_requests::{
|
||||
StakeSaturationResponse, UptimeResponse,
|
||||
},
|
||||
nym_nodes::{CachedNodesResponse, SkimmedNode},
|
||||
NymNetworkDetailsResponse,
|
||||
};
|
||||
pub use nym_coconut_dkg_common::types::EpochId;
|
||||
use nym_contracts_common::IdentityKey;
|
||||
@@ -164,35 +163,6 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all)]
|
||||
async fn get_node_performance_history(
|
||||
&self,
|
||||
node_id: NodeId,
|
||||
page: Option<u32>,
|
||||
per_page: Option<u32>,
|
||||
) -> Result<PerformanceHistoryResponse, NymAPIError> {
|
||||
let mut params = Vec::new();
|
||||
|
||||
if let Some(page) = page {
|
||||
params.push(("page", page.to_string()))
|
||||
}
|
||||
|
||||
if let Some(per_page) = per_page {
|
||||
params.push(("per_page", per_page.to_string()))
|
||||
}
|
||||
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::NYM_NODES_ROUTES,
|
||||
routes::NYM_NODES_PERFORMANCE_HISTORY,
|
||||
&*node_id.to_string(),
|
||||
],
|
||||
¶ms,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all)]
|
||||
async fn get_nodes_described(
|
||||
&self,
|
||||
@@ -209,15 +179,8 @@ pub trait NymApiClientExt: ApiClient {
|
||||
params.push(("per_page", per_page.to_string()))
|
||||
}
|
||||
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::NYM_NODES_ROUTES,
|
||||
routes::NYM_NODES_DESCRIBED,
|
||||
],
|
||||
¶ms,
|
||||
)
|
||||
.await
|
||||
self.get_json(&[routes::API_VERSION, "nym-nodes", "described"], ¶ms)
|
||||
.await
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all)]
|
||||
@@ -236,15 +199,8 @@ pub trait NymApiClientExt: ApiClient {
|
||||
params.push(("per_page", per_page.to_string()))
|
||||
}
|
||||
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::NYM_NODES_ROUTES,
|
||||
routes::NYM_NODES_BONDED,
|
||||
],
|
||||
¶ms,
|
||||
)
|
||||
.await
|
||||
self.get_json(&[routes::API_VERSION, "nym-nodes", "bonded"], ¶ms)
|
||||
.await
|
||||
}
|
||||
|
||||
#[deprecated]
|
||||
@@ -254,7 +210,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
"unstable",
|
||||
routes::NYM_NODES_ROUTES,
|
||||
"nym-nodes",
|
||||
"mixnodes",
|
||||
"skimmed",
|
||||
],
|
||||
@@ -270,7 +226,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
"unstable",
|
||||
routes::NYM_NODES_ROUTES,
|
||||
"nym-nodes",
|
||||
"gateways",
|
||||
"skimmed",
|
||||
],
|
||||
@@ -282,11 +238,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_rewarded_set(&self) -> Result<RewardedSetResponse, NymAPIError> {
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::NYM_NODES_ROUTES,
|
||||
routes::NYM_NODES_REWARDED_SET,
|
||||
],
|
||||
&[routes::API_VERSION, "nym-nodes", "rewarded-set"],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
@@ -319,7 +271,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
"unstable",
|
||||
routes::NYM_NODES_ROUTES,
|
||||
"nym-nodes",
|
||||
"skimmed",
|
||||
"entry-gateways",
|
||||
"all",
|
||||
@@ -356,7 +308,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
"unstable",
|
||||
routes::NYM_NODES_ROUTES,
|
||||
"nym-nodes",
|
||||
"skimmed",
|
||||
"mixnodes",
|
||||
"active",
|
||||
@@ -393,7 +345,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
"unstable",
|
||||
routes::NYM_NODES_ROUTES,
|
||||
"nym-nodes",
|
||||
"skimmed",
|
||||
"mixnodes",
|
||||
"all",
|
||||
@@ -425,12 +377,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
}
|
||||
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
"unstable",
|
||||
routes::NYM_NODES_ROUTES,
|
||||
"skimmed",
|
||||
],
|
||||
&[routes::API_VERSION, "unstable", "nym-nodes", "skimmed"],
|
||||
¶ms,
|
||||
)
|
||||
.await
|
||||
@@ -739,8 +686,8 @@ pub trait NymApiClientExt: ApiClient {
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::NYM_NODES_ROUTES,
|
||||
routes::NYM_NODES_PERFORMANCE,
|
||||
"nym-nodes",
|
||||
"performance",
|
||||
&node_id.to_string(),
|
||||
],
|
||||
NO_PARAMS,
|
||||
@@ -755,8 +702,8 @@ pub trait NymApiClientExt: ApiClient {
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::NYM_NODES_ROUTES,
|
||||
routes::NYM_NODES_ANNOTATION,
|
||||
"nym-nodes",
|
||||
"annotation",
|
||||
&node_id.to_string(),
|
||||
],
|
||||
NO_PARAMS,
|
||||
@@ -850,6 +797,20 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[deprecated]
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn double_spending_filter_v1(&self) -> Result<SpentCredentialsResponse, NymAPIError> {
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::ECASH_ROUTES,
|
||||
routes::DOUBLE_SPENDING_FILTER_V1,
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn partial_expiration_date_signatures(
|
||||
&self,
|
||||
@@ -966,11 +927,7 @@ pub trait NymApiClientExt: ApiClient {
|
||||
request: &NodeRefreshBody,
|
||||
) -> Result<(), NymAPIError> {
|
||||
self.post_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::NYM_NODES_ROUTES,
|
||||
routes::NYM_NODES_REFRESH_DESCRIBED,
|
||||
],
|
||||
&[routes::API_VERSION, "nym-nodes", "refresh-described"],
|
||||
NO_PARAMS,
|
||||
request,
|
||||
)
|
||||
@@ -1014,15 +971,6 @@ pub trait NymApiClientExt: ApiClient {
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
async fn get_network_details(&self) -> Result<NymNetworkDetailsResponse, NymAPIError> {
|
||||
self.get_json(
|
||||
&[routes::API_VERSION, routes::NETWORK, routes::DETAILS],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
|
||||
@@ -13,6 +13,8 @@ pub const DETAILED: &str = "detailed";
|
||||
pub const DETAILED_UNFILTERED: &str = "detailed-unfiltered";
|
||||
pub const ACTIVE: &str = "active";
|
||||
pub const REWARDED: &str = "rewarded";
|
||||
pub const DOUBLE_SPENDING_FILTER_V1: &str = "double-spending-filter-v1";
|
||||
|
||||
pub const ECASH_ROUTES: &str = "ecash";
|
||||
|
||||
pub use ecash::*;
|
||||
@@ -32,19 +34,6 @@ pub mod ecash {
|
||||
pub const EPOCH_ID_PARAM: &str = "epoch_id";
|
||||
}
|
||||
|
||||
pub const NYM_NODES_ROUTES: &str = "nym-nodes";
|
||||
|
||||
pub use nym_nodes::*;
|
||||
pub mod nym_nodes {
|
||||
pub const NYM_NODES_PERFORMANCE_HISTORY: &str = "performance-history";
|
||||
pub const NYM_NODES_PERFORMANCE: &str = "performance";
|
||||
pub const NYM_NODES_ANNOTATION: &str = "annotation";
|
||||
pub const NYM_NODES_DESCRIBED: &str = "described";
|
||||
pub const NYM_NODES_BONDED: &str = "bonded";
|
||||
pub const NYM_NODES_REWARDED_SET: &str = "rewarded-set";
|
||||
pub const NYM_NODES_REFRESH_DESCRIBED: &str = "refresh-described";
|
||||
}
|
||||
|
||||
pub const STATUS_ROUTES: &str = "status";
|
||||
pub const API_STATUS_ROUTES: &str = "api-status";
|
||||
pub const HEALTH: &str = "health";
|
||||
@@ -65,8 +54,6 @@ pub const STAKE_SATURATION: &str = "stake-saturation";
|
||||
pub const INCLUSION_CHANCE: &str = "inclusion-probability";
|
||||
pub const SUBMIT_GATEWAY: &str = "submit-gateway-monitoring-results";
|
||||
pub const SUBMIT_NODE: &str = "submit-node-monitoring-results";
|
||||
pub const PERFORMANCE: &str = "performance";
|
||||
|
||||
pub const SERVICE_PROVIDERS: &str = "services";
|
||||
|
||||
pub const DETAILS: &str = "details";
|
||||
pub const NETWORK: &str = "network";
|
||||
|
||||
-31
@@ -153,20 +153,13 @@ pub trait CosmWasmClient: TendermintRpcClient {
|
||||
let req = QueryAllBalancesRequest {
|
||||
address: address.to_string(),
|
||||
pagination,
|
||||
resolve_denom: false,
|
||||
};
|
||||
|
||||
let mut res = self
|
||||
.make_abci_query::<_, QueryAllBalancesResponse>(path.clone(), req)
|
||||
.await?;
|
||||
|
||||
let early_break = res.balances.is_empty();
|
||||
raw_balances.append(&mut res.balances);
|
||||
|
||||
if early_break {
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some(next_key) = next_page_key(res.pagination) {
|
||||
pagination = Some(create_pagination(next_key))
|
||||
} else {
|
||||
@@ -194,13 +187,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
|
||||
.make_abci_query::<_, QueryTotalSupplyResponse>(path.clone(), req)
|
||||
.await?;
|
||||
|
||||
let early_break = res.supply.is_empty();
|
||||
supply.append(&mut res.supply);
|
||||
|
||||
if early_break {
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some(next_key) = next_page_key(res.pagination) {
|
||||
pagination = Some(create_pagination(next_key))
|
||||
} else {
|
||||
@@ -341,13 +328,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
|
||||
.make_abci_query::<_, QueryCodesResponse>(path.clone(), req)
|
||||
.await?;
|
||||
|
||||
let early_break = res.code_infos.is_empty();
|
||||
raw_codes.append(&mut res.code_infos);
|
||||
|
||||
if early_break {
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some(next_key) = next_page_key(res.pagination) {
|
||||
pagination = Some(create_pagination(next_key))
|
||||
} else {
|
||||
@@ -392,13 +373,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
|
||||
.make_abci_query::<_, QueryContractsByCodeResponse>(path.clone(), req)
|
||||
.await?;
|
||||
|
||||
let early_break = res.contracts.is_empty();
|
||||
raw_contracts.append(&mut res.contracts);
|
||||
|
||||
if early_break {
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some(next_key) = next_page_key(res.pagination) {
|
||||
pagination = Some(create_pagination(next_key))
|
||||
} else {
|
||||
@@ -454,13 +429,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
|
||||
.make_abci_query::<_, QueryContractHistoryResponse>(path.clone(), req)
|
||||
.await?;
|
||||
|
||||
let early_break = res.entries.is_empty();
|
||||
raw_entries.append(&mut res.entries);
|
||||
|
||||
if early_break {
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some(next_key) = next_page_key(res.pagination) {
|
||||
pagination = Some(create_pagination(next_key))
|
||||
} else {
|
||||
|
||||
@@ -4,11 +4,9 @@
|
||||
use crate::rpc::TendermintRpcClient;
|
||||
use async_trait::async_trait;
|
||||
use base64::Engine;
|
||||
use cosmrs::tendermint;
|
||||
use cosmrs::tendermint::{block::Height, evidence::Evidence, Hash};
|
||||
use reqwest::header::HeaderMap;
|
||||
use reqwest::{header, RequestBuilder};
|
||||
use tendermint_rpc::dialect::{v0_34, v0_37, v0_38, LatestDialect};
|
||||
use tendermint_rpc::{
|
||||
client::CompatMode,
|
||||
dialect::{self, Dialect},
|
||||
@@ -23,21 +21,8 @@ macro_rules! perform_with_compat {
|
||||
($self:expr, $request:expr) => {{
|
||||
let request = $request;
|
||||
match $self.compat {
|
||||
CompatMode::V0_38 => {
|
||||
$self
|
||||
.perform_request_with_dialect(request, dialect::v0_38::Dialect)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_37 => {
|
||||
$self
|
||||
.perform_request_with_dialect(request, dialect::v0_37::Dialect)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_34 => {
|
||||
$self
|
||||
.perform_request_with_dialect(request, dialect::v0_34::Dialect)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_37 => $self.perform_v0_37(request).await,
|
||||
CompatMode::V0_34 => $self.perform_v0_34(request).await,
|
||||
}
|
||||
}};
|
||||
}
|
||||
@@ -85,11 +70,7 @@ impl ReqwestRpcClient {
|
||||
.headers(headers)
|
||||
}
|
||||
|
||||
async fn perform_request_with_dialect<R, S>(
|
||||
&self,
|
||||
request: R,
|
||||
_dialect: S,
|
||||
) -> Result<R::Output, Error>
|
||||
async fn perform_request<R, S>(&self, request: R) -> Result<R::Output, Error>
|
||||
where
|
||||
R: SimpleRequest<S>,
|
||||
S: Dialect,
|
||||
@@ -100,25 +81,26 @@ impl ReqwestRpcClient {
|
||||
.send()
|
||||
.await
|
||||
.map_err(TendermintRpcErrorMap::into_rpc_err)?;
|
||||
let response_status = response.status();
|
||||
let bytes = response
|
||||
.bytes()
|
||||
.await
|
||||
.map_err(TendermintRpcErrorMap::into_rpc_err)?;
|
||||
|
||||
// Successful JSON-RPC requests are expected to return a 200 OK HTTP status.
|
||||
// Otherwise, this means that the HTTP request failed as a whole,
|
||||
// as opposed to the JSON-RPC request returning an error,
|
||||
// and we cannot expect the response body to be a valid JSON-RPC response.
|
||||
if response_status != reqwest::StatusCode::OK {
|
||||
// hehe, that's so nasty but we have to somehow convert between different versions of the same lib
|
||||
return Err(Error::http_request_failed(
|
||||
response_status.as_u16().try_into().unwrap(),
|
||||
));
|
||||
}
|
||||
|
||||
R::Response::from_string(bytes).map(Into::into)
|
||||
}
|
||||
|
||||
async fn perform_v0_34<R>(&self, request: R) -> Result<R::Output, Error>
|
||||
where
|
||||
R: SimpleRequest<dialect::v0_34::Dialect>,
|
||||
{
|
||||
self.perform_request(request).await
|
||||
}
|
||||
|
||||
async fn perform_v0_37<R>(&self, request: R) -> Result<R::Output, Error>
|
||||
where
|
||||
R: SimpleRequest<dialect::v0_37::Dialect>,
|
||||
{
|
||||
self.perform_request(request).await
|
||||
}
|
||||
}
|
||||
|
||||
trait TendermintRpcErrorMap {
|
||||
@@ -138,50 +120,18 @@ impl TendermintRpcClient for ReqwestRpcClient {
|
||||
where
|
||||
R: SimpleRequest,
|
||||
{
|
||||
self.perform_request_with_dialect(request, LatestDialect)
|
||||
.await
|
||||
self.perform_request(request).await
|
||||
}
|
||||
|
||||
async fn block<H>(&self, height: H) -> Result<endpoint::block::Response, Error>
|
||||
async fn block_results<H>(&self, height: H) -> Result<block_results::Response, Error>
|
||||
where
|
||||
H: Into<Height> + Send,
|
||||
{
|
||||
perform_with_compat!(self, endpoint::block::Request::new(height.into()))
|
||||
perform_with_compat!(self, block_results::Request::new(height.into()))
|
||||
}
|
||||
|
||||
async fn block_by_hash(
|
||||
&self,
|
||||
hash: tendermint::Hash,
|
||||
) -> Result<endpoint::block_by_hash::Response, Error> {
|
||||
perform_with_compat!(self, endpoint::block_by_hash::Request::new(hash))
|
||||
}
|
||||
|
||||
async fn latest_block(&self) -> Result<endpoint::block::Response, Error> {
|
||||
perform_with_compat!(self, endpoint::block::Request::default())
|
||||
}
|
||||
|
||||
async fn block_results<H>(&self, height: H) -> Result<endpoint::block_results::Response, Error>
|
||||
where
|
||||
H: Into<Height> + Send,
|
||||
{
|
||||
perform_with_compat!(self, endpoint::block_results::Request::new(height.into()))
|
||||
}
|
||||
|
||||
async fn latest_block_results(&self) -> Result<endpoint::block_results::Response, Error> {
|
||||
perform_with_compat!(self, endpoint::block_results::Request::default())
|
||||
}
|
||||
|
||||
async fn block_search(
|
||||
&self,
|
||||
query: Query,
|
||||
page: u32,
|
||||
per_page: u8,
|
||||
order: Order,
|
||||
) -> Result<endpoint::block_search::Response, Error> {
|
||||
perform_with_compat!(
|
||||
self,
|
||||
endpoint::block_search::Request::new(query, page, per_page, order)
|
||||
)
|
||||
async fn latest_block_results(&self) -> Result<block_results::Response, Error> {
|
||||
perform_with_compat!(self, block_results::Request::default())
|
||||
}
|
||||
|
||||
async fn header<H>(&self, height: H) -> Result<endpoint::header::Response, Error>
|
||||
@@ -190,26 +140,11 @@ impl TendermintRpcClient for ReqwestRpcClient {
|
||||
{
|
||||
let height = height.into();
|
||||
match self.compat {
|
||||
CompatMode::V0_38 => {
|
||||
self.perform_request_with_dialect(
|
||||
endpoint::header::Request::new(height),
|
||||
v0_38::Dialect,
|
||||
)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_37 => {
|
||||
self.perform_request_with_dialect(
|
||||
endpoint::header::Request::new(height),
|
||||
v0_37::Dialect,
|
||||
)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_37 => self.perform(endpoint::header::Request::new(height)).await,
|
||||
CompatMode::V0_34 => {
|
||||
// Back-fill with a request to /block endpoint and
|
||||
// taking just the header from the response.
|
||||
let resp = self
|
||||
.perform_request_with_dialect(block::Request::new(height), v0_34::Dialect)
|
||||
.await?;
|
||||
let resp = self.perform_v0_34(block::Request::new(height)).await?;
|
||||
Ok(resp.into())
|
||||
}
|
||||
}
|
||||
@@ -217,25 +152,12 @@ impl TendermintRpcClient for ReqwestRpcClient {
|
||||
|
||||
async fn header_by_hash(&self, hash: Hash) -> Result<header_by_hash::Response, Error> {
|
||||
match self.compat {
|
||||
CompatMode::V0_38 => {
|
||||
self.perform_request_with_dialect(
|
||||
header_by_hash::Request::new(hash),
|
||||
v0_38::Dialect,
|
||||
)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_37 => {
|
||||
self.perform_request_with_dialect(
|
||||
header_by_hash::Request::new(hash),
|
||||
v0_37::Dialect,
|
||||
)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_37 => self.perform(header_by_hash::Request::new(hash)).await,
|
||||
CompatMode::V0_34 => {
|
||||
// Back-fill with a request to /block_by_hash endpoint and
|
||||
// taking just the header from the response.
|
||||
let resp = self
|
||||
.perform_request_with_dialect(block_by_hash::Request::new(hash), v0_34::Dialect)
|
||||
.perform_v0_34(block_by_hash::Request::new(hash))
|
||||
.await?;
|
||||
Ok(resp.into())
|
||||
}
|
||||
@@ -245,18 +167,8 @@ impl TendermintRpcClient for ReqwestRpcClient {
|
||||
/// `/broadcast_evidence`: broadcast an evidence.
|
||||
async fn broadcast_evidence(&self, e: Evidence) -> Result<evidence::Response, Error> {
|
||||
match self.compat {
|
||||
CompatMode::V0_38 => {
|
||||
self.perform_request_with_dialect(evidence::Request::new(e), v0_38::Dialect)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_37 => {
|
||||
self.perform_request_with_dialect(evidence::Request::new(e), v0_37::Dialect)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_34 => {
|
||||
self.perform_request_with_dialect(evidence::Request::new(e), v0_34::Dialect)
|
||||
.await
|
||||
}
|
||||
CompatMode::V0_37 => self.perform(evidence::Request::new(e)).await,
|
||||
CompatMode::V0_34 => self.perform_v0_34(evidence::Request::new(e)).await,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -113,10 +113,6 @@ impl Role {
|
||||
pub fn is_standby(&self) -> bool {
|
||||
matches!(self, Role::Standby)
|
||||
}
|
||||
|
||||
pub fn is_mixnode(&self) -> bool {
|
||||
matches!(self, Role::Layer1 | Role::Layer2 | Role::Layer3)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Role {
|
||||
|
||||
@@ -8,81 +8,81 @@ use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug, PartialEq)]
|
||||
pub enum VestingContractError {
|
||||
#[error("VESTING ({l}): {0}", l = line!())]
|
||||
#[error("VESTING ({}): {0}", line!())]
|
||||
Std(#[from] StdError),
|
||||
|
||||
#[error("VESTING: {0}")]
|
||||
OverflowError(#[from] OverflowError),
|
||||
|
||||
#[error("VESTING ({l}): Account does not exist - {0}", l = line!())]
|
||||
#[error("VESTING ({}): Account does not exist - {0}", line!())]
|
||||
NoAccountForAddress(String),
|
||||
|
||||
#[error("VESTING ({l}): Only admin can perform this action, {0} is not admin", l = line!())]
|
||||
#[error("VESTING ({}): Only admin can perform this action, {0} is not admin", line!())]
|
||||
NotAdmin(String),
|
||||
|
||||
#[error("VESTING ({l}): Balance not found for existing account ({0}), this is a bug", l = line!())]
|
||||
#[error("VESTING ({}): Balance not found for existing account ({0}), this is a bug", line!())]
|
||||
NoBalanceForAddress(String),
|
||||
|
||||
#[error("VESTING ({l}): Insufficient balance for address {0} -> {1}", l = line!())]
|
||||
#[error("VESTING ({}): Insufficient balance for address {0} -> {1}", line!())]
|
||||
InsufficientBalance(String, u128),
|
||||
|
||||
#[error("VESTING ({l}): Insufficient spendable balance for address {0} -> {1}", l = line!())]
|
||||
#[error("VESTING ({}): Insufficient spendable balance for address {0} -> {1}", line!())]
|
||||
InsufficientSpendable(String, u128),
|
||||
|
||||
#[error(
|
||||
"VESTING ({l}):Only delegation owner can perform delegation actions, {0} is not the delegation owner"
|
||||
, l = line!())]
|
||||
"VESTING ({}):Only delegation owner can perform delegation actions, {0} is not the delegation owner"
|
||||
, line!())]
|
||||
NotDelegate(String),
|
||||
|
||||
#[error("VESTING ({l}): Total vesting amount is inprobably low -> {0}, this is likely an error", l = line!())]
|
||||
#[error("VESTING ({}): Total vesting amount is inprobably low -> {0}, this is likely an error", line!())]
|
||||
ImprobableVestingAmount(u128),
|
||||
|
||||
#[error("VESTING ({l}): Address {0} has already bonded a node", l = line!())]
|
||||
#[error("VESTING ({}): Address {0} has already bonded a node", line!())]
|
||||
AlreadyBonded(String),
|
||||
|
||||
#[error("VESTING ({l}): Received empty funds vector", l = line!())]
|
||||
#[error("VESTING ({}): Received empty funds vector", line!())]
|
||||
EmptyFunds,
|
||||
|
||||
#[error("VESTING ({l}): Received wrong denom: {0}, expected {1}", l = line!())]
|
||||
#[error("VESTING ({}): Received wrong denom: {0}, expected {1}", line!())]
|
||||
WrongDenom(String, String),
|
||||
|
||||
#[error("VESTING ({l}): Received multiple denoms, expected 1", l = line!())]
|
||||
#[error("VESTING ({}): Received multiple denoms, expected 1", line!())]
|
||||
MultipleDenoms,
|
||||
|
||||
#[error("VESTING ({l}): No delegations found for account {0}, mix_identity {1}", l = line!())]
|
||||
#[error("VESTING ({}): No delegations found for account {0}, mix_identity {1}", line!())]
|
||||
NoSuchDelegation(Addr, NodeId),
|
||||
|
||||
#[error("VESTING ({l}): Only mixnet contract can perform this operation, got {0}", l = line!())]
|
||||
#[error("VESTING ({}): Only mixnet contract can perform this operation, got {0}", line!())]
|
||||
NotMixnetContract(Addr),
|
||||
|
||||
#[error("VESTING ({l}): Calculation underflowed", l = line!())]
|
||||
#[error("VESTING ({}): Calculation underflowed", line!())]
|
||||
Underflow,
|
||||
|
||||
#[error("VESTING ({l}): No bond found for account {0}", l = line!())]
|
||||
#[error("VESTING ({}): No bond found for account {0}", line!())]
|
||||
NoBondFound(String),
|
||||
|
||||
#[error("VESTING: Attempted to reduce mixnode bond pledge below zero! The current pledge is {current} and we attempted to reduce it by {decrease_by}.")]
|
||||
InvalidBondPledgeReduction { current: Coin, decrease_by: Coin },
|
||||
|
||||
#[error("VESTING ({l}): Action can only be executed by account owner -> {0}", l = line!())]
|
||||
#[error("VESTING ({}): Action can only be executed by account owner -> {0}", line!())]
|
||||
NotOwner(String),
|
||||
|
||||
#[error("VESTING ({l}): Invalid address: {0}", l = line!())]
|
||||
#[error("VESTING ({}): Invalid address: {0}", line!())]
|
||||
InvalidAddress(String),
|
||||
|
||||
#[error("VESTING ({l}): Account already exists: {0}", l = line!())]
|
||||
#[error("VESTING ({}): Account already exists: {0}", line!())]
|
||||
AccountAlreadyExists(String),
|
||||
|
||||
#[error("VESTING ({l}): Staking account already exists: {0}", l = line!())]
|
||||
#[error("VESTING ({}): Staking account already exists: {0}", line!())]
|
||||
StakingAccountAlreadyExists(String),
|
||||
|
||||
#[error("VESTING ({l}): Too few coins sent for vesting account creation, sent {sent}, need at least {need}", l = line!())]
|
||||
#[error("VESTING ({}): Too few coins sent for vesting account creation, sent {sent}, need at least {need}", line!())]
|
||||
MinVestingFunds { sent: u128, need: u128 },
|
||||
|
||||
#[error("VESTING ({l}): Maximum amount of locked coins has already been pledged: {current}, cap is {cap}", l = line!())]
|
||||
#[error("VESTING ({}): Maximum amount of locked coins has already been pledged: {current}, cap is {cap}", line!())]
|
||||
LockedPledgeCapReached { current: Uint128, cap: Uint128 },
|
||||
|
||||
#[error("VESTING: ({l}: Account owned by {owner} has unpopulated vesting periods!", l = line!())]
|
||||
#[error("VESTING: ({}: Account owned by {owner} has unpopulated vesting periods!", line!())]
|
||||
UnpopulatedVestingPeriods { owner: Addr },
|
||||
|
||||
#[error("VESTING: Vesting account associated with {0} already exists, only addresses with not existing vesting accounts can be added as staking addresses")]
|
||||
|
||||
@@ -19,7 +19,7 @@ use std::error::Error;
|
||||
// `SELECT total_tickets, used_tickets FROM ecash_ticketbook WHERE expiration_date >= ?`, today_date
|
||||
// then for each calculate the diff total_tickets - used_tickets and multiply the result by the size of the ticket
|
||||
#[async_trait]
|
||||
pub trait Storage: Clone + Send + Sync {
|
||||
pub trait Storage: Send + Sync {
|
||||
type StorageError: Error;
|
||||
|
||||
async fn close(&self);
|
||||
|
||||
@@ -26,6 +26,7 @@ nym-api-requests = { path = "../../nym-api/nym-api-requests" }
|
||||
nym-credentials = { path = "../credentials" }
|
||||
nym-credentials-interface = { path = "../credentials-interface" }
|
||||
nym-ecash-contract-common = { path = "../cosmwasm-smart-contracts/ecash-contract" }
|
||||
nym-ecash-double-spending = { path = "../ecash-double-spending" }
|
||||
nym-gateway-requests = { path = "../gateway-requests" }
|
||||
nym-gateway-storage = { path = "../gateway-storage" }
|
||||
nym-task = { path = "../task" }
|
||||
|
||||
@@ -13,7 +13,6 @@ use nym_api_requests::constants::MIN_BATCH_REDEMPTION_DELAY;
|
||||
use nym_api_requests::ecash::models::{BatchRedeemTicketsBody, VerifyEcashTicketBody};
|
||||
use nym_credentials_interface::Bandwidth;
|
||||
use nym_credentials_interface::{ClientTicket, TicketType};
|
||||
use nym_validator_client::coconut::EcashApiError;
|
||||
use nym_validator_client::nym_api::EpochId;
|
||||
use nym_validator_client::nyxd::contract_traits::{
|
||||
EcashSigningClient, MultisigQueryClient, MultisigSigningClient, PagedMultisigQueryClient,
|
||||
@@ -353,9 +352,7 @@ impl CredentialHandler {
|
||||
}
|
||||
Err(err) => {
|
||||
error!("failed to send ticket {ticket_id} for verification to ecash signer '{client}': {err}. if we don't reach quorum, we'll retry later");
|
||||
Err(EcashTicketError::ApiFailure(EcashApiError::NymApi {
|
||||
source: err,
|
||||
}))
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "nym-ecash-double-spending"
|
||||
version = "0.1.0"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
bit-vec = { workspace = true }
|
||||
bloomfilter = { workspace = true }
|
||||
nym-network-defaults = { path = "../network-defaults" }
|
||||
@@ -0,0 +1,136 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use bit_vec::BitVec;
|
||||
use bloomfilter::Bloom;
|
||||
use nym_network_defaults::{BloomfilterParameters, ECASH_DS_BLOOMFILTER_PARAMS};
|
||||
|
||||
pub struct DoubleSpendingFilter {
|
||||
params: BloomfilterParameters,
|
||||
inner: Bloom<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl Default for DoubleSpendingFilter {
|
||||
fn default() -> Self {
|
||||
DoubleSpendingFilter::new_empty_ecash()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bloom_from_params<T>(params: &BloomfilterParameters, bitvec: BitVec) -> Bloom<Vec<T>> {
|
||||
assert_eq!(params.bitmap_size, bitvec.len() as u64);
|
||||
|
||||
Bloom::from_bit_vec(
|
||||
bitvec,
|
||||
params.bitmap_size,
|
||||
params.num_hashes,
|
||||
params.sip_keys,
|
||||
)
|
||||
}
|
||||
|
||||
impl DoubleSpendingFilter {
|
||||
pub fn new_empty(params: BloomfilterParameters) -> Self {
|
||||
let bitvec = BitVec::from_elem(params.bitmap_size as usize, false);
|
||||
DoubleSpendingFilter {
|
||||
inner: bloom_from_params(¶ms, bitvec),
|
||||
params,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn params(&self) -> BloomfilterParameters {
|
||||
self.params
|
||||
}
|
||||
|
||||
pub fn rebuild(&self) -> DoubleSpendingFilterBuilder {
|
||||
DoubleSpendingFilterBuilder::new(self.params)
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.inner.clear()
|
||||
}
|
||||
|
||||
pub fn new_empty_ecash() -> Self {
|
||||
DoubleSpendingFilter::new_empty(ECASH_DS_BLOOMFILTER_PARAMS)
|
||||
}
|
||||
|
||||
pub fn builder(params: BloomfilterParameters) -> DoubleSpendingFilterBuilder {
|
||||
DoubleSpendingFilterBuilder::new(params)
|
||||
}
|
||||
|
||||
pub fn from_bytes(params: BloomfilterParameters, bitmap: &[u8]) -> Self {
|
||||
DoubleSpendingFilter {
|
||||
inner: bloom_from_params(¶ms, BitVec::from_bytes(bitmap)),
|
||||
params,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn replace_bitvec(&mut self, new: BitVec) {
|
||||
self.inner = bloom_from_params(&self.params, new)
|
||||
}
|
||||
|
||||
pub fn dump_bitmap(&self) -> Vec<u8> {
|
||||
self.inner.bitmap()
|
||||
}
|
||||
|
||||
pub fn set(&mut self, b: &Vec<u8>) {
|
||||
self.inner.set(b);
|
||||
}
|
||||
|
||||
pub fn check(&self, b: &Vec<u8>) -> bool {
|
||||
self.inner.check(b)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DoubleSpendingFilterBuilder {
|
||||
params: BloomfilterParameters,
|
||||
bit_vec_builder: Option<BitVecBuilder>,
|
||||
}
|
||||
|
||||
impl DoubleSpendingFilterBuilder {
|
||||
pub fn new(params: BloomfilterParameters) -> Self {
|
||||
DoubleSpendingFilterBuilder {
|
||||
params,
|
||||
bit_vec_builder: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_bytes(&mut self, b: &[u8]) -> bool {
|
||||
match &mut self.bit_vec_builder {
|
||||
None => {
|
||||
self.bit_vec_builder = Some(BitVecBuilder::new(b));
|
||||
true
|
||||
}
|
||||
Some(builder) => builder.add_bytes(b),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build(self) -> DoubleSpendingFilter {
|
||||
match self.bit_vec_builder {
|
||||
None => DoubleSpendingFilter::new_empty(self.params),
|
||||
Some(builder) => DoubleSpendingFilter {
|
||||
inner: bloom_from_params(&self.params, builder.finish()),
|
||||
params: self.params,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BitVecBuilder(BitVec);
|
||||
|
||||
impl BitVecBuilder {
|
||||
pub fn new(initial_bitmap: &[u8]) -> Self {
|
||||
BitVecBuilder(BitVec::from_bytes(initial_bitmap))
|
||||
}
|
||||
|
||||
pub fn add_bytes(&mut self, b: &[u8]) -> bool {
|
||||
let add = BitVec::from_bytes(b);
|
||||
if self.0.len() != add.len() {
|
||||
return false;
|
||||
}
|
||||
self.0.or(&add);
|
||||
true
|
||||
}
|
||||
|
||||
pub fn finish(self) -> BitVec {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
@@ -59,12 +59,12 @@ pub enum GatewayRequestsError {
|
||||
source: NymNodeRoutingAddressError,
|
||||
},
|
||||
|
||||
#[error("received request had invalid size. (actual: {0}, but expected one of: {a} (ACK), {r} (REGULAR), {e8}, {e16}, {e32} (EXTENDED))",
|
||||
a = PacketSize::AckPacket.size(),
|
||||
r = PacketSize::RegularPacket.size(),
|
||||
e8 = PacketSize::ExtendedPacket8.size(),
|
||||
e16 = PacketSize::ExtendedPacket16.size(),
|
||||
e32 = PacketSize::ExtendedPacket32.size())
|
||||
#[error("received request had invalid size. (actual: {0}, but expected one of: {} (ACK), {} (REGULAR), {}, {}, {} (EXTENDED))",
|
||||
PacketSize::AckPacket.size(),
|
||||
PacketSize::RegularPacket.size(),
|
||||
PacketSize::ExtendedPacket8.size(),
|
||||
PacketSize::ExtendedPacket16.size(),
|
||||
PacketSize::ExtendedPacket32.size())
|
||||
]
|
||||
RequestOfInvalidSize(usize),
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ use std::str::FromStr;
|
||||
use tungstenite::Message;
|
||||
|
||||
// wrapper for all encrypted requests for ease of use
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum ClientRequest {
|
||||
UpgradeKey {
|
||||
|
||||
@@ -3,22 +3,5 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*/
|
||||
|
||||
|
||||
ALTER TABLE message_store
|
||||
RENAME TO message_store_old;
|
||||
|
||||
-- add new column with message timestamp.
|
||||
-- note: we can't simply alter existing table to add it since the default value is non-constant
|
||||
CREATE TABLE message_store
|
||||
(
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
client_address_bs58 TEXT NOT NULL,
|
||||
content BLOB NOT NULL,
|
||||
timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
INSERT INTO message_store(id, client_address_bs58, content)
|
||||
SELECT id, client_address_bs58, content
|
||||
FROM message_store_old;
|
||||
DROP TABLE message_store_old;
|
||||
|
||||
ADD COLUMN timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP;
|
||||
|
||||
@@ -15,7 +15,6 @@ async-trait = { workspace = true }
|
||||
reqwest = { workspace = true, features = ["json"] }
|
||||
http.workspace = true
|
||||
url = { workspace = true }
|
||||
once_cell = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
@@ -23,13 +22,7 @@ tracing = { workspace = true }
|
||||
|
||||
nym-bin-common = { path = "../bin-common" }
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies]
|
||||
hickory-resolver = { workspace = true, features = ["dns-over-https-rustls", "webpki-roots"] }
|
||||
|
||||
# for request timeout until https://github.com/seanmonstar/reqwest/issues/1135 is fixed
|
||||
[target."cfg(target_arch = \"wasm32\")".dependencies.wasmtimer]
|
||||
workspace = true
|
||||
features = ["tokio"]
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { workspace = true, features=["rt", "macros"] }
|
||||
|
||||
@@ -1,178 +0,0 @@
|
||||
//! DNS resolver configuration for internal lookups.
|
||||
//!
|
||||
//! The resolver itself is the set combination of the google, cloudflare, and quad9 endpoints
|
||||
//! supporting DoH and DoT.
|
||||
//!
|
||||
//! This resolver implements a fallback mechanism where, should the DNS-over-TLS resolution fail, a
|
||||
//! followup resolution will be done using the hosts configured default (e.g. `/etc/resolve.conf` on
|
||||
//! linux).
|
||||
//!
|
||||
//! Requires the `dns-over-https-rustls`, `webpki-roots` feature for the
|
||||
//! `hickory-resolver` crate
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use crate::ClientBuilder;
|
||||
|
||||
use std::{net::SocketAddr, sync::Arc};
|
||||
|
||||
use hickory_resolver::lookup_ip::LookupIp;
|
||||
use hickory_resolver::{
|
||||
config::{LookupIpStrategy, NameServerConfigGroup, ResolverConfig, ResolverOpts},
|
||||
error::ResolveError,
|
||||
lookup_ip::LookupIpIntoIter,
|
||||
TokioAsyncResolver,
|
||||
};
|
||||
use once_cell::sync::OnceCell;
|
||||
use reqwest::dns::{Addrs, Name, Resolve, Resolving};
|
||||
use tracing::warn;
|
||||
|
||||
impl ClientBuilder {
|
||||
/// Override the DNS resolver implementation used by the underlying http client.
|
||||
pub fn dns_resolver<R: Resolve + 'static>(mut self, resolver: Arc<R>) -> Self {
|
||||
self.reqwest_client_builder = self.reqwest_client_builder.dns_resolver(resolver);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
struct SocketAddrs {
|
||||
iter: LookupIpIntoIter,
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("hickory-dns resolver error: {hickory_error}")]
|
||||
/// Error occurring while resolving a hostname into an IP address.
|
||||
pub struct HickoryDnsError {
|
||||
#[from]
|
||||
hickory_error: ResolveError,
|
||||
}
|
||||
|
||||
/// Wrapper around an `AsyncResolver`, which implements the `Resolve` trait.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct HickoryDnsResolver {
|
||||
/// Since we might not have been called in the context of a
|
||||
/// Tokio Runtime in initialization, so we must delay the actual
|
||||
/// construction of the resolver.
|
||||
state: Arc<OnceCell<TokioAsyncResolver>>,
|
||||
fallback: Arc<OnceCell<TokioAsyncResolver>>,
|
||||
}
|
||||
|
||||
impl Resolve for HickoryDnsResolver {
|
||||
fn resolve(&self, name: Name) -> Resolving {
|
||||
let resolver = self.state.clone();
|
||||
let fallback = self.fallback.clone();
|
||||
Box::pin(async move {
|
||||
let resolver = resolver.get_or_try_init(new_resolver)?;
|
||||
|
||||
// try the primary DNS resolver that we set up (DoH or DoT or whatever)
|
||||
let lookup = match resolver.lookup_ip(name.as_str()).await {
|
||||
Ok(res) => res,
|
||||
Err(e) => {
|
||||
// on failure use the fall back system configured DNS resolver
|
||||
warn!("primary DNS failed w/ error {e}: using system fallback");
|
||||
let resolver = fallback.get_or_try_init(new_resolver_system)?;
|
||||
resolver.lookup_ip(name.as_str()).await?
|
||||
}
|
||||
};
|
||||
|
||||
let addrs: Addrs = Box::new(SocketAddrs {
|
||||
iter: lookup.into_iter(),
|
||||
});
|
||||
Ok(addrs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for SocketAddrs {
|
||||
type Item = SocketAddr;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next().map(|ip_addr| SocketAddr::new(ip_addr, 0))
|
||||
}
|
||||
}
|
||||
|
||||
impl HickoryDnsResolver {
|
||||
/// Attempt to resolve a domain name to a set of ['IpAddr']s
|
||||
pub async fn resolve_str(&self, name: &str) -> Result<LookupIp, HickoryDnsError> {
|
||||
let resolver = self.state.get_or_try_init(new_resolver)?;
|
||||
|
||||
// try the primary DNS resolver that we set up (DoH or DoT or whatever)
|
||||
let lookup = match resolver.lookup_ip(name).await {
|
||||
Ok(res) => res,
|
||||
Err(e) => {
|
||||
// on failure use the fall back system configured DNS resolver
|
||||
warn!("primary DNS failed w/ error {e}: using system fallback");
|
||||
let resolver = self.fallback.get_or_try_init(new_resolver_system)?;
|
||||
resolver.lookup_ip(name).await?
|
||||
}
|
||||
};
|
||||
|
||||
Ok(lookup)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new resolver with a custom DoT based configuration. The options are overridden to look
|
||||
/// up for both IPv4 and IPv6 addresses to work with "happy eyeballs" algorithm.
|
||||
fn new_resolver() -> Result<TokioAsyncResolver, HickoryDnsError> {
|
||||
let mut name_servers = NameServerConfigGroup::google_tls();
|
||||
name_servers.merge(NameServerConfigGroup::google_https());
|
||||
// name_servers.merge(NameServerConfigGroup::google_h3());
|
||||
name_servers.merge(NameServerConfigGroup::quad9_tls());
|
||||
name_servers.merge(NameServerConfigGroup::quad9_https());
|
||||
name_servers.merge(NameServerConfigGroup::cloudflare_tls());
|
||||
name_servers.merge(NameServerConfigGroup::cloudflare_https());
|
||||
|
||||
let config = ResolverConfig::from_parts(None, Vec::new(), name_servers);
|
||||
|
||||
let mut opts = ResolverOpts::default();
|
||||
opts.ip_strategy = LookupIpStrategy::Ipv4AndIpv6;
|
||||
// Would like to enable this when 0.25 stabilizes
|
||||
// opts.server_ordering_strategy = ServerOrderingStrategy::RoundRobin;
|
||||
|
||||
Ok(TokioAsyncResolver::tokio(config, opts))
|
||||
}
|
||||
|
||||
/// Create a new resolver with the default configuration, which reads from the system DNS config
|
||||
/// (i.e. `/etc/resolve.conf` in unix). The options are overridden to look up for both IPv4 and IPv6
|
||||
/// addresses to work with "happy eyeballs" algorithm.
|
||||
fn new_resolver_system() -> Result<TokioAsyncResolver, HickoryDnsError> {
|
||||
let (config, mut opts) = hickory_resolver::system_conf::read_system_conf()?;
|
||||
opts.ip_strategy = LookupIpStrategy::Ipv4AndIpv6;
|
||||
Ok(TokioAsyncResolver::tokio(config, opts))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn reqwest_hickory_doh() {
|
||||
let resolver = HickoryDnsResolver::default();
|
||||
let client = reqwest::ClientBuilder::new()
|
||||
.dns_resolver(resolver.into())
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let resp = client
|
||||
.get("http://ifconfig.me:80")
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.bytes()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert!(!resp.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn dns_lookup() -> Result<(), HickoryDnsError> {
|
||||
let resolver = HickoryDnsResolver::default();
|
||||
|
||||
let domain = "ifconfig.me";
|
||||
let addrs = resolver.resolve_str(domain).await?;
|
||||
|
||||
assert!(addrs.into_iter().next().is_some());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -6,23 +6,17 @@ use reqwest::header::HeaderValue;
|
||||
use reqwest::{RequestBuilder, Response, StatusCode};
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Display;
|
||||
use std::time::Duration;
|
||||
use thiserror::Error;
|
||||
use tracing::{instrument, warn};
|
||||
use url::Url;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use std::sync::Arc;
|
||||
use std::{fmt::Display, time::Duration};
|
||||
|
||||
pub use reqwest::IntoUrl;
|
||||
|
||||
mod user_agent;
|
||||
pub use user_agent::UserAgent;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
mod dns;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub use dns::{HickoryDnsError, HickoryDnsResolver};
|
||||
mod user_agent;
|
||||
|
||||
// The timeout is relatively high as we are often making requests over the mixnet, where latency is
|
||||
// high and chatty protocols take a while to complete.
|
||||
@@ -92,18 +86,11 @@ impl ClientBuilder {
|
||||
// TODO: or should we maybe default to https?
|
||||
Self::new(alt)
|
||||
} else {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let reqwest_client_builder = reqwest::ClientBuilder::new();
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let reqwest_client_builder =
|
||||
reqwest::ClientBuilder::new().dns_resolver(Arc::new(HickoryDnsResolver::default()));
|
||||
|
||||
Ok(ClientBuilder {
|
||||
url: url.into_url()?,
|
||||
timeout: None,
|
||||
custom_user_agent: false,
|
||||
reqwest_client_builder,
|
||||
reqwest_client_builder: reqwest::ClientBuilder::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,10 +30,6 @@ pub struct Config {
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn base(&self) -> BaseClientConfig {
|
||||
self.base.clone()
|
||||
}
|
||||
|
||||
pub fn new<S: Into<String>>(id: S, version: S, provider_mix_address: S) -> Self {
|
||||
Config {
|
||||
base: BaseClientConfig::new(id, version),
|
||||
|
||||
@@ -76,10 +76,6 @@ where
|
||||
<S::GatewaysDetailsStore as GatewaysDetailsStore>::StorageError: Sync + Send,
|
||||
<S::KeyStore as KeyStore>::StorageError: Send + Sync,
|
||||
{
|
||||
pub fn config(&self) -> Config {
|
||||
self.config.clone()
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
config: Config,
|
||||
storage: S,
|
||||
@@ -237,7 +233,7 @@ where
|
||||
};
|
||||
|
||||
let mut base_builder =
|
||||
BaseClientBuilder::new(self.config.base(), self.storage, dkg_query_client)
|
||||
BaseClientBuilder::new(&self.config.base, self.storage, dkg_query_client)
|
||||
.with_gateway_setup(self.setup_method)
|
||||
.with_user_agent(self.user_agent);
|
||||
|
||||
@@ -245,7 +241,7 @@ where
|
||||
base_builder = base_builder.with_stored_topology(custom_mixnet)?;
|
||||
}
|
||||
|
||||
let packet_type = self.config.base().debug.traffic.packet_type;
|
||||
let packet_type = self.config.base.debug.traffic.packet_type;
|
||||
let mut started_client = base_builder.start_base().await?;
|
||||
let self_address = started_client.address;
|
||||
let client_input = started_client.client_input.register_producer();
|
||||
@@ -256,7 +252,7 @@ where
|
||||
|
||||
Self::start_socks5_listener(
|
||||
&self.config.socks5,
|
||||
self.config.base().debug,
|
||||
self.config.base.debug,
|
||||
client_input,
|
||||
client_output,
|
||||
client_state,
|
||||
|
||||
@@ -67,7 +67,7 @@ impl Default for ClientStatsReport {
|
||||
pub struct OsInformation {
|
||||
pub(crate) os_type: String,
|
||||
pub(crate) os_version: Option<String>,
|
||||
pub(crate) os_arch: String,
|
||||
pub(crate) os_arch: Option<String>,
|
||||
}
|
||||
|
||||
impl OsInformation {
|
||||
|
||||
@@ -221,7 +221,7 @@ impl TaskManager {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn wait_for_graceful_shutdown(&mut self) {
|
||||
pub(crate) async fn wait_for_graceful_shutdown(&mut self) {
|
||||
if let Some(notify_rx) = self.notify_rx.take() {
|
||||
drop(notify_rx);
|
||||
}
|
||||
@@ -315,7 +315,7 @@ impl TaskClient {
|
||||
const MAX_NAME_LENGTH: usize = 128;
|
||||
const OVERFLOW_NAME: &'static str = "reached maximum TaskClient children name depth";
|
||||
|
||||
const SHUTDOWN_TIMEOUT_WAITING_FOR_SIGNAL_ON_EXIT: Duration = Duration::from_secs(10);
|
||||
const SHUTDOWN_TIMEOUT_WAITING_FOR_SIGNAL_ON_EXIT: Duration = Duration::from_secs(5);
|
||||
|
||||
fn new(
|
||||
notify: watch::Receiver<()>,
|
||||
|
||||
+13
-10
@@ -11,7 +11,7 @@ use std::borrow::Borrow;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fmt::Display;
|
||||
use std::net::IpAddr;
|
||||
use tracing::{debug, trace, warn};
|
||||
use tracing::{debug, warn};
|
||||
|
||||
pub use crate::node::{EntryDetails, RoutingNode, SupportedRoles};
|
||||
pub use error::NymTopologyError;
|
||||
@@ -293,7 +293,7 @@ impl NymTopology {
|
||||
let has_exit_gateways = !self.rewarded_set.exit_gateways.is_empty();
|
||||
let has_entry_gateways = !self.rewarded_set.entry_gateways.is_empty();
|
||||
|
||||
trace!(
|
||||
debug!(
|
||||
has_layer1 = %has_layer1,
|
||||
has_layer2 = %has_layer2,
|
||||
has_layer3 = %has_layer3,
|
||||
@@ -401,17 +401,20 @@ impl NymTopology {
|
||||
});
|
||||
};
|
||||
|
||||
// a 'valid' egress is one that is currently **not** acting as a mixnode
|
||||
// a 'valid' egress is one assigned to either entry role (i.e. entry for another client)
|
||||
// or exit role (as a service provider)
|
||||
if !ignore_epoch_roles {
|
||||
if let Some(role) = self.rewarded_set.role(node.node_id) {
|
||||
if role.is_mixnode() {
|
||||
return Err(NymTopologyError::InvalidEgressRole {
|
||||
node_identity: Box::new(node_identity),
|
||||
});
|
||||
}
|
||||
let Some(role) = self.rewarded_set.role(node.node_id) else {
|
||||
return Err(NymTopologyError::InvalidEgressRole {
|
||||
node_identity: Box::new(node_identity),
|
||||
});
|
||||
};
|
||||
if !matches!(role, Role::EntryGateway | Role::ExitGateway) {
|
||||
return Err(NymTopologyError::InvalidEgressRole {
|
||||
node_identity: Box::new(node_identity),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(node)
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#![allow(clippy::drop_non_drop)]
|
||||
|
||||
use crate::error::WasmCoreError;
|
||||
use nym_client_core::config::ForgetMe;
|
||||
use nym_config::helpers::OptionalSet;
|
||||
use nym_sphinx::params::{PacketSize, PacketType};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -111,8 +110,6 @@ pub struct DebugWasm {
|
||||
/// Defines all configuration options related to stats reporting.
|
||||
#[wasm_bindgen(getter_with_clone)]
|
||||
pub stats_reporting: StatsReportingWasm,
|
||||
|
||||
pub forget_me: ForgetMeWasm,
|
||||
}
|
||||
|
||||
impl Default for DebugWasm {
|
||||
@@ -131,7 +128,6 @@ impl From<DebugWasm> for ConfigDebug {
|
||||
topology: debug.topology.into(),
|
||||
reply_surbs: debug.reply_surbs.into(),
|
||||
stats_reporting: debug.stats_reporting.into(),
|
||||
forget_me: debug.forget_me.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,7 +142,6 @@ impl From<ConfigDebug> for DebugWasm {
|
||||
topology: debug.topology.into(),
|
||||
reply_surbs: debug.reply_surbs.into(),
|
||||
stats_reporting: debug.stats_reporting.into(),
|
||||
forget_me: ForgetMeWasm::from(debug.forget_me),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -395,15 +390,12 @@ pub struct TopologyWasm {
|
||||
|
||||
/// Specifies whether this client should attempt to retrieve all available network nodes
|
||||
/// as opposed to just active mixnodes/gateways.
|
||||
/// Useless without `ignore_epoch_roles = true`
|
||||
pub use_extended_topology: bool,
|
||||
|
||||
/// Specifies whether this client should ignore the current epoch role of the target egress node
|
||||
/// when constructing the final hop packets.
|
||||
pub ignore_egress_epoch_role: bool,
|
||||
|
||||
/// Specifies whether this client should ignore the current epoch role of the ingress node
|
||||
/// when attempting to establish new connection
|
||||
pub ignore_ingress_epoch_role: bool,
|
||||
}
|
||||
|
||||
impl Default for TopologyWasm {
|
||||
@@ -428,7 +420,6 @@ impl From<TopologyWasm> for ConfigTopology {
|
||||
minimum_gateway_performance: topology.minimum_gateway_performance,
|
||||
use_extended_topology: topology.use_extended_topology,
|
||||
ignore_egress_epoch_role: topology.ignore_egress_epoch_role,
|
||||
ignore_ingress_epoch_role: topology.ignore_ingress_epoch_role,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -446,7 +437,6 @@ impl From<ConfigTopology> for TopologyWasm {
|
||||
minimum_gateway_performance: topology.minimum_gateway_performance,
|
||||
use_extended_topology: topology.use_extended_topology,
|
||||
ignore_egress_epoch_role: topology.ignore_egress_epoch_role,
|
||||
ignore_ingress_epoch_role: topology.ignore_ingress_epoch_role,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -546,29 +536,6 @@ impl From<ConfigReplySurbs> for ReplySurbsWasm {
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(inspectable)]
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize, Copy, Default)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ForgetMeWasm {
|
||||
pub client: bool,
|
||||
pub stats: bool,
|
||||
}
|
||||
|
||||
impl From<ForgetMeWasm> for ForgetMe {
|
||||
fn from(value: ForgetMeWasm) -> Self {
|
||||
ForgetMe::new(value.client, value.stats)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ForgetMe> for ForgetMeWasm {
|
||||
fn from(value: ForgetMe) -> Self {
|
||||
Self {
|
||||
client: value.client(),
|
||||
stats: value.stats(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(inspectable)]
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
#![allow(clippy::empty_docs)]
|
||||
|
||||
use super::{
|
||||
AcknowledgementsWasm, CoverTrafficWasm, DebugWasm, ForgetMeWasm, GatewayConnectionWasm,
|
||||
ReplySurbsWasm, StatsReportingWasm, TopologyWasm, TrafficWasm,
|
||||
AcknowledgementsWasm, CoverTrafficWasm, DebugWasm, GatewayConnectionWasm, ReplySurbsWasm,
|
||||
StatsReportingWasm, TopologyWasm, TrafficWasm,
|
||||
};
|
||||
use crate::config::ConfigDebug;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -47,9 +47,6 @@ pub struct DebugWasmOverride {
|
||||
/// Defines all configuration options related to stats reporting.
|
||||
#[tsify(optional)]
|
||||
pub stats_reporting: Option<StatsReportingWasmOverride>,
|
||||
|
||||
#[tsify(optional)]
|
||||
pub forget_me: Option<ForgetMeWasmOverride>,
|
||||
}
|
||||
|
||||
impl From<DebugWasmOverride> for DebugWasm {
|
||||
@@ -62,7 +59,6 @@ impl From<DebugWasmOverride> for DebugWasm {
|
||||
topology: value.topology.map(Into::into).unwrap_or_default(),
|
||||
reply_surbs: value.reply_surbs.map(Into::into).unwrap_or_default(),
|
||||
stats_reporting: value.stats_reporting.map(Into::into).unwrap_or_default(),
|
||||
forget_me: value.forget_me.map(Into::into).unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -278,6 +274,7 @@ pub struct TopologyWasmOverride {
|
||||
|
||||
/// Specifies whether this client should attempt to retrieve all available network nodes
|
||||
/// as opposed to just active mixnodes/gateways.
|
||||
/// Useless without `ignore_epoch_roles = true`
|
||||
#[tsify(optional)]
|
||||
pub use_extended_topology: Option<bool>,
|
||||
|
||||
@@ -285,11 +282,6 @@ pub struct TopologyWasmOverride {
|
||||
/// when constructing the final hop packets.
|
||||
#[tsify(optional)]
|
||||
pub ignore_egress_epoch_role: Option<bool>,
|
||||
|
||||
/// Specifies whether this client should ignore the current epoch role of the ingress node
|
||||
/// when attempting to establish new connection
|
||||
#[tsify(optional)]
|
||||
pub ignore_ingress_epoch_role: Option<bool>,
|
||||
}
|
||||
|
||||
impl From<TopologyWasmOverride> for TopologyWasm {
|
||||
@@ -319,9 +311,6 @@ impl From<TopologyWasmOverride> for TopologyWasm {
|
||||
ignore_egress_epoch_role: value
|
||||
.ignore_egress_epoch_role
|
||||
.unwrap_or(def.ignore_egress_epoch_role),
|
||||
ignore_ingress_epoch_role: value
|
||||
.ignore_ingress_epoch_role
|
||||
.unwrap_or(def.ignore_ingress_epoch_role),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -412,26 +401,6 @@ impl From<ReplySurbsWasmOverride> for ReplySurbsWasm {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Tsify, Debug, Clone, Serialize, Deserialize)]
|
||||
#[tsify(into_wasm_abi, from_wasm_abi)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ForgetMeWasmOverride {
|
||||
#[tsify(optional)]
|
||||
pub client: Option<bool>,
|
||||
|
||||
#[tsify(optional)]
|
||||
pub stats: Option<bool>,
|
||||
}
|
||||
|
||||
impl From<ForgetMeWasmOverride> for ForgetMeWasm {
|
||||
fn from(value: ForgetMeWasmOverride) -> Self {
|
||||
ForgetMeWasm {
|
||||
client: value.client.unwrap_or_default(),
|
||||
stats: value.stats.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Tsify, Debug, Clone, Serialize, Deserialize)]
|
||||
#[tsify(into_wasm_abi, from_wasm_abi)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
@@ -10,7 +10,7 @@ use nym_client_core::client::base_client::storage::GatewaysDetailsStore;
|
||||
use nym_client_core::client::replies::reply_storage::browser_backend;
|
||||
use nym_client_core::config;
|
||||
use nym_client_core::error::ClientCoreError;
|
||||
use nym_client_core::init::helpers::gateways_for_init;
|
||||
use nym_client_core::init::helpers::current_gateways;
|
||||
use nym_client_core::init::types::GatewaySelectionSpecification;
|
||||
use nym_client_core::init::{
|
||||
self, setup_gateway,
|
||||
@@ -132,17 +132,9 @@ pub async fn setup_gateway_from_api(
|
||||
chosen_gateway: Option<IdentityKey>,
|
||||
nym_apis: &[Url],
|
||||
minimum_performance: u8,
|
||||
ignore_epoch_roles: bool,
|
||||
) -> Result<InitialisationResult, WasmCoreError> {
|
||||
let mut rng = thread_rng();
|
||||
let gateways = gateways_for_init(
|
||||
&mut rng,
|
||||
nym_apis,
|
||||
None,
|
||||
minimum_performance,
|
||||
ignore_epoch_roles,
|
||||
)
|
||||
.await?;
|
||||
let gateways = current_gateways(&mut rng, nym_apis, None, minimum_performance).await?;
|
||||
setup_gateway_wasm(client_store, force_tls, chosen_gateway, gateways).await
|
||||
}
|
||||
|
||||
@@ -150,17 +142,9 @@ pub async fn current_gateways_wasm(
|
||||
nym_apis: &[Url],
|
||||
user_agent: Option<UserAgent>,
|
||||
minimum_performance: u8,
|
||||
ignore_epoch_roles: bool,
|
||||
) -> Result<Vec<RoutingNode>, ClientCoreError> {
|
||||
let mut rng = thread_rng();
|
||||
gateways_for_init(
|
||||
&mut rng,
|
||||
nym_apis,
|
||||
user_agent,
|
||||
minimum_performance,
|
||||
ignore_epoch_roles,
|
||||
)
|
||||
.await
|
||||
current_gateways(&mut rng, nym_apis, user_agent, minimum_performance).await
|
||||
}
|
||||
|
||||
pub async fn setup_from_topology(
|
||||
@@ -179,7 +163,6 @@ pub async fn generate_new_client_keys(store: &ClientStorage) -> Result<(), WasmC
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn add_gateway(
|
||||
preferred_gateway: Option<IdentityKey>,
|
||||
latency_based_selection: Option<bool>,
|
||||
@@ -187,7 +170,6 @@ pub async fn add_gateway(
|
||||
nym_apis: &[Url],
|
||||
user_agent: UserAgent,
|
||||
min_performance: u8,
|
||||
ignore_epoch_roles: bool,
|
||||
storage: &ClientStorage,
|
||||
) -> Result<(), WasmCoreError> {
|
||||
let selection_spec = GatewaySelectionSpecification::new(
|
||||
@@ -221,13 +203,8 @@ pub async fn add_gateway(
|
||||
|
||||
// Setup gateway by either registering a new one, or creating a new config from the selected
|
||||
// one but with keys kept, or reusing the gateway configuration.
|
||||
let available_gateways = current_gateways_wasm(
|
||||
nym_apis,
|
||||
Some(user_agent),
|
||||
min_performance,
|
||||
ignore_epoch_roles,
|
||||
)
|
||||
.await?;
|
||||
let available_gateways =
|
||||
current_gateways_wasm(nym_apis, Some(user_agent), min_performance).await?;
|
||||
|
||||
// since we're registering with a brand new gateway,
|
||||
// make sure the list of available gateways doesn't overlap the list of known gateways
|
||||
|
||||
@@ -4,15 +4,13 @@
|
||||
use crate::error::WasmCoreError;
|
||||
use crate::storage::wasm_client_traits::{v1, v2, WasmClientStorage};
|
||||
use async_trait::async_trait;
|
||||
use js_sys::Promise;
|
||||
use js_sys::{Array, Promise};
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen_futures::future_to_promise;
|
||||
use wasm_storage::traits::BaseWasmStorage;
|
||||
use wasm_storage::{
|
||||
Build, Database, RawDbResult, TryFromJs, TryToJs, VersionChangeEvent, WasmStorage,
|
||||
};
|
||||
use wasm_storage::{IdbVersionChangeEvent, WasmStorage};
|
||||
use wasm_utils::error::{simple_js_error, PromisableResult};
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
@@ -46,29 +44,28 @@ impl ClientStorage {
|
||||
// special care must be taken on JS side to ensure it's correctly used there.
|
||||
let passphrase = Zeroizing::new(passphrase);
|
||||
|
||||
let migrate_fn = Some(|evt: VersionChangeEvent, db: Database| -> RawDbResult<()> {
|
||||
let migrate_fn = Some(|evt: &IdbVersionChangeEvent| -> Result<(), JsValue> {
|
||||
// Even if the web-sys bindings expose the version as a f64, the IndexedDB API
|
||||
// works with an unsigned integer.
|
||||
// See <https://github.com/rustwasm/wasm-bindgen/issues/1149>
|
||||
let old_version = evt.old_version() as u32;
|
||||
let db = evt.db();
|
||||
|
||||
if old_version < 1 {
|
||||
// migrating to version 2
|
||||
|
||||
db.create_object_store(v1::KEYS_STORE).build()?;
|
||||
db.create_object_store(v1::CORE_STORE).build()?;
|
||||
db.create_object_store(v1::KEYS_STORE)?;
|
||||
db.create_object_store(v1::CORE_STORE)?;
|
||||
|
||||
db.create_object_store(v2::GATEWAY_REGISTRATIONS_ACTIVE_GATEWAY_STORE)
|
||||
.build()?;
|
||||
db.create_object_store(v2::GATEWAY_REGISTRATIONS_REGISTERED_GATEWAYS_STORE)
|
||||
.build()?;
|
||||
db.create_object_store(v2::GATEWAY_REGISTRATIONS_ACTIVE_GATEWAY_STORE)?;
|
||||
db.create_object_store(v2::GATEWAY_REGISTRATIONS_REGISTERED_GATEWAYS_STORE)?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// version 1 -> unimplemented migration
|
||||
if old_version < 2 {
|
||||
return Err(simple_js_error("this client is incompatible with existing storage. please initialise it again.").into());
|
||||
return Err(simple_js_error("this client is incompatible with existing storage. please initialise it again."));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -115,7 +112,7 @@ impl BaseWasmStorage for ClientStorage {
|
||||
async fn read_value<T, K>(&self, store: &str, key: K) -> Result<Option<T>, Self::StorageError>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
K: TryToJs,
|
||||
K: JsCast,
|
||||
{
|
||||
Ok(self.inner.read_value(store, key).await?)
|
||||
}
|
||||
@@ -128,33 +125,33 @@ impl BaseWasmStorage for ClientStorage {
|
||||
) -> Result<(), Self::StorageError>
|
||||
where
|
||||
T: Serialize,
|
||||
K: TryToJs + TryFromJs,
|
||||
K: JsCast,
|
||||
{
|
||||
Ok(self.inner.store_value(store, key, value).await?)
|
||||
}
|
||||
|
||||
async fn remove_value<K>(&self, store: &str, key: K) -> Result<(), Self::StorageError>
|
||||
where
|
||||
K: TryToJs,
|
||||
K: JsCast,
|
||||
{
|
||||
Ok(self.inner.remove_value(store, key).await?)
|
||||
}
|
||||
|
||||
async fn has_value<K>(&self, store: &str, key: K) -> Result<bool, Self::StorageError>
|
||||
where
|
||||
K: TryToJs,
|
||||
K: JsCast,
|
||||
{
|
||||
Ok(self.inner.has_value(store, key).await?)
|
||||
}
|
||||
|
||||
async fn key_count<K>(&self, store: &str, key: K) -> Result<u32, Self::StorageError>
|
||||
where
|
||||
K: TryToJs,
|
||||
K: JsCast,
|
||||
{
|
||||
Ok(self.inner.key_count(store, key).await?)
|
||||
}
|
||||
|
||||
async fn get_all_keys(&self, store: &str) -> Result<Vec<JsValue>, Self::StorageError> {
|
||||
async fn get_all_keys(&self, store: &str) -> Result<Array, Self::StorageError> {
|
||||
Ok(self.inner.get_all_keys(store).await?)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,8 +286,8 @@ pub trait WasmClientStorage: BaseWasmStorage {
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.map(|arr| {
|
||||
arr.iter()
|
||||
.cloned()
|
||||
arr.to_vec()
|
||||
.into_iter()
|
||||
.filter_map(|key| key.as_string())
|
||||
.collect()
|
||||
})
|
||||
|
||||
@@ -9,7 +9,7 @@ license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
async-trait = { workspace = true }
|
||||
getrandom = { workspace = true, features = ["js"] }
|
||||
futures = { workspace = true }
|
||||
js-sys = { workspace = true }
|
||||
wasm-bindgen = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use indexed_db_futures::web_sys::DomException;
|
||||
use serde_wasm_bindgen::Error;
|
||||
use thiserror::Error;
|
||||
use wasm_bindgen::JsValue;
|
||||
@@ -11,11 +12,15 @@ pub enum StorageError {
|
||||
#[error("{0}")]
|
||||
Json(String),
|
||||
|
||||
#[error("storage failure: {message}")]
|
||||
InternalStorageFailure { message: String },
|
||||
|
||||
#[error("failed to open the db file: {message}")]
|
||||
DbOpenFailure { message: String },
|
||||
#[error("DomException {name} ({code}): {message}")]
|
||||
DomException {
|
||||
/// DomException code
|
||||
code: u16,
|
||||
/// Specific name of the DomException
|
||||
name: String,
|
||||
/// Message given to the DomException
|
||||
message: String,
|
||||
},
|
||||
|
||||
#[error("FATAL ERROR: storage key is somehow present {count} times in the table!")]
|
||||
DuplicateKey { count: u32 },
|
||||
@@ -41,18 +46,12 @@ impl From<StorageError> for JsValue {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<indexed_db_futures::error::Error> for StorageError {
|
||||
fn from(value: indexed_db_futures::error::Error) -> Self {
|
||||
StorageError::InternalStorageFailure {
|
||||
message: value.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<indexed_db_futures::error::OpenDbError> for StorageError {
|
||||
fn from(value: indexed_db_futures::error::OpenDbError) -> Self {
|
||||
StorageError::DbOpenFailure {
|
||||
message: value.to_string(),
|
||||
impl From<DomException> for StorageError {
|
||||
fn from(value: DomException) -> StorageError {
|
||||
StorageError::DomException {
|
||||
name: value.name(),
|
||||
message: value.message(),
|
||||
code: value.code(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
// Copyright 2023-2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::cipher_export::StoredExportedStoreCipher;
|
||||
use crate::error::StorageError;
|
||||
use indexed_db_futures::transaction::TransactionMode;
|
||||
use nym_store_cipher::{
|
||||
Aes256Gcm, Algorithm, EncryptedData, KdfInfo, KeySizeUser, Params, StoreCipher, Unsigned,
|
||||
Version,
|
||||
@@ -14,10 +13,7 @@ use std::future::IntoFuture;
|
||||
use wasm_bindgen::JsValue;
|
||||
use wasm_utils::console_log;
|
||||
|
||||
pub use indexed_db_futures::database::{Database, VersionChangeEvent};
|
||||
pub use indexed_db_futures::prelude::*;
|
||||
pub use indexed_db_futures::primitive::{TryFromJs, TryToJs};
|
||||
pub use indexed_db_futures::Result as RawDbResult;
|
||||
|
||||
mod cipher_export;
|
||||
pub mod error;
|
||||
@@ -58,29 +54,31 @@ impl WasmStorage {
|
||||
passphrase: Option<&[u8]>,
|
||||
) -> Result<Self, StorageError>
|
||||
where
|
||||
F: Fn(VersionChangeEvent, Database) -> RawDbResult<()> + 'static,
|
||||
F: Fn(&IdbVersionChangeEvent) -> Result<(), JsValue> + 'static,
|
||||
{
|
||||
let mut db_req: OpenDbRequest = IdbDatabase::open_u32(db_name, version)?;
|
||||
|
||||
// we must always ensure the cipher table is present
|
||||
let db = Database::open(db_name)
|
||||
.with_version(version)
|
||||
.with_on_upgrade_needed(move |event, db| {
|
||||
db_req.set_on_upgrade_needed(Some(
|
||||
move |evt: &IdbVersionChangeEvent| -> Result<(), JsValue> {
|
||||
// Even if the web-sys bindings expose the version as a f64, the IndexedDB API
|
||||
// works with an unsigned integer.
|
||||
// See <https://github.com/rustwasm/wasm-bindgen/issues/1149>
|
||||
let old_version = event.old_version() as u32;
|
||||
let old_version = evt.old_version() as u32;
|
||||
|
||||
if old_version < 1 {
|
||||
db.create_object_store(CIPHER_INFO_STORE).build()?;
|
||||
evt.db().create_object_store(CIPHER_INFO_STORE)?;
|
||||
}
|
||||
|
||||
if let Some(migrate) = migrate_fn.as_ref() {
|
||||
migrate(event, db)
|
||||
migrate(evt)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
},
|
||||
));
|
||||
|
||||
let db: IdbDatabase = db_req.into_future().await?;
|
||||
let inner = IdbWrapper(db);
|
||||
let store_cipher = inner.setup_store_cipher(passphrase).await?;
|
||||
|
||||
@@ -96,12 +94,13 @@ impl WasmStorage {
|
||||
}
|
||||
|
||||
pub async fn remove(db_name: &str) -> Result<(), StorageError> {
|
||||
Database::delete_by_name(db_name)?.into_future().await?;
|
||||
IdbDatabase::delete_by_name(db_name)?.into_future().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn exists(db_name: &str) -> Result<bool, StorageError> {
|
||||
let db = Database::open(db_name).await?;
|
||||
let db_req: OpenDbRequest = IdbDatabase::open(db_name)?;
|
||||
let db: IdbDatabase = db_req.into_future().await?;
|
||||
|
||||
// if the db was already created before, at the very least cipher info store should exist,
|
||||
// thus the iterator should return at least one value
|
||||
@@ -140,7 +139,7 @@ impl WasmStorage {
|
||||
pub async fn read_value<T, K>(&self, store: &str, key: K) -> Result<Option<T>, StorageError>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
K: TryToJs,
|
||||
K: wasm_bindgen::JsCast,
|
||||
{
|
||||
self.inner
|
||||
.read_value_raw(store, key)
|
||||
@@ -157,7 +156,7 @@ impl WasmStorage {
|
||||
) -> Result<(), StorageError>
|
||||
where
|
||||
T: Serialize,
|
||||
K: TryToJs + TryFromJs,
|
||||
K: wasm_bindgen::JsCast,
|
||||
{
|
||||
self.inner
|
||||
.store_value_raw(store, key, &self.serialize_value(&value)?)
|
||||
@@ -166,14 +165,14 @@ impl WasmStorage {
|
||||
|
||||
pub async fn remove_value<K>(&self, store: &str, key: K) -> Result<(), StorageError>
|
||||
where
|
||||
K: TryToJs,
|
||||
K: wasm_bindgen::JsCast,
|
||||
{
|
||||
self.inner.remove_value_raw(store, key).await
|
||||
}
|
||||
|
||||
pub async fn has_value<K>(&self, store: &str, key: K) -> Result<bool, StorageError>
|
||||
where
|
||||
K: TryToJs,
|
||||
K: wasm_bindgen::JsCast,
|
||||
{
|
||||
match self.key_count(store, key).await? {
|
||||
0 => Ok(false),
|
||||
@@ -184,98 +183,82 @@ impl WasmStorage {
|
||||
|
||||
pub async fn key_count<K>(&self, store: &str, key: K) -> Result<u32, StorageError>
|
||||
where
|
||||
K: TryToJs,
|
||||
K: wasm_bindgen::JsCast,
|
||||
{
|
||||
self.inner.get_key_count(store, key).await
|
||||
}
|
||||
|
||||
pub async fn get_all_keys(&self, store: &str) -> Result<Vec<JsValue>, StorageError> {
|
||||
pub async fn get_all_keys(&self, store: &str) -> Result<js_sys::Array, StorageError> {
|
||||
self.inner.get_all_keys(store).await
|
||||
}
|
||||
}
|
||||
|
||||
struct IdbWrapper(Database);
|
||||
struct IdbWrapper(IdbDatabase);
|
||||
|
||||
impl IdbWrapper {
|
||||
async fn read_value_raw<K>(&self, store: &str, key: K) -> Result<Option<JsValue>, StorageError>
|
||||
where
|
||||
K: TryToJs,
|
||||
K: wasm_bindgen::JsCast,
|
||||
{
|
||||
self.0
|
||||
.transaction(store)
|
||||
.with_mode(TransactionMode::Readonly)
|
||||
.build()?
|
||||
.transaction_on_one_with_mode(store, IdbTransactionMode::Readonly)?
|
||||
.object_store(store)?
|
||||
.get(&key)
|
||||
.primitive()?
|
||||
.get(&key)?
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
async fn store_value_raw<K, T>(
|
||||
async fn store_value_raw<K>(
|
||||
&self,
|
||||
store: &str,
|
||||
key: K,
|
||||
value: &T,
|
||||
value: &JsValue,
|
||||
) -> Result<(), StorageError>
|
||||
where
|
||||
K: TryToJs + TryFromJs,
|
||||
T: TryToJs,
|
||||
K: wasm_bindgen::JsCast,
|
||||
{
|
||||
let tx = self
|
||||
.0
|
||||
.transaction(store)
|
||||
.with_mode(TransactionMode::Readwrite)
|
||||
.build()?;
|
||||
|
||||
let store = tx.object_store(store)?;
|
||||
store.put(value).with_key(key).primitive()?.await?;
|
||||
|
||||
tx.commit().await.map_err(Into::into)
|
||||
self.0
|
||||
.transaction_on_one_with_mode(store, IdbTransactionMode::Readwrite)?
|
||||
.object_store(store)?
|
||||
.put_key_val_owned(key, value)?
|
||||
.into_future()
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
async fn remove_value_raw<K>(&self, store: &str, key: K) -> Result<(), StorageError>
|
||||
where
|
||||
K: TryToJs,
|
||||
{
|
||||
let tx = self
|
||||
.0
|
||||
.transaction(store)
|
||||
.with_mode(TransactionMode::Readwrite)
|
||||
.build()?;
|
||||
|
||||
let store = tx.object_store(store)?;
|
||||
store.delete(key).primitive()?.await?;
|
||||
|
||||
tx.commit().await.map_err(Into::into)
|
||||
}
|
||||
|
||||
async fn get_key_count<K>(&self, store: &str, key: K) -> Result<u32, StorageError>
|
||||
where
|
||||
K: TryToJs,
|
||||
K: wasm_bindgen::JsCast,
|
||||
{
|
||||
self.0
|
||||
.transaction(store)
|
||||
.with_mode(TransactionMode::Readonly)
|
||||
.build()?
|
||||
.transaction_on_one_with_mode(store, IdbTransactionMode::Readwrite)?
|
||||
.object_store(store)?
|
||||
.count()
|
||||
.with_query(key)
|
||||
.primitive()?
|
||||
.delete_owned(key)?
|
||||
.into_future()
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
async fn get_all_keys(&self, store: &str) -> Result<Vec<JsValue>, StorageError> {
|
||||
async fn get_key_count<K>(&self, store: &str, key: K) -> Result<u32, StorageError>
|
||||
where
|
||||
K: wasm_bindgen::JsCast,
|
||||
{
|
||||
self.0
|
||||
.transaction(store)
|
||||
.with_mode(TransactionMode::Readonly)
|
||||
.build()?
|
||||
.transaction_on_one_with_mode(store, IdbTransactionMode::Readwrite)?
|
||||
.object_store(store)?
|
||||
.get_all_keys()
|
||||
.primitive()?
|
||||
.await?
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.count_with_key_owned(key)?
|
||||
.into_future()
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
async fn get_all_keys(&self, store: &str) -> Result<js_sys::Array, StorageError> {
|
||||
self.0
|
||||
.transaction_on_one_with_mode(store, IdbTransactionMode::Readonly)?
|
||||
.object_store(store)?
|
||||
.get_all_keys()?
|
||||
.into_future()
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,11 +3,10 @@
|
||||
|
||||
use crate::WasmStorage;
|
||||
use async_trait::async_trait;
|
||||
use indexed_db_futures::primitive::{TryFromJs, TryToJs};
|
||||
use js_sys::Array;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
use std::error::Error;
|
||||
use wasm_bindgen::JsValue;
|
||||
|
||||
#[async_trait(?Send)]
|
||||
pub trait BaseWasmStorage {
|
||||
@@ -18,7 +17,7 @@ pub trait BaseWasmStorage {
|
||||
async fn read_value<T, K>(&self, store: &str, key: K) -> Result<Option<T>, Self::StorageError>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
K: TryToJs;
|
||||
K: wasm_bindgen::JsCast;
|
||||
|
||||
async fn store_value<T, K>(
|
||||
&self,
|
||||
@@ -28,21 +27,21 @@ pub trait BaseWasmStorage {
|
||||
) -> Result<(), Self::StorageError>
|
||||
where
|
||||
T: Serialize,
|
||||
K: TryToJs + TryFromJs;
|
||||
K: wasm_bindgen::JsCast;
|
||||
|
||||
async fn remove_value<K>(&self, store: &str, key: K) -> Result<(), Self::StorageError>
|
||||
where
|
||||
K: TryToJs;
|
||||
K: wasm_bindgen::JsCast;
|
||||
|
||||
async fn has_value<K>(&self, store: &str, key: K) -> Result<bool, Self::StorageError>
|
||||
where
|
||||
K: TryToJs;
|
||||
K: wasm_bindgen::JsCast;
|
||||
|
||||
async fn key_count<K>(&self, store: &str, key: K) -> Result<u32, Self::StorageError>
|
||||
where
|
||||
K: TryToJs;
|
||||
K: wasm_bindgen::JsCast;
|
||||
|
||||
async fn get_all_keys(&self, store: &str) -> Result<Vec<JsValue>, Self::StorageError>;
|
||||
async fn get_all_keys(&self, store: &str) -> Result<js_sys::Array, Self::StorageError>;
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
@@ -56,7 +55,7 @@ impl BaseWasmStorage for WasmStorage {
|
||||
async fn read_value<T, K>(&self, store: &str, key: K) -> Result<Option<T>, Self::StorageError>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
K: TryToJs,
|
||||
K: wasm_bindgen::JsCast,
|
||||
{
|
||||
self.read_value(store, key).await
|
||||
}
|
||||
@@ -69,33 +68,33 @@ impl BaseWasmStorage for WasmStorage {
|
||||
) -> Result<(), Self::StorageError>
|
||||
where
|
||||
T: Serialize,
|
||||
K: TryToJs + TryFromJs,
|
||||
K: wasm_bindgen::JsCast,
|
||||
{
|
||||
self.store_value(store, key, value).await
|
||||
}
|
||||
|
||||
async fn remove_value<K>(&self, store: &str, key: K) -> Result<(), Self::StorageError>
|
||||
where
|
||||
K: TryToJs,
|
||||
K: wasm_bindgen::JsCast,
|
||||
{
|
||||
self.remove_value(store, key).await
|
||||
}
|
||||
|
||||
async fn has_value<K>(&self, store: &str, key: K) -> Result<bool, Self::StorageError>
|
||||
where
|
||||
K: TryToJs,
|
||||
K: wasm_bindgen::JsCast,
|
||||
{
|
||||
self.has_value(store, key).await
|
||||
}
|
||||
|
||||
async fn key_count<K>(&self, store: &str, key: K) -> Result<u32, Self::StorageError>
|
||||
where
|
||||
K: TryToJs,
|
||||
K: wasm_bindgen::JsCast,
|
||||
{
|
||||
self.key_count(store, key).await
|
||||
}
|
||||
|
||||
async fn get_all_keys(&self, store: &str) -> Result<Vec<JsValue>, Self::StorageError> {
|
||||
async fn get_all_keys(&self, store: &str) -> Result<Array, Self::StorageError> {
|
||||
self.get_all_keys(store).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,7 +325,7 @@ impl PeerController {
|
||||
log::error!("Can't read wireguard kernel data");
|
||||
continue;
|
||||
};
|
||||
self.update_metrics(&host).await;
|
||||
self.update_metrics(&host);
|
||||
|
||||
*self.host_information.write().await = host;
|
||||
}
|
||||
|
||||
Generated
+39
-59
@@ -179,7 +179,7 @@ dependencies = [
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 1.0.64",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -233,7 +233,7 @@ dependencies = [
|
||||
"schemars",
|
||||
"serde",
|
||||
"subtle-encoding",
|
||||
"thiserror 1.0.64",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -267,7 +267,7 @@ dependencies = [
|
||||
"ed25519-zebra",
|
||||
"k256 0.13.3",
|
||||
"rand_core 0.6.4",
|
||||
"thiserror 1.0.64",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -289,7 +289,7 @@ dependencies = [
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 1.0.64",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -320,7 +320,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde-json-wasm",
|
||||
"sha2 0.10.8",
|
||||
"thiserror 1.0.64",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -433,7 +433,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -448,7 +448,7 @@ dependencies = [
|
||||
"cw-utils",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror 1.0.64",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -467,7 +467,7 @@ dependencies = [
|
||||
"prost",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror 1.0.64",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -493,7 +493,7 @@ dependencies = [
|
||||
"schemars",
|
||||
"semver",
|
||||
"serde",
|
||||
"thiserror 1.0.64",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -508,7 +508,7 @@ dependencies = [
|
||||
"schemars",
|
||||
"semver",
|
||||
"serde",
|
||||
"thiserror 1.0.64",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -538,7 +538,7 @@ dependencies = [
|
||||
"schemars",
|
||||
"semver",
|
||||
"serde",
|
||||
"thiserror 1.0.64",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -553,7 +553,7 @@ dependencies = [
|
||||
"cw20",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror 1.0.64",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -570,7 +570,7 @@ dependencies = [
|
||||
"cw3",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror 1.0.64",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -622,7 +622,7 @@ dependencies = [
|
||||
"nym-group-contract-common",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror 1.0.64",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1140,7 +1140,7 @@ dependencies = [
|
||||
"nym-coconut-bandwidth-contract-common",
|
||||
"nym-multisig-contract-common",
|
||||
"serde",
|
||||
"thiserror 1.0.64",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1170,7 +1170,7 @@ dependencies = [
|
||||
"nym-contracts-common",
|
||||
"nym-group-contract-common",
|
||||
"serde",
|
||||
"thiserror 1.0.64",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1196,7 +1196,7 @@ dependencies = [
|
||||
"cw-storage-plus",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror",
|
||||
"vergen",
|
||||
]
|
||||
|
||||
@@ -1210,7 +1210,7 @@ dependencies = [
|
||||
"nym-sphinx-types",
|
||||
"rand",
|
||||
"subtle-encoding",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror",
|
||||
"x25519-dalek",
|
||||
"zeroize",
|
||||
]
|
||||
@@ -1240,7 +1240,7 @@ dependencies = [
|
||||
"semver",
|
||||
"serde",
|
||||
"sylvia",
|
||||
"thiserror 1.0.64",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1254,7 +1254,7 @@ dependencies = [
|
||||
"cw-utils",
|
||||
"cw2",
|
||||
"nym-multisig-contract-common",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1289,7 +1289,7 @@ dependencies = [
|
||||
"rand_chacha",
|
||||
"semver",
|
||||
"serde",
|
||||
"thiserror 1.0.64",
|
||||
"thiserror",
|
||||
"time",
|
||||
]
|
||||
|
||||
@@ -1310,7 +1310,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde-json-wasm",
|
||||
"serde_repr",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror",
|
||||
"time",
|
||||
]
|
||||
|
||||
@@ -1326,7 +1326,7 @@ dependencies = [
|
||||
"cw4",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1349,7 +1349,7 @@ name = "nym-sphinx-types"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"sphinx-packet",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1370,7 +1370,7 @@ dependencies = [
|
||||
"rand_chacha",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 1.0.64",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1383,7 +1383,7 @@ dependencies = [
|
||||
"nym-contracts-common",
|
||||
"nym-mixnet-contract-common",
|
||||
"serde",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1489,9 +1489,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.93"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
||||
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -1666,7 +1666,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde_derive_internals",
|
||||
"syn 2.0.98",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1699,9 +1699,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.25"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03"
|
||||
checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -1741,7 +1741,7 @@ checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1752,7 +1752,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1775,7 +1775,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1934,9 +1934,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.98"
|
||||
version = "2.0.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
|
||||
checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1949,16 +1949,7 @@ version = "1.0.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84"
|
||||
dependencies = [
|
||||
"thiserror-impl 1.0.64",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
|
||||
dependencies = [
|
||||
"thiserror-impl 2.0.11",
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1969,18 +1960,7 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2146,5 +2126,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
@@ -10,7 +10,7 @@ import Stack from '@mui/material/Stack';
|
||||
import Paper from '@mui/material/Paper';
|
||||
import type { SetupMixFetchOps } from '@nymproject/mix-fetch';
|
||||
|
||||
const defaultUrl = 'https://nym.com/favicon.svg';
|
||||
const defaultUrl = 'https://nymtech.net/favicon.svg';
|
||||
const args = { mode: 'unsafe-ignore-cors' };
|
||||
|
||||
const mixFetchOptions: SetupMixFetchOps = {
|
||||
|
||||
@@ -11,38 +11,22 @@ Tor community created a very helpful table called [*Good Bad ISPs*](https://comm
|
||||
|
||||
<div>
|
||||
<Tabs items={[
|
||||
<><code>nym-node</code> - dedicated server</>,
|
||||
<><code>nym-node</code> - VPS</>,
|
||||
<code>nym-node</code>,
|
||||
<code>validator</code>,
|
||||
]} defaultIndex="0">
|
||||
<MyTab>
|
||||
#### `nym-node` - dedicated server
|
||||
If you are about to start a new Nym Node, look for a server with this specification:
|
||||
#### `nym-node`
|
||||
Before we conclude the testing with exact results, these are the rough specs:
|
||||
|
||||
| **Hardware** | **Minimum Specification** |
|
||||
| :--- | ---: |
|
||||
| Type of server | Dedicated or bare metal |
|
||||
| CPU Cores | 4 |
|
||||
| Memory | 8-16 GB RAM |
|
||||
| Storage | 50 GB (preferably 80 GB) |
|
||||
| Connectivity | IPv4, IPv6, TCP/IP, UDP |
|
||||
| Bandwidth | Unmetered if possible, or \> 40Tb |
|
||||
| Port speed | 1Gbps |
|
||||
| **Hardware** | **Minimum Specification** |
|
||||
| :--- | ---: |
|
||||
| CPU Cores | 4 |
|
||||
| Memory | 8 GB RAM |
|
||||
| Storage | 80 GB |
|
||||
| Connectivity | IPv4, IPv6, TCP/IP, UDP |
|
||||
| Bandwidth | > 1Tb |
|
||||
| Port speed | 1Gbps |
|
||||
|
||||
</MyTab>
|
||||
<MyTab>
|
||||
#### `nym-node` - VPS
|
||||
If you are about to start a new Nym Node, look for a server with this specification:
|
||||
|
||||
| **Hardware** | **Minimum Specification** |
|
||||
| :--- | ---: |
|
||||
| Type of server | VPS |
|
||||
| CPU Cores | 4 |
|
||||
| Memory | 8-16 GB RAM |
|
||||
| Storage | 50 GB (preferably 80 GB) |
|
||||
| Connectivity | IPv4, IPv6, TCP/IP, UDP |
|
||||
| Bandwidth | Unmetered if possible, or \> 40Tb |
|
||||
| Port speed | 10Gbps |
|
||||
</MyTab>
|
||||
<MyTab>
|
||||
#### Nyx validator
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import { Callout } from 'nextra/components';
|
||||
|
||||
### Terms & Conditions
|
||||
|
||||
<Callout type="info" emoji="ℹ️">
|
||||
From `nym-node` version `1.1.3` onward is required to accept [**Operators Terms & Conditions**](https://nymtech.net/terms-and-conditions/operators/v1.0.0) in order to be part of the active set. Make sure to read them before you add the flag.
|
||||
</Callout>
|
||||
|
||||
There has been a long ongoing discussion whether and how to apply Terms and Conditions for Nym network operators, with an aim to stay aligned with the philosophy of Free Software and provide legal defense for both node operators and Nym developers. To understand better the reasoning behind this decision, you can listen to the first [Nym Operator Town Hall](https://www.youtube.com/live/7hwb8bAZIuc?si=3mQ2ed7AyUA1SsCp&t=915) introducing the T&Cs or to [Operator AMA with CEO Harry Halpin](https://www.youtube.com/watch?v=yIN-zYQw0I0) from June 4th, 2024, explaining pros and cons of T&Cs implementation.
|
||||
@@ -1,37 +0,0 @@
|
||||
```
|
||||
Thank you for forwarding me the notice you received from <COPYRIGHT_CLAIMANT> regarding <CONTENT>. I would like to assure you that I am not hosting the claimed infringing materials, and furthermore, the Digital Millennium Copyright Act's ("DMCA") safe harbors likely protect you from liability arising from this complaint. The notice is likely based upon misunderstandings about the law and about some of the software I run.
|
||||
|
||||
As you know, the DMCA creates four "safe harbors" for service providers to protect them from copyright liability for the acts of their users, when the ISPs fulfill certain requirements. (17 U.S.C. 512)
|
||||
|
||||
The DMCA's requirements vary depending on the ISP's role. You may be familiar with the "notice and takedown" provisions of section 512(c) of the DMCA; however, those do not apply when an ISP merely acts as a conduit.
|
||||
Instead, the "conduit" safe harbor of section 512(a) of the DMCA has different and less burdensome eligibility requirements, as the D.C. Circuit Court of Appeals held in RIAA v. Verizon (see https://scholar.google.com/scholar_case?case=15815830240179540527) and the Eighth Circuit Court of Appeals confirmed in RIAA v. Charter (see https://scholar.google.com/scholar_case?case=11547531128234336420).
|
||||
|
||||
Under DMCA 512(a), service providers like you are typically protected from damages for copyright infringement claims if you also maintain "a policy that provides for termination in appropriate circumstances of subscribers and account holders of the service provider's system or network who are repeat infringers."
|
||||
|
||||
If you have and implement such a policy, and you otherwise qualify for the safe harbor, you should be free from fear of copyright damages.
|
||||
|
||||
The copyright notice you received was likely triggered by a program I run called Nym. Nym is a network software that helps users to enhance their privacy, security, and safety online. It does not host any content. Rather, it is part of a network of nodes on the Internet that simply pass packets among themselves before sending them to their destinations, just as any Internet intermediary does.
|
||||
|
||||
The difference is that Nym tunnels the connections such that no hop can learn both the source and destination of the packets, giving users protection from nefarious snooping on network traffic. The result is that, unlike most other Internet traffic, the final IP address that the recipient receives is not the IP address of the sender. Nym protects users against hazards such as harassment, spam, and identity theft.
|
||||
|
||||
Nym was conceived during the EU-funded project 'Panoramix' R&D project (Horizon 2020 programme) with the goal of protecting communication privacy by building a comprehensive mixnet infrastructure. This project has received funding from the European Union’s Horizon 2020 research and innovation programme under the Grant Agreement No 653497, "Privacy and Accountability in Networks via Optimized Randomized Mix-nets (Panoramix)”. (For more on Nym, see https://www.nym.com/, For more on Panoramix, see https://panoramix.me/ ). Nym has continued this work by building a decentralized routing, authentication and payment protocol. Nym will enable developers to build their own sustainable privacy-enhanced services on top of its platform.
|
||||
|
||||
I hope, as an organization committed to protecting the privacy of its customers, you'll agree that this is a valuable technology.
|
||||
|
||||
While the Nym node that I run may appear to be the source of material that is alleged to be copyright-infringing, I do not host that material.
|
||||
|
||||
I do not select the material transmitted through the Nym node that I run, and I have no practical means of either identifying the source of such material or preventing its transmission. I do nothing to encourage or promote the use of the Nym network for copyright infringement.
|
||||
|
||||
For these reasons, I am not an infringer of copyright in any materials that are transmitted through the Nym node that I run, either directly or under a theory of contributory or vicarious liability.
|
||||
|
||||
Therefore, you should continue to be protected under the DMCA 512(a) safe harbor without taking any further action.
|
||||
|
||||
Thank you for working with me on this matter. As a loyal subscriber, I appreciate your notifying me of this issue and hope that the protections of DMCA 512 put any concerns you may have to rest.
|
||||
|
||||
If not, please contact me with any further questions.
|
||||
|
||||
Very truly yours,
|
||||
|
||||
Your customer, <YOUR_NAME/PSEUDONYM>
|
||||
```
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
```
|
||||
I am reaching out to introduce myself. I am about to spin up a machine with you to run what is called a “nym node” - think of it as somewhat similar to a Tor exit node. You can always recognize a nym node by our domain names: nym-exit
|
||||
|
||||
Nym node runners are a decentralized community all over the world. We provide secure internet traffic routing services and privacy protection to ordinary people and businesses via the Nym platform and NymVPN app.
|
||||
|
||||
The Nym traffic pattern is somewhat unique, as we route traffic using the “sphinx” packet format (similar to Tor 's onion encryption), which makes all traffic look uniform. I wanted to introduce myself and the machines I am running just so that this unique traffic pattern doesn’t raise any flags or issues with you!
|
||||
|
||||
We have the highest security standards, which also means I simply relay traffic and do not know the end-destination nor its content. I can not be selective about the material transmitted through the Nym node that I run, and I have no practical means of either identifying the source of such material or preventing its transmission. More information can be found at https://nym.com.
|
||||
|
||||
Feel free to ask any questions.
|
||||
|
||||
Many thanks,
|
||||
|
||||
<YOUR_NAME/PSEUDONYM>
|
||||
```
|
||||
+1
-1
@@ -1 +1 @@
|
||||
807_251_217
|
||||
805_903_308
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
44.332
|
||||
44.811
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
1_025_628
|
||||
1_022_821
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
403_625_608
|
||||
402_951_654
|
||||
|
||||
+4
-4
@@ -1,7 +1,7 @@
|
||||
| **Item** | **Description** | **Amount in NYM** |
|
||||
|:-------------------|:------------------------------------------------------|--------------------:|
|
||||
| Total Supply | Maximum amount of NYM token in existence | 1_000_000_000 |
|
||||
| Mixmining Reserve | Tokens releasing for operators rewards | 192_748_782 |
|
||||
| Vesting Tokens | Tokens locked outside of cicrulation for future claim | 0 |
|
||||
| Circulating Supply | Amount of unlocked tokens | 807_251_217 |
|
||||
| Stake Saturation | Optimal size of node self-bond + delegation | 1_025_628 |
|
||||
| Mixmining Reserve | Tokens releasing for operators rewards | 194_096_191 |
|
||||
| Vesting Tokens | Tokens locked outside of cicrulation for future claim | 500 |
|
||||
| Circulating Supply | Amount of unlocked tokens | 805_903_308 |
|
||||
| Stake Saturation | Optimal size of node self-bond + delegation | 1_022_821 |
|
||||
|
||||
@@ -1 +1 @@
|
||||
Monday, February 3rd 2025, 13:47:19 UTC
|
||||
Wednesday, January 8th 2025, 15:01:28 UTC
|
||||
|
||||
@@ -497,6 +497,13 @@ const config = {
|
||||
permanent: true,
|
||||
basePath: false,
|
||||
},
|
||||
{
|
||||
source: "/operators/testing/node-api-check.html",
|
||||
destination:
|
||||
"/docs/operators/nodes/performance-and-testing/node-api-check",
|
||||
permanent: true,
|
||||
basePath: false,
|
||||
},
|
||||
{
|
||||
source: "/operators/testing/prometheus-grafana.html",
|
||||
destination:
|
||||
@@ -656,6 +663,13 @@ const config = {
|
||||
permanent: true,
|
||||
basePath: false,
|
||||
},
|
||||
{
|
||||
source: "/docs/operators/testing/node-api-check",
|
||||
destination:
|
||||
"/docs/operators/nodes/performance-and-testing/node-api-check",
|
||||
permanent: true,
|
||||
basePath: false,
|
||||
},
|
||||
{
|
||||
source: "/docs/operators/nodes/proxy-configuration",
|
||||
destination:
|
||||
|
||||
@@ -4,8 +4,6 @@ The information below is generated with [Redoc](https://redocly.com/docs/redoc)
|
||||
|
||||
There is also an overview of other Validator endpoints at [https://cosmos.directory/nyx](https://cosmos.directory/nyx).
|
||||
|
||||
<br /><br />
|
||||
|
||||
<RedocStandalone
|
||||
specUrl="https://api.nymtech.net/swagger/swagger.yaml"
|
||||
options={{
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { RedocStandalone } from 'redoc';
|
||||
|
||||
The information below is generated with [Redoc](https://redocly.com/docs/redoc) consuming the OpenAPI spec found at [https://api.sandbox.nymtech.net/swagger/swagger.yaml](https://api.sandbox.nymtech.net/swagger/swagger.yaml).
|
||||
|
||||
<br /><br />
|
||||
The information below is generated with [Redoc](https://redocly.com/docs/redoc) consuming the OpenAPI spec found at []() which is also used to generate the Swagger docs deployed at []().
|
||||
|
||||
<RedocStandalone
|
||||
specUrl="https://api.sandbox.nymtech.net/swagger/swagger.yaml"
|
||||
specUrl=""
|
||||
options={{
|
||||
nativeScrollbars: true,
|
||||
theme: {
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
|
||||
The Explorer API is the backend for the [Mixnet Explorer](https://explorer.nymtech.net/).
|
||||
|
||||
**This will soon be deprecated in favour of the [Node Status API](ns-api.mdx).**
|
||||
**This will soon be deprecated in favour of the [Node Status API](../ns-api).**
|
||||
|
||||
The code for this service can be found [in our monorepo](https://github.com/nymtech/nym/tree/develop/explorer).
|
||||
|
||||
@@ -3,8 +3,6 @@ import { RedocStandalone } from 'redoc';
|
||||
|
||||
The information below is generated with [Redoc](https://redocly.com/docs/redoc) consuming the OpenAPI spec found at [https://explorer.nymtech.net/api/v1/openapi.json](https://explorer.nymtech.net/api/v1/openapi.json) which is also used to generate the Swagger docs deployed at [https://explorer.nymtech.net/api/swagger/index.html](https://explorer.nymtech.net/api/swagger/index.html).
|
||||
|
||||
<br /><br />
|
||||
|
||||
<RedocStandalone
|
||||
specUrl="https://sandbox-explorer.nymtech.net/api/v1/openapi.json"
|
||||
options={{
|
||||
|
||||
@@ -2,8 +2,6 @@ import { RedocStandalone } from 'redoc';
|
||||
|
||||
The information below is generated with [Redoc](https://redocly.com/docs/redoc) consuming the OpenAPI spec found at [https://sandbox-explorer.nymtech.net/api/v1/openapi.json](https://sandbox-explorer.nymtech.net/api/v1/openapi.json) which is also used to generate the Swagger docs deployed at [https://sandbox-explorer.nymtech.net/api/swagger/index.html](https://sandbox-explorer.nymtech.net/api/swagger/index.html).
|
||||
|
||||
<br /><br />
|
||||
|
||||
<RedocStandalone
|
||||
specUrl="https://sandbox-explorer.nymtech.net/api/v1/openapi.json"
|
||||
options={{
|
||||
|
||||
@@ -2,8 +2,6 @@ import { RedocStandalone } from 'redoc';
|
||||
|
||||
The information below is generated with [Redoc](https://redocly.com/docs/redoc) consuming the OpenAPI spec found at [https://mainnet-node-status-api.nymtech.cc/api-docs/openapi.json](https://mainnet-node-status-api.nymtech.cc/api-docs/openapi.json) which is also used to generate the Swagger docs deployed at [https://mainnet-node-status-api.nymtech.cc/swagger/](https://mainnet-node-status-api.nymtech.cc/swagger/).
|
||||
|
||||
<br /><br />
|
||||
|
||||
<RedocStandalone
|
||||
specUrl="https://mainnet-node-status-api.nymtech.cc/api-docs/openapi.json"
|
||||
options={{
|
||||
|
||||
@@ -2,8 +2,6 @@ import { RedocStandalone } from 'redoc';
|
||||
|
||||
The information below is generated with [Redoc](https://redocly.com/docs/redoc) consuming the OpenAPI spec found at [https://sandbox-node-status-api.nymte.ch/api-docs/openapi.json](https://sandbox-node-status-api.nymte.ch/api-docs/openapi.json) which is also used to generate the Swagger docs deployed at [https://sandbox-node-status-api.nymte.ch/swagger/](https://sandbox-node-status-api.nymte.ch/swagger/).
|
||||
|
||||
<br /><br />
|
||||
|
||||
<RedocStandalone
|
||||
specUrl="https://sandbox-node-status-api.nymte.ch/api-docs/openapi.json"
|
||||
options={{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user