Compare commits

..

1 Commits

Author SHA1 Message Date
serinko 1ef33ce797 initialise branch - leaving for later focus now 2025-01-29 12:08:24 +01:00
255 changed files with 4695 additions and 7256 deletions
-14
View File
@@ -54,20 +54,6 @@ jobs:
override: true override: true
components: rustfmt, clippy components: rustfmt, clippy
# To avoid running out of disk space, skip generating debug symbols
- name: Set debug to false (unix)
if: contains(matrix.os, 'ubuntu') || contains(matrix.os, 'mac')
run: |
sed -i.bak 's/\[profile.dev\]/\[profile.dev\]\ndebug = false/' Cargo.toml
git diff
- name: Set debug to false (win)
if: contains(matrix.os, 'windows')
shell: pwsh
run: |
(Get-Content Cargo.toml) -replace '\[profile.dev\]', "`$&`ndebug = false" | Set-Content Cargo.toml
git diff
- name: Check formatting - name: Check formatting
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
-2
View File
@@ -9,8 +9,6 @@ on:
paths: paths:
- 'contracts/**' - 'contracts/**'
- 'common/**' - 'common/**'
- 'Cargo.lock'
- 'Cargo.toml'
- '.github/workflows/ci-contracts.yml' - '.github/workflows/ci-contracts.yml'
jobs: jobs:
-6
View File
@@ -30,12 +30,6 @@ jobs:
override: true override: true
components: rustfmt, clippy components: rustfmt, clippy
- name: Set debug to false
working-directory: nym-wallet
run: |
sed -i.bak '1s/^/\[profile.dev\]\ndebug = false\n\n/' Cargo.toml
git diff
- name: Build all binaries - name: Build all binaries
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
-5
View File
@@ -45,11 +45,6 @@ jobs:
- name: Install wasm-bindgen-cli - name: Install wasm-bindgen-cli
run: cargo install wasm-bindgen-cli run: cargo install wasm-bindgen-cli
- name: Set debug to false
run: |
sed -i.bak 's/\[profile.dev\]/\[profile.dev\]\ndebug = false/' Cargo.toml
git diff
- name: "Build" - name: "Build"
run: make sdk-wasm-build run: make sdk-wasm-build
-116
View File
@@ -4,122 +4,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased] ## [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) ## [2025.1-reeses] (2025-01-15)
- Feture/legacy alert ([#5346]) - Feture/legacy alert ([#5346])
Generated
+1081 -1268
View File
File diff suppressed because it is too large Load Diff
+30 -36
View File
@@ -48,6 +48,7 @@ members = [
"common/credentials-interface", "common/credentials-interface",
"common/crypto", "common/crypto",
"common/dkg", "common/dkg",
"common/ecash-double-spending",
"common/ecash-time", "common/ecash-time",
"common/execute", "common/execute",
"common/exit-policy", "common/exit-policy",
@@ -187,21 +188,18 @@ readme = "README.md"
[workspace.dependencies] [workspace.dependencies]
addr = "0.15.6" addr = "0.15.6"
aead = "0.5.2"
aes = "0.8.1" aes = "0.8.1"
aes-gcm = "0.10.1" aes-gcm = "0.10.1"
aes-gcm-siv = "0.11.1" aes-gcm-siv = "0.11.1"
ammonia = "4" aead = "0.5.2"
anyhow = "1.0.95" anyhow = "1.0.95"
arc-swap = "1.7.1" arc-swap = "1.7.1"
argon2 = "0.5.0" argon2 = "0.5.0"
async-trait = "0.1.86" async-trait = "0.1.85"
axum = "0.7.5"
axum-client-ip = "0.6.1" axum-client-ip = "0.6.1"
axum = "0.7.5"
axum-extra = "0.9.4" axum-extra = "0.9.4"
axum-test = "16.2.0"
base64 = "0.22.1" base64 = "0.22.1"
base85rs = "0.1.3"
bincode = "1.3.3" bincode = "1.3.3"
bip39 = { version = "2.0.0", features = ["zeroize"] } bip39 = { version = "2.0.0", features = ["zeroize"] }
bit-vec = "0.7.0" # can we unify those? bit-vec = "0.7.0" # can we unify those?
@@ -212,17 +210,17 @@ bs58 = "0.5.1"
bytecodec = "0.4.15" bytecodec = "0.4.15"
bytes = "1.7.2" bytes = "1.7.2"
cargo_metadata = "0.18.1" cargo_metadata = "0.18.1"
celes = "2.5.0" celes = "2.4.0"
cfg-if = "1.0.0" cfg-if = "1.0.0"
chacha20 = "0.9.0" chacha20 = "0.9.0"
chacha20poly1305 = "0.10.1" chacha20poly1305 = "0.10.1"
chrono = "0.4.39" chrono = "0.4.39"
cipher = "0.4.3" cipher = "0.4.3"
clap = "4.5.30" clap = "4.5.26"
clap_complete = "4.5" clap_complete = "4.5"
clap_complete_fig = "4.5" clap_complete_fig = "4.5"
colored = "2.2" colored = "2.0"
comfy-table = "7.1.4" comfy-table = "7.1.3"
console = "0.15.10" console = "0.15.10"
console-subscriber = "0.1.1" console-subscriber = "0.1.1"
console_error_panic_hook = "0.1" console_error_panic_hook = "0.1"
@@ -242,9 +240,8 @@ doc-comment = "0.3"
dotenvy = "0.15.6" dotenvy = "0.15.6"
ecdsa = "0.16" ecdsa = "0.16"
ed25519-dalek = "2.1" ed25519-dalek = "2.1"
env_logger = "0.11.6"
envy = "0.4"
etherparse = "0.13.0" etherparse = "0.13.0"
envy = "0.4"
eyre = "0.6.9" eyre = "0.6.9"
fastrand = "2.1.1" fastrand = "2.1.1"
flate2 = "1.0.35" flate2 = "1.0.35"
@@ -252,23 +249,23 @@ futures = "0.3.31"
futures-util = "0.3" futures-util = "0.3"
generic-array = "0.14.7" generic-array = "0.14.7"
getrandom = "0.2.10" getrandom = "0.2.10"
getset = "0.1.4" getset = "0.1.3"
handlebars = "3.5.5" handlebars = "3.5.5"
headers = "0.4.0" headers = "0.4.0"
hex = "0.4.3" hex = "0.4.3"
hex-literal = "0.3.3" hex-literal = "0.3.3"
hickory-resolver = "0.24.3" hickory-resolver = "0.24.2"
hkdf = "0.12.3" hkdf = "0.12.3"
hmac = "0.12.1" hmac = "0.12.1"
http = "1" http = "1"
http-body-util = "0.1" http-body-util = "0.1"
httpcodec = "0.2.3" httpcodec = "0.2.3"
human-repr = "1.1.0"
humantime = "2.1.0" humantime = "2.1.0"
humantime-serde = "1.1.1" humantime-serde = "1.1.1"
hyper = "1.6.0" human-repr = "1.1.0"
hyper = "1.4.1"
hyper-util = "0.1" hyper-util = "0.1"
indicatif = "0.17.11" indicatif = "0.17.9"
inquire = "0.6.2" inquire = "0.6.2"
ip_network = "0.4.1" ip_network = "0.4.1"
ipnetwork = "0.20" ipnetwork = "0.20"
@@ -280,12 +277,13 @@ ledger-transport = "0.10.0"
ledger-transport-hid = "0.10.0" ledger-transport-hid = "0.10.0"
log = "0.4" log = "0.4"
maxminddb = "0.23.0" maxminddb = "0.23.0"
rs_merkle = "1.4.2"
mime = "0.3.17" mime = "0.3.17"
moka = { version = "0.12", features = ["future"] } moka = { version = "0.12", features = ["future"] }
nix = "0.27.1" nix = "0.27.1"
notify = "5.1.0" notify = "5.1.0"
okapi = "0.7.0" okapi = "0.7.0"
once_cell = "1.20.3" once_cell = "1.20.2"
opentelemetry = "0.19.0" opentelemetry = "0.19.0"
opentelemetry-jaeger = "0.18.0" opentelemetry-jaeger = "0.18.0"
parking_lot = "0.12.3" parking_lot = "0.12.3"
@@ -294,7 +292,7 @@ petgraph = "0.6.5"
pin-project = "1.1" pin-project = "1.1"
pin-project-lite = "0.2.16" pin-project-lite = "0.2.16"
pretty_env_logger = "0.4.0" pretty_env_logger = "0.4.0"
publicsuffix = "2.3.0" publicsuffix = "2.2.3"
quote = "1" quote = "1"
rand = "0.8.5" rand = "0.8.5"
rand_chacha = "0.3" rand_chacha = "0.3"
@@ -308,21 +306,20 @@ reqwest = { version = "0.12.4", default-features = false }
rocket = "0.5.0" rocket = "0.5.0"
rocket_cors = "0.6.0" rocket_cors = "0.6.0"
rocket_okapi = "0.8.0" rocket_okapi = "0.8.0"
rs_merkle = "1.4.2"
safer-ffi = "0.1.13" safer-ffi = "0.1.13"
schemars = "0.8.21" schemars = "0.8.21"
semver = "1.0.25" semver = "1.0.24"
serde = "1.0.217" serde = "1.0.217"
serde_bytes = "0.11.15" serde_bytes = "0.11.15"
serde_derive = "1.0" serde_derive = "1.0"
serde_json = "1.0.138" serde_json = "1.0.135"
serde_json_path = "0.7.2" serde_json_path = "0.7.1"
serde_repr = "0.1" serde_repr = "0.1"
serde_with = "3.9.0" serde_with = "3.9.0"
serde_yaml = "0.9.25" serde_yaml = "0.9.25"
sha2 = "0.10.8" sha2 = "0.10.8"
si-scale = "0.2.3" si-scale = "0.2.3"
sphinx-packet = "=0.3.1" # make sure to use version below 0.4.0 that supports processing legacy and updated packets sphinx-packet = "0.1.1"
sqlx = "0.7.4" sqlx = "0.7.4"
strum = "0.26" strum = "0.26"
strum_macros = "0.26" strum_macros = "0.26"
@@ -330,32 +327,29 @@ subtle-encoding = "0.5"
syn = "1" syn = "1"
sysinfo = "0.33.0" sysinfo = "0.33.0"
tap = "1.0.1" tap = "1.0.1"
tar = "0.4.44" tar = "0.4.43"
tempfile = "3.15" tempfile = "3.15"
thiserror = "2.0" thiserror = "1.0.64"
time = "0.3.37" time = "0.3.37"
tokio = "1.43" tokio = "1.39"
tokio-postgres = "0.7"
tokio-stream = "0.1.17" tokio-stream = "0.1.17"
tokio-test = "0.4.4" tokio-test = "0.4.4"
tokio-tun = "0.11.5" tokio-tun = "0.11.5"
tokio-tungstenite = { version = "0.20.1" } tokio-tungstenite = { version = "0.20.1" }
tokio-util = "0.7.13" tokio-util = "0.7.13"
toml = "0.8.20" toml = "0.8.19"
tower = "0.5.2" tower = "0.4.13"
tower-http = "0.5.2" tower-http = "0.5.2"
tracing = "0.1.41" tracing = "0.1.41"
tracing-log = "0.2"
tracing-opentelemetry = "0.19.0" tracing-opentelemetry = "0.19.0"
tracing-subscriber = "0.3.19" tracing-subscriber = "0.3.19"
tracing-tree = "0.2.2" tracing-tree = "0.2.2"
tracing-log = "0.2"
ts-rs = "10.1.0" ts-rs = "10.1.0"
tungstenite = { version = "0.20.1", default-features = false } tungstenite = { version = "0.20.1", default-features = false }
uniffi = "0.29.0"
uniffi_build = "0.29.0"
url = "2.5" url = "2.5"
utoipa = "5.2" utoipa = "5.2"
utoipa-swagger-ui = "8.1" utoipa-swagger-ui = "8.0"
utoipauto = "0.2" utoipauto = "0.2"
uuid = "*" uuid = "*"
vergen = { version = "=8.3.1", default-features = false } vergen = { version = "=8.3.1", default-features = false }
@@ -391,10 +385,10 @@ cw4 = { version = "=1.1.2" }
cw-controllers = { version = "=1.1.0" } cw-controllers = { version = "=1.1.0" }
# cosmrs-related # cosmrs-related
bip32 = { version = "0.5.3", default-features = false } bip32 = { version = "0.5.2", default-features = false }
cosmrs = { version = "0.21.1" } cosmrs = { version = "0.21.0" }
tendermint = "0.40.0" tendermint = "0.40.0"
tendermint-rpc = "0.40.0" tendermint-rpc = "0.40.0"
prost = { version = "0.13", default-features = false } prost = { version = "0.13", default-features = false }
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-client" 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>"] authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client" description = "Implementation of the Nym Client"
edition = "2021" edition = "2021"
+2 -6
View File
@@ -56,7 +56,7 @@ pub fn default_data_directory<P: AsRef<Path>>(id: P) -> PathBuf {
.join(DEFAULT_DATA_DIR) .join(DEFAULT_DATA_DIR)
} }
#[derive(Debug, Deserialize, PartialEq, Serialize, Clone)] #[derive(Debug, Deserialize, PartialEq, Serialize)]
pub struct Config { pub struct Config {
#[serde(flatten)] #[serde(flatten)]
pub base: BaseClientConfig, pub base: BaseClientConfig,
@@ -94,10 +94,6 @@ impl CliClientConfig for Config {
} }
impl Config { impl Config {
pub fn base(&self) -> BaseClientConfig {
self.base.clone()
}
pub fn new<S: AsRef<str>>(id: S) -> Self { pub fn new<S: AsRef<str>>(id: S) -> Self {
Config { Config {
base: BaseClientConfig::new(id.as_ref(), env!("CARGO_PKG_VERSION")), 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)] #[serde(default, deny_unknown_fields)]
pub struct Socket { pub struct Socket {
pub socket_type: SocketType, pub socket_type: SocketType,
@@ -107,8 +107,5 @@ enabled = {{ debug.stats_reporting.enabled }}
provider_address = '{{ debug.stats_reporting.provider_address }}' provider_address = '{{ debug.stats_reporting.provider_address }}'
reporting_interval = '{{ debug.stats_reporting.reporting_interval }}' reporting_interval = '{{ debug.stats_reporting.reporting_interval }}'
[debug.forget_me]
client = {{ debug.forget_me.client }}
stats = {{ debug.forget_me.stats }}
"#; "#;
+6 -16
View File
@@ -20,7 +20,7 @@ pub use nym_sphinx::addressing::clients::Recipient;
pub mod config; pub mod config;
type NativeClientBuilder = BaseClientBuilder<QueryHttpRpcNyxdClient, OnDiskPersistent>; type NativeClientBuilder<'a> = BaseClientBuilder<'a, QueryHttpRpcNyxdClient, OnDiskPersistent>;
pub struct SocketClient { pub struct SocketClient {
/// Client configuration options, including, among other things, packet sending rates, /// Client configuration options, including, among other things, packet sending rates,
@@ -32,10 +32,6 @@ pub struct SocketClient {
} }
impl SocketClient { impl SocketClient {
pub fn config(&self) -> Config {
self.config.clone()
}
pub fn new(config: Config, custom_mixnet: Option<PathBuf>) -> Self { pub fn new(config: Config, custom_mixnet: Option<PathBuf>) -> Self {
SocketClient { SocketClient {
config, config,
@@ -49,7 +45,7 @@ impl SocketClient {
client_output: ClientOutput, client_output: ClientOutput,
client_state: ClientState, client_state: ClientState,
self_address: &Recipient, self_address: &Recipient,
task_client: nym_task::TaskClient, shutdown: nym_task::TaskClient,
packet_type: PacketType, packet_type: PacketType,
) { ) {
info!("Starting websocket listener..."); info!("Starting websocket listener...");
@@ -77,15 +73,10 @@ impl SocketClient {
shared_lane_queue_lengths, shared_lane_queue_lengths,
reply_controller_sender, reply_controller_sender,
Some(packet_type), Some(packet_type),
task_client.fork("websocket_handler"),
); );
websocket::Listener::new( websocket::Listener::new(config.socket.host, config.socket.listening_port)
config.socket.host, .start(websocket_handler, shutdown);
config.socket.listening_port,
task_client.with_suffix("websocket_listener"),
)
.start(websocket_handler);
} }
/// blocking version of `start_socket` method. Will run forever (or until SIGINT is sent) /// blocking version of `start_socket` method. Will run forever (or until SIGINT is sent)
@@ -117,9 +108,8 @@ impl SocketClient {
let storage = self.initialise_storage().await?; let storage = self.initialise_storage().await?;
let user_agent = nym_bin_common::bin_info!().into(); let user_agent = nym_bin_common::bin_info!().into();
let mut base_client = let mut base_client = BaseClientBuilder::new(&self.config.base, storage, dkg_query_client)
BaseClientBuilder::new(self.config().base(), storage, dkg_query_client) .with_user_agent(user_agent);
.with_user_agent(user_agent);
if let Some(custom_mixnet) = &self.custom_mixnet { if let Some(custom_mixnet) = &self.custom_mixnet {
base_client = base_client.with_stored_topology(custom_mixnet)?; base_client = base_client.with_stored_topology(custom_mixnet)?;
-1
View File
@@ -82,7 +82,6 @@ impl From<Init> for OverrideConfig {
nyxd_urls: init_config.common_args.nyxd_urls, nyxd_urls: init_config.common_args.nyxd_urls,
enabled_credentials_mode: init_config.common_args.enabled_credentials_mode, enabled_credentials_mode: init_config.common_args.enabled_credentials_mode,
stats_reporting_address: init_config.common_args.stats_reporting_address, stats_reporting_address: init_config.common_args.stats_reporting_address,
forget_me: init_config.common_args.forget_me.into(),
} }
} }
} }
-3
View File
@@ -16,7 +16,6 @@ use nym_bin_common::completions::{fig_generate, ArgShell};
use nym_client::client::Recipient; use nym_client::client::Recipient;
use nym_client_core::cli_helpers::CliClient; 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::base_client::storage::migration_helpers::v1_1_33;
use nym_client_core::config::ForgetMe;
use nym_config::OptionalSet; use nym_config::OptionalSet;
use std::error::Error; use std::error::Error;
use std::net::IpAddr; use std::net::IpAddr;
@@ -107,7 +106,6 @@ pub(crate) struct OverrideConfig {
nyxd_urls: Option<Vec<url::Url>>, nyxd_urls: Option<Vec<url::Url>>,
enabled_credentials_mode: Option<bool>, enabled_credentials_mode: Option<bool>,
stats_reporting_address: Option<Recipient>, stats_reporting_address: Option<Recipient>,
forget_me: ForgetMe,
} }
pub(crate) async fn execute(args: Cli) -> Result<(), Box<dyn Error + Send + Sync>> { 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, args.fastmode,
) )
.with_base(BaseClientConfig::with_disabled_cover_traffic, args.no_cover) .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_port, args.port)
.with_optional(Config::with_host, args.host) .with_optional(Config::with_host, args.host)
.with_optional_custom_env_ext( .with_optional_custom_env_ext(
-1
View File
@@ -41,7 +41,6 @@ impl From<Run> for OverrideConfig {
nyxd_urls: run_config.common_args.nyxd_urls, nyxd_urls: run_config.common_args.nyxd_urls,
enabled_credentials_mode: run_config.common_args.enabled_credentials_mode, enabled_credentials_mode: run_config.common_args.enabled_credentials_mode,
stats_reporting_address: run_config.common_args.stats_reporting_address, stats_reporting_address: run_config.common_args.stats_reporting_address,
forget_me: run_config.common_args.forget_me.into(),
} }
} }
} }
+40 -66
View File
@@ -19,7 +19,6 @@ use nym_sphinx::receiver::ReconstructedMessage;
use nym_task::connections::{ use nym_task::connections::{
ConnectionCommand, ConnectionCommandSender, ConnectionId, LaneQueueLengths, TransmissionLane, ConnectionCommand, ConnectionCommandSender, ConnectionId, LaneQueueLengths, TransmissionLane,
}; };
use nym_task::TaskClient;
use std::time::Duration; use std::time::Duration;
use tokio::net::TcpStream; use tokio::net::TcpStream;
use tokio::time::Instant; use tokio::time::Instant;
@@ -44,11 +43,9 @@ pub(crate) struct HandlerBuilder {
lane_queue_lengths: LaneQueueLengths, lane_queue_lengths: LaneQueueLengths,
reply_controller_sender: ReplyControllerSender, reply_controller_sender: ReplyControllerSender,
packet_type: Option<PacketType>, packet_type: Option<PacketType>,
task_client: TaskClient,
} }
impl HandlerBuilder { impl HandlerBuilder {
#[allow(clippy::too_many_arguments)]
pub(crate) fn new( pub(crate) fn new(
msg_input: InputMessageSender, msg_input: InputMessageSender,
client_connection_tx: ConnectionCommandSender, client_connection_tx: ConnectionCommandSender,
@@ -57,7 +54,6 @@ impl HandlerBuilder {
lane_queue_lengths: LaneQueueLengths, lane_queue_lengths: LaneQueueLengths,
reply_controller_sender: ReplyControllerSender, reply_controller_sender: ReplyControllerSender,
packet_type: Option<PacketType>, packet_type: Option<PacketType>,
task_client: TaskClient,
) -> Self { ) -> Self {
Self { Self {
msg_input, msg_input,
@@ -67,14 +63,11 @@ impl HandlerBuilder {
lane_queue_lengths, lane_queue_lengths,
reply_controller_sender, reply_controller_sender,
packet_type, packet_type,
task_client,
} }
} }
// TODO: make sure we only ever have one active handler // TODO: make sure we only ever have one active handler
pub fn create_active_handler(&self) -> Handler { pub fn create_active_handler(&self) -> Handler {
let mut task_client = self.task_client.fork("active_handler");
task_client.disarm();
Handler { Handler {
msg_input: self.msg_input.clone(), msg_input: self.msg_input.clone(),
client_connection_tx: self.client_connection_tx.clone(), client_connection_tx: self.client_connection_tx.clone(),
@@ -85,7 +78,6 @@ impl HandlerBuilder {
lane_queue_lengths: self.lane_queue_lengths.clone(), lane_queue_lengths: self.lane_queue_lengths.clone(),
reply_controller_sender: self.reply_controller_sender.clone(), reply_controller_sender: self.reply_controller_sender.clone(),
packet_type: self.packet_type, packet_type: self.packet_type,
task_client,
} }
} }
} }
@@ -100,18 +92,16 @@ pub(crate) struct Handler {
lane_queue_lengths: LaneQueueLengths, lane_queue_lengths: LaneQueueLengths,
reply_controller_sender: ReplyControllerSender, reply_controller_sender: ReplyControllerSender,
packet_type: Option<PacketType>, packet_type: Option<PacketType>,
task_client: TaskClient,
} }
impl Drop for Handler { impl Drop for Handler {
fn drop(&mut self) { fn drop(&mut self) {
if let Err(err) = self if self
.buffer_requester .buffer_requester
.unbounded_send(ReceivedBufferMessage::ReceiverDisconnect) .unbounded_send(ReceivedBufferMessage::ReceiverDisconnect)
.is_err()
{ {
if !self.task_client.is_shutdown_poll() { error!("we failed to disconnect the receiver from the buffer! presumably the shutdown procedure has been initiated!")
error!("failed to disconnect the receiver from the buffer: {err}");
}
} }
} }
} }
@@ -135,23 +125,10 @@ impl Handler {
}; };
// get the number of pending replies waiting for reply surbs // get the number of pending replies waiting for reply surbs
let reply_queue_length = match self let reply_queue_length = self
.reply_controller_sender .reply_controller_sender
.get_lane_queue_length(connection_id) .get_lane_queue_length(connection_id)
.await .await;
{
Ok(length) => length,
Err(err) => {
if !self.task_client.is_shutdown_poll() {
error!(
"Failed to get reply queue length for connection {connection_id}: {err}"
);
}
// We're just going to assume that the queue is empty, and I think that's okay
// during shutdown.
0
}
};
let queue_length = base_length + reply_queue_length; let queue_length = base_length + reply_queue_length;
@@ -191,11 +168,10 @@ impl Handler {
// the ack control is now responsible for chunking, etc. // the ack control is now responsible for chunking, etc.
let input_msg = InputMessage::new_regular(recipient, message, lane, self.packet_type); let input_msg = InputMessage::new_regular(recipient, message, lane, self.packet_type);
if let Err(err) = self.msg_input.send(input_msg).await { self.msg_input
if !self.task_client.is_shutdown_poll() { .send(input_msg)
error!("Failed to send message to the input buffer: {err}"); .await
} .expect("InputMessageReceiver has stopped receiving!");
}
// Only reply back with a `LaneQueueLength` if the sender providided a connection id // Only reply back with a `LaneQueueLength` if the sender providided a connection id
let TransmissionLane::ConnectionId(connection_id) = lane else { let TransmissionLane::ConnectionId(connection_id) = lane else {
@@ -224,11 +200,10 @@ impl Handler {
let input_msg = let input_msg =
InputMessage::new_anonymous(recipient, message, reply_surbs, lane, self.packet_type); InputMessage::new_anonymous(recipient, message, reply_surbs, lane, self.packet_type);
if let Err(err) = self.msg_input.send(input_msg).await { self.msg_input
if !self.task_client.is_shutdown_poll() { .send(input_msg)
error!("Failed to send anonymous message to the input buffer: {err}"); .await
} .expect("InputMessageReceiver has stopped receiving!");
}
// Only reply back with a `LaneQueueLength` if the sender providided a connection id // Only reply back with a `LaneQueueLength` if the sender providided a connection id
let TransmissionLane::ConnectionId(connection_id) = lane else { let TransmissionLane::ConnectionId(connection_id) = lane else {
@@ -252,11 +227,10 @@ impl Handler {
}); });
let input_msg = InputMessage::new_reply(recipient_tag, message, lane, self.packet_type); let input_msg = InputMessage::new_reply(recipient_tag, message, lane, self.packet_type);
if let Err(err) = self.msg_input.send(input_msg).await { self.msg_input
if !self.task_client.is_shutdown_poll() { .send(input_msg)
error!("Failed to send reply message to the input buffer: {err}"); .await
} .expect("InputMessageReceiver has stopped receiving!");
}
// Only reply back with a `LaneQueueLength` if the sender providided a connection id // Only reply back with a `LaneQueueLength` if the sender providided a connection id
let TransmissionLane::ConnectionId(connection_id) = lane else { let TransmissionLane::ConnectionId(connection_id) = lane else {
@@ -271,14 +245,9 @@ impl Handler {
} }
fn handle_closed_connection(&self, connection_id: u64) -> Option<ServerResponse> { fn handle_closed_connection(&self, connection_id: u64) -> Option<ServerResponse> {
if let Err(err) = self self.client_connection_tx
.client_connection_tx
.unbounded_send(ConnectionCommand::Close(connection_id)) .unbounded_send(ConnectionCommand::Close(connection_id))
{ .unwrap();
if !self.task_client.is_shutdown_poll() {
error!("Failed to send close connection command: {err}");
}
}
None None
} }
@@ -393,10 +362,11 @@ impl Handler {
} }
} }
async fn listen_for_requests(&mut self, mut msg_receiver: ReconstructedMessagesReceiver) { async fn listen_for_requests(
let mut task_client = self.task_client.fork("select"); &mut self,
task_client.disarm(); mut msg_receiver: ReconstructedMessagesReceiver,
mut task_client: nym_task::TaskClient,
) {
while !task_client.is_shutdown() { while !task_client.is_shutdown() {
tokio::select! { tokio::select! {
// we can either get a client request from the websocket // we can either get a client request from the websocket
@@ -445,7 +415,15 @@ impl Handler {
} }
// consume self to make sure `drop` is called after this is done // consume self to make sure `drop` is called after this is done
pub(crate) async fn handle_connection(mut self, socket: TcpStream) { pub(crate) async fn handle_connection(
mut self,
socket: TcpStream,
mut task_client: nym_task::TaskClient,
) {
// We don't want a crash in the connection handler to trigger a shutdown of the whole
// process.
task_client.disarm();
let ws_stream = match accept_async(socket).await { let ws_stream = match accept_async(socket).await {
Ok(ws_stream) => ws_stream, Ok(ws_stream) => ws_stream,
Err(err) => { Err(err) => {
@@ -458,18 +436,14 @@ impl Handler {
let (reconstructed_sender, reconstructed_receiver) = mpsc::unbounded(); let (reconstructed_sender, reconstructed_receiver) = mpsc::unbounded();
// tell the buffer to start sending stuff to us // tell the buffer to start sending stuff to us
if let Err(err) = self.buffer_requester
self.buffer_requester .unbounded_send(ReceivedBufferMessage::ReceiverAnnounce(
.unbounded_send(ReceivedBufferMessage::ReceiverAnnounce( reconstructed_sender,
reconstructed_sender, ))
)) .expect("the buffer request failed!");
{
if !self.task_client.is_shutdown_poll() {
error!("failed to announce the receiver to the buffer: {err}");
}
}
self.listen_for_requests(reconstructed_receiver).await; self.listen_for_requests(reconstructed_receiver, task_client)
.await;
} }
} }
+17 -11
View File
@@ -3,7 +3,6 @@
use super::handler::HandlerBuilder; use super::handler::HandlerBuilder;
use log::*; use log::*;
use nym_task::TaskClient;
use std::net::IpAddr; use std::net::IpAddr;
use std::{net::SocketAddr, process, sync::Arc}; use std::{net::SocketAddr, process, sync::Arc};
use tokio::io::AsyncWriteExt; use tokio::io::AsyncWriteExt;
@@ -23,19 +22,21 @@ impl State {
pub(crate) struct Listener { pub(crate) struct Listener {
address: SocketAddr, address: SocketAddr,
state: State, state: State,
task_client: TaskClient,
} }
impl Listener { impl Listener {
pub(crate) fn new(host: IpAddr, port: u16, task_client: TaskClient) -> Self { pub(crate) fn new(host: IpAddr, port: u16) -> Self {
Listener { Listener {
address: SocketAddr::new(host, port), address: SocketAddr::new(host, port),
state: State::AwaitingConnection, state: State::AwaitingConnection,
task_client,
} }
} }
pub(crate) async fn run(&mut self, handler: HandlerBuilder) { pub(crate) async fn run(
&mut self,
handler: HandlerBuilder,
mut task_client: nym_task::TaskClient,
) {
let tcp_listener = match tokio::net::TcpListener::bind(self.address).await { let tcp_listener = match tokio::net::TcpListener::bind(self.address).await {
Ok(listener) => listener, Ok(listener) => listener,
Err(err) => { Err(err) => {
@@ -46,11 +47,11 @@ impl Listener {
let notify = Arc::new(Notify::new()); let notify = Arc::new(Notify::new());
while !self.task_client.is_shutdown() { loop {
tokio::select! { tokio::select! {
// When the handler finishes we check if shutdown is signalled // When the handler finishes we check if shutdown is signalled
_ = notify.notified() => { _ = notify.notified() => {
if self.task_client.is_shutdown() { if task_client.is_shutdown() {
log::trace!("Websocket listener: detected shutdown after connection closed"); log::trace!("Websocket listener: detected shutdown after connection closed");
break; break;
} }
@@ -59,7 +60,7 @@ impl Listener {
} }
// ... but when there is no connected client at the time of shutdown being // ... but when there is no connected client at the time of shutdown being
// signalled, we handle it here. // signalled, we handle it here.
_ = self.task_client.recv() => { _ = task_client.recv() => {
if !self.state.is_connected() { if !self.state.is_connected() {
log::trace!("Not connected: shutting down"); log::trace!("Not connected: shutting down");
break; break;
@@ -87,8 +88,9 @@ impl Listener {
// hanging because the executor doesn't come back here // hanging because the executor doesn't come back here
let notify_clone = Arc::clone(&notify); let notify_clone = Arc::clone(&notify);
let fresh_handler = handler.create_active_handler(); let fresh_handler = handler.create_active_handler();
let task_client_handler = task_client.clone();
tokio::spawn(async move { tokio::spawn(async move {
fresh_handler.handle_connection(socket).await; fresh_handler.handle_connection(socket, task_client_handler).await;
notify_clone.notify_one(); notify_clone.notify_one();
}); });
self.state = State::Connected; self.state = State::Connected;
@@ -102,9 +104,13 @@ impl Listener {
log::debug!("Websocket listener: Exiting"); log::debug!("Websocket listener: Exiting");
} }
pub(crate) fn start(mut self, handler: HandlerBuilder) -> JoinHandle<()> { pub(crate) fn start(
mut self,
handler: HandlerBuilder,
shutdown: nym_task::TaskClient,
) -> JoinHandle<()> {
info!("Running websocket on {:?}", self.address.to_string()); info!("Running websocket on {:?}", self.address.to_string());
tokio::spawn(async move { self.run(handler).await }) tokio::spawn(async move { self.run(handler, shutdown).await })
} }
} }
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-socks5-client" name = "nym-socks5-client"
version = "1.1.48" version = "1.1.46"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"] 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" description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021" edition = "2021"
-1
View File
@@ -93,7 +93,6 @@ impl From<Init> for OverrideConfig {
enabled_credentials_mode: init_config.common_args.enabled_credentials_mode, enabled_credentials_mode: init_config.common_args.enabled_credentials_mode,
outfox: false, outfox: false,
stats_reporting_address: init_config.common_args.stats_reporting_address, stats_reporting_address: init_config.common_args.stats_reporting_address,
forget_me: init_config.common_args.forget_me.into(),
} }
} }
} }
+1 -3
View File
@@ -17,7 +17,7 @@ use nym_bin_common::completions::{fig_generate, ArgShell};
use nym_client_core::cli_helpers::CliClient; 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::base_client::storage::migration_helpers::v1_1_33;
use nym_client_core::client::topology_control::geo_aware_provider::CountryGroup; 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_config::OptionalSet;
use nym_sphinx::addressing::Recipient; use nym_sphinx::addressing::Recipient;
use nym_sphinx::params::{PacketSize, PacketType}; use nym_sphinx::params::{PacketSize, PacketType};
@@ -113,7 +113,6 @@ pub(crate) struct OverrideConfig {
enabled_credentials_mode: Option<bool>, enabled_credentials_mode: Option<bool>,
outfox: bool, outfox: bool,
stats_reporting_address: Option<Recipient>, stats_reporting_address: Option<Recipient>,
forget_me: ForgetMe,
} }
pub(crate) async fn execute(args: Cli) -> Result<(), Box<dyn Error + Send + Sync>> { 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, BaseClientConfig::with_topology_structure,
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_anonymous_replies, args.use_anonymous_replies)
.with_optional(Config::with_port, args.port) .with_optional(Config::with_port, args.port)
.with_optional(Config::with_ip, args.ip) .with_optional(Config::with_ip, args.ip)
-1
View File
@@ -65,7 +65,6 @@ impl From<Run> for OverrideConfig {
enabled_credentials_mode: run_config.common_args.enabled_credentials_mode, enabled_credentials_mode: run_config.common_args.enabled_credentials_mode,
outfox: run_config.outfox, outfox: run_config.outfox,
stats_reporting_address: run_config.common_args.stats_reporting_address, stats_reporting_address: run_config.common_args.stats_reporting_address,
forget_me: run_config.common_args.forget_me.into(),
} }
} }
} }
-4
View File
@@ -113,8 +113,4 @@ enabled = {{ core.debug.stats_reporting.enabled }}
provider_address = '{{ core.debug.stats_reporting.provider_address }}' provider_address = '{{ core.debug.stats_reporting.provider_address }}'
reporting_interval = '{{ core.debug.stats_reporting.reporting_interval }}' reporting_interval = '{{ core.debug.stats_reporting.reporting_interval }}'
[core.debug.forget_me]
client = {{ core.debug.forget_me.client }}
stats = {{ core.debug.forget_me.stats }}
"#; "#;
@@ -60,7 +60,7 @@ impl From<IpAddr> for IpPair {
std::net::IpAddr::V4(ipv4_addr) => (ipv4_addr.octets()[2], ipv4_addr.octets()[3]), std::net::IpAddr::V4(ipv4_addr) => (ipv4_addr.octets()[2], ipv4_addr.octets()[3]),
std::net::IpAddr::V6(ipv6_addr) => (ipv6_addr.octets()[14], ipv6_addr.octets()[15]), std::net::IpAddr::V6(ipv6_addr) => (ipv6_addr.octets()[14], ipv6_addr.octets()[15]),
}; };
let last_bytes = ((before_last_byte as u16) << 8) | last_byte as u16; let last_bytes = (before_last_byte as u16) << 8 | last_byte as u16;
let ipv4 = Ipv4Addr::new( let ipv4 = Ipv4Addr::new(
WG_TUN_DEVICE_IP_ADDRESS_V4.octets()[0], WG_TUN_DEVICE_IP_ADDRESS_V4.octets()[0],
WG_TUN_DEVICE_IP_ADDRESS_V4.octets()[1], WG_TUN_DEVICE_IP_ADDRESS_V4.octets()[1],
-1
View File
@@ -40,7 +40,6 @@ nym-crypto = { path = "../crypto" }
nym-explorer-client = { path = "../../explorer-api/explorer-client" } nym-explorer-client = { path = "../../explorer-api/explorer-client" }
nym-gateway-client = { path = "../client-libs/gateway-client" } nym-gateway-client = { path = "../client-libs/gateway-client" }
nym-gateway-requests = { path = "../gateway-requests" } nym-gateway-requests = { path = "../gateway-requests" }
nym-http-api-client = { path = "../http-api-client" }
nym-metrics = { path = "../nym-metrics" } nym-metrics = { path = "../nym-metrics" }
nym-nonexhaustive-delayqueue = { path = "../nonexhaustive-delayqueue" } nym-nonexhaustive-delayqueue = { path = "../nonexhaustive-delayqueue" }
nym-sphinx = { path = "../nymsphinx" } nym-sphinx = { path = "../nymsphinx" }
+2 -79
View File
@@ -145,11 +145,6 @@ impl Config {
self 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 // 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) // as of 12.09.23 the below is true (not sure how this comment will rot in the future)
// medium_toggle: // medium_toggle:
@@ -522,7 +517,7 @@ impl Default for Acknowledgements {
} }
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)] #[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
#[serde(default)] #[serde(default, deny_unknown_fields)]
pub struct Topology { pub struct Topology {
/// The uniform delay every which clients are querying the directory server /// The uniform delay every which clients are querying the directory server
/// to try to obtain a compatible network topology to send sphinx packets through. /// to try to obtain a compatible network topology to send sphinx packets through.
@@ -563,10 +558,6 @@ pub struct Topology {
/// Specifies whether this client should ignore the current epoch role of the target egress node /// Specifies whether this client should ignore the current epoch role of the target egress node
/// when constructing the final hop packets. /// when constructing the final hop packets.
pub ignore_egress_epoch_role: bool, 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)] #[allow(clippy::large_enum_variant)]
@@ -604,9 +595,7 @@ impl Default for Topology {
minimum_mixnode_performance: DEFAULT_MIN_MIXNODE_PERFORMANCE, minimum_mixnode_performance: DEFAULT_MIN_MIXNODE_PERFORMANCE,
minimum_gateway_performance: DEFAULT_MIN_GATEWAY_PERFORMANCE, minimum_gateway_performance: DEFAULT_MIN_GATEWAY_PERFORMANCE,
use_extended_topology: false, use_extended_topology: false,
ignore_egress_epoch_role: false,
ignore_egress_epoch_role: true,
ignore_ingress_epoch_role: true,
} }
} }
} }
@@ -724,9 +713,6 @@ pub struct DebugConfig {
/// Defines all configuration options related to stats reporting. /// Defines all configuration options related to stats reporting.
pub stats_reporting: StatsReporting, pub stats_reporting: StatsReporting,
/// Defines all configuration options related to the forget me flag.
pub forget_me: ForgetMe,
} }
impl DebugConfig { impl DebugConfig {
@@ -749,69 +735,6 @@ impl Default for DebugConfig {
topology: Default::default(), topology: Default::default(),
reply_surbs: Default::default(), reply_surbs: Default::default(),
stats_reporting: 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, maximum_reply_key_age: value.debug.reply_surbs.maximum_reply_key_age,
surb_mix_hops: value.debug.reply_surbs.surb_mix_hops, surb_mix_hops: value.debug.reply_surbs.surb_mix_hops,
}, },
..Default::default() stats_reporting: Default::default(),
}, },
} }
} }
@@ -120,7 +120,6 @@ where
&core.client.nym_api_urls, &core.client.nym_api_urls,
user_agent, user_agent,
core.debug.topology.minimum_gateway_performance, core.debug.topology.minimum_gateway_performance,
core.debug.topology.ignore_ingress_epoch_role,
) )
.await? .await?
}; };
@@ -93,10 +93,6 @@ pub struct CommonClientInitArgs {
/// Sets the address to report statistics /// Sets the address to report statistics
#[cfg_attr(feature = "cli", clap(long, hide = true))] #[cfg_attr(feature = "cli", clap(long, hide = true))]
pub stats_reporting_address: Option<Recipient>, 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> { pub struct InitResultsWithConfig<T> {
@@ -179,7 +175,6 @@ where
&core.client.nym_api_urls, &core.client.nym_api_urls,
user_agent, user_agent,
core.debug.topology.minimum_gateway_performance, core.debug.topology.minimum_gateway_performance,
core.debug.topology.ignore_ingress_epoch_role,
) )
.await? .await?
}; };
@@ -61,8 +61,4 @@ pub struct CommonClientRunArgs {
/// Sets the address to report statistics /// Sets the address to report statistics
#[cfg_attr(feature = "cli", clap(long, hide = true))] #[cfg_attr(feature = "cli", clap(long, hide = true))]
pub stats_reporting_address: Option<Recipient>, 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> // Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use super::mix_traffic::ClientRequestSender;
use super::received_buffer::ReceivedBufferMessage; use super::received_buffer::ReceivedBufferMessage;
use super::statistics_control::StatisticsControl; use super::statistics_control::StatisticsControl;
use crate::client::base_client::storage::helpers::store_client_keys; use crate::client::base_client::storage::helpers::store_client_keys;
@@ -32,15 +31,13 @@ use crate::init::{
setup_gateway, setup_gateway,
types::{GatewaySetup, InitialisationResult}, types::{GatewaySetup, InitialisationResult},
}; };
use crate::{config, spawn_future}; use crate::{config, spawn_future, ForgetMe};
use futures::channel::mpsc; use futures::channel::mpsc;
use log::*; use log::*;
use nym_bandwidth_controller::BandwidthController; use nym_bandwidth_controller::BandwidthController;
use nym_client_core_config_types::ForgetMe;
use nym_client_core_gateways_storage::{GatewayDetails, GatewaysDetailsStore}; use nym_client_core_gateways_storage::{GatewayDetails, GatewaysDetailsStore};
use nym_credential_storage::storage::Storage as CredentialStorage; use nym_credential_storage::storage::Storage as CredentialStorage;
use nym_crypto::asymmetric::{encryption, identity}; use nym_crypto::asymmetric::{encryption, identity};
use nym_crypto::hkdf::DerivationMaterial;
use nym_gateway_client::client::config::GatewayClientConfig; use nym_gateway_client::client::config::GatewayClientConfig;
use nym_gateway_client::{ use nym_gateway_client::{
AcknowledgementReceiver, GatewayClient, GatewayConfig, MixnetMessageReceiver, PacketRouter, AcknowledgementReceiver, GatewayClient, GatewayConfig, MixnetMessageReceiver, PacketRouter,
@@ -178,8 +175,8 @@ impl From<bool> for CredentialsToggle {
} }
} }
pub struct BaseClientBuilder<C, S: MixnetClientStorage> { pub struct BaseClientBuilder<'a, C, S: MixnetClientStorage> {
config: Config, config: &'a Config,
client_store: S, client_store: S,
dkg_query_client: Option<C>, dkg_query_client: Option<C>,
@@ -194,19 +191,19 @@ pub struct BaseClientBuilder<C, S: MixnetClientStorage> {
#[cfg(unix)] #[cfg(unix)]
connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>, connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
derivation_material: Option<DerivationMaterial>, forget_me: ForgetMe,
} }
impl<C, S> BaseClientBuilder<C, S> impl<'a, C, S> BaseClientBuilder<'a, C, S>
where where
S: MixnetClientStorage + 'static, S: MixnetClientStorage + 'static,
C: DkgQueryClient + Send + Sync + 'static, C: DkgQueryClient + Send + Sync + 'static,
{ {
pub fn new( pub fn new(
base_config: Config, base_config: &'a Config,
client_store: S, client_store: S,
dkg_query_client: Option<C>, dkg_query_client: Option<C>,
) -> BaseClientBuilder<C, S> { ) -> BaseClientBuilder<'a, C, S> {
BaseClientBuilder { BaseClientBuilder {
config: base_config, config: base_config,
client_store, client_store,
@@ -219,22 +216,13 @@ where
setup_method: GatewaySetup::MustLoad { gateway_id: None }, setup_method: GatewaySetup::MustLoad { gateway_id: None },
#[cfg(unix)] #[cfg(unix)]
connection_fd_callback: None, connection_fd_callback: None,
derivation_material: None, forget_me: Default::default(),
} }
} }
#[must_use]
pub fn with_derivation_material(
mut self,
derivation_material: Option<DerivationMaterial>,
) -> Self {
self.derivation_material = derivation_material;
self
}
#[must_use] #[must_use]
pub fn with_forget_me(mut self, forget_me: &ForgetMe) -> Self { 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 self
} }
@@ -310,7 +298,7 @@ where
topology_accessor: TopologyAccessor, topology_accessor: TopologyAccessor,
mix_tx: BatchMixMessageSender, mix_tx: BatchMixMessageSender,
stats_tx: ClientStatsSender, stats_tx: ClientStatsSender,
task_client: TaskClient, shutdown: TaskClient,
) { ) {
info!("Starting loop cover traffic stream..."); info!("Starting loop cover traffic stream...");
@@ -323,10 +311,9 @@ where
debug_config.traffic, debug_config.traffic,
debug_config.cover_traffic, debug_config.cover_traffic,
stats_tx, stats_tx,
task_client,
); );
stream.start(); stream.start_with_shutdown(shutdown);
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
@@ -341,7 +328,7 @@ where
reply_controller_receiver: ReplyControllerReceiver, reply_controller_receiver: ReplyControllerReceiver,
lane_queue_lengths: LaneQueueLengths, lane_queue_lengths: LaneQueueLengths,
client_connection_rx: ConnectionCommandReceiver, client_connection_rx: ConnectionCommandReceiver,
task_client: TaskClient, shutdown: TaskClient,
packet_type: PacketType, packet_type: PacketType,
stats_tx: ClientStatsSender, stats_tx: ClientStatsSender,
) { ) {
@@ -359,9 +346,8 @@ where
lane_queue_lengths, lane_queue_lengths,
client_connection_rx, client_connection_rx,
stats_tx, stats_tx,
task_client,
) )
.start(packet_type); .start_with_shutdown(shutdown, packet_type);
} }
// buffer controlling all messages fetched from provider // buffer controlling all messages fetched from provider
@@ -384,9 +370,8 @@ where
reply_key_storage, reply_key_storage,
reply_controller_sender, reply_controller_sender,
metrics_reporter, metrics_reporter,
shutdown,
); );
controller.start() controller.start_with_shutdown(shutdown)
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
@@ -575,22 +560,15 @@ where
topology_accessor: TopologyAccessor, topology_accessor: TopologyAccessor,
local_gateway: NodeIdentity, local_gateway: NodeIdentity,
wait_for_gateway: bool, wait_for_gateway: bool,
mut task_client: TaskClient, mut shutdown: TaskClient,
) -> Result<(), ClientCoreError> { ) -> Result<(), ClientCoreError> {
let topology_refresher_config = let topology_refresher_config =
TopologyRefresherConfig::new(topology_config.topology_refresh_rate); TopologyRefresherConfig::new(topology_config.topology_refresh_rate);
if topology_config.disable_refreshing {
// if we're not spawning the refresher, don't cause shutdown immediately
info!("The background topology refesher is not going to be started");
task_client.disarm();
}
let mut topology_refresher = TopologyRefresher::new( let mut topology_refresher = TopologyRefresher::new(
topology_refresher_config, topology_refresher_config,
topology_accessor, topology_accessor,
topology_provider, topology_provider,
task_client,
); );
// before returning, block entire runtime to refresh the current network view so that any // before returning, block entire runtime to refresh the current network view so that any
// components depending on topology would see a non-empty view // components depending on topology would see a non-empty view
@@ -631,11 +609,15 @@ where
} }
} }
if !topology_config.disable_refreshing { if topology_config.disable_refreshing {
// if we're not spawning the refresher, don't cause shutdown immediately
info!("The topology refesher is not going to be started");
shutdown.disarm();
} else {
// don't spawn the refresher if we don't want to be refreshing the topology. // don't spawn the refresher if we don't want to be refreshing the topology.
// only use the initial values obtained // only use the initial values obtained
info!("Starting topology refresher..."); info!("Starting topology refresher...");
topology_refresher.start(); topology_refresher.start_with_shutdown(shutdown);
} }
Ok(()) Ok(())
@@ -646,29 +628,30 @@ where
user_agent: Option<UserAgent>, user_agent: Option<UserAgent>,
client_stats_id: String, client_stats_id: String,
input_sender: Sender<InputMessage>, input_sender: Sender<InputMessage>,
task_client: TaskClient, shutdown: TaskClient,
) -> ClientStatsSender { ) -> ClientStatsSender {
info!("Starting statistics control..."); info!("Starting statistics control...");
StatisticsControl::create_and_start( StatisticsControl::create_and_start_with_shutdown(
config.debug.stats_reporting, config.debug.stats_reporting,
user_agent user_agent
.map(|u| u.application) .map(|u| u.application)
.unwrap_or("unknown".to_string()), .unwrap_or("unknown".to_string()),
client_stats_id, client_stats_id,
input_sender.clone(), input_sender.clone(),
task_client, shutdown.with_suffix("controller"),
) )
} }
fn start_mix_traffic_controller( fn start_mix_traffic_controller(
gateway_transceiver: Box<dyn GatewayTransceiver + Send>, gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
shutdown: TaskClient, shutdown: TaskClient,
) -> (BatchMixMessageSender, ClientRequestSender) { forget_me: ForgetMe,
) -> BatchMixMessageSender {
info!("Starting mix traffic controller..."); info!("Starting mix traffic controller...");
let (mix_traffic_controller, mix_tx, client_tx) = let (mix_traffic_controller, mix_tx) =
MixTrafficController::new(gateway_transceiver, shutdown); MixTrafficController::new(gateway_transceiver, forget_me);
mix_traffic_controller.start(); 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 // TODO: rename it as it implies the data is persistent whilst one can use InMemBackend
@@ -703,7 +686,6 @@ where
setup_method: GatewaySetup, setup_method: GatewaySetup,
key_store: &S::KeyStore, key_store: &S::KeyStore,
details_store: &S::GatewaysDetailsStore, details_store: &S::GatewaysDetailsStore,
derivation_material: Option<DerivationMaterial>,
) -> Result<InitialisationResult, ClientCoreError> ) -> Result<InitialisationResult, ClientCoreError>
where where
<S::KeyStore as KeyStore>::StorageError: Sync + Send, <S::KeyStore as KeyStore>::StorageError: Sync + Send,
@@ -713,12 +695,7 @@ where
if key_store.load_keys().await.is_err() { if key_store.load_keys().await.is_err() {
info!("could not find valid client keys - a new set will be generated"); info!("could not find valid client keys - a new set will be generated");
let mut rng = OsRng; let mut rng = OsRng;
let keys = if let Some(derivation_material) = derivation_material { let keys = ClientKeys::generate_new(&mut rng);
ClientKeys::from_master_key(&mut rng, &derivation_material)
.map_err(|_| ClientCoreError::HkdfDerivationError {})?
} else {
ClientKeys::generate_new(&mut rng)
};
store_client_keys(keys, key_store).await?; store_client_keys(keys, key_store).await?;
} }
@@ -740,7 +717,6 @@ where
self.setup_method, self.setup_method,
self.client_store.key_store(), self.client_store.key_store(),
self.client_store.gateway_details_store(), self.client_store.gateway_details_store(),
self.derivation_material,
) )
.await?; .await?;
@@ -797,7 +773,7 @@ where
); );
let stats_reporter = Self::start_statistics_control( let stats_reporter = Self::start_statistics_control(
&self.config, self.config,
self.user_agent.clone(), self.user_agent.clone(),
generate_client_stats_id(*self_address.identity()), generate_client_stats_id(*self_address.identity()),
input_sender.clone(), input_sender.clone(),
@@ -823,7 +799,7 @@ where
let gateway_transceiver = Self::setup_gateway_transceiver( let gateway_transceiver = Self::setup_gateway_transceiver(
self.custom_gateway_transceiver, self.custom_gateway_transceiver,
&self.config, self.config,
init_res, init_res,
bandwidth_controller, bandwidth_controller,
&details_store, &details_store,
@@ -857,9 +833,10 @@ where
// traffic stream. // traffic stream.
// The MixTrafficController then sends the actual traffic // 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, gateway_transceiver,
shutdown.fork("mix_traffic_controller"), shutdown.fork("mix_traffic_controller"),
self.forget_me,
); );
// Channels that the websocket listener can use to signal downstream to the real traffic // Channels that the websocket listener can use to signal downstream to the real traffic
@@ -934,8 +911,6 @@ where
}, },
stats_reporter, stats_reporter,
task_handle: shutdown, task_handle: shutdown,
client_request_sender,
forget_me: self.config.debug.forget_me,
}) })
} }
} }
@@ -947,7 +922,6 @@ pub struct BaseClient {
pub client_output: ClientOutputStatus, pub client_output: ClientOutputStatus,
pub client_state: ClientState, pub client_state: ClientState,
pub stats_reporter: ClientStatsSender, pub stats_reporter: ClientStatsSender,
pub client_request_sender: ClientRequestSender,
pub task_handle: TaskHandle, pub task_handle: TaskHandle,
pub forget_me: ForgetMe,
} }
@@ -4,8 +4,6 @@
// TODO: combine those more closely. Perhaps into a single underlying store. // 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? // 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::key_manager::persistence::{InMemEphemeralKeys, KeyStore};
use crate::client::replies::reply_storage; use crate::client::replies::reply_storage;
use crate::client::replies::reply_storage::ReplyStorageBackend; use crate::client::replies::reply_storage::ReplyStorageBackend;
@@ -65,7 +63,7 @@ pub trait MixnetClientStorage {
fn gateway_details_store(&self) -> &Self::GatewaysDetailsStore; fn gateway_details_store(&self) -> &Self::GatewaysDetailsStore;
} }
#[derive(Clone)] #[derive(Clone, Default)]
pub struct Ephemeral { pub struct Ephemeral {
key_store: InMemEphemeralKeys, key_store: InMemEphemeralKeys,
reply_store: reply_storage::Empty, reply_store: reply_storage::Empty,
@@ -73,14 +71,9 @@ pub struct Ephemeral {
gateway_details_store: InMemGatewaysDetails, gateway_details_store: InMemGatewaysDetails,
} }
impl Default for Ephemeral { impl Ephemeral {
fn default() -> Self { pub fn new() -> Self {
Ephemeral { Default::default()
key_store: InMemEphemeralKeys::new(&mut OsRng),
reply_store: Default::default(),
credential_store: Default::default(),
gateway_details_store: Default::default(),
}
} }
} }
@@ -13,7 +13,6 @@ use nym_sphinx::cover::generate_loop_cover_packet;
use nym_sphinx::params::{PacketSize, PacketType}; use nym_sphinx::params::{PacketSize, PacketType};
use nym_sphinx::utils::sample_poisson_duration; use nym_sphinx::utils::sample_poisson_duration;
use nym_statistics_common::clients::{packet_statistics::PacketStatisticsEvent, ClientStatsSender}; use nym_statistics_common::clients::{packet_statistics::PacketStatisticsEvent, ClientStatsSender};
use nym_task::TaskClient;
use rand::{rngs::OsRng, CryptoRng, Rng}; use rand::{rngs::OsRng, CryptoRng, Rng};
use std::pin::Pin; use std::pin::Pin;
use std::sync::Arc; use std::sync::Arc;
@@ -65,8 +64,6 @@ where
packet_type: PacketType, packet_type: PacketType,
stats_tx: ClientStatsSender, stats_tx: ClientStatsSender,
task_client: TaskClient,
} }
impl<R> Stream for LoopCoverTrafficStream<R> impl<R> Stream for LoopCoverTrafficStream<R>
@@ -113,7 +110,6 @@ impl LoopCoverTrafficStream<OsRng> {
traffic_config: config::Traffic, traffic_config: config::Traffic,
cover_config: config::CoverTraffic, cover_config: config::CoverTraffic,
stats_tx: ClientStatsSender, stats_tx: ClientStatsSender,
task_client: TaskClient,
) -> Self { ) -> Self {
let rng = OsRng; let rng = OsRng;
@@ -132,7 +128,6 @@ impl LoopCoverTrafficStream<OsRng> {
secondary_packet_size: traffic_config.secondary_packet_size, secondary_packet_size: traffic_config.secondary_packet_size,
packet_type: traffic_config.packet_type, packet_type: traffic_config.packet_type,
stats_tx, stats_tx,
task_client,
} }
} }
@@ -180,7 +175,7 @@ impl LoopCoverTrafficStream<OsRng> {
} }
}; };
let cover_message = match generate_loop_cover_packet( let cover_message = generate_loop_cover_packet(
&mut self.rng, &mut self.rng,
topology_ref, topology_ref,
&self.ack_key, &self.ack_key,
@@ -189,15 +184,8 @@ impl LoopCoverTrafficStream<OsRng> {
self.cover_traffic.loop_cover_traffic_average_delay, self.cover_traffic.loop_cover_traffic_average_delay,
cover_traffic_packet_size, cover_traffic_packet_size,
self.packet_type, self.packet_type,
) { )
Ok(cover_message) => cover_message, .expect("Somehow failed to generate a loop cover message with a valid topology");
Err(err) => {
warn!(
"Somehow failed to generate a loop cover message with a valid topology: {err}"
);
return;
}
};
if let Err(err) = self.mix_tx.try_send(vec![cover_message]) { if let Err(err) = self.mix_tx.try_send(vec![cover_message]) {
match err { match err {
@@ -229,7 +217,7 @@ impl LoopCoverTrafficStream<OsRng> {
tokio::task::yield_now().await; tokio::task::yield_now().await;
} }
pub fn start(mut self) { pub fn start_with_shutdown(mut self, mut shutdown: nym_task::TaskClient) {
if self.cover_traffic.disable_loop_cover_traffic_stream { if self.cover_traffic.disable_loop_cover_traffic_stream {
// we should have never got here in the first place - the task should have never been created to begin with // we should have never got here in the first place - the task should have never been created to begin with
// so panic and review the code that lead to this branch // so panic and review the code that lead to this branch
@@ -243,8 +231,6 @@ impl LoopCoverTrafficStream<OsRng> {
); );
self.set_next_delay(sampled); self.set_next_delay(sampled);
let mut shutdown = self.task_client.fork("select");
spawn_future(async move { spawn_future(async move {
debug!("Started LoopCoverTrafficStream with graceful shutdown support"); debug!("Started LoopCoverTrafficStream with graceful shutdown support");
@@ -2,10 +2,7 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::client::key_manager::persistence::KeyStore; use crate::client::key_manager::persistence::KeyStore;
use nym_crypto::{ use nym_crypto::asymmetric::{encryption, identity};
asymmetric::{encryption, identity},
hkdf::{DerivationMaterial, InvalidLength},
};
use nym_gateway_requests::shared_key::{LegacySharedKeys, SharedGatewayKey, SharedSymmetricKey}; use nym_gateway_requests::shared_key::{LegacySharedKeys, SharedGatewayKey, SharedSymmetricKey};
use nym_sphinx::acknowledgements::AckKey; use nym_sphinx::acknowledgements::AckKey;
use rand::{CryptoRng, RngCore}; use rand::{CryptoRng, RngCore};
@@ -13,7 +10,6 @@ use std::sync::Arc;
use zeroize::ZeroizeOnDrop; use zeroize::ZeroizeOnDrop;
pub mod persistence; pub mod persistence;
mod test;
// Note: to support key rotation in the future, all keys will require adding an extra smart pointer, // Note: to support key rotation in the future, all keys will require adding an extra smart pointer,
// most likely an AtomicCell, or if it doesn't work as I think it does, a Mutex. Although I think // most likely an AtomicCell, or if it doesn't work as I think it does, a Mutex. Although I think
@@ -47,24 +43,6 @@ impl ClientKeys {
} }
} }
pub fn from_master_key<R>(
rng: &mut R,
derivation_material: &DerivationMaterial,
) -> Result<Self, InvalidLength>
where
R: RngCore + CryptoRng,
{
let secret = derivation_material.derive_secret()?;
Ok(ClientKeys {
identity_keypair: Arc::new(identity::KeyPair::from_secret(
secret,
derivation_material.index(),
)),
encryption_keypair: Arc::new(encryption::KeyPair::new(rng)),
ack_key: Arc::new(AckKey::new(rng)),
})
}
pub fn from_keys( pub fn from_keys(
id_keypair: identity::KeyPair, id_keypair: identity::KeyPair,
enc_keypair: encryption::KeyPair, enc_keypair: encryption::KeyPair,
@@ -3,7 +3,6 @@
use crate::client::key_manager::ClientKeys; use crate::client::key_manager::ClientKeys;
use async_trait::async_trait; use async_trait::async_trait;
use rand::{CryptoRng, RngCore};
use std::error::Error; use std::error::Error;
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::Mutex; use tokio::sync::Mutex;
@@ -196,20 +195,9 @@ impl KeyStore for OnDiskKeys {
} }
} }
#[derive(Clone)] #[derive(Clone, Default)]
pub struct InMemEphemeralKeys { pub struct InMemEphemeralKeys {
keys: Arc<Mutex<ClientKeys>>, keys: Arc<Mutex<Option<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))),
}
}
} }
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
@@ -222,11 +210,11 @@ impl KeyStore for InMemEphemeralKeys {
type StorageError = EphemeralKeysError; type StorageError = EphemeralKeysError;
async fn load_keys(&self) -> Result<ClientKeys, Self::StorageError> { 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> { async fn store_keys(&self, keys: &ClientKeys) -> Result<(), Self::StorageError> {
*self.keys.lock().await = keys.clone(); *self.keys.lock().await = Some(keys.clone());
Ok(()) Ok(())
} }
} }
@@ -1,89 +0,0 @@
#[cfg(test)]
mod tests {
use crate::client::key_manager::ClientKeys;
use nym_crypto::hkdf::DerivationMaterial;
use rand::SeedableRng;
use rand_chacha::ChaCha20Rng;
#[test]
fn test_from_master_key_success() {
// Set up a deterministic RNG.
let seed = [33u8; 32];
let mut rng = ChaCha20Rng::from_seed(seed);
// Set up the derivation material.
let master_key = b"this is a secret master key";
let salt = b"unique-salt";
let derivation_material = DerivationMaterial::new(master_key, 0, salt);
// Generate ClientKeys from the master key.
let client_keys = ClientKeys::from_master_key(&mut rng, &derivation_material)
.expect("Failed to create client keys");
assert_eq!(
client_keys.identity_keypair().public_key().to_string(),
String::from("FX4Undr5LPPBA7zThWWpAKXKQTXSbW1C28PnxbCqUkU4")
);
assert_eq!(
client_keys.identity_keypair().private_key().to_string(),
String::from("6S3uMi2rU5SwyUUYCiMrF5qqdcYnEDMYLggBSvavVzEt")
);
}
#[test]
fn test_from_master_key_deterministic_identity() {
// Using identical derivation material should result in the exactly same identity keypair.
let seed = [1u8; 32];
let mut rng1 = ChaCha20Rng::from_seed(seed);
let mut rng2 = ChaCha20Rng::from_seed(seed);
let master_key = b"another secret master key";
let salt = b"deterministic-salt";
let index = 7u32;
let derivation_material = DerivationMaterial::new(master_key, index, salt);
let client_keys1 = ClientKeys::from_master_key(&mut rng1, &derivation_material)
.expect("Failed to create client keys (first instance)");
let client_keys2 = ClientKeys::from_master_key(&mut rng2, &derivation_material)
.expect("Failed to create client keys (second instance)");
assert_eq!(
client_keys1.identity_keypair().public_key().to_string(),
client_keys2.identity_keypair().public_key().to_string()
);
assert_eq!(
client_keys1.identity_keypair().private_key().to_string(),
client_keys2.identity_keypair().private_key().to_string()
);
}
#[test]
fn test_from_master_key_different_indices() {
// Changing the index should yield a different identity key.
let seed = [5u8; 32];
let mut rng = ChaCha20Rng::from_seed(seed);
let master_key = b"same secret key";
let salt = b"same-salt";
let derivation_material1 = DerivationMaterial::new(master_key, 1, salt);
let derivation_material2 = DerivationMaterial::new(master_key, 2, salt);
let client_keys1 = ClientKeys::from_master_key(&mut rng, &derivation_material1)
.expect("Failed to create client keys for index 1");
let client_keys2 = ClientKeys::from_master_key(&mut rng, &derivation_material2)
.expect("Failed to create client keys for index 2");
assert_ne!(
client_keys1.identity_keypair().public_key().to_string(),
client_keys2.identity_keypair().public_key().to_string()
);
assert_ne!(
client_keys1.identity_keypair().private_key().to_string(),
client_keys2.identity_keypair().private_key().to_string()
);
}
}
@@ -2,18 +2,13 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::client::mix_traffic::transceiver::GatewayTransceiver; use crate::client::mix_traffic::transceiver::GatewayTransceiver;
use crate::error::ClientCoreError; use crate::{spawn_future, ForgetMe};
use crate::spawn_future;
use log::*; use log::*;
use nym_gateway_requests::ClientRequest; use nym_gateway_requests::ClientRequest;
use nym_sphinx::forwarding::packet::MixPacket; use nym_sphinx::forwarding::packet::MixPacket;
use nym_task::TaskClient;
use transceiver::ErasedGatewayError;
pub type BatchMixMessageSender = tokio::sync::mpsc::Sender<Vec<MixPacket>>; pub type BatchMixMessageSender = tokio::sync::mpsc::Sender<Vec<MixPacket>>;
pub type BatchMixMessageReceiver = tokio::sync::mpsc::Receiver<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; pub mod transceiver;
@@ -28,73 +23,52 @@ pub struct MixTrafficController {
gateway_transceiver: Box<dyn GatewayTransceiver + Send>, gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
mix_rx: BatchMixMessageReceiver, mix_rx: BatchMixMessageReceiver,
client_rx: ClientRequestReceiver,
// TODO: this is temporary work-around. // TODO: this is temporary work-around.
// in long run `gateway_client` will be moved away from `MixTrafficController` anyway. // in long run `gateway_client` will be moved away from `MixTrafficController` anyway.
consecutive_gateway_failure_count: usize, consecutive_gateway_failure_count: usize,
forget_me: ForgetMe,
task_client: TaskClient,
} }
impl MixTrafficController { impl MixTrafficController {
pub fn new<T>( pub fn new<T>(
gateway_transceiver: T, gateway_transceiver: T,
task_client: TaskClient, forget_me: ForgetMe,
) -> ( ) -> (MixTrafficController, BatchMixMessageSender)
MixTrafficController,
BatchMixMessageSender,
ClientRequestSender,
)
where where
T: GatewayTransceiver + Send + 'static, T: GatewayTransceiver + Send + 'static,
{ {
let (message_sender, message_receiver) = let (message_sender, message_receiver) =
tokio::sync::mpsc::channel(MIX_MESSAGE_RECEIVER_BUFFER_SIZE); tokio::sync::mpsc::channel(MIX_MESSAGE_RECEIVER_BUFFER_SIZE);
let (client_sender, client_receiver) = tokio::sync::mpsc::channel(1);
( (
MixTrafficController { MixTrafficController {
gateway_transceiver: Box::new(gateway_transceiver), gateway_transceiver: Box::new(gateway_transceiver),
mix_rx: message_receiver, mix_rx: message_receiver,
client_rx: client_receiver,
consecutive_gateway_failure_count: 0, consecutive_gateway_failure_count: 0,
task_client, forget_me,
}, },
message_sender, message_sender,
client_sender,
) )
} }
pub fn new_dynamic( pub fn new_dynamic(
gateway_transceiver: Box<dyn GatewayTransceiver + Send>, gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
task_client: TaskClient, forget_me: ForgetMe,
) -> ( ) -> (MixTrafficController, BatchMixMessageSender) {
MixTrafficController,
BatchMixMessageSender,
ClientRequestSender,
) {
let (message_sender, message_receiver) = let (message_sender, message_receiver) =
tokio::sync::mpsc::channel(MIX_MESSAGE_RECEIVER_BUFFER_SIZE); tokio::sync::mpsc::channel(MIX_MESSAGE_RECEIVER_BUFFER_SIZE);
let (client_sender, client_receiver) = tokio::sync::mpsc::channel(1);
( (
MixTrafficController { MixTrafficController {
gateway_transceiver, gateway_transceiver,
mix_rx: message_receiver, mix_rx: message_receiver,
client_rx: client_receiver,
consecutive_gateway_failure_count: 0, consecutive_gateway_failure_count: 0,
task_client, forget_me,
}, },
message_sender, message_sender,
client_sender,
) )
} }
async fn on_messages( async fn on_messages(&mut self, mut mix_packets: Vec<MixPacket>) {
&mut self,
mut mix_packets: Vec<MixPacket>,
) -> Result<(), ErasedGatewayError> {
debug_assert!(!mix_packets.is_empty()); debug_assert!(!mix_packets.is_empty());
let result = if mix_packets.len() == 1 { let result = if mix_packets.len() == 1 {
@@ -106,60 +80,64 @@ impl MixTrafficController {
.await .await
}; };
if result.is_err() { match result {
self.consecutive_gateway_failure_count += 1; Err(err) => {
} else { error!("Failed to send sphinx packet(s) to the gateway: {err}");
trace!("We *might* have managed to forward sphinx packet(s) to the gateway!"); self.consecutive_gateway_failure_count += 1;
self.consecutive_gateway_failure_count = 0; 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(mut self) { pub fn start_with_shutdown(mut self, mut shutdown: nym_task::TaskClient) {
spawn_future(async move { spawn_future(async move {
debug!("Started MixTrafficController with graceful shutdown support"); debug!("Started MixTrafficController with graceful shutdown support");
while !self.task_client.is_shutdown() { loop {
tokio::select! { tokio::select! {
mix_packets = self.mix_rx.recv() => match mix_packets { mix_packets = self.mix_rx.recv() => match mix_packets {
Some(mix_packets) => { Some(mix_packets) => {
if let Err(err) = self.on_messages(mix_packets).await { 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?
self.task_client.send_we_stopped(Box::new(ClientCoreError::GatewayFailedToForwardMessages));
break;
}
}
}, },
None => { None => {
log::trace!("MixTrafficController: Stopping since channel closed"); log::trace!("MixTrafficController: Stopping since channel closed");
break; break;
} }
}, },
client_request = self.client_rx.recv() => match client_request { _ = shutdown.recv_with_delay() => {
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");
}
},
_ = self.task_client.recv() => {
log::trace!("MixTrafficController: Received shutdown"); log::trace!("MixTrafficController: Received shutdown");
break; break;
} }
} }
} }
self.task_client.recv_timeout().await; 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"); log::debug!("MixTrafficController: Exiting");
}); });
@@ -86,9 +86,7 @@ impl<G: GatewayTransceiver + ?Sized + Send> GatewayTransceiver for Box<G> {
&mut self, &mut self,
message: ClientRequest, message: ClientRequest,
) -> Result<(), GatewayClientError> { ) -> Result<(), GatewayClientError> {
let _ = (**self).send_client_request(message.clone()).await?; (**self).send_client_request(message).await
log::debug!("Sent client request: {:?}", message);
Ok(())
} }
} }
@@ -145,7 +143,14 @@ where
&mut self, &mut self,
message: ClientRequest, message: ClientRequest,
) -> Result<(), GatewayClientError> { ) -> 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)
}
} }
} }
@@ -11,7 +11,6 @@ use nym_sphinx::{
acknowledgements::{identifier::recover_identifier, AckKey}, acknowledgements::{identifier::recover_identifier, AckKey},
chunking::fragment::{FragmentIdentifier, COVER_FRAG_ID}, chunking::fragment::{FragmentIdentifier, COVER_FRAG_ID},
}; };
use nym_task::TaskClient;
use std::sync::Arc; use std::sync::Arc;
/// Module responsible for listening for any data resembling acknowledgements from the network /// Module responsible for listening for any data resembling acknowledgements from the network
@@ -21,7 +20,6 @@ pub(super) struct AcknowledgementListener {
ack_receiver: AcknowledgementReceiver, ack_receiver: AcknowledgementReceiver,
action_sender: AckActionSender, action_sender: AckActionSender,
stats_tx: ClientStatsSender, stats_tx: ClientStatsSender,
task_client: TaskClient,
} }
impl AcknowledgementListener { impl AcknowledgementListener {
@@ -30,14 +28,12 @@ impl AcknowledgementListener {
ack_receiver: AcknowledgementReceiver, ack_receiver: AcknowledgementReceiver,
action_sender: AckActionSender, action_sender: AckActionSender,
stats_tx: ClientStatsSender, stats_tx: ClientStatsSender,
task_client: TaskClient,
) -> Self { ) -> Self {
AcknowledgementListener { AcknowledgementListener {
ack_key, ack_key,
ack_receiver, ack_receiver,
action_sender, action_sender,
stats_tx, stats_tx,
task_client,
} }
} }
@@ -68,14 +64,9 @@ impl AcknowledgementListener {
trace!("Received {} from the mix network", frag_id); trace!("Received {} from the mix network", frag_id);
self.stats_tx self.stats_tx
.report(PacketStatisticsEvent::RealAckReceived(ack_content.len()).into()); .report(PacketStatisticsEvent::RealAckReceived(ack_content.len()).into());
if let Err(err) = self self.action_sender
.action_sender
.unbounded_send(Action::new_remove(frag_id)) .unbounded_send(Action::new_remove(frag_id))
{ .unwrap();
if !self.task_client.is_shutdown_poll() {
error!("Failed to send remove action to action controller: {err}");
}
}
} }
async fn handle_ack_receiver_item(&mut self, item: Vec<Vec<u8>>) { async fn handle_ack_receiver_item(&mut self, item: Vec<Vec<u8>>) {
@@ -85,10 +76,10 @@ impl AcknowledgementListener {
} }
} }
pub(super) async fn run(&mut self) { pub(super) async fn run_with_shutdown(&mut self, mut shutdown: nym_task::TaskClient) {
debug!("Started AcknowledgementListener with graceful shutdown support"); debug!("Started AcknowledgementListener with graceful shutdown support");
while !self.task_client.is_shutdown() { while !shutdown.is_shutdown() {
tokio::select! { tokio::select! {
acks = self.ack_receiver.next() => match acks { acks = self.ack_receiver.next() => match acks {
Some(acks) => self.handle_ack_receiver_item(acks).await, Some(acks) => self.handle_ack_receiver_item(acks).await,
@@ -97,12 +88,12 @@ impl AcknowledgementListener {
break; break;
} }
}, },
_ = self.task_client.recv() => { _ = shutdown.recv_with_delay() => {
log::trace!("AcknowledgementListener: Received shutdown"); log::trace!("AcknowledgementListener: Received shutdown");
} }
} }
} }
self.task_client.recv_timeout().await; shutdown.recv_timeout().await;
log::debug!("AcknowledgementListener: Exiting"); log::debug!("AcknowledgementListener: Exiting");
} }
} }
@@ -9,7 +9,6 @@ use log::*;
use nym_nonexhaustive_delayqueue::{Expired, NonExhaustiveDelayQueue, QueueKey}; use nym_nonexhaustive_delayqueue::{Expired, NonExhaustiveDelayQueue, QueueKey};
use nym_sphinx::chunking::fragment::FragmentIdentifier; use nym_sphinx::chunking::fragment::FragmentIdentifier;
use nym_sphinx::Delay as SphinxDelay; use nym_sphinx::Delay as SphinxDelay;
use nym_task::TaskClient;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
@@ -102,8 +101,6 @@ pub(super) struct ActionController {
/// Channel for notifying `RetransmissionRequestListener` about expired acknowledgements. /// Channel for notifying `RetransmissionRequestListener` about expired acknowledgements.
retransmission_sender: RetransmissionRequestSender, retransmission_sender: RetransmissionRequestSender,
task_client: TaskClient,
} }
impl ActionController { impl ActionController {
@@ -111,7 +108,6 @@ impl ActionController {
config: Config, config: Config,
retransmission_sender: RetransmissionRequestSender, retransmission_sender: RetransmissionRequestSender,
incoming_actions: AckActionReceiver, incoming_actions: AckActionReceiver,
task_client: TaskClient,
) -> Self { ) -> Self {
ActionController { ActionController {
config, config,
@@ -119,7 +115,6 @@ impl ActionController {
pending_acks_timers: NonExhaustiveDelayQueue::new(), pending_acks_timers: NonExhaustiveDelayQueue::new(),
incoming_actions, incoming_actions,
retransmission_sender, retransmission_sender,
task_client,
} }
} }
@@ -221,7 +216,11 @@ impl ActionController {
} }
// note: when the entry expires it's automatically removed from pending_acks_timers // note: when the entry expires it's automatically removed from pending_acks_timers
fn handle_expired_ack_timer(&mut self, expired_ack: Expired<FragmentIdentifier>) { fn handle_expired_ack_timer(
&mut self,
expired_ack: Expired<FragmentIdentifier>,
task_client: &mut nym_task::TaskClient,
) {
// I'm honestly not sure how to handle it, because getting it means other things in our // I'm honestly not sure how to handle it, because getting it means other things in our
// system are already misbehaving. If we ever see this panic, then I guess we should worry // system are already misbehaving. If we ever see this panic, then I guess we should worry
// about it. Perhaps just reschedule it at later point? // about it. Perhaps just reschedule it at later point?
@@ -239,13 +238,15 @@ impl ActionController {
// downgrading an arc and then upgrading vs cloning is difference of 30ns vs 15ns // downgrading an arc and then upgrading vs cloning is difference of 30ns vs 15ns
// so it's literally a NO difference while it might prevent us from unnecessarily // so it's literally a NO difference while it might prevent us from unnecessarily
// resending data (in maybe 1 in 1 million cases, but it's something) // resending data (in maybe 1 in 1 million cases, but it's something)
if let Err(err) = self if self
.retransmission_sender .retransmission_sender
.unbounded_send(Arc::downgrade(pending_ack_data)) .unbounded_send(Arc::downgrade(pending_ack_data))
.is_err()
{ {
if !self.task_client.is_shutdown_poll() { assert!(
log::error!("Failed to send pending ack for retransmission: {err}"); task_client.is_shutdown_poll(),
} "Failed to send pending ack for retransmission"
);
} }
} else { } else {
// this shouldn't cause any issues but shouldn't have happened to begin with! // this shouldn't cause any issues but shouldn't have happened to begin with!
@@ -264,10 +265,10 @@ impl ActionController {
} }
} }
pub(super) async fn run(&mut self) { pub(super) async fn run_with_shutdown(&mut self, mut shutdown: nym_task::TaskClient) {
debug!("Started ActionController with graceful shutdown support"); debug!("Started ActionController with graceful shutdown support");
while !self.task_client.is_shutdown() { loop {
tokio::select! { tokio::select! {
action = self.incoming_actions.next() => match action { action = self.incoming_actions.next() => match action {
Some(action) => self.process_action(action), Some(action) => self.process_action(action),
@@ -279,19 +280,19 @@ impl ActionController {
} }
}, },
expired_ack = self.pending_acks_timers.next() => match expired_ack { expired_ack = self.pending_acks_timers.next() => match expired_ack {
Some(expired_ack) => self.handle_expired_ack_timer(expired_ack), Some(expired_ack) => self.handle_expired_ack_timer(expired_ack, &mut shutdown),
None => { None => {
log::trace!("ActionController: Stopping since ack channel closed"); log::trace!("ActionController: Stopping since ack channel closed");
break; break;
} }
}, },
_ = self.task_client.recv() => { _ = shutdown.recv_with_delay() => {
log::trace!("ActionController: Received shutdown"); log::trace!("ActionController: Received shutdown");
break; break;
} }
} }
} }
self.task_client.recv_timeout().await; shutdown.recv_timeout().await;
log::debug!("ActionController: Exiting"); log::debug!("ActionController: Exiting");
} }
} }
@@ -11,7 +11,6 @@ use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
use nym_sphinx::forwarding::packet::MixPacket; use nym_sphinx::forwarding::packet::MixPacket;
use nym_sphinx::params::PacketType; use nym_sphinx::params::PacketType;
use nym_task::connections::TransmissionLane; use nym_task::connections::TransmissionLane;
use nym_task::TaskClient;
use rand::{CryptoRng, Rng}; use rand::{CryptoRng, Rng};
/// Module responsible for dealing with the received messages: splitting them, creating acknowledgements, /// Module responsible for dealing with the received messages: splitting them, creating acknowledgements,
@@ -24,7 +23,6 @@ where
input_receiver: InputMessageReceiver, input_receiver: InputMessageReceiver,
message_handler: MessageHandler<R>, message_handler: MessageHandler<R>,
reply_controller_sender: ReplyControllerSender, reply_controller_sender: ReplyControllerSender,
task_client: TaskClient,
} }
impl<R> InputMessageListener<R> impl<R> InputMessageListener<R>
@@ -38,13 +36,11 @@ where
input_receiver: InputMessageReceiver, input_receiver: InputMessageReceiver,
message_handler: MessageHandler<R>, message_handler: MessageHandler<R>,
reply_controller_sender: ReplyControllerSender, reply_controller_sender: ReplyControllerSender,
task_client: TaskClient,
) -> Self { ) -> Self {
InputMessageListener { InputMessageListener {
input_receiver, input_receiver,
message_handler, message_handler,
reply_controller_sender, reply_controller_sender,
task_client,
} }
} }
@@ -67,14 +63,8 @@ where
lane: TransmissionLane, lane: TransmissionLane,
) { ) {
// offload reply handling to the dedicated task // offload reply handling to the dedicated task
if let Err(err) = self self.reply_controller_sender
.reply_controller_sender
.send_reply(recipient_tag, data, lane) .send_reply(recipient_tag, data, lane)
{
if !self.task_client.is_shutdown_poll() {
error!("failed to send a reply - {err}");
}
}
} }
async fn handle_plain_message( async fn handle_plain_message(
@@ -174,10 +164,10 @@ where
}; };
} }
pub(super) async fn run(&mut self) { pub(super) async fn run_with_shutdown(&mut self, mut shutdown: nym_task::TaskClient) {
debug!("Started InputMessageListener with graceful shutdown support"); debug!("Started InputMessageListener with graceful shutdown support");
while !self.task_client.is_shutdown() { while !shutdown.is_shutdown() {
tokio::select! { tokio::select! {
input_msg = self.input_receiver.recv() => match input_msg { input_msg = self.input_receiver.recv() => match input_msg {
Some(input_msg) => { Some(input_msg) => {
@@ -188,12 +178,12 @@ where
break; break;
} }
}, },
_ = self.task_client.recv() => { _ = shutdown.recv_with_delay() => {
log::trace!("InputMessageListener: Received shutdown"); log::trace!("InputMessageListener: Received shutdown");
} }
} }
} }
self.task_client.recv_timeout().await; shutdown.recv_timeout().await;
log::debug!("InputMessageListener: Exiting"); log::debug!("InputMessageListener: Exiting");
} }
} }
@@ -24,7 +24,6 @@ use nym_sphinx::{
Delay as SphinxDelay, Delay as SphinxDelay,
}; };
use nym_statistics_common::clients::ClientStatsSender; use nym_statistics_common::clients::ClientStatsSender;
use nym_task::TaskClient;
use rand::{CryptoRng, Rng}; use rand::{CryptoRng, Rng};
use std::{ use std::{
sync::{Arc, Weak}, sync::{Arc, Weak},
@@ -67,7 +66,7 @@ pub(crate) enum PacketDestination {
/// Structure representing a data `Fragment` that is on-route to the specified `Recipient` /// Structure representing a data `Fragment` that is on-route to the specified `Recipient`
#[derive(Debug)] #[derive(Debug)]
pub struct PendingAcknowledgement { pub(crate) struct PendingAcknowledgement {
message_chunk: Fragment, message_chunk: Fragment,
delay: SphinxDelay, delay: SphinxDelay,
destination: PacketDestination, destination: PacketDestination,
@@ -217,7 +216,6 @@ where
message_handler: MessageHandler<R>, message_handler: MessageHandler<R>,
reply_controller_sender: ReplyControllerSender, reply_controller_sender: ReplyControllerSender,
stats_tx: ClientStatsSender, stats_tx: ClientStatsSender,
task_client: TaskClient,
) -> Self { ) -> Self {
let (retransmission_tx, retransmission_rx) = mpsc::unbounded(); let (retransmission_tx, retransmission_rx) = mpsc::unbounded();
@@ -227,7 +225,6 @@ where
action_config, action_config,
retransmission_tx, retransmission_tx,
connectors.ack_action_receiver, connectors.ack_action_receiver,
task_client.fork("action_controller"),
); );
// will listen for any acks coming from the network // will listen for any acks coming from the network
@@ -236,7 +233,6 @@ where
connectors.ack_receiver, connectors.ack_receiver,
connectors.ack_action_sender.clone(), connectors.ack_action_sender.clone(),
stats_tx, stats_tx,
task_client.fork("acknowledgement_listener"),
); );
// will listen for any new messages from the client // will listen for any new messages from the client
@@ -244,7 +240,6 @@ where
connectors.input_receiver, connectors.input_receiver,
message_handler.clone(), message_handler.clone(),
reply_controller_sender.clone(), reply_controller_sender.clone(),
task_client.fork("input_message_listener"),
); );
// will listen for any ack timeouts and trigger retransmission // will listen for any ack timeouts and trigger retransmission
@@ -254,16 +249,12 @@ where
message_handler, message_handler,
retransmission_rx, retransmission_rx,
reply_controller_sender, reply_controller_sender,
task_client.fork("retransmission_request_listener"),
); );
// will listen for events indicating the packet was sent through the network so that // will listen for events indicating the packet was sent through the network so that
// the retransmission timer should be started. // the retransmission timer should be started.
let sent_notification_listener = SentNotificationListener::new( let sent_notification_listener =
connectors.sent_notifier, SentNotificationListener::new(connectors.sent_notifier, connectors.ack_action_sender);
connectors.ack_action_sender,
task_client.with_suffix("sent_notification_listener"),
);
AcknowledgementController { AcknowledgementController {
acknowledgement_listener, acknowledgement_listener,
@@ -274,35 +265,53 @@ where
} }
} }
pub(super) fn start(self, packet_type: PacketType) { pub(super) fn start_with_shutdown(
self,
shutdown: nym_task::TaskClient,
packet_type: PacketType,
) {
let mut acknowledgement_listener = self.acknowledgement_listener; let mut acknowledgement_listener = self.acknowledgement_listener;
let mut input_message_listener = self.input_message_listener; let mut input_message_listener = self.input_message_listener;
let mut retransmission_request_listener = self.retransmission_request_listener; let mut retransmission_request_listener = self.retransmission_request_listener;
let mut sent_notification_listener = self.sent_notification_listener; let mut sent_notification_listener = self.sent_notification_listener;
let mut action_controller = self.action_controller; let mut action_controller = self.action_controller;
let shutdown_handle = shutdown.fork("acknowledgement_listener");
spawn_future(async move { spawn_future(async move {
acknowledgement_listener.run().await; acknowledgement_listener
.run_with_shutdown(shutdown_handle)
.await;
debug!("The acknowledgement listener has finished execution!"); debug!("The acknowledgement listener has finished execution!");
}); });
let shutdown_handle = shutdown.fork("input_message_listener");
spawn_future(async move { spawn_future(async move {
input_message_listener.run().await; input_message_listener
.run_with_shutdown(shutdown_handle)
.await;
debug!("The input listener has finished execution!"); debug!("The input listener has finished execution!");
}); });
let shutdown_handle = shutdown.fork("retransmission_request_listener");
spawn_future(async move { spawn_future(async move {
retransmission_request_listener.run(packet_type).await; retransmission_request_listener
.run_with_shutdown(shutdown_handle, packet_type)
.await;
debug!("The retransmission request listener has finished execution!"); debug!("The retransmission request listener has finished execution!");
}); });
let shutdown_handle = shutdown.fork("sent_notification_listener");
spawn_future(async move { spawn_future(async move {
sent_notification_listener.run().await; sent_notification_listener
.run_with_shutdown(shutdown_handle)
.await;
debug!("The sent notification listener has finished execution!"); debug!("The sent notification listener has finished execution!");
}); });
spawn_future(async move { spawn_future(async move {
action_controller.run().await; action_controller
.run_with_shutdown(shutdown.with_suffix("action_controller"))
.await;
debug!("The controller has finished execution!"); debug!("The controller has finished execution!");
}); });
} }
@@ -14,7 +14,7 @@ use log::*;
use nym_sphinx::chunking::fragment::Fragment; use nym_sphinx::chunking::fragment::Fragment;
use nym_sphinx::preparer::PreparedFragment; use nym_sphinx::preparer::PreparedFragment;
use nym_sphinx::{addressing::clients::Recipient, params::PacketType}; use nym_sphinx::{addressing::clients::Recipient, params::PacketType};
use nym_task::{connections::TransmissionLane, TaskClient}; use nym_task::connections::TransmissionLane;
use rand::{CryptoRng, Rng}; use rand::{CryptoRng, Rng};
use std::sync::{Arc, Weak}; use std::sync::{Arc, Weak};
@@ -25,7 +25,6 @@ pub(super) struct RetransmissionRequestListener<R> {
message_handler: MessageHandler<R>, message_handler: MessageHandler<R>,
request_receiver: RetransmissionRequestReceiver, request_receiver: RetransmissionRequestReceiver,
reply_controller_sender: ReplyControllerSender, reply_controller_sender: ReplyControllerSender,
task_client: TaskClient,
} }
impl<R> RetransmissionRequestListener<R> impl<R> RetransmissionRequestListener<R>
@@ -38,7 +37,6 @@ where
message_handler: MessageHandler<R>, message_handler: MessageHandler<R>,
request_receiver: RetransmissionRequestReceiver, request_receiver: RetransmissionRequestReceiver,
reply_controller_sender: ReplyControllerSender, reply_controller_sender: ReplyControllerSender,
task_client: TaskClient,
) -> Self { ) -> Self {
RetransmissionRequestListener { RetransmissionRequestListener {
maximum_retransmissions, maximum_retransmissions,
@@ -46,7 +44,6 @@ where
message_handler, message_handler,
request_receiver, request_receiver,
reply_controller_sender, reply_controller_sender,
task_client,
} }
} }
@@ -82,12 +79,9 @@ where
if let Some(limit) = self.maximum_retransmissions { if let Some(limit) = self.maximum_retransmissions {
if timed_out_ack.retransmissions >= limit { if timed_out_ack.retransmissions >= limit {
warn!("reached maximum number of allowed retransmissions for the packet"); warn!("reached maximum number of allowed retransmissions for the packet");
if let Err(err) = self self.action_sender
.action_sender
.unbounded_send(Action::new_remove(frag_id)) .unbounded_send(Action::new_remove(frag_id))
{ .unwrap();
error!("Failed to send remove action to the controller: {err}");
}
return; return;
} }
} }
@@ -99,16 +93,11 @@ where
} => { } => {
// if this is retransmission for reply, offload it to the dedicated task // if this is retransmission for reply, offload it to the dedicated task
// that deals with all the surbs // that deals with all the surbs
if let Err(err) = self.reply_controller_sender.send_retransmission_data( return self.reply_controller_sender.send_retransmission_data(
*recipient_tag, *recipient_tag,
weak_timed_out_ack, weak_timed_out_ack,
*extra_surb_request, *extra_surb_request,
) { );
if !self.task_client.is_shutdown_poll() {
error!("Failed to send retransmission data to the reply controller: {err}");
}
}
return;
} }
PacketDestination::KnownRecipient(recipient) => { PacketDestination::KnownRecipient(recipient) => {
self.prepare_normal_retransmission_chunk( self.prepare_normal_retransmission_chunk(
@@ -125,12 +114,9 @@ where
Err(err) => { Err(err) => {
warn!("Could not retransmit the packet - {err}"); warn!("Could not retransmit the packet - {err}");
// we NEED to start timer here otherwise we will have this guy permanently stuck in memory // we NEED to start timer here otherwise we will have this guy permanently stuck in memory
if let Err(err) = self self.action_sender
.action_sender
.unbounded_send(Action::new_start_timer(frag_id)) .unbounded_send(Action::new_start_timer(frag_id))
{ .unwrap();
error!("Failed to send start timer action to the controller: {err}");
}
return; return;
} }
}; };
@@ -155,14 +141,9 @@ where
// is sent to the `OutQueueControl` and has gone through its internal queue // is sent to the `OutQueueControl` and has gone through its internal queue
// with the additional poisson delay. // with the additional poisson delay.
// And since Actions are executed in order `UpdateTimer` will HAVE TO be executed before `StartTimer` // And since Actions are executed in order `UpdateTimer` will HAVE TO be executed before `StartTimer`
if let Err(err) = self self.action_sender
.action_sender
.unbounded_send(Action::new_update_pending_ack(frag_id, new_delay)) .unbounded_send(Action::new_update_pending_ack(frag_id, new_delay))
{ .unwrap();
if !self.task_client.is_shutdown_poll() {
error!("Failed to send update pending ack action to the controller: {err}");
}
}
// send to `OutQueueControl` to eventually send to the mix network // send to `OutQueueControl` to eventually send to the mix network
self.message_handler self.message_handler
@@ -176,10 +157,14 @@ where
.await .await
} }
pub(super) async fn run(&mut self, packet_type: PacketType) { pub(super) async fn run_with_shutdown(
&mut self,
mut shutdown: nym_task::TaskClient,
packet_type: PacketType,
) {
debug!("Started RetransmissionRequestListener with graceful shutdown support"); debug!("Started RetransmissionRequestListener with graceful shutdown support");
while !self.task_client.is_shutdown() { while !shutdown.is_shutdown() {
tokio::select! { tokio::select! {
timed_out_ack = self.request_receiver.next() => match timed_out_ack { timed_out_ack = self.request_receiver.next() => match timed_out_ack {
Some(timed_out_ack) => self.on_retransmission_request(timed_out_ack, packet_type).await, Some(timed_out_ack) => self.on_retransmission_request(timed_out_ack, packet_type).await,
@@ -188,12 +173,12 @@ where
break; break;
} }
}, },
_ = self.task_client.recv() => { _ = shutdown.recv() => {
log::trace!("RetransmissionRequestListener: Received shutdown"); log::trace!("RetransmissionRequestListener: Received shutdown");
} }
} }
} }
self.task_client.recv_timeout().await; shutdown.recv_timeout().await;
log::debug!("RetransmissionRequestListener: Exiting"); log::debug!("RetransmissionRequestListener: Exiting");
} }
} }
@@ -6,7 +6,6 @@ use super::SentPacketNotificationReceiver;
use futures::StreamExt; use futures::StreamExt;
use log::*; use log::*;
use nym_sphinx::chunking::fragment::{FragmentIdentifier, COVER_FRAG_ID}; use nym_sphinx::chunking::fragment::{FragmentIdentifier, COVER_FRAG_ID};
use nym_task::TaskClient;
/// Module responsible for starting up retransmission timers. /// Module responsible for starting up retransmission timers.
/// It is required because when we send our packet to the `real traffic stream` controlled /// It is required because when we send our packet to the `real traffic stream` controlled
@@ -15,19 +14,16 @@ use nym_task::TaskClient;
pub(super) struct SentNotificationListener { pub(super) struct SentNotificationListener {
sent_notifier: SentPacketNotificationReceiver, sent_notifier: SentPacketNotificationReceiver,
action_sender: AckActionSender, action_sender: AckActionSender,
task_client: TaskClient,
} }
impl SentNotificationListener { impl SentNotificationListener {
pub(super) fn new( pub(super) fn new(
sent_notifier: SentPacketNotificationReceiver, sent_notifier: SentPacketNotificationReceiver,
action_sender: AckActionSender, action_sender: AckActionSender,
task_client: TaskClient,
) -> Self { ) -> Self {
SentNotificationListener { SentNotificationListener {
sent_notifier, sent_notifier,
action_sender, action_sender,
task_client,
} }
} }
@@ -36,20 +32,15 @@ impl SentNotificationListener {
trace!("sent off a cover message - no need to start retransmission timer!"); trace!("sent off a cover message - no need to start retransmission timer!");
return; return;
} }
if let Err(err) = self self.action_sender
.action_sender
.unbounded_send(Action::new_start_timer(frag_id)) .unbounded_send(Action::new_start_timer(frag_id))
{ .unwrap();
if !self.task_client.is_shutdown_poll() {
error!("Failed to send start timer action to action controller: {err}");
}
}
} }
pub(super) async fn run(&mut self) { pub(super) async fn run_with_shutdown(&mut self, mut shutdown: nym_task::TaskClient) {
debug!("Started SentNotificationListener with graceful shutdown support"); debug!("Started SentNotificationListener with graceful shutdown support");
while !self.task_client.is_shutdown() { loop {
tokio::select! { tokio::select! {
frag_id = self.sent_notifier.next() => match frag_id { frag_id = self.sent_notifier.next() => match frag_id {
Some(frag_id) => { Some(frag_id) => {
@@ -60,13 +51,13 @@ impl SentNotificationListener {
break; break;
} }
}, },
_ = self.task_client.recv() => { _ = shutdown.recv_with_delay() => {
log::trace!("SentNotificationListener: Received shutdown"); log::trace!("SentNotificationListener: Received shutdown");
break; break;
} }
} }
} }
assert!(self.task_client.is_shutdown_poll()); assert!(shutdown.is_shutdown_poll());
log::debug!("SentNotificationListener: Exiting"); log::debug!("SentNotificationListener: Exiting");
} }
} }
@@ -19,7 +19,6 @@ use nym_sphinx::params::{PacketSize, PacketType};
use nym_sphinx::preparer::{MessagePreparer, PreparedFragment}; use nym_sphinx::preparer::{MessagePreparer, PreparedFragment};
use nym_sphinx::Delay; use nym_sphinx::Delay;
use nym_task::connections::TransmissionLane; use nym_task::connections::TransmissionLane;
use nym_task::TaskClient;
use nym_topology::{NymRouteProvider, NymTopologyError}; use nym_topology::{NymRouteProvider, NymTopologyError};
use rand::{CryptoRng, Rng}; use rand::{CryptoRng, Rng};
use std::collections::HashMap; use std::collections::HashMap;
@@ -150,14 +149,12 @@ pub(crate) struct MessageHandler<R> {
topology_access: TopologyAccessor, topology_access: TopologyAccessor,
reply_key_storage: SentReplyKeys, reply_key_storage: SentReplyKeys,
tag_storage: UsedSenderTags, tag_storage: UsedSenderTags,
task_client: TaskClient,
} }
impl<R> MessageHandler<R> impl<R> MessageHandler<R>
where where
R: CryptoRng + Rng, R: CryptoRng + Rng,
{ {
#[allow(clippy::too_many_arguments)]
pub(crate) fn new( pub(crate) fn new(
config: Config, config: Config,
rng: R, rng: R,
@@ -166,7 +163,6 @@ where
topology_access: TopologyAccessor, topology_access: TopologyAccessor,
reply_key_storage: SentReplyKeys, reply_key_storage: SentReplyKeys,
tag_storage: UsedSenderTags, tag_storage: UsedSenderTags,
task_client: TaskClient,
) -> Self ) -> Self
where where
R: Copy, R: Copy,
@@ -187,7 +183,6 @@ where
topology_access, topology_access,
reply_key_storage, reply_key_storage,
tag_storage, tag_storage,
task_client,
} }
} }
@@ -614,25 +609,15 @@ where
} }
pub(crate) fn update_ack_delay(&self, id: FragmentIdentifier, new_delay: Delay) { pub(crate) fn update_ack_delay(&self, id: FragmentIdentifier, new_delay: Delay) {
if let Err(err) = self self.action_sender
.action_sender
.unbounded_send(Action::UpdatePendingAck(id, new_delay)) .unbounded_send(Action::UpdatePendingAck(id, new_delay))
{ .expect("action control task has died")
if !self.task_client.is_shutdown_poll() {
error!("Failed to send update action to the controller: {err}");
}
}
} }
pub(crate) fn insert_pending_acks(&self, pending_acks: Vec<PendingAcknowledgement>) { pub(crate) fn insert_pending_acks(&self, pending_acks: Vec<PendingAcknowledgement>) {
if let Err(err) = self self.action_sender
.action_sender
.unbounded_send(Action::new_insert(pending_acks)) .unbounded_send(Action::new_insert(pending_acks))
{ .expect("action control task has died")
if !self.task_client.is_shutdown_poll() {
error!("Failed to send insert action to the controller: {err}");
}
}
} }
// tells real message sender (with the poisson timer) to send this to the mix network // tells real message sender (with the poisson timer) to send this to the mix network
@@ -641,14 +626,9 @@ where
messages: Vec<RealMessage>, messages: Vec<RealMessage>,
transmission_lane: TransmissionLane, transmission_lane: TransmissionLane,
) { ) {
if let Err(err) = self self.real_message_sender
.real_message_sender
.send((messages, transmission_lane)) .send((messages, transmission_lane))
.await .await
{ .expect("real message receiver task (OutQueueControl) has died");
if !self.task_client.is_shutdown_poll() {
error!("Failed to forward messages to the real message sender: {err}");
}
}
} }
} }
@@ -31,7 +31,6 @@ use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::params::PacketType; use nym_sphinx::params::PacketType;
use nym_statistics_common::clients::ClientStatsSender; use nym_statistics_common::clients::ClientStatsSender;
use nym_task::connections::{ConnectionCommandReceiver, LaneQueueLengths}; use nym_task::connections::{ConnectionCommandReceiver, LaneQueueLengths};
use nym_task::TaskClient;
use rand::{rngs::OsRng, CryptoRng, Rng}; use rand::{rngs::OsRng, CryptoRng, Rng};
use std::sync::Arc; use std::sync::Arc;
@@ -148,7 +147,6 @@ impl RealMessagesController<OsRng> {
lane_queue_lengths: LaneQueueLengths, lane_queue_lengths: LaneQueueLengths,
client_connection_rx: ConnectionCommandReceiver, client_connection_rx: ConnectionCommandReceiver,
stats_tx: ClientStatsSender, stats_tx: ClientStatsSender,
task_client: TaskClient,
) -> Self { ) -> Self {
let rng = OsRng; let rng = OsRng;
@@ -179,7 +177,6 @@ impl RealMessagesController<OsRng> {
topology_access.clone(), topology_access.clone(),
reply_storage.key_storage(), reply_storage.key_storage(),
reply_storage.tags_storage(), reply_storage.tags_storage(),
task_client.fork("message_handler"),
); );
let ack_control = AcknowledgementController::new( let ack_control = AcknowledgementController::new(
@@ -189,7 +186,6 @@ impl RealMessagesController<OsRng> {
message_handler.clone(), message_handler.clone(),
reply_controller_sender, reply_controller_sender,
stats_tx.clone(), stats_tx.clone(),
task_client.fork("ack_control"),
); );
let reply_control = ReplyController::new( let reply_control = ReplyController::new(
@@ -197,7 +193,6 @@ impl RealMessagesController<OsRng> {
message_handler, message_handler,
reply_storage, reply_storage,
reply_controller_receiver, reply_controller_receiver,
task_client.fork("reply_controller"),
); );
let out_queue_control = OutQueueControl::new( let out_queue_control = OutQueueControl::new(
@@ -210,7 +205,6 @@ impl RealMessagesController<OsRng> {
lane_queue_lengths, lane_queue_lengths,
client_connection_rx, client_connection_rx,
stats_tx, stats_tx,
task_client.with_suffix("out_queue_control"),
); );
RealMessagesController { RealMessagesController {
@@ -220,20 +214,22 @@ impl RealMessagesController<OsRng> {
} }
} }
pub fn start(self, packet_type: PacketType) { pub fn start_with_shutdown(self, shutdown: nym_task::TaskClient, packet_type: PacketType) {
let mut out_queue_control = self.out_queue_control; let mut out_queue_control = self.out_queue_control;
let ack_control = self.ack_control; let ack_control = self.ack_control;
let mut reply_control = self.reply_control; let mut reply_control = self.reply_control;
let shutdown_handle = shutdown.fork("out_queue_control");
spawn_future(async move { spawn_future(async move {
out_queue_control.run().await; out_queue_control.run_with_shutdown(shutdown_handle).await;
debug!("The out queue controller has finished execution!"); debug!("The out queue controller has finished execution!");
}); });
let shutdown_handle = shutdown.fork("reply_control");
spawn_future(async move { spawn_future(async move {
reply_control.run().await; reply_control.run_with_shutdown(shutdown_handle).await;
debug!("The reply controller has finished execution!"); debug!("The reply controller has finished execution!");
}); });
ack_control.start(packet_type); ack_control.start_with_shutdown(shutdown.with_suffix("ack_control"), packet_type);
} }
} }
@@ -22,7 +22,6 @@ use nym_statistics_common::clients::{packet_statistics::PacketStatisticsEvent, C
use nym_task::connections::{ use nym_task::connections::{
ConnectionCommand, ConnectionCommandReceiver, ConnectionId, LaneQueueLengths, TransmissionLane, ConnectionCommand, ConnectionCommandReceiver, ConnectionId, LaneQueueLengths, TransmissionLane,
}; };
use nym_task::TaskClient;
use rand::{CryptoRng, Rng}; use rand::{CryptoRng, Rng};
use std::pin::Pin; use std::pin::Pin;
use std::sync::Arc; use std::sync::Arc;
@@ -118,8 +117,6 @@ where
/// Channel used for sending metrics events (specifically `PacketStatistics` events) to the metrics tracker. /// Channel used for sending metrics events (specifically `PacketStatistics` events) to the metrics tracker.
stats_tx: ClientStatsSender, stats_tx: ClientStatsSender,
task_client: TaskClient,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -179,7 +176,6 @@ where
lane_queue_lengths: LaneQueueLengths, lane_queue_lengths: LaneQueueLengths,
client_connection_rx: ConnectionCommandReceiver, client_connection_rx: ConnectionCommandReceiver,
stats_tx: ClientStatsSender, stats_tx: ClientStatsSender,
task_client: TaskClient,
) -> Self { ) -> Self {
OutQueueControl { OutQueueControl {
config, config,
@@ -194,7 +190,6 @@ where
client_connection_rx, client_connection_rx,
lane_queue_lengths, lane_queue_lengths,
stats_tx, stats_tx,
task_client,
} }
} }
@@ -203,9 +198,7 @@ where
// queues and client load rather than the required delay. So realistically we can treat // queues and client load rather than the required delay. So realistically we can treat
// whatever is about to happen as negligible additional delay. // whatever is about to happen as negligible additional delay.
trace!("{} is about to get sent to the mixnet", frag_id); trace!("{} is about to get sent to the mixnet", frag_id);
if let Err(err) = self.sent_notifier.unbounded_send(frag_id) { self.sent_notifier.unbounded_send(frag_id).unwrap();
error!("Failed to notify about sent message: {err}");
}
} }
fn loop_cover_message_size(&mut self) -> PacketSize { fn loop_cover_message_size(&mut self) -> PacketSize {
@@ -278,9 +271,7 @@ where
}; };
if let Err(err) = self.mix_tx.send(vec![next_message]).await { if let Err(err) = self.mix_tx.send(vec![next_message]).await {
if !self.task_client.is_shutdown_poll() { log::error!("Failed to send: {err}");
log::error!("Failed to send: {err}");
}
} else { } else {
let event = if fragment_id.is_some() { let event = if fragment_id.is_some() {
PacketStatisticsEvent::RealPacketSent(packet_size) PacketStatisticsEvent::RealPacketSent(packet_size)
@@ -513,7 +504,7 @@ where
} }
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
fn log_status(&self, shutdown: &mut TaskClient) { fn log_status(&self, shutdown: &mut nym_task::TaskClient) {
use crate::error::ClientCoreStatusMessage; use crate::error::ClientCoreStatusMessage;
let packets = self.transmission_buffer.total_size(); let packets = self.transmission_buffer.total_size();
@@ -544,19 +535,17 @@ where
} }
} }
pub(super) async fn run(&mut self) { pub(super) async fn run_with_shutdown(&mut self, mut shutdown: nym_task::TaskClient) {
debug!("Started OutQueueControl with graceful shutdown support"); debug!("Started OutQueueControl with graceful shutdown support");
let mut shutdown = self.task_client.fork("select");
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
{ {
let mut status_timer = tokio::time::interval(Duration::from_secs(5)); let mut status_timer = tokio::time::interval(Duration::from_secs(5));
while !shutdown.is_shutdown() { loop {
tokio::select! { tokio::select! {
biased; biased;
_ = shutdown.recv() => { _ = shutdown.recv_with_delay() => {
log::trace!("OutQueueControl: Received shutdown"); log::trace!("OutQueueControl: Received shutdown");
break; break;
} }
@@ -20,7 +20,6 @@ use nym_sphinx::message::{NymMessage, PlainMessage};
use nym_sphinx::params::ReplySurbKeyDigestAlgorithm; use nym_sphinx::params::ReplySurbKeyDigestAlgorithm;
use nym_sphinx::receiver::{MessageReceiver, MessageRecoveryError, ReconstructedMessage}; use nym_sphinx::receiver::{MessageReceiver, MessageRecoveryError, ReconstructedMessage};
use nym_statistics_common::clients::{packet_statistics::PacketStatisticsEvent, ClientStatsSender}; use nym_statistics_common::clients::{packet_statistics::PacketStatisticsEvent, ClientStatsSender};
use nym_task::TaskClient;
use std::collections::HashSet; use std::collections::HashSet;
use std::sync::Arc; use std::sync::Arc;
@@ -153,7 +152,6 @@ struct ReceivedMessagesBuffer<R: MessageReceiver> {
inner: Arc<Mutex<ReceivedMessagesBufferInner<R>>>, inner: Arc<Mutex<ReceivedMessagesBufferInner<R>>>,
reply_key_storage: SentReplyKeys, reply_key_storage: SentReplyKeys,
reply_controller_sender: ReplyControllerSender, reply_controller_sender: ReplyControllerSender,
task_client: TaskClient,
} }
impl<R: MessageReceiver> ReceivedMessagesBuffer<R> { impl<R: MessageReceiver> ReceivedMessagesBuffer<R> {
@@ -162,7 +160,6 @@ impl<R: MessageReceiver> ReceivedMessagesBuffer<R> {
reply_key_storage: SentReplyKeys, reply_key_storage: SentReplyKeys,
reply_controller_sender: ReplyControllerSender, reply_controller_sender: ReplyControllerSender,
stats_tx: ClientStatsSender, stats_tx: ClientStatsSender,
task_client: TaskClient,
) -> Self { ) -> Self {
ReceivedMessagesBuffer { ReceivedMessagesBuffer {
inner: Arc::new(Mutex::new(ReceivedMessagesBufferInner { inner: Arc::new(Mutex::new(ReceivedMessagesBufferInner {
@@ -175,7 +172,6 @@ impl<R: MessageReceiver> ReceivedMessagesBuffer<R> {
})), })),
reply_key_storage, reply_key_storage,
reply_controller_sender, reply_controller_sender,
task_client,
} }
} }
@@ -261,15 +257,11 @@ impl<R: MessageReceiver> ReceivedMessagesBuffer<R> {
} }
}; };
if let Err(err) = self.reply_controller_sender.send_additional_surbs( self.reply_controller_sender.send_additional_surbs(
msg.sender_tag, msg.sender_tag,
reply_surbs, reply_surbs,
from_surb_request, from_surb_request,
) { )
if !self.task_client.is_shutdown_poll() {
error!("{err}");
}
}
} }
reconstructed reconstructed
} }
@@ -284,14 +276,8 @@ impl<R: MessageReceiver> ReceivedMessagesBuffer<R> {
ReplyMessageContent::Data { message } => reconstructed.push(message.into()), ReplyMessageContent::Data { message } => reconstructed.push(message.into()),
ReplyMessageContent::SurbRequest { recipient, amount } => { ReplyMessageContent::SurbRequest { recipient, amount } => {
debug!("received request for {amount} additional reply SURBs from {recipient}"); debug!("received request for {amount} additional reply SURBs from {recipient}");
if let Err(err) = self self.reply_controller_sender
.reply_controller_sender .send_additional_surbs_request(*recipient, amount);
.send_additional_surbs_request(*recipient, amount)
{
if !self.task_client.is_shutdown_poll() {
error!("{err}");
}
}
} }
} }
} }
@@ -413,19 +399,16 @@ pub enum ReceivedBufferMessage {
struct RequestReceiver<R: MessageReceiver> { struct RequestReceiver<R: MessageReceiver> {
received_buffer: ReceivedMessagesBuffer<R>, received_buffer: ReceivedMessagesBuffer<R>,
query_receiver: ReceivedBufferRequestReceiver, query_receiver: ReceivedBufferRequestReceiver,
task_client: TaskClient,
} }
impl<R: MessageReceiver> RequestReceiver<R> { impl<R: MessageReceiver> RequestReceiver<R> {
fn new( fn new(
received_buffer: ReceivedMessagesBuffer<R>, received_buffer: ReceivedMessagesBuffer<R>,
query_receiver: ReceivedBufferRequestReceiver, query_receiver: ReceivedBufferRequestReceiver,
task_client: TaskClient,
) -> Self { ) -> Self {
RequestReceiver { RequestReceiver {
received_buffer, received_buffer,
query_receiver, query_receiver,
task_client,
} }
} }
@@ -440,12 +423,12 @@ impl<R: MessageReceiver> RequestReceiver<R> {
} }
} }
async fn run(&mut self) { async fn run_with_shutdown(&mut self, mut shutdown: nym_task::TaskClient) {
debug!("Started RequestReceiver with graceful shutdown support"); debug!("Started RequestReceiver with graceful shutdown support");
while !self.task_client.is_shutdown() { while !shutdown.is_shutdown() {
tokio::select! { tokio::select! {
biased; biased;
_ = self.task_client.recv() => { _ = shutdown.recv_with_delay() => {
log::trace!("RequestReceiver: Received shutdown"); log::trace!("RequestReceiver: Received shutdown");
} }
request = self.query_receiver.next() => { request = self.query_receiver.next() => {
@@ -458,7 +441,7 @@ impl<R: MessageReceiver> RequestReceiver<R> {
}, },
} }
} }
self.task_client.recv().await; shutdown.recv_timeout().await;
log::debug!("RequestReceiver: Exiting"); log::debug!("RequestReceiver: Exiting");
} }
} }
@@ -466,25 +449,25 @@ impl<R: MessageReceiver> RequestReceiver<R> {
struct FragmentedMessageReceiver<R: MessageReceiver> { struct FragmentedMessageReceiver<R: MessageReceiver> {
received_buffer: ReceivedMessagesBuffer<R>, received_buffer: ReceivedMessagesBuffer<R>,
mixnet_packet_receiver: MixnetMessageReceiver, mixnet_packet_receiver: MixnetMessageReceiver,
task_client: TaskClient,
} }
impl<R: MessageReceiver> FragmentedMessageReceiver<R> { impl<R: MessageReceiver> FragmentedMessageReceiver<R> {
fn new( fn new(
received_buffer: ReceivedMessagesBuffer<R>, received_buffer: ReceivedMessagesBuffer<R>,
mixnet_packet_receiver: MixnetMessageReceiver, mixnet_packet_receiver: MixnetMessageReceiver,
task_client: TaskClient,
) -> Self { ) -> Self {
FragmentedMessageReceiver { FragmentedMessageReceiver {
received_buffer, received_buffer,
mixnet_packet_receiver, mixnet_packet_receiver,
task_client,
} }
} }
async fn run(&mut self) -> Result<(), MessageRecoveryError> { async fn run_with_shutdown(
&mut self,
mut shutdown: nym_task::TaskClient,
) -> Result<(), MessageRecoveryError> {
debug!("Started FragmentedMessageReceiver with graceful shutdown support"); debug!("Started FragmentedMessageReceiver with graceful shutdown support");
while !self.task_client.is_shutdown() { while !shutdown.is_shutdown() {
tokio::select! { tokio::select! {
new_messages = self.mixnet_packet_receiver.next() => { new_messages = self.mixnet_packet_receiver.next() => {
if let Some(new_messages) = new_messages { if let Some(new_messages) = new_messages {
@@ -494,12 +477,12 @@ impl<R: MessageReceiver> FragmentedMessageReceiver<R> {
break; break;
} }
}, },
_ = self.task_client.recv_with_delay() => { _ = shutdown.recv_with_delay() => {
log::trace!("FragmentedMessageReceiver: Received shutdown"); log::trace!("FragmentedMessageReceiver: Received shutdown");
} }
} }
} }
self.task_client.recv_timeout().await; shutdown.recv_timeout().await;
log::debug!("FragmentedMessageReceiver: Exiting"); log::debug!("FragmentedMessageReceiver: Exiting");
Ok(()) Ok(())
} }
@@ -518,42 +501,41 @@ impl<R: MessageReceiver + Clone + Send + 'static> ReceivedMessagesBufferControll
reply_key_storage: SentReplyKeys, reply_key_storage: SentReplyKeys,
reply_controller_sender: ReplyControllerSender, reply_controller_sender: ReplyControllerSender,
metrics_reporter: ClientStatsSender, metrics_reporter: ClientStatsSender,
task_client: TaskClient,
) -> Self { ) -> Self {
let received_buffer = ReceivedMessagesBuffer::new( let received_buffer = ReceivedMessagesBuffer::new(
local_encryption_keypair, local_encryption_keypair,
reply_key_storage, reply_key_storage,
reply_controller_sender, reply_controller_sender,
metrics_reporter, metrics_reporter,
task_client.fork("received_messages_buffer"),
); );
ReceivedMessagesBufferController { ReceivedMessagesBufferController {
fragmented_message_receiver: FragmentedMessageReceiver::new( fragmented_message_receiver: FragmentedMessageReceiver::new(
received_buffer.clone(), received_buffer.clone(),
mixnet_packet_receiver, mixnet_packet_receiver,
task_client.fork("fragmented_message_receiver"),
),
request_receiver: RequestReceiver::new(
received_buffer,
query_receiver,
task_client.with_suffix("request_receiver"),
), ),
request_receiver: RequestReceiver::new(received_buffer, query_receiver),
} }
} }
pub fn start(self) { pub fn start_with_shutdown(self, shutdown: nym_task::TaskClient) {
let mut fragmented_message_receiver = self.fragmented_message_receiver; let mut fragmented_message_receiver = self.fragmented_message_receiver;
let mut request_receiver = self.request_receiver; let mut request_receiver = self.request_receiver;
let shutdown_handle = shutdown.fork("fragmented_message_receiver");
spawn_future(async move { spawn_future(async move {
match fragmented_message_receiver.run().await { match fragmented_message_receiver
.run_with_shutdown(shutdown_handle)
.await
{
Ok(_) => {} Ok(_) => {}
Err(e) => error!("{e}"), Err(e) => error!("{e}"),
} }
}); });
spawn_future(async move { spawn_future(async move {
request_receiver.run().await; request_receiver
.run_with_shutdown(shutdown.with_suffix("request_receiver"))
.await;
}); });
} }
} }
@@ -12,7 +12,6 @@ use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
use nym_sphinx::anonymous_replies::ReplySurb; use nym_sphinx::anonymous_replies::ReplySurb;
use nym_sphinx::chunking::fragment::{Fragment, FragmentIdentifier}; use nym_sphinx::chunking::fragment::{Fragment, FragmentIdentifier};
use nym_task::connections::{ConnectionId, TransmissionLane}; use nym_task::connections::{ConnectionId, TransmissionLane};
use nym_task::TaskClient;
use rand::{CryptoRng, Rng}; use rand::{CryptoRng, Rng};
use std::cmp::{max, min}; use std::cmp::{max, min};
use std::collections::btree_map::Entry; use std::collections::btree_map::Entry;
@@ -69,9 +68,6 @@ pub struct ReplyController<R> {
message_handler: MessageHandler<R>, message_handler: MessageHandler<R>,
full_reply_storage: CombinedReplyStorage, full_reply_storage: CombinedReplyStorage,
// Listen for shutdown signals
task_client: TaskClient,
} }
impl<R> ReplyController<R> impl<R> ReplyController<R>
@@ -83,7 +79,6 @@ where
message_handler: MessageHandler<R>, message_handler: MessageHandler<R>,
full_reply_storage: CombinedReplyStorage, full_reply_storage: CombinedReplyStorage,
request_receiver: ReplyControllerReceiver, request_receiver: ReplyControllerReceiver,
task_client: TaskClient,
) -> Self { ) -> Self {
ReplyController { ReplyController {
config, config,
@@ -92,7 +87,6 @@ where
pending_retransmissions: HashMap::new(), pending_retransmissions: HashMap::new(),
message_handler, message_handler,
full_reply_storage, full_reply_storage,
task_client,
} }
} }
@@ -852,11 +846,9 @@ where
// todo!() // todo!()
// } // }
pub(crate) async fn run(&mut self) { pub(crate) async fn run_with_shutdown(&mut self, mut shutdown: nym_task::TaskClient) {
debug!("Started ReplyController with graceful shutdown support"); debug!("Started ReplyController with graceful shutdown support");
let mut shutdown = self.task_client.fork("select");
let polling_rate = Duration::from_secs(5); let polling_rate = Duration::from_secs(5);
let mut stale_inspection = new_interval_stream(polling_rate); let mut stale_inspection = new_interval_stream(polling_rate);
@@ -868,7 +860,7 @@ where
while !shutdown.is_shutdown() { while !shutdown.is_shutdown() {
tokio::select! { tokio::select! {
biased; biased;
_ = shutdown.recv() => { _ = shutdown.recv_with_delay() => {
log::trace!("ReplyController: Received shutdown"); log::trace!("ReplyController: Received shutdown");
}, },
req = self.request_receiver.next() => match req { req = self.request_receiver.next() => match req {
@@ -15,27 +15,6 @@ pub(crate) fn new_control_channels() -> (ReplyControllerSender, ReplyControllerR
(tx.into(), rx) (tx.into(), rx)
} }
#[derive(Debug, thiserror::Error)]
pub enum ReplyControllerSenderError {
#[error("failed to send retransmission data to reply controller")]
SendRetransmissionData(#[source] mpsc::TrySendError<ReplyControllerMessage>),
#[error("failed to send reply to reply controller")]
SendReply(#[source] mpsc::TrySendError<ReplyControllerMessage>),
#[error("failed to send additional surbs to reply controller")]
AdditionalSurbs(#[source] mpsc::TrySendError<ReplyControllerMessage>),
#[error("failed to send additional surbs request to reply controller")]
AdditionalSurbsRequest(#[source] mpsc::TrySendError<ReplyControllerMessage>),
#[error("failed to request lane queue length from reply controller")]
LaneQueueLength(#[source] mpsc::TrySendError<ReplyControllerMessage>),
#[error("response channel was dropped before we could receive the response")]
ResponseChannelDropped(#[source] oneshot::Canceled),
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ReplyControllerSender(mpsc::UnboundedSender<ReplyControllerMessage>); pub struct ReplyControllerSender(mpsc::UnboundedSender<ReplyControllerMessage>);
@@ -51,14 +30,14 @@ impl ReplyControllerSender {
recipient: AnonymousSenderTag, recipient: AnonymousSenderTag,
timed_out_ack: Weak<PendingAcknowledgement>, timed_out_ack: Weak<PendingAcknowledgement>,
extra_surb_request: bool, extra_surb_request: bool,
) -> Result<(), ReplyControllerSenderError> { ) {
self.0 self.0
.unbounded_send(ReplyControllerMessage::RetransmitReply { .unbounded_send(ReplyControllerMessage::RetransmitReply {
recipient, recipient,
timed_out_ack, timed_out_ack,
extra_surb_request, extra_surb_request,
}) })
.map_err(ReplyControllerSenderError::SendRetransmissionData) .expect("ReplyControllerReceiver has died!")
} }
pub(crate) fn send_reply( pub(crate) fn send_reply(
@@ -66,14 +45,14 @@ impl ReplyControllerSender {
recipient: AnonymousSenderTag, recipient: AnonymousSenderTag,
message: Vec<u8>, message: Vec<u8>,
lane: TransmissionLane, lane: TransmissionLane,
) -> Result<(), ReplyControllerSenderError> { ) {
self.0 self.0
.unbounded_send(ReplyControllerMessage::SendReply { .unbounded_send(ReplyControllerMessage::SendReply {
recipient, recipient,
message, message,
lane, lane,
}) })
.map_err(ReplyControllerSenderError::SendReply) .expect("ReplyControllerReceiver has died!")
} }
pub(crate) fn send_additional_surbs( pub(crate) fn send_additional_surbs(
@@ -81,47 +60,42 @@ impl ReplyControllerSender {
sender_tag: AnonymousSenderTag, sender_tag: AnonymousSenderTag,
reply_surbs: Vec<ReplySurb>, reply_surbs: Vec<ReplySurb>,
from_surb_request: bool, from_surb_request: bool,
) -> Result<(), ReplyControllerSenderError> { ) {
self.0 self.0
.unbounded_send(ReplyControllerMessage::AdditionalSurbs { .unbounded_send(ReplyControllerMessage::AdditionalSurbs {
sender_tag, sender_tag,
reply_surbs, reply_surbs,
from_surb_request, from_surb_request,
}) })
.map_err(ReplyControllerSenderError::AdditionalSurbs) .expect("ReplyControllerReceiver has died!")
} }
pub(crate) fn send_additional_surbs_request( pub(crate) fn send_additional_surbs_request(&self, recipient: Recipient, amount: u32) {
&self,
recipient: Recipient,
amount: u32,
) -> Result<(), ReplyControllerSenderError> {
self.0 self.0
.unbounded_send(ReplyControllerMessage::AdditionalSurbsRequest { .unbounded_send(ReplyControllerMessage::AdditionalSurbsRequest {
recipient: Box::new(recipient), recipient: Box::new(recipient),
amount, amount,
}) })
.map_err(ReplyControllerSenderError::AdditionalSurbsRequest) .expect("ReplyControllerReceiver has died!")
} }
pub async fn get_lane_queue_length( pub async fn get_lane_queue_length(&self, connection_id: ConnectionId) -> usize {
&self,
connection_id: ConnectionId,
) -> Result<usize, ReplyControllerSenderError> {
let (response_tx, response_rx) = oneshot::channel(); let (response_tx, response_rx) = oneshot::channel();
if let Err(err) = self self.0
.0
.unbounded_send(ReplyControllerMessage::LaneQueueLength { .unbounded_send(ReplyControllerMessage::LaneQueueLength {
connection_id, connection_id,
response_channel: response_tx, response_channel: response_tx,
}) })
{ .expect("ReplyControllerReceiver has died!");
return Err(ReplyControllerSenderError::LaneQueueLength(err));
}
response_rx match response_rx.await {
.await Ok(length) => length,
.map_err(ReplyControllerSenderError::ResponseChannelDropped) Err(_) => {
error!("The reply controller has dropped our response channel!");
// TODO: should we panic here instead? this message implies something weird and unrecoverable has happened
0
}
}
} }
} }
@@ -136,10 +110,7 @@ impl ReplyQueueLengths {
} }
} }
pub async fn get_lane_queue_length( pub async fn get_lane_queue_length(&self, connection_id: ConnectionId) -> usize {
&self,
connection_id: ConnectionId,
) -> Result<usize, ReplyControllerSenderError> {
self.reply_controller_sender self.reply_controller_sender
.get_lane_queue_length(connection_id) .get_lane_queue_length(connection_id)
.await .await
@@ -149,7 +120,7 @@ impl ReplyQueueLengths {
pub(crate) type ReplyControllerReceiver = mpsc::UnboundedReceiver<ReplyControllerMessage>; pub(crate) type ReplyControllerReceiver = mpsc::UnboundedReceiver<ReplyControllerMessage>;
#[derive(Debug)] #[derive(Debug)]
pub enum ReplyControllerMessage { pub(crate) enum ReplyControllerMessage {
RetransmitReply { RetransmitReply {
recipient: AnonymousSenderTag, recipient: AnonymousSenderTag,
timed_out_ack: Weak<PendingAcknowledgement>, timed_out_ack: Weak<PendingAcknowledgement>,
@@ -22,7 +22,7 @@ use nym_sphinx::addressing::Recipient;
use nym_statistics_common::clients::{ use nym_statistics_common::clients::{
ClientStatsController, ClientStatsReceiver, ClientStatsSender, ClientStatsController, ClientStatsReceiver, ClientStatsSender,
}; };
use nym_task::{connections::TransmissionLane, TaskClient}; use nym_task::connections::TransmissionLane;
use std::time::Duration; use std::time::Duration;
use crate::{ use crate::{
@@ -51,9 +51,6 @@ pub(crate) struct StatisticsControl {
/// Config for stats reporting (enabled, address, interval) /// Config for stats reporting (enabled, address, interval)
reporting_config: StatsReporting, reporting_config: StatsReporting,
/// Task client for listening for shutdown
task_client: TaskClient,
} }
impl StatisticsControl { impl StatisticsControl {
@@ -62,24 +59,19 @@ impl StatisticsControl {
client_type: String, client_type: String,
client_stats_id: String, client_stats_id: String,
report_tx: InputMessageSender, report_tx: InputMessageSender,
task_client: TaskClient,
) -> (Self, ClientStatsSender) { ) -> (Self, ClientStatsSender) {
let (stats_tx, stats_rx) = tokio::sync::mpsc::unbounded_channel(); let (stats_tx, stats_rx) = tokio::sync::mpsc::unbounded_channel();
let stats = ClientStatsController::new(client_stats_id, client_type); let stats = ClientStatsController::new(client_stats_id, client_type);
let mut task_client_stats_sender = task_client.fork("stats_sender");
task_client_stats_sender.disarm();
( (
StatisticsControl { StatisticsControl {
stats, stats,
stats_rx, stats_rx,
report_tx, report_tx,
reporting_config, reporting_config,
task_client,
}, },
ClientStatsSender::new(Some(stats_tx), task_client_stats_sender), ClientStatsSender::new(Some(stats_tx)),
) )
} }
@@ -99,7 +91,7 @@ impl StatisticsControl {
} }
} }
async fn run(&mut self) { async fn run_with_shutdown(&mut self, mut task_client: nym_task::TaskClient) {
log::debug!("Started StatisticsControl with graceful shutdown support"); log::debug!("Started StatisticsControl with graceful shutdown support");
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
@@ -129,13 +121,8 @@ impl StatisticsControl {
let mut snapshot_interval = let mut snapshot_interval =
gloo_timers::future::IntervalStream::new(SNAPSHOT_INTERVAL.as_millis() as u32); gloo_timers::future::IntervalStream::new(SNAPSHOT_INTERVAL.as_millis() as u32);
while !self.task_client.is_shutdown() { loop {
tokio::select! { tokio::select! {
biased;
_ = self.task_client.recv() => {
log::trace!("StatisticsControl: Received shutdown");
break;
},
stats_event = self.stats_rx.recv() => match stats_event { stats_event = self.stats_rx.recv() => match stats_event {
Some(stats_event) => self.stats.handle_event(stats_event), Some(stats_event) => self.stats.handle_event(stats_event),
None => { None => {
@@ -157,34 +144,34 @@ impl StatisticsControl {
} }
_ = local_report_interval.next() => { _ = local_report_interval.next() => {
self.stats.local_report(&mut self.task_client); 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"); log::debug!("StatisticsControl: Exiting");
} }
pub(crate) fn start(mut self) { pub(crate) fn start_with_shutdown(mut self, task_client: nym_task::TaskClient) {
spawn_future(async move { spawn_future(async move {
self.run().await; self.run_with_shutdown(task_client).await;
}) })
} }
pub(crate) fn create_and_start( pub(crate) fn create_and_start_with_shutdown(
reporting_config: StatsReporting, reporting_config: StatsReporting,
client_type: String, client_type: String,
client_stats_id: String, client_stats_id: String,
report_tx: InputMessageSender, report_tx: InputMessageSender,
task_client: TaskClient, task_client: nym_task::TaskClient,
) -> ClientStatsSender { ) -> ClientStatsSender {
let (controller, sender) = Self::create( let (controller, sender) =
reporting_config, Self::create(reporting_config, client_type, client_stats_id, report_tx);
client_type, controller.start_with_shutdown(task_client);
client_stats_id,
report_tx,
task_client,
);
controller.start();
sender sender
} }
} }
@@ -6,7 +6,6 @@ pub(crate) use accessor::{TopologyAccessor, TopologyReadPermit};
use futures::StreamExt; use futures::StreamExt;
use log::*; use log::*;
use nym_sphinx::addressing::nodes::NodeIdentity; use nym_sphinx::addressing::nodes::NodeIdentity;
use nym_task::TaskClient;
use nym_topology::NymTopologyError; use nym_topology::NymTopologyError;
use std::time::Duration; use std::time::Duration;
@@ -44,8 +43,6 @@ pub struct TopologyRefresher {
refresh_rate: Duration, refresh_rate: Duration,
consecutive_failure_count: usize, consecutive_failure_count: usize,
task_client: TaskClient,
} }
impl TopologyRefresher { impl TopologyRefresher {
@@ -53,14 +50,12 @@ impl TopologyRefresher {
cfg: TopologyRefresherConfig, cfg: TopologyRefresherConfig,
topology_accessor: TopologyAccessor, topology_accessor: TopologyAccessor,
topology_provider: Box<dyn TopologyProvider + Send + Sync>, topology_provider: Box<dyn TopologyProvider + Send + Sync>,
task_client: TaskClient,
) -> Self { ) -> Self {
TopologyRefresher { TopologyRefresher {
topology_provider, topology_provider,
topology_accessor, topology_accessor,
refresh_rate: cfg.refresh_rate, refresh_rate: cfg.refresh_rate,
consecutive_failure_count: 0, consecutive_failure_count: 0,
task_client,
} }
} }
@@ -147,7 +142,7 @@ impl TopologyRefresher {
} }
} }
pub fn start(mut self) { pub fn start_with_shutdown(mut self, mut shutdown: nym_task::TaskClient) {
spawn_future(async move { spawn_future(async move {
debug!("Started TopologyRefresher with graceful shutdown support"); debug!("Started TopologyRefresher with graceful shutdown support");
@@ -160,17 +155,17 @@ impl TopologyRefresher {
let mut interval = let mut interval =
gloo_timers::future::IntervalStream::new(self.refresh_rate.as_millis() as u32); gloo_timers::future::IntervalStream::new(self.refresh_rate.as_millis() as u32);
while !self.task_client.is_shutdown() { while !shutdown.is_shutdown() {
tokio::select! { tokio::select! {
_ = interval.next() => { _ = interval.next() => {
self.try_refresh().await; self.try_refresh().await;
}, },
_ = self.task_client.recv() => { _ = shutdown.recv() => {
log::trace!("TopologyRefresher: Received shutdown"); log::trace!("TopologyRefresher: Received shutdown");
}, },
} }
} }
self.task_client.recv_timeout().await; shutdown.recv_timeout().await;
log::debug!("TopologyRefresher: Exiting"); log::debug!("TopologyRefresher: Exiting");
}) })
} }
-13
View File
@@ -36,13 +36,6 @@ pub enum ClientCoreError {
#[error("no gateway with id: {0}")] #[error("no gateway with id: {0}")]
NoGatewayWithId(String), 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")] #[error("no gateways on network")]
NoGatewaysOnNetwork, NoGatewaysOnNetwork,
@@ -103,9 +96,6 @@ pub enum ClientCoreError {
#[error("timed out while trying to establish gateway connection")] #[error("timed out while trying to establish gateway connection")]
GatewayConnectionTimeout, GatewayConnectionTimeout,
#[error("failed to forward mix messages to gateway")]
GatewayFailedToForwardMessages,
#[error("no ping measurements for the gateway ({identity}) performed")] #[error("no ping measurements for the gateway ({identity}) performed")]
NoGatewayMeasurements { identity: String }, NoGatewayMeasurements { identity: String },
@@ -222,9 +212,6 @@ pub enum ClientCoreError {
"fresh registration with gateway {gateway_id} somehow requires an additional key upgrade!" "fresh registration with gateway {gateway_id} somehow requires an additional key upgrade!"
)] )]
UnexpectedKeyUpgrade { gateway_id: String }, UnexpectedKeyUpgrade { gateway_id: String },
#[error("failed to derive keys from master key")]
HkdfDerivationError {},
} }
/// Set of messages that the client can send to listeners via the task manager /// Set of messages that the client can send to listeners via the task manager
+4 -6
View File
@@ -15,9 +15,6 @@ use std::{sync::Arc, time::Duration};
use tungstenite::Message; use tungstenite::Message;
use url::Url; use url::Url;
#[cfg(not(target_arch = "wasm32"))]
use crate::init::websockets::connect_async;
use nym_topology::NodeId; use nym_topology::NodeId;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use tokio::net::TcpStream; use tokio::net::TcpStream;
@@ -26,6 +23,8 @@ use tokio::time::sleep;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use tokio::time::Instant; use tokio::time::Instant;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use tokio_tungstenite::connect_async;
#[cfg(not(target_arch = "wasm32"))]
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream}; use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
use wasm_utils::websocket::JSWebsocket; use wasm_utils::websocket::JSWebsocket;
@@ -92,7 +91,6 @@ pub async fn gateways_for_init<R: Rng>(
nym_apis: &[Url], nym_apis: &[Url],
user_agent: Option<UserAgent>, user_agent: Option<UserAgent>,
minimum_performance: u8, minimum_performance: u8,
ignore_epoch_roles: bool,
) -> Result<Vec<RoutingNode>, ClientCoreError> { ) -> Result<Vec<RoutingNode>, ClientCoreError> {
let nym_api = nym_apis let nym_api = nym_apis
.choose(rng) .choose(rng)
@@ -114,7 +112,7 @@ pub async fn gateways_for_init<R: Rng>(
// (we don't want instability) // (we don't want instability)
let valid_gateways = gateways let valid_gateways = gateways
.iter() .iter()
.filter(|g| ignore_epoch_roles || !g.supported_roles.mixnode) .filter(|g| !g.supported_roles.mixnode)
.filter(|g| g.performance.round_to_integer() >= minimum_performance) .filter(|g| g.performance.round_to_integer() >= minimum_performance)
.filter_map(|gateway| gateway.try_into().ok()) .filter_map(|gateway| gateway.try_into().ok())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@@ -133,7 +131,7 @@ pub async fn gateways_for_init<R: Rng>(
async fn connect(endpoint: &str) -> Result<WsConn, ClientCoreError> { async fn connect(endpoint: &str) -> Result<WsConn, ClientCoreError> {
match tokio::time::timeout(CONN_TIMEOUT, connect_async(endpoint)).await { match tokio::time::timeout(CONN_TIMEOUT, connect_async(endpoint)).await {
Err(_elapsed) => Err(ClientCoreError::GatewayConnectionTimeout), 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), Ok(Ok((stream, _))) => Ok(stream),
} }
} }
-2
View File
@@ -26,8 +26,6 @@ use serde::Serialize;
pub mod helpers; pub mod helpers;
pub mod types; pub mod types;
#[cfg(not(target_arch = "wasm32"))]
pub(crate) mod websockets;
// helpers for error wrapping // helpers for error wrapping
-44
View File
@@ -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)
}
+45
View File
@@ -33,3 +33,48 @@ where
{ {
tokio::spawn(future); 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
}
}
@@ -27,7 +27,6 @@ nym-credential-storage = { path = "../../credential-storage" }
nym-credentials-interface = { path = "../../credentials-interface" } nym-credentials-interface = { path = "../../credentials-interface" }
nym-crypto = { path = "../../crypto" } nym-crypto = { path = "../../crypto" }
nym-gateway-requests = { path = "../../gateway-requests" } nym-gateway-requests = { path = "../../gateway-requests" }
nym-http-api-client = { path = "../../http-api-client" }
nym-network-defaults = { path = "../../network-defaults" } nym-network-defaults = { path = "../../network-defaults" }
nym-sphinx = { path = "../../nymsphinx" } nym-sphinx = { path = "../../nymsphinx" }
nym-statistics-common = { path = "../../statistics" } nym-statistics-common = { path = "../../statistics" }
@@ -40,6 +40,8 @@ use url::Url;
use std::os::fd::RawFd; use std::os::fd::RawFd;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use tokio::time::sleep; use tokio::time::sleep;
#[cfg(not(target_arch = "wasm32"))]
use tokio_tungstenite::connect_async;
#[cfg(not(unix))] #[cfg(not(unix))]
use std::os::raw::c_int as RawFd; use std::os::raw::c_int as RawFd;
@@ -51,11 +53,6 @@ use zeroize::Zeroizing;
pub mod config; 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 struct GatewayConfig {
pub gateway_identity: identity::PublicKey, pub gateway_identity: identity::PublicKey,
@@ -204,15 +201,23 @@ impl<C, St> GatewayClient<C, St> {
"Attemting to establish connection to gateway at: {}", "Attemting to establish connection to gateway at: {}",
self.gateway_address self.gateway_address
); );
let (ws_stream, _) = connect_async( let ws_stream = match connect_async(&self.gateway_address).await {
&self.gateway_address, Ok((ws_stream, _)) => ws_stream,
#[cfg(unix)] Err(error) => {
self.connection_fd_callback.clone(), return Err(GatewayClientError::NetworkConnectionFailed {
) address: self.gateway_address.clone(),
.await?; source: error,
})
}
};
self.connection = SocketState::Available(Box::new(ws_stream)); self.connection = SocketState::Available(Box::new(ws_stream));
#[cfg(unix)]
if let (Some(callback), Some(fd)) = (self.connection_fd_callback.as_ref(), self.ws_fd()) {
callback.as_ref()(fd);
}
Ok(()) Ok(())
} }
@@ -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> { async fn read_control_response(&mut self) -> Result<ServerResponse, GatewayClientError> {
// we use the fact that all request responses are Message::Text and only pushed // we use the fact that all request responses are Message::Text and only pushed
// sphinx packets are Message::Binary // sphinx packets are Message::Binary
@@ -1053,7 +1045,7 @@ impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
connection: SocketState::NotConnected, connection: SocketState::NotConnected,
packet_router, packet_router,
bandwidth_controller: None, bandwidth_controller: None,
stats_reporter: ClientStatsSender::new(None, task_client.clone()), stats_reporter: ClientStatsSender::new(None),
negotiated_protocol: None, negotiated_protocol: None,
#[cfg(unix)] #[cfg(unix)]
connection_fd_callback: None, connection_fd_callback: None,
@@ -1,88 +0,0 @@
use crate::error::GatewayClientError;
use nym_http_api_client::HickoryDnsResolver;
#[cfg(unix)]
use std::{
os::fd::{AsRawFd, RawFd},
sync::Arc,
};
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,
#[cfg(unix)] connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
) -> Result<(WebSocketStream<MaybeTlsStream<TcpStream>>, Response), GatewayClientError> {
use tokio::net::TcpSocket;
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 mut stream = Err(GatewayClientError::NoEndpointForConnection {
address: endpoint.to_owned(),
});
for sock_addr in sock_addrs {
let socket = if sock_addr.is_ipv4() {
TcpSocket::new_v4()
} else {
TcpSocket::new_v6()
}
.map_err(|err| GatewayClientError::NetworkConnectionFailed {
address: endpoint.to_owned(),
source: err.into(),
})?;
#[cfg(unix)]
if let Some(callback) = connection_fd_callback.as_ref() {
callback.as_ref()(socket.as_raw_fd());
}
match socket.connect(sock_addr).await {
Ok(s) => {
stream = Ok(s);
break;
}
Err(err) => {
stream = Err(GatewayClientError::NetworkConnectionFailed {
address: endpoint.to_owned(),
source: err.into(),
});
continue;
}
}
}
tokio_tungstenite::client_async_tls(endpoint, stream?)
.await
.map_err(|error| GatewayClientError::NetworkConnectionFailed {
address: endpoint.to_owned(),
source: error,
})
}
@@ -43,15 +43,8 @@ pub enum GatewayClientError {
#[error("connection failed: {address}: {source}")] #[error("connection failed: {address}: {source}")]
NetworkConnectionFailed { address: String, source: WsError }, NetworkConnectionFailed { address: String, source: WsError },
#[error("no socket address for endpoint: {address}")]
NoEndpointForConnection { address: String },
#[error("Invalid URL: {0}")] #[error("Invalid URL: {0}")]
InvalidUrl(String), InvalidURL(String),
#[cfg(not(target_arch = "wasm32"))]
#[error("resolution failed: {0}")]
ResolutionFailed(#[from] nym_http_api_client::HickoryDnsError),
#[error("No shared key was provided or obtained")] #[error("No shared key was provided or obtained")]
NoSharedKeyAvailable, NoSharedKeyAvailable,
@@ -11,7 +11,8 @@ use crate::{
use nym_api_requests::ecash::models::{ use nym_api_requests::ecash::models::{
AggregatedCoinIndicesSignatureResponse, AggregatedExpirationDateSignatureResponse, AggregatedCoinIndicesSignatureResponse, AggregatedExpirationDateSignatureResponse,
BatchRedeemTicketsBody, EcashBatchTicketRedemptionResponse, EcashTicketVerificationResponse, BatchRedeemTicketsBody, EcashBatchTicketRedemptionResponse, EcashTicketVerificationResponse,
IssuedTicketbooksChallengeResponse, IssuedTicketbooksForResponse, VerifyEcashTicketBody, IssuedTicketbooksChallengeResponse, IssuedTicketbooksForResponse, SpentCredentialsResponse,
VerifyEcashTicketBody,
}; };
use nym_api_requests::ecash::{ use nym_api_requests::ecash::{
BlindSignRequestBody, BlindedSignatureResponse, PartialCoinIndicesSignatureResponse, BlindSignRequestBody, BlindedSignatureResponse, PartialCoinIndicesSignatureResponse,
@@ -646,6 +647,13 @@ impl NymApiClient {
.await?) .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( pub async fn partial_expiration_date_signatures(
&self, &self,
expiration_date: Option<Date>, expiration_date: Option<Date>,
@@ -65,12 +65,6 @@ pub enum EcashApiError {
#[from] #[from]
source: cosmrs::ErrorReport, source: cosmrs::ErrorReport,
}, },
#[error("nym api error")]
NymApi {
#[from]
source: crate::ValidatorClientError,
},
} }
impl TryFrom<ContractVKShare> for EcashApiClient { impl TryFrom<ContractVKShare> for EcashApiClient {
@@ -31,7 +31,6 @@ pub use nym_api_requests::{
StakeSaturationResponse, UptimeResponse, StakeSaturationResponse, UptimeResponse,
}, },
nym_nodes::{CachedNodesResponse, SkimmedNode}, nym_nodes::{CachedNodesResponse, SkimmedNode},
NymNetworkDetailsResponse,
}; };
pub use nym_coconut_dkg_common::types::EpochId; pub use nym_coconut_dkg_common::types::EpochId;
use nym_contracts_common::IdentityKey; use nym_contracts_common::IdentityKey;
@@ -850,6 +849,20 @@ pub trait NymApiClientExt: ApiClient {
.await .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))] #[instrument(level = "debug", skip(self))]
async fn partial_expiration_date_signatures( async fn partial_expiration_date_signatures(
&self, &self,
@@ -1014,15 +1027,6 @@ pub trait NymApiClientExt: ApiClient {
) )
.await .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))] #[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 DETAILED_UNFILTERED: &str = "detailed-unfiltered";
pub const ACTIVE: &str = "active"; pub const ACTIVE: &str = "active";
pub const REWARDED: &str = "rewarded"; pub const REWARDED: &str = "rewarded";
pub const DOUBLE_SPENDING_FILTER_V1: &str = "double-spending-filter-v1";
pub const ECASH_ROUTES: &str = "ecash"; pub const ECASH_ROUTES: &str = "ecash";
pub use ecash::*; pub use ecash::*;
@@ -65,8 +67,6 @@ pub const STAKE_SATURATION: &str = "stake-saturation";
pub const INCLUSION_CHANCE: &str = "inclusion-probability"; pub const INCLUSION_CHANCE: &str = "inclusion-probability";
pub const SUBMIT_GATEWAY: &str = "submit-gateway-monitoring-results"; pub const SUBMIT_GATEWAY: &str = "submit-gateway-monitoring-results";
pub const SUBMIT_NODE: &str = "submit-node-monitoring-results"; pub const SUBMIT_NODE: &str = "submit-node-monitoring-results";
pub const PERFORMANCE: &str = "performance";
pub const SERVICE_PROVIDERS: &str = "services"; pub const SERVICE_PROVIDERS: &str = "services";
pub const DETAILS: &str = "details";
pub const NETWORK: &str = "network";
@@ -8,81 +8,81 @@ use thiserror::Error;
#[derive(Error, Debug, PartialEq)] #[derive(Error, Debug, PartialEq)]
pub enum VestingContractError { pub enum VestingContractError {
#[error("VESTING ({l}): {0}", l = line!())] #[error("VESTING ({}): {0}", line!())]
Std(#[from] StdError), Std(#[from] StdError),
#[error("VESTING: {0}")] #[error("VESTING: {0}")]
OverflowError(#[from] OverflowError), OverflowError(#[from] OverflowError),
#[error("VESTING ({l}): Account does not exist - {0}", l = line!())] #[error("VESTING ({}): Account does not exist - {0}", line!())]
NoAccountForAddress(String), 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), 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), 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), 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), InsufficientSpendable(String, u128),
#[error( #[error(
"VESTING ({l}):Only delegation owner can perform delegation actions, {0} is not the delegation owner" "VESTING ({}):Only delegation owner can perform delegation actions, {0} is not the delegation owner"
, l = line!())] , line!())]
NotDelegate(String), 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), 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), AlreadyBonded(String),
#[error("VESTING ({l}): Received empty funds vector", l = line!())] #[error("VESTING ({}): Received empty funds vector", line!())]
EmptyFunds, EmptyFunds,
#[error("VESTING ({l}): Received wrong denom: {0}, expected {1}", l = line!())] #[error("VESTING ({}): Received wrong denom: {0}, expected {1}", line!())]
WrongDenom(String, String), WrongDenom(String, String),
#[error("VESTING ({l}): Received multiple denoms, expected 1", l = line!())] #[error("VESTING ({}): Received multiple denoms, expected 1", line!())]
MultipleDenoms, 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), 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), NotMixnetContract(Addr),
#[error("VESTING ({l}): Calculation underflowed", l = line!())] #[error("VESTING ({}): Calculation underflowed", line!())]
Underflow, Underflow,
#[error("VESTING ({l}): No bond found for account {0}", l = line!())] #[error("VESTING ({}): No bond found for account {0}", line!())]
NoBondFound(String), 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}.")] #[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 }, 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), NotOwner(String),
#[error("VESTING ({l}): Invalid address: {0}", l = line!())] #[error("VESTING ({}): Invalid address: {0}", line!())]
InvalidAddress(String), InvalidAddress(String),
#[error("VESTING ({l}): Account already exists: {0}", l = line!())] #[error("VESTING ({}): Account already exists: {0}", line!())]
AccountAlreadyExists(String), AccountAlreadyExists(String),
#[error("VESTING ({l}): Staking account already exists: {0}", l = line!())] #[error("VESTING ({}): Staking account already exists: {0}", line!())]
StakingAccountAlreadyExists(String), 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 }, 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 }, 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 }, 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")] #[error("VESTING: Vesting account associated with {0} already exists, only addresses with not existing vesting accounts can be added as staking addresses")]
@@ -26,6 +26,7 @@ nym-api-requests = { path = "../../nym-api/nym-api-requests" }
nym-credentials = { path = "../credentials" } nym-credentials = { path = "../credentials" }
nym-credentials-interface = { path = "../credentials-interface" } nym-credentials-interface = { path = "../credentials-interface" }
nym-ecash-contract-common = { path = "../cosmwasm-smart-contracts/ecash-contract" } 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-requests = { path = "../gateway-requests" }
nym-gateway-storage = { path = "../gateway-storage" } nym-gateway-storage = { path = "../gateway-storage" }
nym-task = { path = "../task" } 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_api_requests::ecash::models::{BatchRedeemTicketsBody, VerifyEcashTicketBody};
use nym_credentials_interface::Bandwidth; use nym_credentials_interface::Bandwidth;
use nym_credentials_interface::{ClientTicket, TicketType}; use nym_credentials_interface::{ClientTicket, TicketType};
use nym_validator_client::coconut::EcashApiError;
use nym_validator_client::nym_api::EpochId; use nym_validator_client::nym_api::EpochId;
use nym_validator_client::nyxd::contract_traits::{ use nym_validator_client::nyxd::contract_traits::{
EcashSigningClient, MultisigQueryClient, MultisigSigningClient, PagedMultisigQueryClient, EcashSigningClient, MultisigQueryClient, MultisigSigningClient, PagedMultisigQueryClient,
@@ -353,9 +352,7 @@ impl CredentialHandler {
} }
Err(err) => { 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"); 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 { Ok(false)
source: err,
}))
} }
} }
} }
+4 -4
View File
@@ -24,7 +24,6 @@ ed25519-dalek = { workspace = true, features = ["rand_core"], optional = true }
rand = { workspace = true, optional = true } rand = { workspace = true, optional = true }
serde_bytes = { workspace = true, optional = true } serde_bytes = { workspace = true, optional = true }
serde = { workspace = true, features = ["derive"], optional = true } serde = { workspace = true, features = ["derive"], optional = true }
sha2 = { workspace = true, optional = true }
subtle-encoding = { workspace = true, features = ["bech32-preview"] } subtle-encoding = { workspace = true, features = ["bech32-preview"] }
thiserror = { workspace = true } thiserror = { workspace = true }
zeroize = { workspace = true, optional = true, features = ["zeroize_derive"] } zeroize = { workspace = true, optional = true, features = ["zeroize_derive"] }
@@ -37,10 +36,11 @@ nym-pemstore = { path = "../../common/pemstore", version = "0.3.0" }
rand_chacha = { workspace = true } rand_chacha = { workspace = true }
[features] [features]
default = [] default = ["sphinx"]
aead = ["dep:aead", "aead/std", "aes-gcm-siv", "generic-array"] aead = ["dep:aead", "aead/std", "aes-gcm-siv", "generic-array"]
serde = ["dep:serde", "serde_bytes", "ed25519-dalek/serde", "x25519-dalek/serde"] serde = ["dep:serde", "serde_bytes", "ed25519-dalek/serde", "x25519-dalek/serde"]
asymmetric = ["x25519-dalek", "ed25519-dalek", "zeroize"] asymmetric = ["x25519-dalek", "ed25519-dalek", "zeroize"]
hashing = ["blake3", "digest", "hkdf", "hmac", "generic-array", "sha2"] hashing = ["blake3", "digest", "hkdf", "hmac", "generic-array"]
stream_cipher = ["aes", "ctr", "cipher", "generic-array"] stream_cipher = ["aes", "ctr", "cipher", "generic-array"]
sphinx = ["nym-sphinx-types/sphinx"] sphinx = ["nym-sphinx-types/sphinx"]
outfox = ["nym-sphinx-types/outfox"]
+100 -18
View File
@@ -202,18 +202,6 @@ impl PemStorableKey for PublicKey {
} }
} }
impl From<x25519_dalek::PublicKey> for PublicKey {
fn from(public_key: x25519_dalek::PublicKey) -> Self {
PublicKey(public_key)
}
}
impl From<PublicKey> for x25519_dalek::PublicKey {
fn from(public_key: PublicKey) -> Self {
public_key.0
}
}
#[derive(Zeroize, ZeroizeOnDrop)] #[derive(Zeroize, ZeroizeOnDrop)]
pub struct PrivateKey(x25519_dalek::StaticSecret); pub struct PrivateKey(x25519_dalek::StaticSecret);
@@ -320,15 +308,109 @@ impl PemStorableKey for PrivateKey {
} }
} }
impl From<x25519_dalek::StaticSecret> for PrivateKey { // compatibility with sphinx keys:
fn from(secret: x25519_dalek::StaticSecret) -> Self { #[cfg(feature = "sphinx")]
PrivateKey(secret) impl From<PublicKey> for nym_sphinx_types::PublicKey {
fn from(key: PublicKey) -> Self {
nym_sphinx_types::PublicKey::from(key.to_bytes())
} }
} }
impl AsRef<x25519_dalek::StaticSecret> for PrivateKey { #[cfg(feature = "sphinx")]
fn as_ref(&self) -> &x25519_dalek::StaticSecret { impl<'a> From<&'a PublicKey> for nym_sphinx_types::PublicKey {
&self.0 fn from(key: &'a PublicKey) -> Self {
nym_sphinx_types::PublicKey::from((*key).to_bytes())
}
}
#[cfg(feature = "sphinx")]
impl From<nym_sphinx_types::PublicKey> for PublicKey {
fn from(pub_key: nym_sphinx_types::PublicKey) -> Self {
Self(x25519_dalek::PublicKey::from(*pub_key.as_bytes()))
}
}
#[cfg(feature = "sphinx")]
impl From<PrivateKey> for nym_sphinx_types::PrivateKey {
fn from(key: PrivateKey) -> Self {
nym_sphinx_types::PrivateKey::from(key.to_bytes())
}
}
#[cfg(feature = "sphinx")]
impl<'a> From<&'a PrivateKey> for nym_sphinx_types::PrivateKey {
fn from(key: &'a PrivateKey) -> Self {
nym_sphinx_types::PrivateKey::from(key.to_bytes())
}
}
#[cfg(feature = "sphinx")]
impl From<nym_sphinx_types::PrivateKey> for PrivateKey {
fn from(private_key: nym_sphinx_types::PrivateKey) -> Self {
let private_key_bytes = private_key.to_bytes();
assert_eq!(private_key_bytes.len(), PRIVATE_KEY_SIZE);
Self::from_bytes(&private_key_bytes).unwrap()
}
}
#[cfg(test)]
mod sphinx_key_conversion {
use super::*;
use rand_chacha::rand_core::SeedableRng;
use rand_chacha::ChaCha20Rng;
pub(super) fn test_rng() -> ChaCha20Rng {
let dummy_seed = [42u8; 32];
ChaCha20Rng::from_seed(dummy_seed)
}
const NUM_ITERATIONS: usize = 100;
#[test]
fn works_for_forward_conversion() {
let mut rng = test_rng();
for _ in 0..NUM_ITERATIONS {
let keys = KeyPair::new(&mut rng);
let private = &keys.private_key;
let public = &keys.public_key;
let dummy_remote = KeyPair::new(&mut rng);
let dh1 = private.diffie_hellman(&dummy_remote.public_key);
let public_bytes = public.to_bytes();
let sphinx_private: nym_sphinx_types::PrivateKey = private.into();
let recovered_private = PrivateKey::from(sphinx_private);
let dh2 = recovered_private.diffie_hellman(&dummy_remote.public_key);
let sphinx_public: nym_sphinx_types::PublicKey = public.into();
let recovered_public = PublicKey::from(sphinx_public);
assert_eq!(public_bytes, recovered_public.to_bytes());
// even though the byte representation of the private key changed, the resultant DH is the same
// which is what matters
assert_eq!(dh1, dh2);
}
}
#[test]
fn works_for_backward_conversion() {
for _ in 0..NUM_ITERATIONS {
let (sphinx_private, sphinx_public) = nym_sphinx_types::crypto::keygen();
let private_bytes = sphinx_private.to_bytes();
let public_bytes = sphinx_public.as_bytes();
let private: PrivateKey = sphinx_private.into();
let recovered_sphinx_private: nym_sphinx_types::PrivateKey = private.into();
let public: PublicKey = sphinx_public.into();
let recovered_sphinx_public: nym_sphinx_types::PublicKey = public.into();
assert_eq!(private_bytes, recovered_sphinx_private.to_bytes());
assert_eq!(public_bytes, recovered_sphinx_public.as_bytes());
}
} }
} }
+3 -37
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
pub use ed25519_dalek::SignatureError; pub use ed25519_dalek::SignatureError;
use ed25519_dalek::{SecretKey, Signer, SigningKey}; use ed25519_dalek::{Signer, SigningKey};
pub use ed25519_dalek::{Verifier, PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, SIGNATURE_LENGTH}; pub use ed25519_dalek::{Verifier, PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, SIGNATURE_LENGTH};
use nym_pemstore::traits::{PemStorableKey, PemStorableKeyPair}; use nym_pemstore::traits::{PemStorableKey, PemStorableKeyPair};
use std::fmt::{self, Debug, Display, Formatter}; use std::fmt::{self, Debug, Display, Formatter};
@@ -18,7 +18,7 @@ pub mod serde_helpers;
use nym_sphinx_types::{DestinationAddressBytes, DESTINATION_ADDRESS_LENGTH}; use nym_sphinx_types::{DestinationAddressBytes, DESTINATION_ADDRESS_LENGTH};
#[cfg(feature = "rand")] #[cfg(feature = "rand")]
use rand::{CryptoRng, Rng, RngCore}; use rand::{CryptoRng, RngCore};
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::de::Error as SerdeError; use serde::de::Error as SerdeError;
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
@@ -62,33 +62,16 @@ pub struct KeyPair {
// nothing secret about public key // nothing secret about public key
#[zeroize(skip)] #[zeroize(skip)]
public_key: PublicKey, public_key: PublicKey,
#[zeroize(skip)]
index: u32,
} }
/// All keys will always have an index field populated this is to prevent anyone from figuring out if
/// the keys are derived or random, and alter their behaviour based on that.
impl KeyPair { impl KeyPair {
#[cfg(feature = "rand")] #[cfg(feature = "rand")]
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Self { pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
let index = rng.gen();
let ed25519_signing_key = ed25519_dalek::SigningKey::generate(rng); let ed25519_signing_key = ed25519_dalek::SigningKey::generate(rng);
KeyPair { KeyPair {
private_key: PrivateKey(ed25519_signing_key.to_bytes()), private_key: PrivateKey(ed25519_signing_key.to_bytes()),
public_key: PublicKey(ed25519_signing_key.verifying_key()), public_key: PublicKey(ed25519_signing_key.verifying_key()),
index,
}
}
pub fn from_secret(secret: SecretKey, index: u32) -> Self {
let ed25519_signing_key = SigningKey::from(secret);
KeyPair {
private_key: PrivateKey(ed25519_signing_key.to_bytes()),
public_key: PublicKey(ed25519_signing_key.verifying_key()),
index,
} }
} }
@@ -104,31 +87,15 @@ impl KeyPair {
Ok(KeyPair { Ok(KeyPair {
private_key: PrivateKey::from_bytes(priv_bytes)?, private_key: PrivateKey::from_bytes(priv_bytes)?,
public_key: PublicKey::from_bytes(pub_bytes)?, public_key: PublicKey::from_bytes(pub_bytes)?,
index: fake_index(pub_bytes),
}) })
} }
} }
/// Reduces a byte slice into a u32 value by XOR-ing all its bytes into a 4-byte accumulator.
/// The process iterates over every byte in the input slice, XOR-ing each one into a slot based on its index modulo 4.
/// If the input slice contains fewer than 4 bytes, the remaining positions in the accumulator remain zero.
/// Finally, the accumulator is interpreted in big-endian order to produce the resulting u32.
/// Index is used to verify deterministic identity key, master key and salt are also requried for verification.
fn fake_index(input: &[u8]) -> u32 {
let mut accumulator = [0u8; 4];
for (i, &byte) in input.iter().enumerate() {
accumulator[i % 4] ^= byte;
}
u32::from_be_bytes(accumulator)
}
impl From<PrivateKey> for KeyPair { impl From<PrivateKey> for KeyPair {
fn from(private_key: PrivateKey) -> Self { fn from(private_key: PrivateKey) -> Self {
let public_key = (&private_key).into();
KeyPair { KeyPair {
public_key, public_key: (&private_key).into(),
private_key, private_key,
index: fake_index(public_key.to_bytes().as_ref()),
} }
} }
} }
@@ -148,7 +115,6 @@ impl PemStorableKeyPair for KeyPair {
KeyPair { KeyPair {
private_key, private_key,
public_key, public_key,
index: fake_index(public_key.to_bytes().as_ref()),
} }
} }
} }
-81
View File
@@ -8,10 +8,6 @@ use hkdf::{
}, },
Hkdf, Hkdf,
}; };
use sha2::{Sha256, Sha512};
pub use hkdf::InvalidLength;
use zeroize::ZeroizeOnDrop;
/// Perform HKDF `extract` then `expand` as a single step. /// Perform HKDF `extract` then `expand` as a single step.
pub fn extract_then_expand<D>( pub fn extract_then_expand<D>(
@@ -32,80 +28,3 @@ where
Ok(okm) Ok(okm)
} }
/// `DerivationMaterial` encapsulates parameters for deterministic key derivation using
/// HKDF (SHA-512).
///
/// It consists of:
/// - A master key (`master_key`): the base secret.
/// - An index (`index`): ensures unique derivations.
/// - A salt (`salt`): adds additional uniqueness, should be application specific.
///
/// Use the `derive_secret()` method to generate a 32-byte secret. To prepare for a new derivation,
/// call the `next()` method, which increments the index. **It is the caller's responsibility to
/// track and persist the derivation index if keys need to be rederived.**
///
/// # Example
///
/// ```rust
/// use nym_crypto::hkdf::DerivationMaterial;
///
/// let master_key = [0u8; 32]; // your secret master key
/// let salt = b"unique-salt-value";
/// let material = DerivationMaterial::new(master_key, 0, salt);
///
/// // Derive a secret
/// let secret = material.derive_secret().expect("Failed to derive secret");
///
/// // Prepare for the next derivation
/// let next_material = material.next();
/// ```
#[derive(ZeroizeOnDrop)]
pub struct DerivationMaterial {
master_key: [u8; 32],
index: u32,
salt: Vec<u8>,
}
impl DerivationMaterial {
pub fn index(&self) -> u32 {
self.index
}
pub fn salt(&self) -> &[u8] {
&self.salt
}
/// Derives a 32-byte seed from a master seed and an index using HKDF (with SHA-512).
///
/// The `salt` and the use of the index (as info) bind this derivation to an application/client.
pub fn derive_secret(&self) -> Result<[u8; 32], hkdf::InvalidLength> {
let salt = &self.salt;
let info = self.index.to_be_bytes(); // Use the index as info
let hk = Hkdf::<Sha512>::new(Some(salt), &self.master_key);
let mut okm = [0u8; 32];
hk.expand(&info, &mut okm)?;
Ok(okm)
}
pub fn new<T: AsRef<[u8]>>(master_key: T, index: u32, salt: &[u8]) -> Self {
// Coerce master_key to [u8; 32]
let mut hasher = Sha256::new();
hasher.update(master_key.as_ref());
let master_key = hasher.finalize().into();
Self {
master_key,
index,
salt: salt.to_vec(),
}
}
pub fn next(&self) -> Self {
Self {
master_key: self.master_key,
index: self.index + 1,
salt: self.salt.clone(),
}
}
}
+14
View File
@@ -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" }
+136
View File
@@ -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(&params, 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(&params, 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
}
}
+6 -6
View File
@@ -59,12 +59,12 @@ pub enum GatewayRequestsError {
source: NymNodeRoutingAddressError, source: NymNodeRoutingAddressError,
}, },
#[error("received request had invalid size. (actual: {0}, but expected one of: {a} (ACK), {r} (REGULAR), {e8}, {e16}, {e32} (EXTENDED))", #[error("received request had invalid size. (actual: {0}, but expected one of: {} (ACK), {} (REGULAR), {}, {}, {} (EXTENDED))",
a = PacketSize::AckPacket.size(), PacketSize::AckPacket.size(),
r = PacketSize::RegularPacket.size(), PacketSize::RegularPacket.size(),
e8 = PacketSize::ExtendedPacket8.size(), PacketSize::ExtendedPacket8.size(),
e16 = PacketSize::ExtendedPacket16.size(), PacketSize::ExtendedPacket16.size(),
e32 = PacketSize::ExtendedPacket32.size()) PacketSize::ExtendedPacket32.size())
] ]
RequestOfInvalidSize(usize), RequestOfInvalidSize(usize),
@@ -13,7 +13,7 @@ use std::str::FromStr;
use tungstenite::Message; use tungstenite::Message;
// wrapper for all encrypted requests for ease of use // wrapper for all encrypted requests for ease of use
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug)]
#[non_exhaustive] #[non_exhaustive]
pub enum ClientRequest { pub enum ClientRequest {
UpgradeKey { UpgradeKey {
@@ -3,22 +3,5 @@
* SPDX-License-Identifier: GPL-3.0-only * SPDX-License-Identifier: GPL-3.0-only
*/ */
ALTER TABLE message_store ALTER TABLE message_store
RENAME TO message_store_old; ADD COLUMN timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP;
-- 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;
+1 -1
View File
@@ -12,7 +12,7 @@ license.workspace = true
[dependencies] [dependencies]
async-trait = { workspace = true } async-trait = { workspace = true }
reqwest = { workspace = true, features = ["json", "gzip"] } reqwest = { workspace = true, features = ["json"] }
http.workspace = true http.workspace = true
url = { workspace = true } url = { workspace = true }
once_cell = { workspace = true } once_cell = { workspace = true }
-1
View File
@@ -40,7 +40,6 @@ struct SocketAddrs {
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
#[error("hickory-dns resolver error: {hickory_error}")] #[error("hickory-dns resolver error: {hickory_error}")]
/// Error occurring while resolving a hostname into an IP address.
pub struct HickoryDnsError { pub struct HickoryDnsError {
#[from] #[from]
hickory_error: ResolveError, hickory_error: ResolveError,
File diff suppressed because it is too large Load Diff
-5
View File
@@ -7,16 +7,11 @@ use http::HeaderValue;
use nym_bin_common::build_information::{BinaryBuildInformation, BinaryBuildInformationOwned}; use nym_bin_common::build_information::{BinaryBuildInformation, BinaryBuildInformationOwned};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// Characteristic elements sent to the API providing basic context information of the requesting client.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct UserAgent { pub struct UserAgent {
/// The internal crate / application / subsystem making use of API client
pub application: String, pub application: String,
/// version of the calling crate / application / subsystem
pub version: String, pub version: String,
/// client platform
pub platform: String, pub platform: String,
/// source commit version for the calling calling crate / subsystem
pub git_commit: String, pub git_commit: String,
} }
-1
View File
@@ -20,7 +20,6 @@ mime = { workspace = true }
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true serde_json.workspace = true
serde_yaml = { workspace = true } serde_yaml = { workspace = true }
subtle.workspace = true
tower = { workspace = true } tower = { workspace = true }
tracing.workspace = true tracing.workspace = true
utoipa = { workspace = true, optional = true } utoipa = { workspace = true, optional = true }
@@ -7,7 +7,6 @@ use axum::{extract::Request, response::Response};
use futures::future::BoxFuture; use futures::future::BoxFuture;
use std::sync::Arc; use std::sync::Arc;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use subtle::ConstantTimeEq;
use tower::{Layer, Service}; use tower::{Layer, Service};
use tracing::{debug, instrument, trace}; use tracing::{debug, instrument, trace};
use zeroize::Zeroizing; use zeroize::Zeroizing;
@@ -77,7 +76,7 @@ impl<S> RequireAuth<S> {
return Err("`Authorization` header must contain non-empty `Bearer` token"); return Err("`Authorization` header must contain non-empty `Bearer` token");
} }
if bool::from(self.bearer_token.as_bytes().ct_ne(bearer_token.as_bytes())) { if self.bearer_token.as_str() != bearer_token {
return Err("`Authorization` header does not contain the correct `Bearer` token"); return Err("`Authorization` header does not contain the correct `Bearer` token");
} }
+1 -1
View File
@@ -26,4 +26,4 @@ utoipa = [ "dep:utoipa" ]
[build-dependencies] [build-dependencies]
regex = { workspace = true } regex = { workspace = true }
cargo_metadata = { workspace = true } cargo_metadata = { version = "0.18" }
+1 -1
View File
@@ -31,7 +31,7 @@ nym-pemstore = { path = "../pemstore" }
nym-network-defaults = { path = "../network-defaults", default-features = false } nym-network-defaults = { path = "../network-defaults", default-features = false }
[dev-dependencies] [dev-dependencies]
criterion = { workspace = true, features = ["html_reports"] } criterion = { version = "0.5.1", features = ["html_reports"] }
[[bench]] [[bench]]
+2
View File
@@ -48,10 +48,12 @@ features = ["sync"]
[features] [features]
default = ["sphinx"] default = ["sphinx"]
sphinx = [ sphinx = [
"nym-crypto/sphinx",
"nym-sphinx-params/sphinx", "nym-sphinx-params/sphinx",
"nym-sphinx-types/sphinx", "nym-sphinx-types/sphinx",
] ]
outfox = [ outfox = [
"nym-crypto/outfox",
"nym-sphinx-params/outfox", "nym-sphinx-params/outfox",
"nym-sphinx-types/outfox", "nym-sphinx-types/outfox",
] ]
+1 -1
View File
@@ -8,7 +8,7 @@ license = { workspace = true }
repository = { workspace = true } repository = { workspace = true }
[dependencies] [dependencies]
nym-crypto = { path = "../../crypto", features = ["asymmetric", "sphinx"] } # all addresses are expressed in terms on their crypto keys nym-crypto = { path = "../../crypto", features = ["asymmetric"] } # all addresses are expressed in terms on their crypto keys
nym-sphinx-types = { path = "../types", features = ["sphinx"] } # we need to be able to refer to some types defined inside sphinx crate nym-sphinx-types = { path = "../types", features = ["sphinx"] } # we need to be able to refer to some types defined inside sphinx crate
serde = { workspace = true } # implementing serialization/deserialization for some types, like `Recipient` serde = { workspace = true } # implementing serialization/deserialization for some types, like `Recipient`
thiserror = { workspace = true } thiserror = { workspace = true }
@@ -559,7 +559,7 @@ mod tests {
let mut address_bytes = [0; NODE_ADDRESS_LENGTH]; let mut address_bytes = [0; NODE_ADDRESS_LENGTH];
rng.fill_bytes(&mut address_bytes); rng.fill_bytes(&mut address_bytes);
let dummy_private = PrivateKey::random_from_rng(rng); let dummy_private = PrivateKey::new_with_rng(rng);
let pub_key = (&dummy_private).into(); let pub_key = (&dummy_private).into();
Node { Node {
address: NodeAddressBytes::from_bytes(address_bytes), address: NodeAddressBytes::from_bytes(address_bytes),
+9 -14
View File
@@ -130,33 +130,28 @@ impl Decoder for NymCodec {
mod packet_encoding { mod packet_encoding {
use super::*; use super::*;
use nym_sphinx_types::{ use nym_sphinx_types::{
Delay as SphinxDelay, Destination, DestinationAddressBytes, Node, NodeAddressBytes, crypto, Delay as SphinxDelay, Destination, DestinationAddressBytes, Node, NodeAddressBytes,
PrivateKey, DESTINATION_ADDRESS_LENGTH, IDENTIFIER_LENGTH, NODE_ADDRESS_LENGTH, DESTINATION_ADDRESS_LENGTH, IDENTIFIER_LENGTH, NODE_ADDRESS_LENGTH,
}; };
fn random_pubkey() -> nym_sphinx_types::PublicKey {
let private_key = PrivateKey::random();
(&private_key).into()
}
fn make_valid_outfox_packet(size: PacketSize) -> NymPacket { fn make_valid_outfox_packet(size: PacketSize) -> NymPacket {
let node1_pk = random_pubkey(); let (_, node1_pk) = crypto::keygen();
let node1 = Node::new( let node1 = Node::new(
NodeAddressBytes::from_bytes([5u8; NODE_ADDRESS_LENGTH]), NodeAddressBytes::from_bytes([5u8; NODE_ADDRESS_LENGTH]),
node1_pk, node1_pk,
); );
let node2_pk = random_pubkey(); let (_, node2_pk) = crypto::keygen();
let node2 = Node::new( let node2 = Node::new(
NodeAddressBytes::from_bytes([4u8; NODE_ADDRESS_LENGTH]), NodeAddressBytes::from_bytes([4u8; NODE_ADDRESS_LENGTH]),
node2_pk, node2_pk,
); );
let node3_pk = random_pubkey(); let (_, node3_pk) = crypto::keygen();
let node3 = Node::new( let node3 = Node::new(
NodeAddressBytes::from_bytes([2u8; NODE_ADDRESS_LENGTH]), NodeAddressBytes::from_bytes([2u8; NODE_ADDRESS_LENGTH]),
node3_pk, node3_pk,
); );
let node4_pk = random_pubkey(); let (_, node4_pk) = crypto::keygen();
let node4 = Node::new( let node4 = Node::new(
NodeAddressBytes::from_bytes([2u8; NODE_ADDRESS_LENGTH]), NodeAddressBytes::from_bytes([2u8; NODE_ADDRESS_LENGTH]),
node4_pk, node4_pk,
@@ -175,17 +170,17 @@ mod packet_encoding {
} }
fn make_valid_sphinx_packet(size: PacketSize) -> NymPacket { fn make_valid_sphinx_packet(size: PacketSize) -> NymPacket {
let node1_pk = random_pubkey(); let (_, node1_pk) = crypto::keygen();
let node1 = Node::new( let node1 = Node::new(
NodeAddressBytes::from_bytes([5u8; NODE_ADDRESS_LENGTH]), NodeAddressBytes::from_bytes([5u8; NODE_ADDRESS_LENGTH]),
node1_pk, node1_pk,
); );
let node2_pk = random_pubkey(); let (_, node2_pk) = crypto::keygen();
let node2 = Node::new( let node2 = Node::new(
NodeAddressBytes::from_bytes([4u8; NODE_ADDRESS_LENGTH]), NodeAddressBytes::from_bytes([4u8; NODE_ADDRESS_LENGTH]),
node2_pk, node2_pk,
); );
let node3_pk = random_pubkey(); let (_, node3_pk) = crypto::keygen();
let node3 = Node::new( let node3 = Node::new(
NodeAddressBytes::from_bytes([2u8; NODE_ADDRESS_LENGTH]), NodeAddressBytes::from_bytes([2u8; NODE_ADDRESS_LENGTH]),
node3_pk, node3_pk,
+23 -82
View File
@@ -4,10 +4,8 @@ use nym_sphinx_addressing::nodes::{NymNodeRoutingAddress, NymNodeRoutingAddressE
use nym_sphinx_params::{PacketSize, PacketType}; use nym_sphinx_params::{PacketSize, PacketType};
use nym_sphinx_types::{ use nym_sphinx_types::{
Delay as SphinxDelay, DestinationAddressBytes, NodeAddressBytes, NymPacket, NymPacketError, Delay as SphinxDelay, DestinationAddressBytes, NodeAddressBytes, NymPacket, NymPacketError,
NymProcessedPacket, OutfoxError, PrivateKey, ProcessedPacketData, SphinxError, NymProcessedPacket, OutfoxError, PrivateKey, ProcessedPacket, SphinxError,
Version as SphinxPacketVersion,
}; };
use std::fmt::Display;
use thiserror::Error; use thiserror::Error;
use crate::packet::FramedNymPacket; use crate::packet::FramedNymPacket;
@@ -15,38 +13,12 @@ use nym_metrics::nanos;
use nym_sphinx_forwarding::packet::MixPacket; use nym_sphinx_forwarding::packet::MixPacket;
#[derive(Debug)] #[derive(Debug)]
pub enum MixProcessingResultData { pub enum MixProcessingResult {
/// Contains unwrapped data that should first get delayed before being sent to next hop. /// Contains unwrapped data that should first get delayed before being sent to next hop.
ForwardHop { ForwardHop(MixPacket, Option<SphinxDelay>),
packet: MixPacket,
delay: Option<SphinxDelay>,
},
/// Contains all data extracted out of the final hop packet that could be forwarded to the destination. /// Contains all data extracted out of the final hop packet that could be forwarded to the destination.
FinalHop { final_hop_data: ProcessedFinalHop }, FinalHop(ProcessedFinalHop),
}
#[derive(Debug, Copy, Clone)]
pub enum MixPacketVersion {
Outfox,
Sphinx(SphinxPacketVersion),
}
impl Display for MixPacketVersion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
MixPacketVersion::Outfox => "outfox".fmt(f),
MixPacketVersion::Sphinx(sphinx_version) => {
write!(f, "sphinx-{}", sphinx_version.value())
}
}
}
}
#[derive(Debug)]
pub struct MixProcessingResult {
pub packet_version: MixPacketVersion,
pub processing_data: MixProcessingResultData,
} }
type ForwardAck = MixPacket; type ForwardAck = MixPacket;
@@ -135,63 +107,37 @@ fn perform_final_processing(
) -> Result<MixProcessingResult, PacketProcessingError> { ) -> Result<MixProcessingResult, PacketProcessingError> {
match packet { match packet {
NymProcessedPacket::Sphinx(packet) => { NymProcessedPacket::Sphinx(packet) => {
let processing_data = match packet.data { match packet {
ProcessedPacketData::ForwardHop { ProcessedPacket::ForwardHop(packet, address, delay) => {
next_hop_packet, process_forward_hop(NymPacket::Sphinx(*packet), address, delay, packet_type)
next_hop_address, }
delay,
} => process_forward_hop(
NymPacket::Sphinx(next_hop_packet),
next_hop_address,
delay,
packet_type,
),
// right now there's no use for the surb_id included in the header - probably it should get removed from the // right now there's no use for the surb_id included in the header - probably it should get removed from the
// sphinx all together? // sphinx all together?
ProcessedPacketData::FinalHop { ProcessedPacket::FinalHop(destination, _, payload) => process_final_hop(
destination,
identifier: _,
payload,
} => process_final_hop(
destination, destination,
payload.recover_plaintext()?, payload.recover_plaintext()?,
packet_size, packet_size,
packet_type, packet_type,
), ),
}?; }
Ok(MixProcessingResult {
packet_version: MixPacketVersion::Sphinx(packet.version),
processing_data,
})
} }
NymProcessedPacket::Outfox(packet) => { NymProcessedPacket::Outfox(packet) => {
let next_address = *packet.next_address(); let next_address = *packet.next_address();
let packet = packet.into_packet(); let packet = packet.into_packet();
if packet.is_final_hop() { if packet.is_final_hop() {
let processing_data = process_final_hop( process_final_hop(
DestinationAddressBytes::from_bytes(next_address), DestinationAddressBytes::from_bytes(next_address),
packet.recover_plaintext()?.to_vec(), packet.recover_plaintext()?.to_vec(),
packet_size, packet_size,
packet_type, packet_type,
)?; )
Ok(MixProcessingResult {
packet_version: MixPacketVersion::Outfox,
processing_data,
})
} else { } else {
let packet = MixPacket::new( let mix_packet = MixPacket::new(
NymNodeRoutingAddress::try_from_bytes(&next_address)?, NymNodeRoutingAddress::try_from_bytes(&next_address)?,
NymPacket::Outfox(packet), NymPacket::Outfox(packet),
PacketType::Outfox, PacketType::Outfox,
); );
Ok(MixProcessingResult { Ok(MixProcessingResult::ForwardHop(mix_packet, None))
packet_version: MixPacketVersion::Outfox,
processing_data: MixProcessingResultData::ForwardHop {
packet,
delay: None,
},
})
} }
} }
} }
@@ -202,16 +148,14 @@ fn process_final_hop(
payload: Vec<u8>, payload: Vec<u8>,
packet_size: PacketSize, packet_size: PacketSize,
packet_type: PacketType, packet_type: PacketType,
) -> Result<MixProcessingResultData, PacketProcessingError> { ) -> Result<MixProcessingResult, PacketProcessingError> {
let (forward_ack, message) = split_into_ack_and_message(payload, packet_size, packet_type)?; let (forward_ack, message) = split_into_ack_and_message(payload, packet_size, packet_type)?;
Ok(MixProcessingResultData::FinalHop { Ok(MixProcessingResult::FinalHop(ProcessedFinalHop {
final_hop_data: ProcessedFinalHop { destination,
destination, forward_ack,
forward_ack, message,
message, }))
},
})
} }
fn split_into_ack_and_message( fn split_into_ack_and_message(
@@ -267,14 +211,11 @@ fn process_forward_hop(
forward_address: NodeAddressBytes, forward_address: NodeAddressBytes,
delay: SphinxDelay, delay: SphinxDelay,
packet_type: PacketType, packet_type: PacketType,
) -> Result<MixProcessingResultData, PacketProcessingError> { ) -> Result<MixProcessingResult, PacketProcessingError> {
let next_hop_address = NymNodeRoutingAddress::try_from(forward_address)?; let next_hop_address = NymNodeRoutingAddress::try_from(forward_address)?;
let packet = MixPacket::new(next_hop_address, packet, packet_type); let mix_packet = MixPacket::new(next_hop_address, packet, packet_type);
Ok(MixProcessingResultData::ForwardHop { Ok(MixProcessingResult::ForwardHop(mix_packet, Some(delay)))
packet,
delay: Some(delay),
})
} }
// TODO: what more could we realistically test here? // TODO: what more could we realistically test here?
+2 -2
View File
@@ -16,5 +16,5 @@ nym-sphinx-types = { path = "../types" }
[features] [features]
default = ["sphinx"] default = ["sphinx"]
sphinx = ["nym-sphinx-types/outfox"] sphinx = ["nym-crypto/sphinx", "nym-sphinx-types/outfox"]
outfox = ["nym-sphinx-types/outfox"] outfox = ["nym-crypto/outfox", "nym-sphinx-types/outfox"]
+7 -12
View File
@@ -1,22 +1,14 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use std::{array::TryFromSliceError, fmt};
use thiserror::Error;
#[cfg(feature = "outfox")]
use nym_outfox::packet::{OutfoxPacket, OutfoxProcessedPacket};
#[cfg(feature = "sphinx")]
use sphinx_packet::{SphinxPacket, SphinxPacketBuilder};
#[cfg(feature = "outfox")] #[cfg(feature = "outfox")]
pub use nym_outfox::{ pub use nym_outfox::{
constants::MIN_PACKET_SIZE, constants::MIX_PARAMS_LEN, constants::OUTFOX_PACKET_OVERHEAD, constants::MIN_PACKET_SIZE, constants::MIX_PARAMS_LEN, constants::OUTFOX_PACKET_OVERHEAD,
error::OutfoxError, error::OutfoxError,
}; };
// re-exporting types and constants available in sphinx // re-exporting types and constants available in sphinx
#[cfg(feature = "outfox")]
use nym_outfox::packet::{OutfoxPacket, OutfoxProcessedPacket};
#[cfg(feature = "sphinx")] #[cfg(feature = "sphinx")]
pub use sphinx_packet::{ pub use sphinx_packet::{
constants::{ constants::{
@@ -29,9 +21,12 @@ pub use sphinx_packet::{
payload::{Payload, PAYLOAD_OVERHEAD_SIZE}, payload::{Payload, PAYLOAD_OVERHEAD_SIZE},
route::{Destination, DestinationAddressBytes, Node, NodeAddressBytes, SURBIdentifier}, route::{Destination, DestinationAddressBytes, Node, NodeAddressBytes, SURBIdentifier},
surb::{SURBMaterial, SURB}, surb::{SURBMaterial, SURB},
version::Version, Error as SphinxError, ProcessedPacket,
Error as SphinxError, ProcessedPacket, ProcessedPacketData,
}; };
#[cfg(feature = "sphinx")]
use sphinx_packet::{SphinxPacket, SphinxPacketBuilder};
use std::{array::TryFromSliceError, fmt};
use thiserror::Error;
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum NymPacketError { pub enum NymPacketError {
@@ -30,10 +30,6 @@ pub struct Config {
} }
impl 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 { pub fn new<S: Into<String>>(id: S, version: S, provider_mix_address: S) -> Self {
Config { Config {
base: BaseClientConfig::new(id, version), base: BaseClientConfig::new(id, version),
+3 -7
View File
@@ -76,10 +76,6 @@ where
<S::GatewaysDetailsStore as GatewaysDetailsStore>::StorageError: Sync + Send, <S::GatewaysDetailsStore as GatewaysDetailsStore>::StorageError: Sync + Send,
<S::KeyStore as KeyStore>::StorageError: Send + Sync, <S::KeyStore as KeyStore>::StorageError: Send + Sync,
{ {
pub fn config(&self) -> Config {
self.config.clone()
}
pub fn new( pub fn new(
config: Config, config: Config,
storage: S, storage: S,
@@ -237,7 +233,7 @@ where
}; };
let mut base_builder = 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_gateway_setup(self.setup_method)
.with_user_agent(self.user_agent); .with_user_agent(self.user_agent);
@@ -245,7 +241,7 @@ where
base_builder = base_builder.with_stored_topology(custom_mixnet)?; 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 mut started_client = base_builder.start_base().await?;
let self_address = started_client.address; let self_address = started_client.address;
let client_input = started_client.client_input.register_producer(); let client_input = started_client.client_input.register_producer();
@@ -256,7 +252,7 @@ where
Self::start_socks5_listener( Self::start_socks5_listener(
&self.config.socks5, &self.config.socks5,
self.config.base().debug, self.config.base.debug,
client_input, client_input,
client_output, client_output,
client_state, client_state,
+3 -12
View File
@@ -25,28 +25,19 @@ pub type ClientStatsReceiver = tokio::sync::mpsc::UnboundedReceiver<ClientStatsE
#[derive(Clone)] #[derive(Clone)]
pub struct ClientStatsSender { pub struct ClientStatsSender {
stats_tx: Option<UnboundedSender<ClientStatsEvents>>, stats_tx: Option<UnboundedSender<ClientStatsEvents>>,
task_client: TaskClient,
} }
impl ClientStatsSender { impl ClientStatsSender {
/// Create a new statistics Sender /// Create a new statistics Sender
pub fn new( pub fn new(stats_tx: Option<UnboundedSender<ClientStatsEvents>>) -> Self {
stats_tx: Option<UnboundedSender<ClientStatsEvents>>, ClientStatsSender { stats_tx }
task_client: TaskClient,
) -> Self {
ClientStatsSender {
stats_tx,
task_client,
}
} }
/// Report a statistics event using the sender. /// Report a statistics event using the sender.
pub fn report(&self, event: ClientStatsEvents) { pub fn report(&self, event: ClientStatsEvents) {
if let Some(tx) = &self.stats_tx { if let Some(tx) = &self.stats_tx {
if let Err(err) = tx.send(event) { if let Err(err) = tx.send(event) {
if !self.task_client.is_shutdown_poll() { log::error!("Failed to send stats event: {:?}", err);
log::error!("Failed to send stats event: {err}");
}
} }
} }
} }
+2 -6
View File
@@ -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() { if let Some(notify_rx) = self.notify_rx.take() {
drop(notify_rx); drop(notify_rx);
} }
@@ -299,8 +299,6 @@ impl Clone for TaskClient {
None None
}; };
log::debug!("Cloned task client: {name:?}");
TaskClient { TaskClient {
name, name,
shutdown: AtomicBool::new(self.shutdown.load(Ordering::Relaxed)), shutdown: AtomicBool::new(self.shutdown.load(Ordering::Relaxed)),
@@ -317,7 +315,7 @@ impl TaskClient {
const MAX_NAME_LENGTH: usize = 128; const MAX_NAME_LENGTH: usize = 128;
const OVERFLOW_NAME: &'static str = "reached maximum TaskClient children name depth"; 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( fn new(
notify: watch::Receiver<()>, notify: watch::Receiver<()>,
@@ -346,7 +344,6 @@ impl TaskClient {
format!("unknown-{suffix}") format!("unknown-{suffix}")
}; };
log::debug!("Forked task client: {child_name}");
child.name = Some(child_name); child.name = Some(child_name);
child child
} }
@@ -380,7 +377,6 @@ impl TaskClient {
} else { } else {
format!("unknown-{suffix}") format!("unknown-{suffix}")
}; };
log::debug!("Renamed task client: {name}");
self.named(name) self.named(name)
} }
+1 -1
View File
@@ -27,7 +27,7 @@ wasm-bindgen = { workspace = true, optional = true }
## internal ## internal
nym-config = { path = "../config" } nym-config = { path = "../config" }
nym-crypto = { path = "../crypto" } nym-crypto = { path = "../crypto", features = ["sphinx", "outfox"] }
nym-mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" } nym-mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" }
nym-sphinx-addressing = { path = "../nymsphinx/addressing" } nym-sphinx-addressing = { path = "../nymsphinx/addressing" }
nym-sphinx-types = { path = "../nymsphinx/types", features = [ nym-sphinx-types = { path = "../nymsphinx/types", features = [
+2 -2
View File
@@ -11,7 +11,7 @@ use std::borrow::Borrow;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::fmt::Display; use std::fmt::Display;
use std::net::IpAddr; use std::net::IpAddr;
use tracing::{debug, trace, warn}; use tracing::{debug, warn};
pub use crate::node::{EntryDetails, RoutingNode, SupportedRoles}; pub use crate::node::{EntryDetails, RoutingNode, SupportedRoles};
pub use error::NymTopologyError; pub use error::NymTopologyError;
@@ -293,7 +293,7 @@ impl NymTopology {
let has_exit_gateways = !self.rewarded_set.exit_gateways.is_empty(); let has_exit_gateways = !self.rewarded_set.exit_gateways.is_empty();
let has_entry_gateways = !self.rewarded_set.entry_gateways.is_empty(); let has_entry_gateways = !self.rewarded_set.entry_gateways.is_empty();
trace!( debug!(
has_layer1 = %has_layer1, has_layer1 = %has_layer1,
has_layer2 = %has_layer2, has_layer2 = %has_layer2,
has_layer3 = %has_layer3, has_layer3 = %has_layer3,
+1 -1
View File
@@ -105,7 +105,7 @@ impl<'a> From<&'a RoutingNode> for SphinxNode {
.try_into() .try_into()
.unwrap(); .unwrap();
SphinxNode::new(node_address_bytes, node.sphinx_key.into()) SphinxNode::new(node_address_bytes, (&node.sphinx_key).into())
} }
} }
-34
View File
@@ -5,7 +5,6 @@
#![allow(clippy::drop_non_drop)] #![allow(clippy::drop_non_drop)]
use crate::error::WasmCoreError; use crate::error::WasmCoreError;
use nym_client_core::config::ForgetMe;
use nym_config::helpers::OptionalSet; use nym_config::helpers::OptionalSet;
use nym_sphinx::params::{PacketSize, PacketType}; use nym_sphinx::params::{PacketSize, PacketType};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -111,8 +110,6 @@ pub struct DebugWasm {
/// Defines all configuration options related to stats reporting. /// Defines all configuration options related to stats reporting.
#[wasm_bindgen(getter_with_clone)] #[wasm_bindgen(getter_with_clone)]
pub stats_reporting: StatsReportingWasm, pub stats_reporting: StatsReportingWasm,
pub forget_me: ForgetMeWasm,
} }
impl Default for DebugWasm { impl Default for DebugWasm {
@@ -131,7 +128,6 @@ impl From<DebugWasm> for ConfigDebug {
topology: debug.topology.into(), topology: debug.topology.into(),
reply_surbs: debug.reply_surbs.into(), reply_surbs: debug.reply_surbs.into(),
stats_reporting: debug.stats_reporting.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(), topology: debug.topology.into(),
reply_surbs: debug.reply_surbs.into(), reply_surbs: debug.reply_surbs.into(),
stats_reporting: debug.stats_reporting.into(), stats_reporting: debug.stats_reporting.into(),
forget_me: ForgetMeWasm::from(debug.forget_me),
} }
} }
} }
@@ -400,10 +395,6 @@ pub struct TopologyWasm {
/// Specifies whether this client should ignore the current epoch role of the target egress node /// Specifies whether this client should ignore the current epoch role of the target egress node
/// when constructing the final hop packets. /// when constructing the final hop packets.
pub ignore_egress_epoch_role: bool, 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 { impl Default for TopologyWasm {
@@ -428,7 +419,6 @@ impl From<TopologyWasm> for ConfigTopology {
minimum_gateway_performance: topology.minimum_gateway_performance, minimum_gateway_performance: topology.minimum_gateway_performance,
use_extended_topology: topology.use_extended_topology, use_extended_topology: topology.use_extended_topology,
ignore_egress_epoch_role: topology.ignore_egress_epoch_role, ignore_egress_epoch_role: topology.ignore_egress_epoch_role,
ignore_ingress_epoch_role: topology.ignore_ingress_epoch_role,
} }
} }
} }
@@ -446,7 +436,6 @@ impl From<ConfigTopology> for TopologyWasm {
minimum_gateway_performance: topology.minimum_gateway_performance, minimum_gateway_performance: topology.minimum_gateway_performance,
use_extended_topology: topology.use_extended_topology, use_extended_topology: topology.use_extended_topology,
ignore_egress_epoch_role: topology.ignore_egress_epoch_role, ignore_egress_epoch_role: topology.ignore_egress_epoch_role,
ignore_ingress_epoch_role: topology.ignore_ingress_epoch_role,
} }
} }
} }
@@ -546,29 +535,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)] #[wasm_bindgen(inspectable)]
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)] #[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
+2 -34
View File
@@ -8,8 +8,8 @@
#![allow(clippy::empty_docs)] #![allow(clippy::empty_docs)]
use super::{ use super::{
AcknowledgementsWasm, CoverTrafficWasm, DebugWasm, ForgetMeWasm, GatewayConnectionWasm, AcknowledgementsWasm, CoverTrafficWasm, DebugWasm, GatewayConnectionWasm, ReplySurbsWasm,
ReplySurbsWasm, StatsReportingWasm, TopologyWasm, TrafficWasm, StatsReportingWasm, TopologyWasm, TrafficWasm,
}; };
use crate::config::ConfigDebug; use crate::config::ConfigDebug;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -47,9 +47,6 @@ pub struct DebugWasmOverride {
/// Defines all configuration options related to stats reporting. /// Defines all configuration options related to stats reporting.
#[tsify(optional)] #[tsify(optional)]
pub stats_reporting: Option<StatsReportingWasmOverride>, pub stats_reporting: Option<StatsReportingWasmOverride>,
#[tsify(optional)]
pub forget_me: Option<ForgetMeWasmOverride>,
} }
impl From<DebugWasmOverride> for DebugWasm { impl From<DebugWasmOverride> for DebugWasm {
@@ -62,7 +59,6 @@ impl From<DebugWasmOverride> for DebugWasm {
topology: value.topology.map(Into::into).unwrap_or_default(), topology: value.topology.map(Into::into).unwrap_or_default(),
reply_surbs: value.reply_surbs.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(), stats_reporting: value.stats_reporting.map(Into::into).unwrap_or_default(),
forget_me: value.forget_me.map(Into::into).unwrap_or_default(),
} }
} }
} }
@@ -285,11 +281,6 @@ pub struct TopologyWasmOverride {
/// when constructing the final hop packets. /// when constructing the final hop packets.
#[tsify(optional)] #[tsify(optional)]
pub ignore_egress_epoch_role: Option<bool>, 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 { impl From<TopologyWasmOverride> for TopologyWasm {
@@ -319,9 +310,6 @@ impl From<TopologyWasmOverride> for TopologyWasm {
ignore_egress_epoch_role: value ignore_egress_epoch_role: value
.ignore_egress_epoch_role .ignore_egress_epoch_role
.unwrap_or(def.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 +400,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)] #[derive(Tsify, Debug, Clone, Serialize, Deserialize)]
#[tsify(into_wasm_abi, from_wasm_abi)] #[tsify(into_wasm_abi, from_wasm_abi)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
+4 -27
View File
@@ -132,17 +132,9 @@ pub async fn setup_gateway_from_api(
chosen_gateway: Option<IdentityKey>, chosen_gateway: Option<IdentityKey>,
nym_apis: &[Url], nym_apis: &[Url],
minimum_performance: u8, minimum_performance: u8,
ignore_epoch_roles: bool,
) -> Result<InitialisationResult, WasmCoreError> { ) -> Result<InitialisationResult, WasmCoreError> {
let mut rng = thread_rng(); let mut rng = thread_rng();
let gateways = gateways_for_init( let gateways = gateways_for_init(&mut rng, nym_apis, None, minimum_performance).await?;
&mut rng,
nym_apis,
None,
minimum_performance,
ignore_epoch_roles,
)
.await?;
setup_gateway_wasm(client_store, force_tls, chosen_gateway, gateways).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], nym_apis: &[Url],
user_agent: Option<UserAgent>, user_agent: Option<UserAgent>,
minimum_performance: u8, minimum_performance: u8,
ignore_epoch_roles: bool,
) -> Result<Vec<RoutingNode>, ClientCoreError> { ) -> Result<Vec<RoutingNode>, ClientCoreError> {
let mut rng = thread_rng(); let mut rng = thread_rng();
gateways_for_init( gateways_for_init(&mut rng, nym_apis, user_agent, minimum_performance).await
&mut rng,
nym_apis,
user_agent,
minimum_performance,
ignore_epoch_roles,
)
.await
} }
pub async fn setup_from_topology( pub async fn setup_from_topology(
@@ -179,7 +163,6 @@ pub async fn generate_new_client_keys(store: &ClientStorage) -> Result<(), WasmC
Ok(()) Ok(())
} }
#[allow(clippy::too_many_arguments)]
pub async fn add_gateway( pub async fn add_gateway(
preferred_gateway: Option<IdentityKey>, preferred_gateway: Option<IdentityKey>,
latency_based_selection: Option<bool>, latency_based_selection: Option<bool>,
@@ -187,7 +170,6 @@ pub async fn add_gateway(
nym_apis: &[Url], nym_apis: &[Url],
user_agent: UserAgent, user_agent: UserAgent,
min_performance: u8, min_performance: u8,
ignore_epoch_roles: bool,
storage: &ClientStorage, storage: &ClientStorage,
) -> Result<(), WasmCoreError> { ) -> Result<(), WasmCoreError> {
let selection_spec = GatewaySelectionSpecification::new( 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 // 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. // one but with keys kept, or reusing the gateway configuration.
let available_gateways = current_gateways_wasm( let available_gateways =
nym_apis, current_gateways_wasm(nym_apis, Some(user_agent), min_performance).await?;
Some(user_agent),
min_performance,
ignore_epoch_roles,
)
.await?;
// since we're registering with a brand new gateway, // since we're registering with a brand new gateway,
// make sure the list of available gateways doesn't overlap the list of known gateways // make sure the list of available gateways doesn't overlap the list of known gateways

Some files were not shown because too many files have changed in this diff Show More