Compare commits

..

2 Commits

Author SHA1 Message Date
durch da8fd4a2e6 If not exists 2024-11-27 10:33:14 +01:00
durch 50405de8d4 Add indexes to monitor run and testing route 2024-11-27 10:30:11 +01:00
1248 changed files with 26007 additions and 52890 deletions
+1
View File
@@ -4,3 +4,4 @@
**/node_modules **/node_modules
**/target **/target
dist dist
documentation
-1
View File
@@ -1 +0,0 @@
nym-validator-rewarder/.sqlx/** diff=nodiff
-2
View File
@@ -31,5 +31,3 @@ updates:
update-types: update-types:
- "patch" - "patch"
open-pull-requests-limit: 10 open-pull-requests-limit: 10
assignees:
- "octol"
@@ -79,6 +79,7 @@ jobs:
target/release/nym-socks5-client target/release/nym-socks5-client
target/release/nym-api target/release/nym-api
target/release/nym-network-requester target/release/nym-network-requester
target/release/nym-data-observatory
target/release/nym-cli target/release/nym-cli
target/release/nymvisor target/release/nymvisor
target/release/nym-node target/release/nym-node
@@ -96,6 +97,7 @@ jobs:
cp target/release/nym-socks5-client $OUTPUT_DIR cp target/release/nym-socks5-client $OUTPUT_DIR
cp target/release/nym-api $OUTPUT_DIR cp target/release/nym-api $OUTPUT_DIR
cp target/release/nym-network-requester $OUTPUT_DIR cp target/release/nym-network-requester $OUTPUT_DIR
cp target/release/nym-data-observatory $OUTPUT_DIR
cp target/release/nymvisor $OUTPUT_DIR cp target/release/nymvisor $OUTPUT_DIR
cp target/release/nym-node $OUTPUT_DIR cp target/release/nym-node $OUTPUT_DIR
cp target/release/nym-cli $OUTPUT_DIR cp target/release/nym-cli $OUTPUT_DIR
+9 -26
View File
@@ -8,18 +8,16 @@ on:
- 'explorer-api/**' - 'explorer-api/**'
- 'gateway/**' - 'gateway/**'
- 'integrations/**' - 'integrations/**'
- 'nym-api/**' - 'mixnode/**'
- 'nym-credential-proxy/**'
- 'nym-network-monitor/**'
- 'nym-node/**'
- 'nym-node-status-api/**'
- 'nym-outfox/**'
- 'nym-validator-rewarder/**'
- 'nyx-chain-watcher/**'
- 'sdk/ffi/**'
- 'sdk/rust/**' - 'sdk/rust/**'
- 'sdk/lib/**'
- 'service-providers/**' - 'service-providers/**'
- 'nym-browser-extension/storage/**' - 'nym-network-monitor/**'
- 'nym-api/**'
- 'nym-node/**'
- 'nym-outfox/**'
- 'nym-data-observatory/**'
- 'nym-validator-rewarder/**'
- 'tools/**' - 'tools/**'
- 'wasm/**' - 'wasm/**'
- 'Cargo.toml' - 'Cargo.toml'
@@ -32,11 +30,10 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [ arc-ubuntu-20.04, custom-windows-11, custom-runner-mac-m1 ] os: [ arc-ubuntu-20.04, custom-runner-mac-m1 ]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
IPINFO_API_TOKEN: ${{ secrets.IPINFO_API_TOKEN }}
steps: steps:
- name: Install Dependencies (Linux) - name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler
@@ -54,20 +51,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:
-6
View File
@@ -1,7 +1,6 @@
name: ci-sdk-wasm name: ci-sdk-wasm
on: on:
workflow_dispatch:
pull_request: pull_request:
paths: paths:
- 'wasm/**' - 'wasm/**'
@@ -45,11 +44,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
-1
View File
@@ -15,7 +15,6 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
IPINFO_API_TOKEN: ${{ secrets.IPINFO_API_TOKEN }}
continue-on-error: true continue-on-error: true
steps: steps:
- name: Check out repository code - name: Check out repository code
@@ -56,6 +56,14 @@ jobs:
rustup target add aarch64-linux-android \ rustup target add aarch64-linux-android \
x86_64-linux-android x86_64-linux-android
- name: Build lib nym-socks5-listener
working-directory: sdk/lib/socks5-listener/
env:
RELEASE: true
RUSTFLAGS: "-C link-args=-Wl,--hash-style=gnu"
# build for arm64 and x86_64
run: ./build-android.sh aarch64 x86_64
- name: Build APKs (unsigned) - name: Build APKs (unsigned)
working-directory: nym-connect/native/android working-directory: nym-connect/native/android
env: env:
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder" git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml - name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.1 uses: mikefarah/yq@v4.44.5
id: get_version id: get_version
with: with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-credential-proxy/Cargo.toml cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-credential-proxy/Cargo.toml
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder" git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml - name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.1 uses: mikefarah/yq@v4.44.5
id: get_version id: get_version
with: with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder" git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml - name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.1 uses: mikefarah/yq@v4.44.5
id: get_version id: get_version
with: with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-network-monitor/Cargo.toml cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-network-monitor/Cargo.toml
@@ -8,7 +8,7 @@ on:
description: Which gateway probe git ref to build the image with description: Which gateway probe git ref to build the image with
env: env:
WORKING_DIRECTORY: "nym-node-status-api/nym-node-status-agent" WORKING_DIRECTORY: "nym-node-status-agent"
CONTAINER_NAME: "node-status-agent" CONTAINER_NAME: "node-status-agent"
jobs: jobs:
@@ -31,7 +31,7 @@ jobs:
git config --global user.name "Lawrence Stalder" git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml - name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.1 uses: mikefarah/yq@v4.44.5
id: get_version id: get_version
with: with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
@@ -58,4 +58,4 @@ jobs:
- name: BuildAndPushImageOnHarbor - name: BuildAndPushImageOnHarbor
run: | run: |
docker build --build-arg GIT_REF=${{ github.event.inputs.gateway_probe_git_ref }} -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.result }}-${{ steps.cleanup_gateway_probe_ref.outputs.git_ref }} docker build --build-arg GIT_REF=${{ github.event.inputs.gateway_probe_git_ref }} -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.result }}-${{ steps.cleanup_gateway_probe_ref.outputs.git_ref }}
docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags
+2 -2
View File
@@ -3,7 +3,7 @@ on:
workflow_dispatch: workflow_dispatch:
env: env:
WORKING_DIRECTORY: "nym-node-status-api/nym-node-status-api" WORKING_DIRECTORY: "nym-node-status-api"
CONTAINER_NAME: "node-status-api" CONTAINER_NAME: "node-status-api"
jobs: jobs:
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder" git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml - name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.1 uses: mikefarah/yq@v4.44.5
id: get_version id: get_version
with: with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder" git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml - name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.1 uses: mikefarah/yq@v4.44.5
id: get_version id: get_version
with: with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
@@ -1,55 +0,0 @@
name: Build and upload Nyx Chain Watcher container to harbor.nymte.ch
on:
workflow_dispatch:
env:
WORKING_DIRECTORY: "nyx-chain-watcher"
CONTAINER_NAME: "nyx-chain-watcher"
jobs:
build-container:
runs-on: arc-ubuntu-22.04-dind
steps:
- name: Login to Harbor
uses: docker/login-action@v3
with:
registry: harbor.nymte.ch
username: ${{ secrets.HARBOR_ROBOT_USERNAME }}
password: ${{ secrets.HARBOR_ROBOT_SECRET }}
- name: Checkout repo
uses: actions/checkout@v4
- name: Configure git identity
run: |
git config --global user.email "lawrence@nymtech.net"
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.1
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
- name: Check if tag exists
run: |
if git rev-parse ${{ steps.get_version.outputs.value }} >/dev/null 2>&1; then
echo "Tag ${{ steps.get_version.outputs.value }} already exists"
fi
- name: Remove existing tag if exists
run: |
if git rev-parse ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} >/dev/null 2>&1; then
git push --delete origin ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}
git tag -d ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}
fi
- name: Create tag
run: |
git tag -a ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} -m "Version ${{ steps.get_version.outputs.result }}"
git push origin ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}
- name: BuildAndPushImageOnHarbor
run: |
docker build -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.result }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:latest
docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags
@@ -26,10 +26,10 @@ jobs:
git config --global user.name "Lawrence Stalder" git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml - name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.1 uses: mikefarah/yq@v4.44.5
id: get_version id: get_version
with: with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-credential-proxy/Cargo.toml
- name: Remove existing tag if exists - name: Remove existing tag if exists
run: | run: |
+1 -8
View File
@@ -51,11 +51,4 @@ ppa-private-key.b64
ppa-private-key.asc ppa-private-key.asc
nym-network-monitor/topology.json nym-network-monitor/topology.json
nym-network-monitor/__pycache__ nym-network-monitor/__pycache__
nym-network-monitor/*.key nym-network-monitor/*.key
nym-network-monitor/.envrc
nym-network-monitor/.envrc
nym-api/redocly/formatted-openapi.json
*.sqlite
.build
-347
View File
@@ -4,353 +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)
- Feture/legacy alert ([#5346])
- chore: readjusted --mode behaviour to fix the regression ([#5331])
- chore: apply 1.84 linter suggestions ([#5330])
- bugfix: make sure refresh data key matches bond info ([#5329])
- reduce log severity for number of packets being delayed ([#5321])
- feat: warn users if node is run in exit mode only ([#5320])
- Bugfix/contract version assignment ([#5318])
- fixed client session histogram buckets ([#5316])
- amend 250gb limit ([#5313])
- feature: expand nym-node prometheus metrics ([#5298])
- Cherry picked #5286 ([#5287])
- Add close to credential storage ([#5283])
- feature: wireguard metrics ([#5278])
- Add PATCH support to nym-http-api-client ([#5260])
- chore: removed legacy socks5 listener ([#5259])
- bugfix: make sure to apply gateway score filtering when choosing initial node ([#5256])
- Update TS bindings ([#5255])
- Add conversion unit tests for auth msg ([#5251])
- Add control messages to GatewayTransciver ([#5247])
- Remove unneeded async function annotation ([#5246])
- bugfix: make sure to update timestamp of last batch verification to prevent double redemption ([#5239])
- Add FromStr impl for UserAgent ([#5236])
- Extend swagger docs ([#5235])
- TicketType derive Hash and Eq ([#5233])
- Add fd callback to client core ([#5230])
- Extend raw ws fd for gateway client ([#5218])
- Shipping raw metrics to PG ([#5216])
- Change sqlite journal mode to WAL ([#5213])
- Derive serialize for UserAgent ([#5210])
- Restore Location fields ([#5208])
- better date serialization ([#5207])
- Fix overflow ([#5204])
- feature: hopefully final steps of the smoosh™️ ([#5201])
- Fix overflow ([#5184])
- NS API - Gateway stats scraping ([#5180])
- introduced initial internal commands for nym-cli: ecash key and request generation ([#5174])
- Move NS client to separate package under NS API ([#5171])
- build(deps): bump micromatch from 4.0.4 to 4.0.8 in /testnet-faucet ([#4813])
[#5346]: https://github.com/nymtech/nym/pull/5346
[#5331]: https://github.com/nymtech/nym/pull/5331
[#5330]: https://github.com/nymtech/nym/pull/5330
[#5329]: https://github.com/nymtech/nym/pull/5329
[#5321]: https://github.com/nymtech/nym/pull/5321
[#5320]: https://github.com/nymtech/nym/pull/5320
[#5318]: https://github.com/nymtech/nym/pull/5318
[#5316]: https://github.com/nymtech/nym/pull/5316
[#5313]: https://github.com/nymtech/nym/pull/5313
[#5298]: https://github.com/nymtech/nym/pull/5298
[#5287]: https://github.com/nymtech/nym/pull/5287
[#5283]: https://github.com/nymtech/nym/pull/5283
[#5278]: https://github.com/nymtech/nym/pull/5278
[#5260]: https://github.com/nymtech/nym/pull/5260
[#5259]: https://github.com/nymtech/nym/pull/5259
[#5256]: https://github.com/nymtech/nym/pull/5256
[#5255]: https://github.com/nymtech/nym/pull/5255
[#5251]: https://github.com/nymtech/nym/pull/5251
[#5247]: https://github.com/nymtech/nym/pull/5247
[#5246]: https://github.com/nymtech/nym/pull/5246
[#5239]: https://github.com/nymtech/nym/pull/5239
[#5236]: https://github.com/nymtech/nym/pull/5236
[#5235]: https://github.com/nymtech/nym/pull/5235
[#5233]: https://github.com/nymtech/nym/pull/5233
[#5230]: https://github.com/nymtech/nym/pull/5230
[#5218]: https://github.com/nymtech/nym/pull/5218
[#5216]: https://github.com/nymtech/nym/pull/5216
[#5213]: https://github.com/nymtech/nym/pull/5213
[#5210]: https://github.com/nymtech/nym/pull/5210
[#5208]: https://github.com/nymtech/nym/pull/5208
[#5207]: https://github.com/nymtech/nym/pull/5207
[#5204]: https://github.com/nymtech/nym/pull/5204
[#5201]: https://github.com/nymtech/nym/pull/5201
[#5184]: https://github.com/nymtech/nym/pull/5184
[#5180]: https://github.com/nymtech/nym/pull/5180
[#5174]: https://github.com/nymtech/nym/pull/5174
[#5171]: https://github.com/nymtech/nym/pull/5171
[#4813]: https://github.com/nymtech/nym/pull/4813
## [2024.14-crunch-patched] (2024-12-17)
- Fixes an issue to allow previously registred clients to connect to latest nym-nodes
- Fixes compatibility issues between nym-nodes and older clients
## [2024.14-crunch] (2024-12-11)
- Merge/release/2024.14-crunch ([#5242])
- bugfix: added explicit openapi servers to account for route prefixes ([#5237])
- Further config score adjustments ([#5225])
- feature: remve any filtering on node semver ([#5224])
- Backport #5218 ([#5220])
- Derive serialize for UserAgent (#5210) ([#5217])
- dont consider legacy nodes for rewarded set selection ([#5215])
- introduce UNSTABLE endpoints for returning network monitor run details ([#5214])
- Nmv2 add debug config ([#5212])
- nym-api NMv1 adjustments ([#5209])
- adjusted config score penalty calculation ([#5206])
- Fix backwards compat mac generation ([#5202])
- merge crunch into develop ([#5199])
- Update Security disclosure email, public key and policy ([#5195])
- Guard storage access with cache ([#5193])
- chore: apply 1.84 linter suggestions ([#5192])
- improvement: make internal gateway clients use the same topology cache ([#5191])
- Bugfix/credential proxy sequencing ([#5187])
- Add monitor_run and testing_route indexes ([#5182])
- Add indexes to monitor run and testing route ([#5181])
- bugfix: fixed nym-node config migrations (again) ([#5179])
- bugfix: use default value for verloc config when deserialising missing values ([#5177])
- Remove peers with no allowed ip from storage ([#5175])
- Move two minor jobs to free tier github hosted runners ([#5169])
- Add support for DELETE to nym-http-api-client ([#5166])
- Fix env var name ([#5165])
- Add strum::EnumIter for TicketType ([#5164])
- Add export_to_env to NymNetworkDetails ([#5162])
- bugfix: correctly expose ecash-related data on nym-api ([#5155])
- fix: validator-rewarder GH job ([#5151])
- build(deps): bump cross-spawn from 7.0.3 to 7.0.6 in /testnet-faucet ([#5150])
- build(deps): bump mikefarah/yq from 4.44.3 to 4.44.5 ([#5149])
- start session collection for exit gateways ([#5148])
- add version to clientStatsReport ([#5147])
- update serde_json_path due to compilation issue ([#5144])
- chore: remove standalone legacy mixnode/gateway binaries ([#5135])
- [Product Data] Set up country reporting from vpn-client ([#5134])
- removed ci-nym-api-tests.yml which was running outdated (and broken) tests ([#5133])
- CI: reduce jobs running on cluster ([#5132])
- [DOCS/operators]: Release changes v2024.13-magura & Tokenomics pages v1.0 ([#5128])
- NS Agent auth with NS API ([#5127])
- [Product Data] Config deserialization bug fix ([#5126])
- bugfix: don't send empty BankMsg in ecash contract ([#5121])
- [Product data] Data consumption with ecash ticket ([#5120])
- feat: add GH workflow for nym-validator-rewarder ([#5119])
- feat: add Dockerfile and add env vars for clap arguments ([#5118])
- feature: config score ([#5117])
- [Product Data] Add stats reporting configuration in client config ([#5115])
- Correct IPv6 address generation ([#5113])
- feature: rewarding for ticketbook issuance ([#5112])
- Add granular log on nym-node ([#5111])
- Send mixnet packet stats using task client ([#5109])
- Expose time range ([#5108])
- [Product Data] Client-side stats collection ([#5107])
- chore: ecash contract migration to remove unused 'redemption_gateway_share' ([#5104])
- [Product Data] Better unique user count on gateways ([#5084])
- feat: add nym node GH workflow ([#5080])
- IPv6 support for wireguard ([#5059])
- Node Status API ([#5050])
- Authenticator CLI client mode ([#5044])
- Integrate nym-credential-proxy into workspace ([#5027])
- [Product Data] Introduce data persistence on gateways ([#5022])
- Bump the patch-updates group across 1 directory with 10 updates ([#5011])
- build(deps): bump once_cell from 1.19.0 to 1.20.2 ([#4952])
- Create TaskStatusEvent trait instead of piggybacking on Error ([#4919])
- build(deps): bump lazy_static from 1.4.0 to 1.5.0 ([#4913])
- Sync code with .env in build.rs ([#4876])
- build(deps): bump axios from 1.6.0 to 1.7.5 in /nym-api/tests ([#4790])
- Bump elliptic from 6.5.4 to 6.5.7 in /testnet-faucet ([#4768])
[#5242]: https://github.com/nymtech/nym/pull/5242
[#5237]: https://github.com/nymtech/nym/pull/5237
[#5225]: https://github.com/nymtech/nym/pull/5225
[#5224]: https://github.com/nymtech/nym/pull/5224
[#5220]: https://github.com/nymtech/nym/pull/5220
[#5217]: https://github.com/nymtech/nym/pull/5217
[#5215]: https://github.com/nymtech/nym/pull/5215
[#5214]: https://github.com/nymtech/nym/pull/5214
[#5212]: https://github.com/nymtech/nym/pull/5212
[#5209]: https://github.com/nymtech/nym/pull/5209
[#5206]: https://github.com/nymtech/nym/pull/5206
[#5202]: https://github.com/nymtech/nym/pull/5202
[#5199]: https://github.com/nymtech/nym/pull/5199
[#5195]: https://github.com/nymtech/nym/pull/5195
[#5193]: https://github.com/nymtech/nym/pull/5193
[#5192]: https://github.com/nymtech/nym/pull/5192
[#5191]: https://github.com/nymtech/nym/pull/5191
[#5187]: https://github.com/nymtech/nym/pull/5187
[#5182]: https://github.com/nymtech/nym/pull/5182
[#5181]: https://github.com/nymtech/nym/pull/5181
[#5179]: https://github.com/nymtech/nym/pull/5179
[#5177]: https://github.com/nymtech/nym/pull/5177
[#5175]: https://github.com/nymtech/nym/pull/5175
[#5169]: https://github.com/nymtech/nym/pull/5169
[#5166]: https://github.com/nymtech/nym/pull/5166
[#5165]: https://github.com/nymtech/nym/pull/5165
[#5164]: https://github.com/nymtech/nym/pull/5164
[#5162]: https://github.com/nymtech/nym/pull/5162
[#5155]: https://github.com/nymtech/nym/pull/5155
[#5151]: https://github.com/nymtech/nym/pull/5151
[#5150]: https://github.com/nymtech/nym/pull/5150
[#5149]: https://github.com/nymtech/nym/pull/5149
[#5148]: https://github.com/nymtech/nym/pull/5148
[#5147]: https://github.com/nymtech/nym/pull/5147
[#5144]: https://github.com/nymtech/nym/pull/5144
[#5135]: https://github.com/nymtech/nym/pull/5135
[#5134]: https://github.com/nymtech/nym/pull/5134
[#5133]: https://github.com/nymtech/nym/pull/5133
[#5132]: https://github.com/nymtech/nym/pull/5132
[#5128]: https://github.com/nymtech/nym/pull/5128
[#5127]: https://github.com/nymtech/nym/pull/5127
[#5126]: https://github.com/nymtech/nym/pull/5126
[#5121]: https://github.com/nymtech/nym/pull/5121
[#5120]: https://github.com/nymtech/nym/pull/5120
[#5119]: https://github.com/nymtech/nym/pull/5119
[#5118]: https://github.com/nymtech/nym/pull/5118
[#5117]: https://github.com/nymtech/nym/pull/5117
[#5115]: https://github.com/nymtech/nym/pull/5115
[#5113]: https://github.com/nymtech/nym/pull/5113
[#5112]: https://github.com/nymtech/nym/pull/5112
[#5111]: https://github.com/nymtech/nym/pull/5111
[#5109]: https://github.com/nymtech/nym/pull/5109
[#5108]: https://github.com/nymtech/nym/pull/5108
[#5107]: https://github.com/nymtech/nym/pull/5107
[#5104]: https://github.com/nymtech/nym/pull/5104
[#5084]: https://github.com/nymtech/nym/pull/5084
[#5080]: https://github.com/nymtech/nym/pull/5080
[#5059]: https://github.com/nymtech/nym/pull/5059
[#5050]: https://github.com/nymtech/nym/pull/5050
[#5044]: https://github.com/nymtech/nym/pull/5044
[#5027]: https://github.com/nymtech/nym/pull/5027
[#5022]: https://github.com/nymtech/nym/pull/5022
[#5011]: https://github.com/nymtech/nym/pull/5011
[#4952]: https://github.com/nymtech/nym/pull/4952
[#4919]: https://github.com/nymtech/nym/pull/4919
[#4913]: https://github.com/nymtech/nym/pull/4913
[#4876]: https://github.com/nymtech/nym/pull/4876
[#4790]: https://github.com/nymtech/nym/pull/4790
[#4768]: https://github.com/nymtech/nym/pull/4768
## [2024.13-magura-drift] (2024-11-29)
- Optimised syncing bandwidth information to storage
## [2024.13-magura-patched] (2024-11-22) ## [2024.13-magura-patched] (2024-11-22)
- [experimental] allow clients to change between deterministic route selection based on packet headers and a pseudorandom distribution - [experimental] allow clients to change between deterministic route selection based on packet headers and a pseudorandom distribution
Generated
+1321 -2042
View File
File diff suppressed because it is too large Load Diff
+94 -91
View File
@@ -48,12 +48,13 @@ 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",
"common/gateway-requests", "common/gateway-requests",
"common/gateway-stats-storage",
"common/gateway-storage", "common/gateway-storage",
"common/gateway-stats-storage",
"common/http-api-client", "common/http-api-client",
"common/http-api-common", "common/http-api-common",
"common/inclusion-probability", "common/inclusion-probability",
@@ -92,52 +93,47 @@ members = [
"common/topology", "common/topology",
"common/tun", "common/tun",
"common/types", "common/types",
"common/verloc",
"common/wasm/client-core", "common/wasm/client-core",
"common/wasm/storage", "common/wasm/storage",
"common/wasm/utils", "common/wasm/utils",
"common/wireguard", "common/wireguard",
"common/wireguard-types", "common/wireguard-types",
"documentation/autodoc", # "documentation/autodoc",
"explorer-api", "explorer-api",
"explorer-api/explorer-api-requests", "explorer-api/explorer-api-requests",
"explorer-api/explorer-client", "explorer-api/explorer-client",
"gateway", "gateway",
"integrations/bity", "integrations/bity",
"mixnode",
"sdk/ffi/cpp",
"sdk/ffi/go",
"sdk/ffi/shared",
"sdk/lib/socks5-listener",
"sdk/rust/nym-sdk",
"service-providers/authenticator",
"service-providers/common",
"service-providers/ip-packet-router",
"service-providers/network-requester",
"nym-api", "nym-api",
"nym-api/nym-api-requests", "nym-api/nym-api-requests",
"nym-browser-extension/storage", "nym-browser-extension/storage",
"nym-credential-proxy/nym-credential-proxy", "nym-credential-proxy/nym-credential-proxy",
"nym-credential-proxy/nym-credential-proxy-requests", "nym-credential-proxy/nym-credential-proxy-requests",
"nym-credential-proxy/vpn-api-lib-wasm", "nym-credential-proxy/vpn-api-lib-wasm",
"nym-data-observatory",
"nym-network-monitor", "nym-network-monitor",
"nym-node", "nym-node",
"nym-node/nym-node-http-api",
"nym-node/nym-node-requests",
"nym-node-status-api/nym-node-status-agent", "nym-node-status-api/nym-node-status-agent",
"nym-node-status-api/nym-node-status-api", "nym-node-status-api/nym-node-status-api",
"nym-node-status-api/nym-node-status-client", "nym-node-status-api/nym-node-status-client",
"nym-node/nym-node-metrics",
"nym-node/nym-node-requests",
"nym-outfox", "nym-outfox",
"nym-validator-rewarder", "nym-validator-rewarder",
"nyx-chain-watcher",
"sdk/ffi/cpp",
"sdk/ffi/go",
"sdk/ffi/shared",
"sdk/rust/nym-sdk",
"service-providers/authenticator",
"service-providers/common",
"service-providers/ip-packet-router",
"service-providers/network-requester",
"tools/echo-server", "tools/echo-server",
"tools/echo-server",
"tools/internal/contract-state-importer/importer-cli",
"tools/internal/contract-state-importer/importer-contract",
"tools/internal/mixnet-connectivity-check",
# "tools/internal/sdk-version-bump",
"tools/internal/ssl-inject", "tools/internal/ssl-inject",
# "tools/internal/sdk-version-bump",
"tools/internal/testnet-manager", "tools/internal/testnet-manager",
"tools/internal/testnet-manager",
"tools/internal/testnet-manager/dkg-bypass-contract",
"tools/internal/testnet-manager/dkg-bypass-contract", "tools/internal/testnet-manager/dkg-bypass-contract",
"tools/nym-cli", "tools/nym-cli",
"tools/nym-id-cli", "tools/nym-id-cli",
@@ -149,19 +145,26 @@ members = [
"wasm/mix-fetch", "wasm/mix-fetch",
"wasm/node-tester", "wasm/node-tester",
"wasm/zknym-lib", "wasm/zknym-lib",
"tools/echo-server",
"tools/internal/contract-state-importer/importer-cli",
"tools/internal/contract-state-importer/importer-contract",
"tools/internal/testnet-manager",
"tools/internal/testnet-manager/dkg-bypass-contract",
] ]
default-members = [ default-members = [
"clients/native", "clients/native",
"clients/socks5", "clients/socks5",
"explorer-api", "explorer-api",
"gateway",
"mixnode",
"nym-api", "nym-api",
"nym-credential-proxy/nym-credential-proxy", "nym-credential-proxy/nym-credential-proxy",
"nym-data-observatory",
"nym-node", "nym-node",
"nym-node-status-api/nym-node-status-agent", "nym-node-status-api/nym-node-status-agent",
"nym-node-status-api/nym-node-status-api", "nym-node-status-api/nym-node-status-api",
"nym-validator-rewarder", "nym-validator-rewarder",
"nyx-chain-watcher",
"service-providers/authenticator", "service-providers/authenticator",
"service-providers/ip-packet-router", "service-providers/ip-packet-router",
"service-providers/network-requester", "service-providers/network-requester",
@@ -172,6 +175,7 @@ exclude = [
"explorer", "explorer",
"contracts", "contracts",
"nym-wallet", "nym-wallet",
"nym-vpn/ui/src-tauri",
"cpu-cycles", "cpu-cycles",
] ]
@@ -187,49 +191,45 @@ 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.90"
arc-swap = "1.7.1"
argon2 = "0.5.0" argon2 = "0.5.0"
async-trait = "0.1.86" async-trait = "0.1.83"
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?
bitvec = "1.0.0" bitvec = "1.0.0"
blake3 = "1.5.5" blake3 = "1.5.4"
bloomfilter = "1.0.14" bloomfilter = "1.0.14"
bs58 = "0.5.1" 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.31"
cipher = "0.4.3" cipher = "0.4.3"
clap = "4.5.30" clap = "4.5.20"
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.1"
console = "0.15.10" console = "0.15.8"
console-subscriber = "0.1.1" console-subscriber = "0.1.1"
console_error_panic_hook = "0.1" console_error_panic_hook = "0.1"
const-str = "0.5.6" const-str = "0.5.6"
const_format = "0.2.34" const_format = "0.2.33"
criterion = "0.5" criterion = "0.4"
csv = "1.3.1" csv = "1.3.0"
ctr = "0.9.1" ctr = "0.9.1"
cupid = "0.6.1" cupid = "0.6.1"
curve25519-dalek = "4.1" curve25519-dalek = "4.1"
@@ -242,33 +242,30 @@ 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.34"
futures = "0.3.31" futures = "0.3.28"
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"
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" hyper = "1.4.1"
hyper-util = "0.1" hyper-util = "0.1"
indicatif = "0.17.11" indicatif = "0.17.8"
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,21 +277,22 @@ 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"
pem = "0.8" pem = "0.8"
petgraph = "0.6.5" petgraph = "0.6.5"
pin-project = "1.1" pin-project = "1.1"
pin-project-lite = "0.2.16" pin-project-lite = "0.2.14"
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,15 +306,14 @@ 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.23"
serde = "1.0.217" serde = "1.0.211"
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.132"
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"
@@ -328,39 +325,36 @@ strum = "0.26"
strum_macros = "0.26" strum_macros = "0.26"
subtle-encoding = "0.5" subtle-encoding = "0.5"
syn = "1" syn = "1"
sysinfo = "0.33.0" sysinfo = "0.30.13"
tap = "1.0.1" tap = "1.0.1"
tar = "0.4.43" tar = "0.4.42"
tempfile = "3.15" tempfile = "3.14"
thiserror = "2.0" thiserror = "1.0.64"
time = "0.3.37" time = "0.3.30"
tokio = "1.43" tokio = "1.39"
tokio-postgres = "0.7" tokio-stream = "0.1.16"
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.12"
toml = "0.8.20" toml = "0.8.14"
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.37"
tracing-log = "0.2"
tracing-opentelemetry = "0.19.0" tracing-opentelemetry = "0.19.0"
tracing-subscriber = "0.3.19" tracing-subscriber = "0.3.16"
tracing-tree = "0.2.2" tracing-tree = "0.2.2"
ts-rs = "10.1.0" tracing-log = "0.2"
ts-rs = "10.0.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 = "4.2"
utoipa-swagger-ui = "8.1" utoipa-swagger-ui = "7.1"
utoipauto = "0.2" utoipauto = "0.1"
uuid = "*" uuid = "*"
vergen = { version = "=8.3.1", default-features = false } vergen = { version = "=8.3.1", default-features = false }
walkdir = "2" walkdir = "2"
wasm-bindgen-test = "0.3.49" wasm-bindgen-test = "0.3.43"
x25519-dalek = "2.0.0" x25519-dalek = "2.0.0"
zeroize = "1.6.0" zeroize = "1.6.0"
@@ -391,26 +385,31 @@ 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 }
# temporarily using a fork again (yay.) because we need staking and slashing support (which are already on main but not released)
cosmrs = { version = "0.21.1" } # plus response message parsing (which is, as of the time of writing this message, waiting to get merged)
tendermint = "0.40.0" #cosmrs = { path = "../cosmos-rust-fork/cosmos-rust/cosmrs" }
tendermint-rpc = "0.40.0" cosmrs = { git = "https://github.com/cosmos/cosmos-rust", rev = "4b1332e6d8258ac845cef71589c8d362a669675a" } # unfortuntely we need a fork by yours truly to get the staking support
prost = { version = "0.13", default-features = false } tendermint = "0.37.0" # same version as used by cosmrs
tendermint-rpc = "0.37.0" # same version as used by cosmrs
prost = { version = "0.12", default-features = false }
# wasm-related dependencies # wasm-related dependencies
gloo-utils = "0.2.0" gloo-utils = "0.2.0"
gloo-net = "0.6.0" gloo-net = "0.5.0"
indexed_db_futures = "0.6.0" # use a separate branch due to feature unification failures
js-sys = "0.3.76" # this is blocked until the upstream removes outdates `wasm_bindgen` feature usage
# indexed_db_futures = "0.4.1"
indexed_db_futures = { git = "https://github.com/TiemenSch/rust-indexed-db", branch = "update-uuid" }
js-sys = "0.3.70"
serde-wasm-bindgen = "0.6.5" serde-wasm-bindgen = "0.6.5"
tsify = "0.4.5" tsify = "0.4.5"
wasm-bindgen = "0.2.99" wasm-bindgen = "0.2.95"
wasm-bindgen-futures = "0.4.49" wasm-bindgen-futures = "0.4.45"
wasmtimer = "0.4.1" wasmtimer = "0.2.0"
web-sys = "0.3.76" web-sys = "0.3.72"
# Profile settings for individual crates # Profile settings for individual crates
@@ -420,6 +419,10 @@ web-sys = "0.3.76"
[profile.dev.package.sqlx-macros] [profile.dev.package.sqlx-macros]
opt-level = 3 opt-level = 3
[profile.release.package.nym-socks5-listener]
strip = true
codegen-units = 1
[profile.release.package.nym-client-wasm] [profile.release.package.nym-client-wasm]
# lto = true # lto = true
opt-level = 'z' opt-level = 'z'
-23
View File
@@ -1,23 +0,0 @@
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
+12 -9
View File
@@ -13,8 +13,7 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
* `nym-client` - an executable which you can build into your own applications. Use it for interacting with Nym nodes. * `nym-client` - an executable which you can build into your own applications. Use it for interacting with Nym nodes.
* `nym-socks5-client` - a Socks5 proxy you can run on your machine and use with existing applications. * `nym-socks5-client` - a Socks5 proxy you can run on your machine and use with existing applications.
* `nym-explorer` - a (projected) block explorer and (existing) mixnet viewer. * `nym-explorer` - a (projected) block explorer and (existing) mixnet viewer.
* `nym-wallet` - a desktop wallet implemented using the [Tauri](https://tauri.app)) framework. * `nym-wallet` - a desktop wallet implemented using the [Tauri](https://tauri.studio/en/docs/about/intro) framework.
* `nym-cli` - a tool for interacting with the network from the CLI.
<!-- coming soon <!-- coming soon
* `nym-network-monitor` - sends packets through the full system to check that they are working as expected, and stores node uptime histories as the basis of a rewards system ("mixmining" or "proof-of-mixing"). * `nym-network-monitor` - sends packets through the full system to check that they are working as expected, and stores node uptime histories as the basis of a rewards system ("mixmining" or "proof-of-mixing").
--> -->
@@ -36,20 +35,24 @@ client ───► Gateway ──┘ mix │ mix ┌─►mix ───►
### Building ### Building
* Platform build instructions are available on Nym [Operators Guide documentation](https://nymtech.net/operators/binaries/building-nym.html). * Platform build instructions are available on Nym [Operators Guide documentation](https://nymtech.net/operators/binaries/building-nym.html).
* Wallet build instructions are available [here](https://github.com/nymtech/nym/tree/master/nym-wallet#installation-prerequisites---linux--mac). * Wallet build instructions are available on Nym [Technical docs](https://nymtech.net/docs/wallet/desktop-wallet.html).
### Developing ### Developing
There's a [`sandbox.env`](https://github.com/nymtech/nym/envs/sandbox.env) file provided which you can rename to `.env` if you want convenient testing environment. Read more about sandbox environment in our [Operators Guide page](https://nymtech.net/operators/sandbox.html).
References for developers: References for developers:
* [Dev Docs](https://nym.com/docs/developers) * [Developers Portal](https://nymtech.net/developers)
* [SDKs](https://nym.com/docs/developers/rust) * [Typescript SDKs](https://sdk.nymtech.net/)
* [Network Docs](https://nym.com/docs/network) * [Technical Documentation - Nym network overview](https://nymtech.net/docs/)
* [Release Cycle - git flow](https://nym.com/docs/operators/release-cycle) * [Release Cycle - git flow](https://nymtech.net/operators/release-cycle.html)
### Developer chat ### Developer chat
You can chat to us in the #dev channel on [Matrix](https://matrix.to/#/#dev:nymtech.chat) or on the [Nym Forum](https://forum.nymtech.net). You can chat to us in two places:
* The #dev channel on [Matrix](https://matrix.to/#/#dev:nymtech.chat)
* The various developer channels on [Discord](https://nymtech.net/go/discord)
### Tokenomics & Rewards ### Tokenomics & Rewards
@@ -66,4 +69,4 @@ As a general approach, licensing is as follows this pattern:
- libraries and components are Apache 2.0 or MIT - libraries and components are Apache 2.0 or MIT
- documentation is Apache 2.0 or CC0-1.0 - documentation is Apache 2.0 or CC0-1.0
Nym Node Operators and Validators Terms and Conditions can be found [here](https://nym.com/operators-validators-terms). Nym Node Operators and Validators Temrs and Conditions can be found [here](https://nymtech.net/terms-and-conditions/operators/v1.0.0).
+56 -68
View File
@@ -3,23 +3,37 @@ Critical bug or security issue 💥
If you're here because you're trying to figure out how to notify us of a security issue, send us a PGP encrypted email to: If you're here because you're trying to figure out how to notify us of a security issue, send us a PGP encrypted email to:
``` ```
security@nym.com security@nymte.ch
``` ```
Encrypted with our public key which is available below in plain text and also on keyservers: Encrypted with our public key which is available below in plain text and also on keyservers:
``` ```
sec rsa4096/7C3C727F05090550 2023-10-30 [SC] [expire : 2026-10-29] pub rsa4096 2023-10-30 [SC] [expire : 2026-10-29]
24B2592E801A5AAA8666C8BA7C3C727F05090550 24B2592E801A5AAA8666C8BA7C3C727F05090550
uid [ ultime ] Security Nym Technologies <security@nym.com> uid [ ultime ] Security Nym Technologies <security@nymte.ch>
ssb rsa4096/ACD0FBD79DC70ACC 2023-10-30 [E] [expire : 2026-10-29] sub rsa4096 2023-10-30 [E] [expire : 2026-10-29]
``` ```
The fingerprint of the key is on the second line above. The fingerprint of the key is on the second line above.
If you need to chat __urgently__ to our team for a __critical__ security issue:
go to Matrix, and alert the core engineers with a private direct message:
Jedrzej Stuczynski @jstuczyn:nymtech.chat
Mark Sinclair @mark:nymtech.chat
Raphaël Walther @raphael:nymtech.chat
Please avoid opening public issues on GitHub that contain information about a potential security vulnerability as this makes it difficult to reduce the impact and harm of valid security issues. Please avoid opening public issues on GitHub that contain information about a potential security vulnerability as this makes it difficult to reduce the impact and harm of valid security issues.
If you don't know what Matrix is, you can follow this documentation to create an account on this federation of instant messaging servers:
[Matrix for Instant Messaging](https://matrix.org/docs/chat_basics/matrix-for-im/)
``` ```
-----BEGIN PGP PUBLIC KEY BLOCK----- -----BEGIN PGP PUBLIC KEY BLOCK-----
@@ -34,69 +48,43 @@ vMFUIzBMHOPXH16036zGyFMC1esRd2qqil4b9KtLgCOkrD1VgpjcveoA0VyMJCN6
LmKTrVjwjjDMxby+d49BolRWGnCofXozXwvNQx+CYv8M2WPErTpyYoofYFtpqr7A LmKTrVjwjjDMxby+d49BolRWGnCofXozXwvNQx+CYv8M2WPErTpyYoofYFtpqr7A
fIufc/e0+um3zoGIbHejrhsbuH9Qf+MKsI+Ng93bdDtjeHz6MEgAlsTm0qeizYpj fIufc/e0+um3zoGIbHejrhsbuH9Qf+MKsI+Ng93bdDtjeHz6MEgAlsTm0qeizYpj
IyKZIObPmfvrAm08hFZ8JnGk+XuooF36XWbJYjCCy0bOyMw1r7ZG99TcSwARAQAB IyKZIObPmfvrAm08hFZ8JnGk+XuooF36XWbJYjCCy0bOyMw1r7ZG99TcSwARAQAB
tCxTZWN1cml0eSBOeW0gVGVjaG5vbG9naWVzIDxzZWN1cml0eUBueW0uY29tPokC tC1TZWN1cml0eSBOeW0gVGVjaG5vbG9naWVzIDxzZWN1cml0eUBueW10ZS5jaD6J
VAQTAQoAPhYhBCSyWS6AGlqqhmbIunw8cn8FCQVQBQJnSd5VAhsDBQkFo5qABQsJ AlQEEwEKAD4WIQQkslkugBpaqoZmyLp8PHJ/BQkFUAUCZT9elwIbAwUJBaOagAUL
CAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJEHw8cn8FCQVQPPIP/ipGz2zLAjE2dSE3 CQgHAwUVCgkICwUWAgMBAAIeAQIXgAAKCRB8PHJ/BQkFUL7dD/9zO73uI5VR+SWx
VcqOvras0DfqIL9HDm26Dg6QO2D/4YRntw0RqVyuy+zFnRUm+RZCKLPLUzbQ9Wjb PFmJW+9QsPiQbVRvGwNZurctmQ2s2Pe0vHRELFeqD5oYvSx2Lequ3Ir+zn/C3kDM
G/Og5ttQVYQMu5eKu7OMvXkrbRo3teZFU+8IL08zIW6pyf9haxO6YMhLRy6cLYwW kNs40obSL6jCBiLPkxEY0JqzPM9jZr7EjvlibWV3f6DxooRIqEyfN57I3OBGlqZE
0EYC6Qzn5gz3kI7VkI8fWfs2Dk4XEV3D+SVtBoF6KRxMXT6HZvpzoMSEJZBoNj8S 0Mx7sQuCcgau8C70DF952QhKUwXC2cmpmDKHVEEoio1xGSD4dQhGapCB32RQGtna
jw0TF8TFUQf49jUQbIHumukMswolrHi8a5ej8DSfNwSgz+Tt8oh5lu01kyUJiHn7 OGfAO9celNMvSq0Lp+aJxeACmWFY5T4/y79JPcT5vSs/yEIRmaH/fn2piwaFBsIq
nuHaY4Y9cHUVAOSwq/hovG52+ZE1r3aiswvle/B19o9pKeWWVvacSptGxDQagBtQ gHJJMxO3740P1hF8j7KWUoUofuFaEALHBpEpjWTOj8ej1wmFlu+5F+jSVoc781Wb
igoNLdRvY0XN2TEyX9pOHR0AoVOxtIW11CpkKuDbQG9vPwovqJ2L6+Fh3pzHYzcI ZZXu04cOBXnGTogzSxMpBe9TtLb28zd6WzFotC25KTI3pngMzXsQGLJLOwvoZKiS
2GIShNm/Z2SZBiUqbljJe9H4UAT/aHgMINkEG8qzUKwO42MA5HJT7YbHTR17/QSF LFjPRjg1rwobmB3Q3J2W5GYSveia0CDsZGP+g87GVVf/oD2Djpa68xyVYwIYeA6T
Il5dhneRzmSbNcW2rdRwx/BmzrcsFJfqCt4JG/WDF293xSOjhFqQYvU4gCO+OB7o 3DNdS77qHiRuGiS4kWXyVjDqOICboR4uCvt09zlkBuLDdTWqWYARUvZjtjs4w/Ol
KXjX907XXDjS2KEJ71OGqVfk/P7BqEfQNfrLtb02TyXJAPQXHhybv23c4E7zUs9V rdrBI3A88ti8fRldYaNpu17ME1ilpN44yKoJtqiWc3Tisk8eYLfx6c7FQF3PrRva
lMjNizzxYB96uwJb0LAB2ijzEwoP91uGT2tFjk6F08x2QiArmXUdgrv44b39Stia mr7FZvhFsYML5CeNFHTEzN6Y3jjKN/60DvCfodWnWFK47Txkl8UAXGY2W9B0fWqQ
gJS0GYKqSzyr10xHhUuDA+GKYtcitC1TZWN1cml0eSBOeW0gVGVjaG5vbG9naWVz wUVr8uLuMyyMiKbeoufi7rGOj6AMErkCDQRlP16XARAA8FGmD5J3tM1BOM1niJxZ
IDxzZWN1cml0eUBueW10ZS5jaD6JAjYEMAEKACAWIQQkslkugBpaqoZmyLp8PHJ/ JTdCauzEtxEoBL0RuqGBkR8U29sRM6DwuzjU7PwscFnBaGyU+eU73GwGkH3ozFfF
BQkFUAUCZ0nftQIdIAAKCRB8PHJ/BQkFUFHDEACtyNuUEjKCLAT5mSfow85PjFgo tllYhQrhP/kkN+0rEO5Xi+nR+4JCFRqrf3nJXAAPfiksURMp8er1dUOY2/e1ZSoL
o8kHjQr/IIQ7ZbBOHeJJcrxDuypssiLh5XUjF3x5BiBfZ6vCxSb81RRwsDMp0mA1 tS+nzUivV8CfE+pgj/5YtGwPC+KYHLATkKkMELCrbW4UO06VWOqQsvr6kivXuJQQ
qzv9G8sgW0HTQUnZ9oH6CYut2NgzAnQpmuacrunm9Zy0FJ3ejbmwUY/NqK6gJkle LdEAMpBlADmXFG45DmPKQzsBWUgvTwyGy3LX0nys8cgpex9BH8hhr01QmGyP469s
66duHKhAy7DWjj7amd0C8bPDR+PA44fI3MezDHkQNaauKZTRqd1TqH8Qk5PAl4cB N3cNrtFuu8U6RAsiCD/8mlBuD3EQEU5SF0lc7kCICAZk+wElmXnimEi0TOYsbz6k
o5gVzeZh/U7/usvtGhazAIUF5BqK6bTmDnYopg+2x8jjwrG4+08GrttZkNjBLXeA 90lteicX70rA9GNeyI76H+VSOYvWpkRwaJAgUdzrAM1o9SHASq+cZ6nD85OZioQk
Y/2U064yMz12LPv01qqAFdZ+coRy/ps/gOQTz34/VeW0CFy7TMqs4t3vSBWTqU7w DWM6+Q+sf2oen0qJnnGmUr93kJIC0PIdgrXRrtiNfeRa1Z/H0LmREyyEMoFiVivn
hnw/qj6cM33fdxctj6KDgJSCkZdx2fvwXgxiPqUa5+j9FlFBeD5RDAl6g6t8N1/K z1vVk85Oq6Sf3ltUwvmDzuuJOtsp2Qp6+x6Snn/yKauI4uf4Cf/wKUch4r6Bwgg5
Xca+zNYuSZgc297q1D+mtSD1C7uJNPxoAl+Bv5KNKpsjfQ+m04++CIFtGyX22aCA Dw49ky7lwlnALio4GIVoGLpLef93wWoDmp4Klyh3ZPf2nB0U91u3bHRUo7m+D7QJ
h2/tHwQZIXhOiMAKOoupidDVDhgxtCJ3Ps416xL0sTZfsPfg+j1Uv/Em9pzPClEl 98cyKtqLLzjg7szGf60pIWNWRsadYQT3bSncynqknAjOV3BCvx6/ivsnpj//QjYR
fX6+1O4DdSyZUQ4VsjMu/H5W/NQdbHgmqFrxQ6WX/0s5GMwO6GMDiPe8sOrwz9wD HtviUAcQ1DBB6UC6q23FIs0AEQEAAYkCPAQYAQoAJhYhBCSyWS6AGlqqhmbIunw8
WYtyjafxXOHEZ1OjYX5gr7bGaG4oKc2btTJN0B3Phg4dStnHCNjEYccxuV3507fj cn8FCQVQBQJlP16XAhsMBQkFo5qAAAoJEHw8cn8FCQVQzukP/iLxjOxT+UpPR//c
HnNotkpXF2nGLxy+PYkCVAQTAQoAPhYhBCSyWS6AGlqqhmbIunw8cn8FCQVQBQJl prDVSLkP4pF5bmw36U07jvqpS+/KTXsxiiQleffRabOpNLcd+K1ueavyt9nnIwHH
P16XAhsDBQkFo5qABQsJCAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJEHw8cn8FCQVQ tHS9kM9A7DBw3LnpEbXki46QDCCI6niGijlLOEeAWqnocwMNTT05wVVgCtO3DQP2
vt0P/3M7ve4jlVH5JbE8WYlb71Cw+JBtVG8bA1m6ty2ZDazY97S8dEQsV6oPmhi9 MoSCcqHpXDChvOyr5d5xjYLVJhlctIMSomcVzGryjknPu0Yj/TkC/4c+m86ZWQUD
LHYt6q7civ7Of8LeQMyQ2zjShtIvqMIGIs+TERjQmrM8z2NmvsSO+WJtZXd/oPGi HqMHQIuiEenvb62/F4c5OJIRZPEn70wdddkgJuJU3eHdHrnuhCkjCC93GQGbGj03
hEioTJ83nsjc4EaWpkTQzHuxC4JyBq7wLvQMX3nZCEpTBcLZyamYModUQSiKjXEZ Zqos6699y6hmPeD3U5IUv8ujwZYVCCuDm8gJfrp3R6WLfeZeK9WmTVBpCzsDg3fV
IPh1CEZqkIHfZFAa2do4Z8A71x6U0y9KrQun5onF4AKZYVjlPj/Lv0k9xPm9Kz/I hSwmOk6pp8DAq1/Dev3yRkFggCEyGK6c9b+a0CRBncl8e5Q0QQIzNiS/uExQP3h+
QhGZof9+famLBoUGwiqAckkzE7fvjQ/WEXyPspZShSh+4VoQAscGkSmNZM6Px6PX ELJs3P0MLP+6FWhNUry09n3lnWkr1hY+v1M0GAxbfdv/tsCN1Pq/VQEz+CTqXqya
CYWW77kX6NJWhzvzVZtlle7Thw4FecZOiDNLEykF71O0tvbzN3pbMWi0LbkpMjem ftWldOHWw6Hh+gtwxcHjG4MBOrO5oICQ3lh2hGwQ58cDgZYSK/OGgJ9BggFl1CcM
eAzNexAYsks7C+hkqJIsWM9GODWvChuYHdDcnZbkZhK96JrQIOxkY/6DzsZVV/+g 0uGC0/TRCI1zt/4y+7efSZQMZkHo7VC/3MFbp2hcNejpW+BxVuwKTunFvWK3TLhq
PYOOlrrzHJVjAhh4DpPcM11LvuoeJG4aJLiRZfJWMOo4gJuhHi4K+3T3OWQG4sN1 sSlQ5yyhqchooepsFHq9bosKFjLJC01uprBv1rinoNduOy43FbyS7JPRRspANN0R
NapZgBFS9mO2OzjD86Wt2sEjcDzy2Lx9GV1ho2m7XswTWKWk3jjIqgm2qJZzdOKy iC2pMbWdE0ZTQaFq6tPIg058pjqi
Tx5gt/HpzsVAXc+tG9qavsVm+EWxgwvkJ40UdMTM3pjeOMo3/rQO8J+h1adYUrjt =nqgX
PGSXxQBcZjZb0HR9apDBRWvy4u4zLIyIpt6i5+LusY6PoAwSuQINBGU/XpcBEADw
UaYPkne0zUE4zWeInFklN0Jq7MS3ESgEvRG6oYGRHxTb2xEzoPC7ONTs/CxwWcFo
bJT55TvcbAaQfejMV8W2WViFCuE/+SQ37SsQ7leL6dH7gkIVGqt/eclcAA9+KSxR
Eynx6vV1Q5jb97VlKgu1L6fNSK9XwJ8T6mCP/li0bA8L4pgcsBOQqQwQsKttbhQ7
TpVY6pCy+vqSK9e4lBAt0QAykGUAOZcUbjkOY8pDOwFZSC9PDIbLctfSfKzxyCl7
H0EfyGGvTVCYbI/jr2w3dw2u0W67xTpECyIIP/yaUG4PcRARTlIXSVzuQIgIBmT7
ASWZeeKYSLRM5ixvPqT3SW16JxfvSsD0Y17Ijvof5VI5i9amRHBokCBR3OsAzWj1
IcBKr5xnqcPzk5mKhCQNYzr5D6x/ah6fSomecaZSv3eQkgLQ8h2CtdGu2I195FrV
n8fQuZETLIQygWJWK+fPW9WTzk6rpJ/eW1TC+YPO64k62ynZCnr7HpKef/Ipq4ji
5/gJ//ApRyHivoHCCDkPDj2TLuXCWcAuKjgYhWgYukt5/3fBagOangqXKHdk9/ac
HRT3W7dsdFSjub4PtAn3xzIq2osvOODuzMZ/rSkhY1ZGxp1hBPdtKdzKeqScCM5X
cEK/Hr+K+yemP/9CNhEe2+JQBxDUMEHpQLqrbcUizQARAQABiQI8BBgBCgAmFiEE
JLJZLoAaWqqGZsi6fDxyfwUJBVAFAmU/XpcCGwwFCQWjmoAACgkQfDxyfwUJBVDO
6Q/+IvGM7FP5Sk9H/9ymsNVIuQ/ikXlubDfpTTuO+qlL78pNezGKJCV599Fps6k0
tx34rW55q/K32ecjAce0dL2Qz0DsMHDcuekRteSLjpAMIIjqeIaKOUs4R4Baqehz
Aw1NPTnBVWAK07cNA/YyhIJyoelcMKG87Kvl3nGNgtUmGVy0gxKiZxXMavKOSc+7
RiP9OQL/hz6bzplZBQMeowdAi6IR6e9vrb8Xhzk4khFk8SfvTB112SAm4lTd4d0e
ue6EKSMIL3cZAZsaPTdmqizrr33LqGY94PdTkhS/y6PBlhUIK4ObyAl+undHpYt9
5l4r1aZNUGkLOwODd9WFLCY6TqmnwMCrX8N6/fJGQWCAITIYrpz1v5rQJEGdyXx7
lDRBAjM2JL+4TFA/eH4Qsmzc/Qws/7oVaE1SvLT2feWdaSvWFj6/UzQYDFt92/+2
wI3U+r9VATP4JOperJp+1aV04dbDoeH6C3DFweMbgwE6s7mggJDeWHaEbBDnxwOB
lhIr84aAn0GCAWXUJwzS4YLT9NEIjXO3/jL7t59JlAxmQejtUL/cwVunaFw16Olb
4HFW7ApO6cW9YrdMuGqxKVDnLKGpyGih6mwUer1uiwoWMskLTW6msG/WuKeg1247
LjcVvJLsk9FGykA03RGILakxtZ0TRlNBoWrq08iDTnymOqI=
=QPTf
-----END PGP PUBLIC KEY BLOCK----- -----END PGP PUBLIC KEY BLOCK-----
``` ```
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-client" name = "nym-client"
version = "1.1.49" version = "1.1.44"
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(
+28 -2
View File
@@ -3,10 +3,13 @@
use crate::commands::try_load_current_config; use crate::commands::try_load_current_config;
use crate::{ use crate::{
client::SocketClient, client::{config::Config, SocketClient},
commands::{override_config, OverrideConfig}, commands::{override_config, OverrideConfig},
error::ClientError,
}; };
use clap::Args; use clap::Args;
use log::*;
use nym_bin_common::version_checker::is_minor_version_compatible;
use nym_client_core::cli_helpers::client_run::CommonClientRunArgs; use nym_client_core::cli_helpers::client_run::CommonClientRunArgs;
use std::error::Error; use std::error::Error;
use std::net::IpAddr; use std::net::IpAddr;
@@ -41,7 +44,25 @@ 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(), }
}
}
// this only checks compatibility between config the binary. It does not take into consideration
// network version. It might do so in the future.
fn version_check(cfg: &Config) -> bool {
let binary_version = env!("CARGO_PKG_VERSION");
let config_version = &cfg.base.client.version;
if binary_version == config_version {
true
} else {
warn!("The native-client binary has different version than what is specified in config file! {} and {}", binary_version, config_version);
if is_minor_version_compatible(binary_version, config_version) {
info!("but they are still semver compatible. However, consider running the `upgrade` command");
true
} else {
error!("and they are semver incompatible! - please run the `upgrade` command before attempting `run` again");
false
} }
} }
} }
@@ -52,6 +73,11 @@ pub(crate) async fn execute(args: Run) -> Result<(), Box<dyn Error + Send + Sync
let mut config = try_load_current_config(&args.common_args.id).await?; let mut config = try_load_current_config(&args.common_args.id).await?;
config = override_config(config, OverrideConfig::from(args.clone())); config = override_config(config, OverrideConfig::from(args.clone()));
if !version_check(&config) {
error!("failed the local version check");
return Err(Box::new(ClientError::FailedLocalVersionCheck));
}
SocketClient::new(config, args.common_args.custom_mixnet) SocketClient::new(config, args.common_args.custom_mixnet)
.run_socket_forever() .run_socket_forever()
.await .await
+3
View File
@@ -17,6 +17,9 @@ pub enum ClientError {
#[error("Failed to validate the loaded config")] #[error("Failed to validate the loaded config")]
ConfigValidationFailure, ConfigValidationFailure,
#[error("Failed local version check, client and config mismatch")]
FailedLocalVersionCheck,
#[error("Attempted to start the client in invalid socket mode")] #[error("Attempted to start the client in invalid socket mode")]
InvalidSocketMode, InvalidSocketMode,
+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.49" version = "1.1.44"
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)
+33 -2
View File
@@ -2,8 +2,14 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::commands::try_load_current_config; use crate::commands::try_load_current_config;
use crate::commands::{override_config, OverrideConfig}; use crate::config::Config;
use crate::{
commands::{override_config, OverrideConfig},
error::Socks5ClientError,
};
use clap::Args; use clap::Args;
use log::*;
use nym_bin_common::version_checker::is_minor_version_compatible;
use nym_client_core::cli_helpers::client_run::CommonClientRunArgs; use nym_client_core::cli_helpers::client_run::CommonClientRunArgs;
use nym_client_core::client::base_client::storage::OnDiskPersistent; use nym_client_core::client::base_client::storage::OnDiskPersistent;
use nym_client_core::client::topology_control::geo_aware_provider::CountryGroup; use nym_client_core::client::topology_control::geo_aware_provider::CountryGroup;
@@ -65,7 +71,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(),
} }
} }
} }
@@ -77,12 +82,38 @@ fn validate_country_group(s: &str) -> Result<CountryGroup, String> {
} }
} }
// this only checks compatibility between config the binary. It does not take into consideration
// network version. It might do so in the future.
fn version_check(cfg: &Config) -> bool {
let binary_version = env!("CARGO_PKG_VERSION");
let config_version = &cfg.core.base.client.version;
if binary_version == config_version {
true
} else {
warn!(
"The socks5-client binary has different version than what is specified in config file! {binary_version} and {config_version}",
);
if is_minor_version_compatible(binary_version, config_version) {
info!("but they are still semver compatible. However, consider running the `upgrade` command");
true
} else {
error!("and they are semver incompatible! - please run the `upgrade` command before attempting `run` again");
false
}
}
}
pub(crate) async fn execute(args: Run) -> Result<(), Box<dyn std::error::Error + Send + Sync>> { pub(crate) async fn execute(args: Run) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
eprintln!("Starting client {}...", args.common_args.id); eprintln!("Starting client {}...", args.common_args.id);
let mut config = try_load_current_config(&args.common_args.id).await?; let mut config = try_load_current_config(&args.common_args.id).await?;
config = override_config(config, OverrideConfig::from(args.clone())); config = override_config(config, OverrideConfig::from(args.clone()));
if !version_check(&config) {
error!("failed the local version check");
return Err(Box::new(Socks5ClientError::FailedLocalVersionCheck));
}
let storage = let storage =
OnDiskPersistent::from_paths(config.storage_paths.common_paths, &config.core.base.debug) OnDiskPersistent::from_paths(config.storage_paths.common_paths, &config.core.base.debug)
.await?; .await?;
-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 }}
"#; "#;
+3
View File
@@ -14,6 +14,9 @@ pub enum Socks5ClientError {
#[error("Failed to validate the loaded config")] #[error("Failed to validate the loaded config")]
ConfigValidationFailure, ConfigValidationFailure,
#[error("Failed local version check, client and config mismatch")]
FailedLocalVersionCheck,
#[error("Fail to bind address")] #[error("Fail to bind address")]
FailToBindAddress, FailToBindAddress,
-1
View File
@@ -8,7 +8,6 @@ pub mod v3;
pub mod v4; pub mod v4;
mod error; mod error;
mod util;
pub use error::Error; pub use error::Error;
pub use v4 as latest; pub use v4 as latest;
-71
View File
@@ -1,71 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#[cfg(test)]
pub(crate) mod tests {
pub(crate) const CREDENTIAL_BYTES: [u8; 1245] = [
0, 0, 4, 133, 96, 179, 223, 185, 136, 23, 213, 166, 59, 203, 66, 69, 209, 181, 227, 254,
16, 102, 98, 237, 59, 119, 170, 111, 31, 194, 51, 59, 120, 17, 115, 229, 79, 91, 11, 139,
154, 2, 212, 23, 68, 70, 167, 3, 240, 54, 224, 171, 221, 1, 69, 48, 60, 118, 119, 249, 123,
35, 172, 227, 131, 96, 232, 209, 187, 123, 4, 197, 102, 90, 96, 45, 125, 135, 140, 99, 1,
151, 17, 131, 143, 157, 97, 107, 139, 232, 212, 87, 14, 115, 253, 255, 166, 167, 186, 43,
90, 96, 173, 105, 120, 40, 10, 163, 250, 224, 214, 200, 178, 4, 160, 16, 130, 59, 76, 193,
39, 240, 3, 101, 141, 209, 183, 226, 186, 207, 56, 210, 187, 7, 164, 240, 164, 205, 37, 81,
184, 214, 193, 195, 90, 205, 238, 225, 195, 104, 12, 123, 203, 57, 233, 243, 215, 145, 195,
196, 57, 38, 125, 172, 18, 47, 63, 165, 110, 219, 180, 40, 58, 116, 92, 254, 160, 98, 48,
92, 254, 232, 107, 184, 80, 234, 60, 160, 235, 249, 76, 41, 38, 165, 28, 40, 136, 74, 48,
166, 50, 245, 23, 201, 140, 101, 79, 93, 235, 128, 186, 146, 126, 180, 134, 43, 13, 186,
19, 195, 48, 168, 201, 29, 216, 95, 176, 198, 132, 188, 64, 39, 212, 150, 32, 52, 53, 38,
228, 199, 122, 226, 217, 75, 40, 191, 151, 48, 164, 242, 177, 79, 14, 122, 105, 151, 85,
88, 199, 162, 17, 96, 103, 83, 178, 128, 9, 24, 30, 74, 108, 241, 85, 240, 166, 97, 241,
85, 199, 11, 198, 226, 234, 70, 107, 145, 28, 208, 114, 51, 12, 234, 108, 101, 202, 112,
48, 185, 22, 159, 67, 109, 49, 27, 149, 90, 109, 32, 226, 112, 7, 201, 208, 209, 104, 31,
97, 134, 204, 145, 27, 181, 206, 181, 106, 32, 110, 136, 115, 249, 201, 111, 5, 245, 203,
71, 121, 169, 126, 151, 178, 236, 59, 221, 195, 48, 135, 115, 6, 50, 227, 74, 97, 107, 107,
213, 90, 2, 203, 154, 138, 47, 128, 52, 134, 128, 224, 51, 65, 240, 90, 8, 55, 175, 180,
178, 204, 206, 168, 110, 51, 57, 189, 169, 48, 169, 136, 121, 99, 51, 170, 178, 214, 74, 1,
96, 151, 167, 25, 173, 180, 171, 155, 10, 55, 142, 234, 190, 113, 90, 79, 80, 244, 71, 166,
30, 235, 113, 150, 133, 1, 218, 17, 109, 111, 223, 24, 216, 177, 41, 2, 204, 65, 221, 212,
207, 236, 144, 6, 65, 224, 55, 42, 1, 1, 161, 134, 118, 127, 111, 220, 110, 127, 240, 71,
223, 129, 12, 93, 20, 220, 60, 56, 71, 146, 184, 95, 132, 69, 28, 56, 53, 192, 213, 22,
119, 230, 152, 225, 182, 188, 163, 219, 37, 175, 247, 73, 14, 247, 38, 72, 243, 1, 48, 131,
59, 8, 13, 96, 143, 185, 127, 241, 161, 217, 24, 149, 193, 40, 16, 30, 202, 151, 28, 119,
240, 153, 101, 156, 61, 193, 72, 245, 199, 181, 12, 231, 65, 166, 67, 142, 121, 207, 202,
58, 197, 113, 188, 248, 42, 124, 105, 48, 161, 241, 55, 209, 36, 194, 27, 63, 233, 144,
189, 85, 117, 234, 9, 139, 46, 31, 206, 114, 95, 131, 29, 240, 13, 81, 142, 140, 133, 33,
30, 41, 141, 37, 80, 217, 95, 221, 76, 115, 86, 201, 165, 51, 252, 9, 28, 209, 1, 48, 150,
74, 248, 212, 187, 222, 66, 210, 3, 200, 19, 217, 171, 184, 42, 148, 53, 150, 57, 50, 6,
227, 227, 62, 49, 42, 148, 148, 157, 82, 191, 58, 24, 34, 56, 98, 120, 89, 105, 176, 85,
15, 253, 241, 41, 153, 195, 136, 1, 48, 142, 126, 213, 101, 223, 79, 133, 230, 105, 38,
161, 149, 2, 21, 136, 150, 42, 72, 218, 85, 146, 63, 223, 58, 108, 186, 183, 248, 62, 20,
47, 34, 113, 160, 177, 204, 181, 16, 24, 212, 224, 35, 84, 51, 168, 56, 136, 11, 1, 48,
135, 242, 62, 149, 230, 178, 32, 224, 119, 26, 234, 163, 237, 224, 114, 95, 112, 140, 170,
150, 96, 125, 136, 221, 180, 78, 18, 11, 12, 184, 2, 198, 217, 119, 43, 69, 4, 172, 109,
55, 183, 40, 131, 172, 161, 88, 183, 101, 1, 48, 173, 216, 22, 73, 42, 255, 211, 93, 249,
87, 159, 115, 61, 91, 55, 130, 17, 216, 60, 34, 122, 55, 8, 244, 244, 153, 151, 57, 5, 144,
178, 55, 249, 64, 211, 168, 34, 148, 56, 89, 92, 203, 70, 124, 219, 152, 253, 165, 0, 32,
203, 116, 63, 7, 240, 222, 82, 86, 11, 149, 167, 72, 224, 55, 190, 66, 201, 65, 168, 184,
96, 47, 194, 241, 168, 124, 7, 74, 214, 250, 37, 76, 32, 218, 69, 122, 103, 215, 145, 169,
24, 212, 229, 168, 106, 10, 144, 31, 13, 25, 178, 242, 250, 106, 159, 40, 48, 163, 165, 61,
130, 57, 146, 4, 73, 32, 254, 233, 125, 135, 212, 29, 111, 4, 177, 114, 15, 210, 170, 82,
108, 110, 62, 166, 81, 209, 106, 176, 156, 14, 133, 242, 60, 127, 120, 242, 28, 97, 0, 1,
32, 103, 93, 109, 89, 240, 91, 1, 84, 150, 50, 206, 157, 203, 49, 220, 120, 234, 175, 234,
150, 126, 225, 94, 163, 164, 199, 138, 114, 62, 99, 106, 112, 1, 32, 171, 40, 220, 82, 241,
203, 76, 146, 111, 139, 182, 179, 237, 182, 115, 75, 128, 201, 107, 43, 214, 0, 135, 217,
160, 68, 150, 232, 144, 114, 237, 98, 32, 30, 134, 232, 59, 93, 163, 253, 244, 13, 202, 52,
147, 168, 83, 121, 123, 95, 21, 210, 209, 225, 223, 143, 49, 10, 205, 238, 1, 22, 83, 81,
70, 1, 32, 26, 76, 6, 234, 160, 50, 139, 102, 161, 232, 155, 106, 130, 171, 226, 210, 233,
178, 85, 247, 71, 123, 55, 53, 46, 67, 148, 137, 156, 207, 208, 107, 1, 32, 102, 31, 4, 98,
110, 156, 144, 61, 229, 140, 198, 84, 196, 238, 128, 35, 131, 182, 137, 125, 241, 95, 69,
131, 170, 27, 2, 144, 75, 72, 242, 102, 3, 32, 121, 80, 45, 173, 56, 65, 218, 27, 40, 251,
197, 32, 169, 104, 123, 110, 90, 78, 153, 166, 38, 9, 129, 228, 99, 8, 1, 116, 142, 233,
162, 69, 32, 216, 169, 159, 116, 95, 12, 63, 176, 195, 6, 183, 123, 135, 75, 61, 112, 106,
83, 235, 176, 41, 27, 248, 48, 71, 165, 170, 12, 92, 103, 103, 81, 32, 58, 74, 75, 145,
192, 94, 153, 69, 80, 128, 241, 3, 16, 117, 192, 86, 161, 103, 44, 174, 211, 196, 182, 124,
55, 11, 107, 142, 49, 88, 6, 41, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 37, 139, 240, 0, 0,
0, 0, 0, 0, 0, 1,
];
pub(crate) const RECIPIENT: &str = "CytBseW6yFXUMzz4SGAKdNLGR7q3sJLLYxyBGvutNEQV.4QXYyEVc5fUDjmmi8PrHN9tdUFV4PCvSJE1278cHyvoe@4sBbL1ngf1vtNqykydQKTFh26sQCw888GpUqvPvyNB4f";
}
@@ -29,7 +29,7 @@ pub type Taken = Option<SystemTime>;
pub const BANDWIDTH_CAP_PER_DAY: u64 = 1024 * 1024 * 1024; // 1 GB pub const BANDWIDTH_CAP_PER_DAY: u64 = 1024 * 1024 * 1024; // 1 GB
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct InitMessage { pub struct InitMessage {
/// Base64 encoded x25519 public key /// Base64 encoded x25519 public key
pub pub_key: PeerPublicKey, pub pub_key: PeerPublicKey,
@@ -41,7 +41,7 @@ impl InitMessage {
} }
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct FinalMessage { pub struct FinalMessage {
/// Gateway client data /// Gateway client data
pub gateway_client: GatewayClient, pub gateway_client: GatewayClient,
@@ -50,28 +50,28 @@ pub struct FinalMessage {
pub credential: Option<CredentialSpendingData>, pub credential: Option<CredentialSpendingData>,
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RegistrationData { pub struct RegistrationData {
pub nonce: u64, pub nonce: u64,
pub gateway_data: GatewayClient, pub gateway_data: GatewayClient,
pub wg_port: u16, pub wg_port: u16,
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RegistredData { pub struct RegistredData {
pub pub_key: PeerPublicKey, pub pub_key: PeerPublicKey,
pub private_ip: IpAddr, pub private_ip: IpAddr,
pub wg_port: u16, pub wg_port: u16,
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RemainingBandwidthData { pub struct RemainingBandwidthData {
pub available_bandwidth: i64, pub available_bandwidth: i64,
} }
/// Client that wants to register sends its PublicKey bytes mac digest encrypted with a DH shared secret. /// Client that wants to register sends its PublicKey bytes mac digest encrypted with a DH shared secret.
/// Gateway/Nym node can then verify pub_key payload using the same process /// Gateway/Nym node can then verify pub_key payload using the same process
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct GatewayClient { pub struct GatewayClient {
/// Base64 encoded x25519 public key /// Base64 encoded x25519 public key
pub pub_key: PeerPublicKey, pub pub_key: PeerPublicKey,
@@ -147,7 +147,7 @@ impl GatewayClient {
// TODO: change the inner type into generic array of size HmacSha256::OutputSize // TODO: change the inner type into generic array of size HmacSha256::OutputSize
// TODO2: rely on our internal crypto/hmac // TODO2: rely on our internal crypto/hmac
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone)]
pub struct ClientMac(Vec<u8>); pub struct ClientMac(Vec<u8>);
impl fmt::Display for ClientMac { impl fmt::Display for ClientMac {
@@ -87,7 +87,7 @@ impl AuthenticatorRequest {
} }
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum AuthenticatorRequestData { pub enum AuthenticatorRequestData {
Initial(InitMessage), Initial(InitMessage),
Final(Box<FinalMessage>), Final(Box<FinalMessage>),
@@ -100,28 +100,28 @@ impl AuthenticatorResponse {
} }
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum AuthenticatorResponseData { pub enum AuthenticatorResponseData {
PendingRegistration(PendingRegistrationResponse), PendingRegistration(PendingRegistrationResponse),
Registered(RegisteredResponse), Registered(RegisteredResponse),
RemainingBandwidth(RemainingBandwidthResponse), RemainingBandwidth(RemainingBandwidthResponse),
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PendingRegistrationResponse { pub struct PendingRegistrationResponse {
pub request_id: u64, pub request_id: u64,
pub reply_to: Recipient, pub reply_to: Recipient,
pub reply: RegistrationData, pub reply: RegistrationData,
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RegisteredResponse { pub struct RegisteredResponse {
pub request_id: u64, pub request_id: u64,
pub reply_to: Recipient, pub reply_to: Recipient,
pub reply: RegistredData, pub reply: RegistredData,
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RemainingBandwidthResponse { pub struct RemainingBandwidthResponse {
pub request_id: u64, pub request_id: u64,
pub reply_to: Recipient, pub reply_to: Recipient,
@@ -19,24 +19,6 @@ impl From<v2::request::AuthenticatorRequest> for v3::request::AuthenticatorReque
} }
} }
impl TryFrom<v3::request::AuthenticatorRequest> for v2::request::AuthenticatorRequest {
type Error = crate::Error;
fn try_from(
authenticator_request: v3::request::AuthenticatorRequest,
) -> Result<Self, Self::Error> {
Ok(Self {
protocol: Protocol {
version: 2,
service_provider_type: ServiceProviderType::Authenticator,
},
data: authenticator_request.data.try_into()?,
reply_to: authenticator_request.reply_to,
request_id: authenticator_request.request_id,
})
}
}
impl From<v2::request::AuthenticatorRequestData> for v3::request::AuthenticatorRequestData { impl From<v2::request::AuthenticatorRequestData> for v3::request::AuthenticatorRequestData {
fn from(authenticator_request_data: v2::request::AuthenticatorRequestData) -> Self { fn from(authenticator_request_data: v2::request::AuthenticatorRequestData) -> Self {
match authenticator_request_data { match authenticator_request_data {
@@ -53,29 +35,6 @@ impl From<v2::request::AuthenticatorRequestData> for v3::request::AuthenticatorR
} }
} }
impl TryFrom<v3::request::AuthenticatorRequestData> for v2::request::AuthenticatorRequestData {
type Error = crate::Error;
fn try_from(
authenticator_request_data: v3::request::AuthenticatorRequestData,
) -> Result<Self, Self::Error> {
match authenticator_request_data {
v3::request::AuthenticatorRequestData::Initial(init_msg) => Ok(
v2::request::AuthenticatorRequestData::Initial(init_msg.into()),
),
v3::request::AuthenticatorRequestData::Final(gw_client) => Ok(
v2::request::AuthenticatorRequestData::Final(gw_client.into()),
),
v3::request::AuthenticatorRequestData::QueryBandwidth(pub_key) => Ok(
v2::request::AuthenticatorRequestData::QueryBandwidth(pub_key),
),
v3::request::AuthenticatorRequestData::TopUpBandwidth(_) => Err(
Self::Error::Conversion("no top up bandwidth variant in v2".to_string()),
),
}
}
}
impl From<v2::registration::InitMessage> for v3::registration::InitMessage { impl From<v2::registration::InitMessage> for v3::registration::InitMessage {
fn from(init_msg: v2::registration::InitMessage) -> Self { fn from(init_msg: v2::registration::InitMessage) -> Self {
Self { Self {
@@ -84,14 +43,6 @@ impl From<v2::registration::InitMessage> for v3::registration::InitMessage {
} }
} }
impl From<v3::registration::InitMessage> for v2::registration::InitMessage {
fn from(init_msg: v3::registration::InitMessage) -> Self {
Self {
pub_key: init_msg.pub_key,
}
}
}
impl From<Box<v2::registration::FinalMessage>> for Box<v3::registration::FinalMessage> { impl From<Box<v2::registration::FinalMessage>> for Box<v3::registration::FinalMessage> {
fn from(gw_client: Box<v2::registration::FinalMessage>) -> Self { fn from(gw_client: Box<v2::registration::FinalMessage>) -> Self {
Box::new(v3::registration::FinalMessage { Box::new(v3::registration::FinalMessage {
@@ -101,15 +52,6 @@ impl From<Box<v2::registration::FinalMessage>> for Box<v3::registration::FinalMe
} }
} }
impl From<Box<v3::registration::FinalMessage>> for Box<v2::registration::FinalMessage> {
fn from(gw_client: Box<v3::registration::FinalMessage>) -> Self {
Box::new(v2::registration::FinalMessage {
gateway_client: gw_client.gateway_client.into(),
credential: gw_client.credential,
})
}
}
impl From<v2::registration::GatewayClient> for v3::registration::GatewayClient { impl From<v2::registration::GatewayClient> for v3::registration::GatewayClient {
fn from(gw_client: v2::registration::GatewayClient) -> Self { fn from(gw_client: v2::registration::GatewayClient) -> Self {
Self { Self {
@@ -151,10 +93,7 @@ impl TryFrom<v3::response::AuthenticatorResponse> for v2::response::Authenticato
Ok(Self { Ok(Self {
data: authenticator_response.data.try_into()?, data: authenticator_response.data.try_into()?,
reply_to: authenticator_response.reply_to, reply_to: authenticator_response.reply_to,
protocol: Protocol { protocol: authenticator_response.protocol,
version: 2,
service_provider_type: authenticator_response.protocol.service_provider_type,
},
}) })
} }
} }
@@ -162,10 +101,7 @@ impl TryFrom<v3::response::AuthenticatorResponse> for v2::response::Authenticato
impl From<v2::response::AuthenticatorResponse> for v3::response::AuthenticatorResponse { impl From<v2::response::AuthenticatorResponse> for v3::response::AuthenticatorResponse {
fn from(value: v2::response::AuthenticatorResponse) -> Self { fn from(value: v2::response::AuthenticatorResponse) -> Self {
Self { Self {
protocol: Protocol { protocol: value.protocol,
version: 3,
service_provider_type: value.protocol.service_provider_type,
},
data: value.data.into(), data: value.data.into(),
reply_to: value.reply_to, reply_to: value.reply_to,
} }
@@ -334,511 +270,3 @@ impl From<v2::registration::RemainingBandwidthData> for v3::registration::Remain
} }
} }
} }
#[cfg(test)]
mod tests {
use std::{net::IpAddr, str::FromStr};
use nym_credentials_interface::CredentialSpendingData;
use nym_crypto::asymmetric::encryption::PrivateKey;
use nym_sphinx::addressing::Recipient;
use nym_wireguard_types::PeerPublicKey;
use x25519_dalek::PublicKey;
use super::*;
use crate::util::tests::{CREDENTIAL_BYTES, RECIPIENT};
#[test]
fn upgrade_initial_req() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) = v2::request::AuthenticatorRequest::new_initial_request(
v2::registration::InitMessage::new(pub_key),
reply_to,
);
let upgraded_msg = v3::request::AuthenticatorRequest::from(msg);
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v3::request::AuthenticatorRequestData::Initial(v3::registration::InitMessage {
pub_key
})
);
}
#[test]
fn downgrade_initial_req() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) = v3::request::AuthenticatorRequest::new_initial_request(
v3::registration::InitMessage::new(pub_key),
reply_to,
);
let downgraded_msg = v2::request::AuthenticatorRequest::try_from(msg).unwrap();
assert_eq!(
downgraded_msg.protocol,
Protocol {
version: 2,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
downgraded_msg.data,
v2::request::AuthenticatorRequestData::Initial(v2::registration::InitMessage {
pub_key
})
);
}
#[test]
fn upgrade_final_req() {
let mut rng = rand::thread_rng();
let local_secret = PrivateKey::new(&mut rng);
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
let private_ip = IpAddr::from_str("10.10.10.10").unwrap();
let nonce = 42;
let gateway_client = v2::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
private_ip,
nonce,
);
let credential = Some(CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap());
let final_message = v2::registration::FinalMessage {
gateway_client,
credential: credential.clone(),
};
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) =
v2::request::AuthenticatorRequest::new_final_request(final_message, reply_to);
let upgraded_msg = v3::request::AuthenticatorRequest::from(msg);
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v3::request::AuthenticatorRequestData::Final(Box::new(
v3::registration::FinalMessage {
gateway_client: v3::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
private_ip,
nonce,
),
credential
}
))
);
}
#[test]
fn downgrade_final_req() {
let mut rng = rand::thread_rng();
let local_secret = PrivateKey::new(&mut rng);
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
let private_ip = IpAddr::from_str("10.10.10.10").unwrap();
let nonce = 42;
let gateway_client = v3::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
private_ip,
nonce,
);
let credential = Some(CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap());
let final_message = v3::registration::FinalMessage {
gateway_client,
credential: credential.clone(),
};
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) =
v3::request::AuthenticatorRequest::new_final_request(final_message, reply_to);
let upgraded_msg = v2::request::AuthenticatorRequest::try_from(msg).unwrap();
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 2,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v2::request::AuthenticatorRequestData::Final(Box::new(
v2::registration::FinalMessage {
gateway_client: v2::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
private_ip,
nonce,
),
credential
}
))
);
}
#[test]
fn upgrade_query_req() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) = v2::request::AuthenticatorRequest::new_query_request(pub_key, reply_to);
let upgraded_msg = v3::request::AuthenticatorRequest::from(msg);
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v3::request::AuthenticatorRequestData::QueryBandwidth(pub_key)
);
}
#[test]
fn downgrade_query_req() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) = v3::request::AuthenticatorRequest::new_query_request(pub_key, reply_to);
let downgraded_msg = v2::request::AuthenticatorRequest::try_from(msg).unwrap();
assert_eq!(
downgraded_msg.protocol,
Protocol {
version: 2,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
downgraded_msg.data,
v2::request::AuthenticatorRequestData::QueryBandwidth(pub_key)
);
}
#[test]
fn downgrade_topup_req() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let credential = CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap();
let top_up_message = v3::topup::TopUpMessage {
pub_key,
credential,
};
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) =
v3::request::AuthenticatorRequest::new_topup_request(top_up_message, reply_to);
assert!(v2::request::AuthenticatorRequest::try_from(msg).is_err());
}
#[test]
fn upgrade_pending_reg_resp() {
let mut rng = rand::thread_rng();
let local_secret = PrivateKey::new(&mut rng);
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
let private_ip = IpAddr::from_str("10.10.10.10").unwrap();
let nonce = 42;
let wg_port = 51822;
let gateway_data = v2::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
private_ip,
nonce,
);
let registration_data = v2::registration::RegistrationData {
nonce,
gateway_data,
wg_port,
};
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v2::response::AuthenticatorResponse::new_pending_registration_success(
registration_data,
request_id,
reply_to,
);
let upgraded_msg = v3::response::AuthenticatorResponse::from(msg);
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v3::response::AuthenticatorResponseData::PendingRegistration(
v3::response::PendingRegistrationResponse {
request_id,
reply_to,
reply: v3::registration::RegistrationData {
nonce,
gateway_data: v3::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
private_ip,
nonce,
),
wg_port,
}
}
)
);
}
#[test]
fn downgrade_pending_reg_resp() {
let mut rng = rand::thread_rng();
let local_secret = PrivateKey::new(&mut rng);
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
let private_ip = IpAddr::from_str("10.10.10.10").unwrap();
let nonce = 42;
let wg_port = 51822;
let gateway_data = v3::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
private_ip,
nonce,
);
let registration_data = v3::registration::RegistrationData {
nonce,
gateway_data,
wg_port,
};
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v3::response::AuthenticatorResponse::new_pending_registration_success(
registration_data,
request_id,
reply_to,
);
let downgraded_msg = v2::response::AuthenticatorResponse::try_from(msg).unwrap();
assert_eq!(
downgraded_msg.protocol,
Protocol {
version: 2,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
downgraded_msg.data,
v2::response::AuthenticatorResponseData::PendingRegistration(
v2::response::PendingRegistrationResponse {
request_id,
reply_to,
reply: v2::registration::RegistrationData {
nonce,
gateway_data: v2::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
private_ip,
nonce,
),
wg_port,
}
}
)
);
}
#[test]
fn upgrade_registered_resp() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let private_ip = IpAddr::from_str("10.10.10.10").unwrap();
let wg_port = 51822;
let registred_data = v2::registration::RegistredData {
pub_key,
private_ip,
wg_port,
};
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v2::response::AuthenticatorResponse::new_registered(
registred_data,
reply_to,
request_id,
);
let upgraded_msg = v3::response::AuthenticatorResponse::from(msg);
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v3::response::AuthenticatorResponseData::Registered(v3::response::RegisteredResponse {
request_id,
reply_to,
reply: v3::registration::RegistredData {
wg_port,
pub_key,
private_ip
}
})
);
}
#[test]
fn downgrade_registered_resp() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let private_ip = IpAddr::from_str("10.10.10.10").unwrap();
let wg_port = 51822;
let registred_data = v3::registration::RegistredData {
pub_key,
private_ip,
wg_port,
};
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v3::response::AuthenticatorResponse::new_registered(
registred_data,
reply_to,
request_id,
);
let downgraded_msg = v2::response::AuthenticatorResponse::try_from(msg).unwrap();
assert_eq!(
downgraded_msg.protocol,
Protocol {
version: 2,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
downgraded_msg.data,
v2::response::AuthenticatorResponseData::Registered(v2::response::RegisteredResponse {
request_id,
reply_to,
reply: v2::registration::RegistredData {
wg_port,
pub_key,
private_ip
}
})
);
}
#[test]
fn upgrade_remaining_bandwidth_resp() {
let available_bandwidth = 42;
let remaining_bandwidth_data = Some(v2::registration::RemainingBandwidthData {
available_bandwidth,
});
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v2::response::AuthenticatorResponse::new_remaining_bandwidth(
remaining_bandwidth_data,
reply_to,
request_id,
);
let upgraded_msg = v3::response::AuthenticatorResponse::from(msg);
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v3::response::AuthenticatorResponseData::RemainingBandwidth(
v3::response::RemainingBandwidthResponse {
request_id,
reply_to,
reply: Some(v3::registration::RemainingBandwidthData {
available_bandwidth,
})
}
)
);
}
#[test]
fn downgrade_remaining_bandwidth_resp() {
let available_bandwidth = 42;
let remaining_bandwidth_data = Some(v3::registration::RemainingBandwidthData {
available_bandwidth,
});
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v3::response::AuthenticatorResponse::new_remaining_bandwidth(
remaining_bandwidth_data,
reply_to,
request_id,
);
let downgraded_msg = v2::response::AuthenticatorResponse::try_from(msg).unwrap();
assert_eq!(
downgraded_msg.protocol,
Protocol {
version: 2,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
downgraded_msg.data,
v2::response::AuthenticatorResponseData::RemainingBandwidth(
v2::response::RemainingBandwidthResponse {
request_id,
reply_to,
reply: Some(v2::registration::RemainingBandwidthData {
available_bandwidth,
})
}
)
);
}
#[test]
fn downgrade_topup_resp() {
let available_bandwidth = 42;
let remaining_bandwidth_data = v3::registration::RemainingBandwidthData {
available_bandwidth,
};
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v3::response::AuthenticatorResponse::new_topup_bandwidth(
remaining_bandwidth_data,
reply_to,
request_id,
);
assert!(v2::response::AuthenticatorResponse::try_from(msg).is_err());
}
}
@@ -29,7 +29,7 @@ pub type Taken = Option<SystemTime>;
pub const BANDWIDTH_CAP_PER_DAY: u64 = 250 * 1024 * 1024 * 1024; // 250 GB pub const BANDWIDTH_CAP_PER_DAY: u64 = 250 * 1024 * 1024 * 1024; // 250 GB
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct InitMessage { pub struct InitMessage {
/// Base64 encoded x25519 public key /// Base64 encoded x25519 public key
pub pub_key: PeerPublicKey, pub pub_key: PeerPublicKey,
@@ -41,7 +41,7 @@ impl InitMessage {
} }
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct FinalMessage { pub struct FinalMessage {
/// Gateway client data /// Gateway client data
pub gateway_client: GatewayClient, pub gateway_client: GatewayClient,
@@ -50,28 +50,28 @@ pub struct FinalMessage {
pub credential: Option<CredentialSpendingData>, pub credential: Option<CredentialSpendingData>,
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RegistrationData { pub struct RegistrationData {
pub nonce: u64, pub nonce: u64,
pub gateway_data: GatewayClient, pub gateway_data: GatewayClient,
pub wg_port: u16, pub wg_port: u16,
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RegistredData { pub struct RegistredData {
pub pub_key: PeerPublicKey, pub pub_key: PeerPublicKey,
pub private_ip: IpAddr, pub private_ip: IpAddr,
pub wg_port: u16, pub wg_port: u16,
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RemainingBandwidthData { pub struct RemainingBandwidthData {
pub available_bandwidth: i64, pub available_bandwidth: i64,
} }
/// Client that wants to register sends its PublicKey bytes mac digest encrypted with a DH shared secret. /// Client that wants to register sends its PublicKey bytes mac digest encrypted with a DH shared secret.
/// Gateway/Nym node can then verify pub_key payload using the same process /// Gateway/Nym node can then verify pub_key payload using the same process
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct GatewayClient { pub struct GatewayClient {
/// Base64 encoded x25519 public key /// Base64 encoded x25519 public key
pub pub_key: PeerPublicKey, pub pub_key: PeerPublicKey,
@@ -147,7 +147,7 @@ impl GatewayClient {
// TODO: change the inner type into generic array of size HmacSha256::OutputSize // TODO: change the inner type into generic array of size HmacSha256::OutputSize
// TODO2: rely on our internal crypto/hmac // TODO2: rely on our internal crypto/hmac
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone)]
pub struct ClientMac(Vec<u8>); pub struct ClientMac(Vec<u8>);
impl fmt::Display for ClientMac { impl fmt::Display for ClientMac {
@@ -106,7 +106,7 @@ impl AuthenticatorRequest {
} }
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum AuthenticatorRequestData { pub enum AuthenticatorRequestData {
Initial(InitMessage), Initial(InitMessage),
Final(Box<FinalMessage>), Final(Box<FinalMessage>),
@@ -120,7 +120,7 @@ impl AuthenticatorResponse {
} }
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum AuthenticatorResponseData { pub enum AuthenticatorResponseData {
PendingRegistration(PendingRegistrationResponse), PendingRegistration(PendingRegistrationResponse),
Registered(RegisteredResponse), Registered(RegisteredResponse),
@@ -128,28 +128,28 @@ pub enum AuthenticatorResponseData {
TopUpBandwidth(TopUpBandwidthResponse), TopUpBandwidth(TopUpBandwidthResponse),
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PendingRegistrationResponse { pub struct PendingRegistrationResponse {
pub request_id: u64, pub request_id: u64,
pub reply_to: Recipient, pub reply_to: Recipient,
pub reply: RegistrationData, pub reply: RegistrationData,
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RegisteredResponse { pub struct RegisteredResponse {
pub request_id: u64, pub request_id: u64,
pub reply_to: Recipient, pub reply_to: Recipient,
pub reply: RegistredData, pub reply: RegistredData,
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RemainingBandwidthResponse { pub struct RemainingBandwidthResponse {
pub request_id: u64, pub request_id: u64,
pub reply_to: Recipient, pub reply_to: Recipient,
pub reply: Option<RemainingBandwidthData>, pub reply: Option<RemainingBandwidthData>,
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct TopUpBandwidthResponse { pub struct TopUpBandwidthResponse {
pub request_id: u64, pub request_id: u64,
pub reply_to: Recipient, pub reply_to: Recipient,
@@ -5,7 +5,7 @@ use nym_credentials_interface::CredentialSpendingData;
use nym_wireguard_types::PeerPublicKey; use nym_wireguard_types::PeerPublicKey;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TopUpMessage { pub struct TopUpMessage {
/// Base64 encoded x25519 public key /// Base64 encoded x25519 public key
pub pub_key: PeerPublicKey, pub pub_key: PeerPublicKey,
+104 -609
View File
@@ -3,82 +3,37 @@
use nym_service_provider_requests_common::{Protocol, ServiceProviderType}; use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
use crate::{v3, v4}; use crate::{v2, v3, v4};
impl TryFrom<v3::request::AuthenticatorRequest> for v4::request::AuthenticatorRequest { impl From<v3::request::AuthenticatorRequest> for v4::request::AuthenticatorRequest {
type Error = crate::Error; fn from(authenticator_request: v3::request::AuthenticatorRequest) -> Self {
fn try_from( Self {
authenticator_request: v3::request::AuthenticatorRequest,
) -> Result<Self, Self::Error> {
Ok(Self {
protocol: Protocol { protocol: Protocol {
version: 4, version: 4,
service_provider_type: ServiceProviderType::Authenticator, service_provider_type: ServiceProviderType::Authenticator,
}, },
data: authenticator_request.data.try_into()?, data: authenticator_request.data.into(),
reply_to: authenticator_request.reply_to, reply_to: authenticator_request.reply_to,
request_id: authenticator_request.request_id, request_id: authenticator_request.request_id,
})
}
}
impl TryFrom<v4::request::AuthenticatorRequest> for v3::request::AuthenticatorRequest {
type Error = crate::Error;
fn try_from(
authenticator_request: v4::request::AuthenticatorRequest,
) -> Result<Self, Self::Error> {
Ok(Self {
protocol: Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator,
},
data: authenticator_request.data.try_into()?,
reply_to: authenticator_request.reply_to,
request_id: authenticator_request.request_id,
})
}
}
impl TryFrom<v3::request::AuthenticatorRequestData> for v4::request::AuthenticatorRequestData {
type Error = crate::Error;
fn try_from(
authenticator_request_data: v3::request::AuthenticatorRequestData,
) -> Result<Self, Self::Error> {
match authenticator_request_data {
v3::request::AuthenticatorRequestData::Initial(init_msg) => Ok(
v4::request::AuthenticatorRequestData::Initial(init_msg.into()),
),
v3::request::AuthenticatorRequestData::Final(_) => Err(Self::Error::Conversion(
"mac hash breaking change".to_string(),
)),
v3::request::AuthenticatorRequestData::QueryBandwidth(pub_key) => Ok(
v4::request::AuthenticatorRequestData::QueryBandwidth(pub_key),
),
v3::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => Ok(
v4::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message.into()),
),
} }
} }
} }
impl TryFrom<v4::request::AuthenticatorRequestData> for v3::request::AuthenticatorRequestData { impl From<v3::request::AuthenticatorRequestData> for v4::request::AuthenticatorRequestData {
type Error = crate::Error; fn from(authenticator_request_data: v3::request::AuthenticatorRequestData) -> Self {
fn try_from(
authenticator_request_data: v4::request::AuthenticatorRequestData,
) -> Result<Self, Self::Error> {
match authenticator_request_data { match authenticator_request_data {
v4::request::AuthenticatorRequestData::Initial(init_msg) => Ok( v3::request::AuthenticatorRequestData::Initial(init_msg) => {
v3::request::AuthenticatorRequestData::Initial(init_msg.into()), v4::request::AuthenticatorRequestData::Initial(init_msg.into())
), }
v4::request::AuthenticatorRequestData::Final(_) => Err(Self::Error::Conversion( v3::request::AuthenticatorRequestData::Final(gw_client) => {
"mac hash breaking change".to_string(), v4::request::AuthenticatorRequestData::Final(gw_client.into())
)), }
v4::request::AuthenticatorRequestData::QueryBandwidth(pub_key) => Ok( v3::request::AuthenticatorRequestData::QueryBandwidth(pub_key) => {
v3::request::AuthenticatorRequestData::QueryBandwidth(pub_key), v4::request::AuthenticatorRequestData::QueryBandwidth(pub_key)
), }
v4::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => Ok( v3::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => {
v3::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message.into()), v4::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message.into())
), }
} }
} }
} }
@@ -91,11 +46,12 @@ impl From<v3::registration::InitMessage> for v4::registration::InitMessage {
} }
} }
impl From<v4::registration::InitMessage> for v3::registration::InitMessage { impl From<Box<v3::registration::FinalMessage>> for Box<v4::registration::FinalMessage> {
fn from(init_msg: v4::registration::InitMessage) -> Self { fn from(gw_client: Box<v3::registration::FinalMessage>) -> Self {
Self { Box::new(v4::registration::FinalMessage {
pub_key: init_msg.pub_key, gateway_client: gw_client.gateway_client.into(),
} credential: gw_client.credential,
})
} }
} }
@@ -108,26 +64,67 @@ impl From<Box<v3::topup::TopUpMessage>> for Box<v4::topup::TopUpMessage> {
} }
} }
impl From<Box<v4::topup::TopUpMessage>> for Box<v3::topup::TopUpMessage> { impl From<v2::registration::GatewayClient> for v4::registration::GatewayClient {
fn from(top_up_message: Box<v4::topup::TopUpMessage>) -> Self { fn from(gw_client: v2::registration::GatewayClient) -> Self {
Box::new(v3::topup::TopUpMessage { Self {
pub_key: top_up_message.pub_key, pub_key: gw_client.pub_key,
credential: top_up_message.credential, private_ips: gw_client.private_ip.into(),
}) mac: gw_client.mac.into(),
}
} }
} }
impl TryFrom<v3::response::AuthenticatorResponse> for v4::response::AuthenticatorResponse { impl From<v3::registration::GatewayClient> for v4::registration::GatewayClient {
type Error = crate::Error; fn from(gw_client: v3::registration::GatewayClient) -> Self {
fn try_from(value: v3::response::AuthenticatorResponse) -> Result<Self, Self::Error> { Self {
Ok(Self { pub_key: gw_client.pub_key,
protocol: Protocol { private_ips: gw_client.private_ip.into(),
version: 4, mac: gw_client.mac.into(),
service_provider_type: value.protocol.service_provider_type, }
}, }
data: value.data.try_into()?, }
reply_to: value.reply_to,
}) impl From<v4::registration::GatewayClient> for v3::registration::GatewayClient {
fn from(gw_client: v4::registration::GatewayClient) -> Self {
Self {
pub_key: gw_client.pub_key,
private_ip: gw_client.private_ips.ipv4.into(),
mac: gw_client.mac.into(),
}
}
}
impl From<v4::registration::GatewayClient> for v2::registration::GatewayClient {
fn from(gw_client: v4::registration::GatewayClient) -> Self {
Self {
pub_key: gw_client.pub_key,
private_ip: gw_client.private_ips.ipv4.into(),
mac: gw_client.mac.into(),
}
}
}
impl From<v2::registration::ClientMac> for v4::registration::ClientMac {
fn from(mac: v2::registration::ClientMac) -> Self {
Self::new(mac.to_vec())
}
}
impl From<v3::registration::ClientMac> for v4::registration::ClientMac {
fn from(mac: v3::registration::ClientMac) -> Self {
Self::new(mac.to_vec())
}
}
impl From<v4::registration::ClientMac> for v3::registration::ClientMac {
fn from(mac: v4::registration::ClientMac) -> Self {
Self::new(mac.to_vec())
}
}
impl From<v4::registration::ClientMac> for v2::registration::ClientMac {
fn from(mac: v4::registration::ClientMac) -> Self {
Self::new(mac.to_vec())
} }
} }
@@ -140,40 +137,11 @@ impl TryFrom<v4::response::AuthenticatorResponse> for v3::response::Authenticato
Ok(Self { Ok(Self {
data: authenticator_response.data.try_into()?, data: authenticator_response.data.try_into()?,
reply_to: authenticator_response.reply_to, reply_to: authenticator_response.reply_to,
protocol: Protocol { protocol: authenticator_response.protocol,
version: 3,
service_provider_type: authenticator_response.protocol.service_provider_type,
},
}) })
} }
} }
impl TryFrom<v3::response::AuthenticatorResponseData> for v4::response::AuthenticatorResponseData {
type Error = crate::Error;
fn try_from(
authenticator_response_data: v3::response::AuthenticatorResponseData,
) -> Result<Self, Self::Error> {
match authenticator_response_data {
v3::response::AuthenticatorResponseData::PendingRegistration(_) => Err(
Self::Error::Conversion("mac hash breaking change".to_string()),
),
v3::response::AuthenticatorResponseData::Registered(registered_response) => Ok(
v4::response::AuthenticatorResponseData::Registered(registered_response.into()),
),
v3::response::AuthenticatorResponseData::RemainingBandwidth(
remaining_bandwidth_response,
) => Ok(v4::response::AuthenticatorResponseData::RemainingBandwidth(
remaining_bandwidth_response.into(),
)),
v3::response::AuthenticatorResponseData::TopUpBandwidth(top_up_response) => Ok(
v4::response::AuthenticatorResponseData::TopUpBandwidth(top_up_response.into()),
),
}
}
}
impl TryFrom<v4::response::AuthenticatorResponseData> for v3::response::AuthenticatorResponseData { impl TryFrom<v4::response::AuthenticatorResponseData> for v3::response::AuthenticatorResponseData {
type Error = crate::Error; type Error = crate::Error;
@@ -181,10 +149,13 @@ impl TryFrom<v4::response::AuthenticatorResponseData> for v3::response::Authenti
authenticator_response_data: v4::response::AuthenticatorResponseData, authenticator_response_data: v4::response::AuthenticatorResponseData,
) -> Result<Self, Self::Error> { ) -> Result<Self, Self::Error> {
match authenticator_response_data { match authenticator_response_data {
v4::response::AuthenticatorResponseData::PendingRegistration(_) => Err( v4::response::AuthenticatorResponseData::PendingRegistration(
Self::Error::Conversion("mac hash breaking change".to_string()), pending_registration_response,
) => Ok(
v3::response::AuthenticatorResponseData::PendingRegistration(
pending_registration_response.into(),
),
), ),
v4::response::AuthenticatorResponseData::Registered(registered_response) => Ok( v4::response::AuthenticatorResponseData::Registered(registered_response) => Ok(
v3::response::AuthenticatorResponseData::Registered(registered_response.into()), v3::response::AuthenticatorResponseData::Registered(registered_response.into()),
), ),
@@ -202,6 +173,16 @@ impl TryFrom<v4::response::AuthenticatorResponseData> for v3::response::Authenti
} }
} }
impl From<v4::response::PendingRegistrationResponse> for v3::response::PendingRegistrationResponse {
fn from(value: v4::response::PendingRegistrationResponse) -> Self {
Self {
request_id: value.request_id,
reply_to: value.reply_to,
reply: value.reply.into(),
}
}
}
impl From<v4::response::RegisteredResponse> for v3::response::RegisteredResponse { impl From<v4::response::RegisteredResponse> for v3::response::RegisteredResponse {
fn from(value: v4::response::RegisteredResponse) -> Self { fn from(value: v4::response::RegisteredResponse) -> Self {
Self { Self {
@@ -212,26 +193,6 @@ impl From<v4::response::RegisteredResponse> for v3::response::RegisteredResponse
} }
} }
impl From<v3::response::RegisteredResponse> for v4::response::RegisteredResponse {
fn from(value: v3::response::RegisteredResponse) -> Self {
Self {
request_id: value.request_id,
reply_to: value.reply_to,
reply: value.reply.into(),
}
}
}
impl From<v3::response::RemainingBandwidthResponse> for v4::response::RemainingBandwidthResponse {
fn from(value: v3::response::RemainingBandwidthResponse) -> Self {
Self {
request_id: value.request_id,
reply_to: value.reply_to,
reply: value.reply.map(Into::into),
}
}
}
impl From<v4::response::RemainingBandwidthResponse> for v3::response::RemainingBandwidthResponse { impl From<v4::response::RemainingBandwidthResponse> for v3::response::RemainingBandwidthResponse {
fn from(value: v4::response::RemainingBandwidthResponse) -> Self { fn from(value: v4::response::RemainingBandwidthResponse) -> Self {
Self { Self {
@@ -242,31 +203,11 @@ impl From<v4::response::RemainingBandwidthResponse> for v3::response::RemainingB
} }
} }
impl From<v3::response::TopUpBandwidthResponse> for v4::response::TopUpBandwidthResponse { impl From<v4::registration::RegistrationData> for v3::registration::RegistrationData {
fn from(value: v3::response::TopUpBandwidthResponse) -> Self { fn from(value: v4::registration::RegistrationData) -> Self {
Self { Self {
request_id: value.request_id, nonce: value.nonce,
reply_to: value.reply_to, gateway_data: value.gateway_data.into(),
reply: value.reply.into(),
}
}
}
impl From<v4::response::TopUpBandwidthResponse> for v3::response::TopUpBandwidthResponse {
fn from(value: v4::response::TopUpBandwidthResponse) -> Self {
Self {
request_id: value.request_id,
reply_to: value.reply_to,
reply: value.reply.into(),
}
}
}
impl From<v3::registration::RegistredData> for v4::registration::RegistredData {
fn from(value: v3::registration::RegistredData) -> Self {
Self {
pub_key: value.pub_key,
private_ips: value.private_ip.into(),
wg_port: value.wg_port, wg_port: value.wg_port,
} }
} }
@@ -282,14 +223,6 @@ impl From<v4::registration::RegistredData> for v3::registration::RegistredData {
} }
} }
impl From<v3::registration::RemainingBandwidthData> for v4::registration::RemainingBandwidthData {
fn from(value: v3::registration::RemainingBandwidthData) -> Self {
Self {
available_bandwidth: value.available_bandwidth,
}
}
}
impl From<v4::registration::RemainingBandwidthData> for v3::registration::RemainingBandwidthData { impl From<v4::registration::RemainingBandwidthData> for v3::registration::RemainingBandwidthData {
fn from(value: v4::registration::RemainingBandwidthData) -> Self { fn from(value: v4::registration::RemainingBandwidthData) -> Self {
Self { Self {
@@ -297,441 +230,3 @@ impl From<v4::registration::RemainingBandwidthData> for v3::registration::Remain
} }
} }
} }
#[cfg(test)]
mod tests {
use std::{
net::{Ipv4Addr, Ipv6Addr},
str::FromStr,
};
use nym_credentials_interface::CredentialSpendingData;
use nym_crypto::asymmetric::encryption::PrivateKey;
use nym_sphinx::addressing::Recipient;
use nym_wireguard_types::PeerPublicKey;
use x25519_dalek::PublicKey;
use super::*;
use crate::util::tests::{CREDENTIAL_BYTES, RECIPIENT};
#[test]
fn upgrade_initial_req() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) = v3::request::AuthenticatorRequest::new_initial_request(
v3::registration::InitMessage::new(pub_key),
reply_to,
);
let upgraded_msg = v4::request::AuthenticatorRequest::try_from(msg).unwrap();
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 4,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v4::request::AuthenticatorRequestData::Initial(v4::registration::InitMessage {
pub_key
})
);
}
#[test]
fn downgrade_initial_req() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) = v4::request::AuthenticatorRequest::new_initial_request(
v4::registration::InitMessage::new(pub_key),
reply_to,
);
let downgraded_msg = v3::request::AuthenticatorRequest::try_from(msg).unwrap();
assert_eq!(
downgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
downgraded_msg.data,
v3::request::AuthenticatorRequestData::Initial(v3::registration::InitMessage {
pub_key
})
);
}
#[test]
fn upgrade_final_req() {
let mut rng = rand::thread_rng();
let local_secret = PrivateKey::new(&mut rng);
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap();
let nonce = 42;
let gateway_client = v3::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
ipv4.into(),
nonce,
);
let credential = Some(CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap());
let final_message = v3::registration::FinalMessage {
gateway_client,
credential: credential.clone(),
};
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) =
v3::request::AuthenticatorRequest::new_final_request(final_message, reply_to);
assert!(v4::request::AuthenticatorRequest::try_from(msg).is_err());
}
#[test]
fn downgrade_final_req() {
let mut rng = rand::thread_rng();
let local_secret = PrivateKey::new(&mut rng);
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap();
let private_ips =
v4::registration::IpPair::new(ipv4, Ipv6Addr::from_str("fc01::10").unwrap());
let nonce = 42;
let gateway_client = v4::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
private_ips,
nonce,
);
let credential = Some(CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap());
let final_message = v4::registration::FinalMessage {
gateway_client,
credential: credential.clone(),
};
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) =
v4::request::AuthenticatorRequest::new_final_request(final_message, reply_to);
assert!(v3::request::AuthenticatorRequest::try_from(msg).is_err());
}
#[test]
fn upgrade_query_req() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) = v3::request::AuthenticatorRequest::new_query_request(pub_key, reply_to);
let upgraded_msg = v4::request::AuthenticatorRequest::try_from(msg).unwrap();
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 4,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v4::request::AuthenticatorRequestData::QueryBandwidth(pub_key)
);
}
#[test]
fn downgrade_query_req() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) = v4::request::AuthenticatorRequest::new_query_request(pub_key, reply_to);
let downgraded_msg = v3::request::AuthenticatorRequest::try_from(msg).unwrap();
assert_eq!(
downgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
downgraded_msg.data,
v3::request::AuthenticatorRequestData::QueryBandwidth(pub_key)
);
}
#[test]
fn downgrade_topup_req() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let credential = CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap();
let top_up_message = v4::topup::TopUpMessage {
pub_key,
credential: credential.clone(),
};
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) =
v4::request::AuthenticatorRequest::new_topup_request(top_up_message, reply_to);
let downgraded_msg = v3::request::AuthenticatorRequest::try_from(msg).unwrap();
assert_eq!(
downgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
downgraded_msg.data,
v3::request::AuthenticatorRequestData::TopUpBandwidth(Box::new(
v3::topup::TopUpMessage {
pub_key,
credential
}
))
);
}
#[test]
fn upgrade_pending_reg_resp() {
let mut rng = rand::thread_rng();
let local_secret = PrivateKey::new(&mut rng);
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap();
let nonce = 42;
let wg_port = 51822;
let gateway_data = v3::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
ipv4.into(),
nonce,
);
let registration_data = v3::registration::RegistrationData {
nonce,
gateway_data,
wg_port,
};
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v3::response::AuthenticatorResponse::new_pending_registration_success(
registration_data,
request_id,
reply_to,
);
assert!(v4::response::AuthenticatorResponse::try_from(msg).is_err());
}
#[test]
fn downgrade_pending_reg_resp() {
let mut rng = rand::thread_rng();
let local_secret = PrivateKey::new(&mut rng);
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap();
let private_ips =
v4::registration::IpPair::new(ipv4, Ipv6Addr::from_str("fc01::10").unwrap());
let nonce = 42;
let wg_port = 51822;
let gateway_data = v4::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
private_ips,
nonce,
);
let registration_data = v4::registration::RegistrationData {
nonce,
gateway_data,
wg_port,
};
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v4::response::AuthenticatorResponse::new_pending_registration_success(
registration_data,
request_id,
reply_to,
);
assert!(v3::response::AuthenticatorResponse::try_from(msg).is_err());
}
#[test]
fn upgrade_registered_resp() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let ipv4 = Ipv4Addr::from_str("10.1.10.10").unwrap();
let private_ips =
v4::registration::IpPair::new(ipv4, Ipv6Addr::from_str("fc01::a0a").unwrap());
let wg_port = 51822;
let registred_data = v3::registration::RegistredData {
pub_key,
private_ip: ipv4.into(),
wg_port,
};
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v3::response::AuthenticatorResponse::new_registered(
registred_data,
reply_to,
request_id,
);
let upgraded_msg = v4::response::AuthenticatorResponse::try_from(msg).unwrap();
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 4,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v4::response::AuthenticatorResponseData::Registered(v4::response::RegisteredResponse {
request_id,
reply_to,
reply: v4::registration::RegistredData {
wg_port,
pub_key,
private_ips
}
})
);
}
#[test]
fn downgrade_registered_resp() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap();
let private_ips =
v4::registration::IpPair::new(ipv4, Ipv6Addr::from_str("fc01::10").unwrap());
let wg_port = 51822;
let registred_data = v4::registration::RegistredData {
pub_key,
private_ips,
wg_port,
};
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v4::response::AuthenticatorResponse::new_registered(
registred_data,
reply_to,
request_id,
);
let downgraded_msg = v3::response::AuthenticatorResponse::try_from(msg).unwrap();
assert_eq!(
downgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
downgraded_msg.data,
v3::response::AuthenticatorResponseData::Registered(v3::response::RegisteredResponse {
request_id,
reply_to,
reply: v3::registration::RegistredData {
wg_port,
pub_key,
private_ip: ipv4.into()
}
})
);
}
#[test]
fn upgrade_remaining_bandwidth_resp() {
let available_bandwidth = 42;
let remaining_bandwidth_data = Some(v3::registration::RemainingBandwidthData {
available_bandwidth,
});
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v3::response::AuthenticatorResponse::new_remaining_bandwidth(
remaining_bandwidth_data,
reply_to,
request_id,
);
let upgraded_msg = v4::response::AuthenticatorResponse::try_from(msg).unwrap();
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 4,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v4::response::AuthenticatorResponseData::RemainingBandwidth(
v4::response::RemainingBandwidthResponse {
request_id,
reply_to,
reply: Some(v4::registration::RemainingBandwidthData {
available_bandwidth,
})
}
)
);
}
#[test]
fn downgrade_remaining_bandwidth_resp() {
let available_bandwidth = 42;
let remaining_bandwidth_data = Some(v4::registration::RemainingBandwidthData {
available_bandwidth,
});
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v4::response::AuthenticatorResponse::new_remaining_bandwidth(
remaining_bandwidth_data,
reply_to,
request_id,
);
let downgraded_msg = v3::response::AuthenticatorResponse::try_from(msg).unwrap();
assert_eq!(
downgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
downgraded_msg.data,
v3::response::AuthenticatorResponseData::RemainingBandwidth(
v3::response::RemainingBandwidthResponse {
request_id,
reply_to,
reply: Some(v3::registration::RemainingBandwidthData {
available_bandwidth,
})
}
)
);
}
#[test]
fn downgrade_topup_resp() {
let available_bandwidth = 42;
let remaining_bandwidth_data = v4::registration::RemainingBandwidthData {
available_bandwidth,
};
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v4::response::AuthenticatorResponse::new_topup_bandwidth(
remaining_bandwidth_data,
reply_to,
request_id,
);
assert!(v3::response::AuthenticatorResponse::try_from(msg).is_err());
}
}
@@ -28,7 +28,7 @@ pub type HmacSha256 = Hmac<Sha256>;
pub type Nonce = u64; pub type Nonce = u64;
pub type Taken = Option<SystemTime>; pub type Taken = Option<SystemTime>;
pub const BANDWIDTH_CAP_PER_DAY: u64 = 250 * 1024 * 1024 * 1024; // 250 GB pub const BANDWIDTH_CAP_PER_DAY: u64 = 1024 * 1024 * 1024; // 1 GB
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct IpPair { pub struct IpPair {
@@ -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],
@@ -81,7 +81,7 @@ impl From<IpAddr> for IpPair {
} }
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct InitMessage { pub struct InitMessage {
/// Base64 encoded x25519 public key /// Base64 encoded x25519 public key
pub pub_key: PeerPublicKey, pub pub_key: PeerPublicKey,
@@ -93,7 +93,7 @@ impl InitMessage {
} }
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct FinalMessage { pub struct FinalMessage {
/// Gateway client data /// Gateway client data
pub gateway_client: GatewayClient, pub gateway_client: GatewayClient,
@@ -102,28 +102,28 @@ pub struct FinalMessage {
pub credential: Option<CredentialSpendingData>, pub credential: Option<CredentialSpendingData>,
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RegistrationData { pub struct RegistrationData {
pub nonce: u64, pub nonce: u64,
pub gateway_data: GatewayClient, pub gateway_data: GatewayClient,
pub wg_port: u16, pub wg_port: u16,
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RegistredData { pub struct RegistredData {
pub pub_key: PeerPublicKey, pub pub_key: PeerPublicKey,
pub private_ips: IpPair, pub private_ips: IpPair,
pub wg_port: u16, pub wg_port: u16,
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RemainingBandwidthData { pub struct RemainingBandwidthData {
pub available_bandwidth: i64, pub available_bandwidth: i64,
} }
/// Client that wants to register sends its PublicKey bytes mac digest encrypted with a DH shared secret. /// Client that wants to register sends its PublicKey bytes mac digest encrypted with a DH shared secret.
/// Gateway/Nym node can then verify pub_key payload using the same process /// Gateway/Nym node can then verify pub_key payload using the same process
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct GatewayClient { pub struct GatewayClient {
/// Base64 encoded x25519 public key /// Base64 encoded x25519 public key
pub pub_key: PeerPublicKey, pub pub_key: PeerPublicKey,
@@ -199,7 +199,7 @@ impl GatewayClient {
// TODO: change the inner type into generic array of size HmacSha256::OutputSize // TODO: change the inner type into generic array of size HmacSha256::OutputSize
// TODO2: rely on our internal crypto/hmac // TODO2: rely on our internal crypto/hmac
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone)]
pub struct ClientMac(Vec<u8>); pub struct ClientMac(Vec<u8>);
impl fmt::Display for ClientMac { impl fmt::Display for ClientMac {
@@ -20,7 +20,7 @@ fn generate_random() -> u64 {
rng.next_u64() rng.next_u64()
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AuthenticatorRequest { pub struct AuthenticatorRequest {
pub protocol: Protocol, pub protocol: Protocol,
pub data: AuthenticatorRequestData, pub data: AuthenticatorRequestData,
@@ -106,7 +106,7 @@ impl AuthenticatorRequest {
} }
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum AuthenticatorRequestData { pub enum AuthenticatorRequestData {
Initial(InitMessage), Initial(InitMessage),
Final(Box<FinalMessage>), Final(Box<FinalMessage>),
@@ -10,7 +10,7 @@ use crate::make_bincode_serializer;
use super::VERSION; use super::VERSION;
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AuthenticatorResponse { pub struct AuthenticatorResponse {
pub protocol: Protocol, pub protocol: Protocol,
pub data: AuthenticatorResponseData, pub data: AuthenticatorResponseData,
@@ -120,7 +120,7 @@ impl AuthenticatorResponse {
} }
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum AuthenticatorResponseData { pub enum AuthenticatorResponseData {
PendingRegistration(PendingRegistrationResponse), PendingRegistration(PendingRegistrationResponse),
Registered(RegisteredResponse), Registered(RegisteredResponse),
@@ -128,28 +128,28 @@ pub enum AuthenticatorResponseData {
TopUpBandwidth(TopUpBandwidthResponse), TopUpBandwidth(TopUpBandwidthResponse),
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PendingRegistrationResponse { pub struct PendingRegistrationResponse {
pub request_id: u64, pub request_id: u64,
pub reply_to: Recipient, pub reply_to: Recipient,
pub reply: RegistrationData, pub reply: RegistrationData,
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RegisteredResponse { pub struct RegisteredResponse {
pub request_id: u64, pub request_id: u64,
pub reply_to: Recipient, pub reply_to: Recipient,
pub reply: RegistredData, pub reply: RegistredData,
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RemainingBandwidthResponse { pub struct RemainingBandwidthResponse {
pub request_id: u64, pub request_id: u64,
pub reply_to: Recipient, pub reply_to: Recipient,
pub reply: Option<RemainingBandwidthData>, pub reply: Option<RemainingBandwidthData>,
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct TopUpBandwidthResponse { pub struct TopUpBandwidthResponse {
pub request_id: u64, pub request_id: u64,
pub reply_to: Recipient, pub reply_to: Recipient,
@@ -5,7 +5,7 @@ use nym_credentials_interface::CredentialSpendingData;
use nym_wireguard_types::PeerPublicKey; use nym_wireguard_types::PeerPublicKey;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TopUpMessage { pub struct TopUpMessage {
/// Base64 encoded x25519 public key /// Base64 encoded x25519 public key
pub pub_key: PeerPublicKey, pub pub_key: PeerPublicKey,
@@ -17,7 +17,7 @@ use nym_validator_client::coconut::all_ecash_api_clients;
use nym_validator_client::nym_api::EpochId; use nym_validator_client::nym_api::EpochId;
use nym_validator_client::nyxd::contract_traits::EcashSigningClient; use nym_validator_client::nyxd::contract_traits::EcashSigningClient;
use nym_validator_client::nyxd::contract_traits::{DkgQueryClient, EcashQueryClient}; use nym_validator_client::nyxd::contract_traits::{DkgQueryClient, EcashQueryClient};
use nym_validator_client::nyxd::cosmwasm_client::ContractResponseData; use nym_validator_client::nyxd::cosmwasm_client::ToSingletonContractData;
use nym_validator_client::EcashApiClient; use nym_validator_client::EcashApiClient;
use rand::rngs::OsRng; use rand::rngs::OsRng;
+7 -4
View File
@@ -105,24 +105,26 @@ impl<C, St: Storage> BandwidthController<C, St> {
async fn get_aggregate_verification_key( async fn get_aggregate_verification_key(
&self, &self,
epoch_id: EpochId, epoch_id: EpochId,
ecash_apis: &mut ApiClientsWrapper<'_, C>, apis: &mut ApiClientsWrapper,
) -> Result<VerificationKeyAuth, BandwidthControllerError> ) -> Result<VerificationKeyAuth, BandwidthControllerError>
where where
C: DkgQueryClient + Sync + Send, C: DkgQueryClient + Sync + Send,
<St as Storage>::StorageError: Send + Sync + 'static, <St as Storage>::StorageError: Send + Sync + 'static,
{ {
let ecash_apis = apis.get_or_init(epoch_id, &self.client).await?;
get_aggregate_verification_key(&self.storage, epoch_id, ecash_apis).await get_aggregate_verification_key(&self.storage, epoch_id, ecash_apis).await
} }
async fn get_coin_index_signatures( async fn get_coin_index_signatures(
&self, &self,
epoch_id: EpochId, epoch_id: EpochId,
ecash_apis: &mut ApiClientsWrapper<'_, C>, apis: &mut ApiClientsWrapper,
) -> Result<Vec<AnnotatedCoinIndexSignature>, BandwidthControllerError> ) -> Result<Vec<AnnotatedCoinIndexSignature>, BandwidthControllerError>
where where
C: DkgQueryClient + Sync + Send, C: DkgQueryClient + Sync + Send,
<St as Storage>::StorageError: Send + Sync + 'static, <St as Storage>::StorageError: Send + Sync + 'static,
{ {
let ecash_apis = apis.get_or_init(epoch_id, &self.client).await?;
get_coin_index_signatures(&self.storage, epoch_id, ecash_apis).await get_coin_index_signatures(&self.storage, epoch_id, ecash_apis).await
} }
@@ -130,12 +132,13 @@ impl<C, St: Storage> BandwidthController<C, St> {
&self, &self,
epoch_id: EpochId, epoch_id: EpochId,
expiration_date: Date, expiration_date: Date,
ecash_apis: &mut ApiClientsWrapper<'_, C>, apis: &mut ApiClientsWrapper,
) -> Result<Vec<AnnotatedExpirationDateSignature>, BandwidthControllerError> ) -> Result<Vec<AnnotatedExpirationDateSignature>, BandwidthControllerError>
where where
C: DkgQueryClient + Sync + Send, C: DkgQueryClient + Sync + Send,
<St as Storage>::StorageError: Send + Sync + 'static, <St as Storage>::StorageError: Send + Sync + 'static,
{ {
let ecash_apis = apis.get_or_init(epoch_id, &self.client).await?;
get_expiration_date_signatures(&self.storage, epoch_id, expiration_date, ecash_apis).await get_expiration_date_signatures(&self.storage, epoch_id, expiration_date, ecash_apis).await
} }
@@ -151,7 +154,7 @@ impl<C, St: Storage> BandwidthController<C, St> {
{ {
let epoch_id = retrieved_ticketbook.ticketbook.epoch_id(); let epoch_id = retrieved_ticketbook.ticketbook.epoch_id();
let expiration_date = retrieved_ticketbook.ticketbook.expiration_date(); let expiration_date = retrieved_ticketbook.ticketbook.expiration_date();
let mut api_clients = ApiClientsWrapper::new(&self.client, epoch_id); let mut api_clients = Default::default();
let verification_key = self let verification_key = self
.get_aggregate_verification_key(epoch_id, &mut api_clients) .get_aggregate_verification_key(epoch_id, &mut api_clients)
+20 -63
View File
@@ -21,67 +21,30 @@ use rand::thread_rng;
use std::fmt::Display; use std::fmt::Display;
use std::future::Future; use std::future::Future;
pub(crate) trait EcashClientsProvider { // it really doesn't need the RwLock because it's never moved across tasks,
async fn try_get_ecash_clients( // but we need all the Send/Sync action
&mut self, #[derive(Default)]
) -> Result<Vec<EcashApiClient>, BandwidthControllerError>; pub(crate) struct ApiClientsWrapper(Option<Vec<EcashApiClient>>);
}
impl EcashClientsProvider for Vec<EcashApiClient> { impl ApiClientsWrapper {
async fn try_get_ecash_clients( pub(crate) async fn get_or_init<C>(
&mut self, &mut self,
) -> Result<Vec<EcashApiClient>, BandwidthControllerError> {
Ok(self.clone())
}
}
impl<C> EcashClientsProvider for &mut ApiClientsWrapper<'_, C>
where
C: DkgQueryClient + Sync + Send,
{
async fn try_get_ecash_clients(
&mut self,
) -> Result<Vec<EcashApiClient>, BandwidthControllerError> {
self.clients().await
}
}
pub(crate) enum ApiClientsWrapper<'a, C> {
Uninitialised {
query_client: &'a C,
epoch_id: EpochId, epoch_id: EpochId,
}, dkg_client: &C,
Cached { ) -> Result<Vec<EcashApiClient>, BandwidthControllerError>
clients: Vec<EcashApiClient>,
},
}
impl<'a, C> ApiClientsWrapper<'a, C> {
pub(crate) fn new(query_client: &'a C, epoch_id: EpochId) -> Self {
ApiClientsWrapper::Uninitialised {
query_client,
epoch_id,
}
}
async fn clients(&mut self) -> Result<Vec<EcashApiClient>, BandwidthControllerError>
where where
C: DkgQueryClient + Sync + Send, C: DkgQueryClient + Sync + Send,
{ {
match self { if let Some(cached) = &self.0 {
ApiClientsWrapper::Uninitialised { return Ok(cached.clone());
query_client,
epoch_id,
} => {
let clients = all_ecash_api_clients(*query_client, *epoch_id).await?;
*self = ApiClientsWrapper::Cached {
clients: clients.clone(),
};
Ok(clients)
}
ApiClientsWrapper::Cached { clients } => Ok(clients.clone()),
} }
let clients = all_ecash_api_clients(dkg_client, epoch_id).await?;
// technically we don't have to be cloning all the clients here, but it's way simpler than
// dealing with locking and whatnot given the performance penalty is negligible
self.0 = Some(clients.clone());
Ok(clients)
} }
} }
@@ -113,7 +76,7 @@ where
pub(crate) async fn get_aggregate_verification_key<St>( pub(crate) async fn get_aggregate_verification_key<St>(
storage: &St, storage: &St,
epoch_id: EpochId, epoch_id: EpochId,
mut ecash_apis: impl EcashClientsProvider, ecash_apis: Vec<EcashApiClient>,
) -> Result<VerificationKeyAuth, BandwidthControllerError> ) -> Result<VerificationKeyAuth, BandwidthControllerError>
where where
St: Storage, St: Storage,
@@ -127,8 +90,6 @@ where
return Ok(stored); return Ok(stored);
}; };
let ecash_apis = ecash_apis.try_get_ecash_clients().await?;
let master_vk = query_random_apis_until_success( let master_vk = query_random_apis_until_success(
ecash_apis, ecash_apis,
|api| async move { api.api_client.master_verification_key(Some(epoch_id)).await }, |api| async move { api.api_client.master_verification_key(Some(epoch_id)).await },
@@ -154,7 +115,7 @@ where
pub(crate) async fn get_coin_index_signatures<St>( pub(crate) async fn get_coin_index_signatures<St>(
storage: &St, storage: &St,
epoch_id: EpochId, epoch_id: EpochId,
mut ecash_apis: impl EcashClientsProvider, ecash_apis: Vec<EcashApiClient>,
) -> Result<Vec<AnnotatedCoinIndexSignature>, BandwidthControllerError> ) -> Result<Vec<AnnotatedCoinIndexSignature>, BandwidthControllerError>
where where
St: Storage, St: Storage,
@@ -168,8 +129,6 @@ where
return Ok(stored); return Ok(stored);
}; };
let ecash_apis = ecash_apis.try_get_ecash_clients().await?;
let index_sigs = query_random_apis_until_success( let index_sigs = query_random_apis_until_success(
ecash_apis, ecash_apis,
|api| async move { |api| async move {
@@ -200,7 +159,7 @@ pub(crate) async fn get_expiration_date_signatures<St>(
storage: &St, storage: &St,
epoch_id: EpochId, epoch_id: EpochId,
expiration_date: Date, expiration_date: Date,
mut ecash_apis: impl EcashClientsProvider, ecash_apis: Vec<EcashApiClient>,
) -> Result<Vec<AnnotatedExpirationDateSignature>, BandwidthControllerError> ) -> Result<Vec<AnnotatedExpirationDateSignature>, BandwidthControllerError>
where where
St: Storage, St: Storage,
@@ -214,8 +173,6 @@ where
return Ok(stored); return Ok(stored);
}; };
let ecash_apis = ecash_apis.try_get_ecash_clients().await?;
let expiration_sigs = query_random_apis_until_success( let expiration_sigs = query_random_apis_until_success(
ecash_apis, ecash_apis,
|api| async move { |api| async move {
+2 -1
View File
@@ -15,6 +15,7 @@ const-str = { workspace = true }
log = { workspace = true } log = { workspace = true }
pretty_env_logger = { workspace = true } pretty_env_logger = { workspace = true }
schemars = { workspace = true, features = ["preserve_order"], optional = true } schemars = { workspace = true, features = ["preserve_order"], optional = true }
semver.workspace = true
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true, optional = true } serde_json = { workspace = true, optional = true }
@@ -43,5 +44,5 @@ tracing = [
"tracing-opentelemetry", "tracing-opentelemetry",
"opentelemetry", "opentelemetry",
] ]
clap = ["dep:clap", "dep:clap_complete", "dep:clap_complete_fig"] clap = [ "dep:clap", "dep:clap_complete", "dep:clap_complete_fig" ]
models = [] models = []
+1
View File
@@ -3,6 +3,7 @@
pub mod build_information; pub mod build_information;
pub mod logging; pub mod logging;
pub mod version_checker;
#[cfg(feature = "clap")] #[cfg(feature = "clap")]
pub mod completions; pub mod completions;
@@ -0,0 +1,78 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub use semver::Version;
/// Checks if the version is minor version compatible.
///
/// Checks whether given `version` is compatible with a given semantic version requirement `req`
/// according to major-minor semver rules. The semantic version requirement can be passed as a full,
/// concrete version number, because that's what we'll have in our Cargo.toml files (e.g. 0.3.2).
/// The patch number in the requirement gets dropped and replaced with a wildcard (0.3.*) as all
/// minor versions should be compatible with each other.
pub fn is_minor_version_compatible(version: &str, req: &str) -> bool {
let expected_version = match Version::parse(version) {
Ok(v) => v,
Err(_) => return false,
};
let req_version = match Version::parse(req) {
Ok(v) => v,
Err(_) => return false,
};
expected_version.major == req_version.major && expected_version.minor == req_version.minor
}
pub fn parse_version(raw_version: &str) -> Result<Version, semver::Error> {
Version::parse(raw_version)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn version_0_3_0_is_compatible_with_requirement_0_3_x() {
assert!(is_minor_version_compatible("0.3.0", "0.3.2"));
}
#[test]
fn version_0_3_1_is_compatible_with_minimum_requirement_0_3_x() {
assert!(is_minor_version_compatible("0.3.1", "0.3.2"));
}
#[test]
fn version_0_3_2_is_compatible_with_minimum_requirement_0_3_x() {
assert!(is_minor_version_compatible("0.3.2", "0.3.0"));
}
#[test]
fn version_0_2_0_is_not_compatible_with_requirement_0_3_x() {
assert!(!is_minor_version_compatible("0.2.0", "0.3.2"));
}
#[test]
fn version_0_4_0_is_not_compatible_with_requirement_0_3_x() {
assert!(!is_minor_version_compatible("0.4.0", "0.3.2"));
}
#[test]
fn version_1_3_2_is_not_compatible_with_requirement_0_3_x() {
assert!(!is_minor_version_compatible("1.3.2", "0.3.2"));
}
#[test]
fn version_0_4_0_rc_1_is_compatible_with_version_0_4_0_rc_1() {
assert!(is_minor_version_compatible("0.4.0-rc.1", "0.4.0-rc.1"));
}
#[test]
fn returns_false_on_foo_version() {
assert!(!is_minor_version_compatible("foo", "0.3.2"));
}
#[test]
fn returns_false_on_bar_version() {
assert!(!is_minor_version_compatible("0.3.2", "bar"));
}
}
+2 -4
View File
@@ -3,7 +3,7 @@ name = "nym-client-core"
version = "1.1.15" version = "1.1.15"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"] authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2021" edition = "2021"
rust-version = "1.76" rust-version = "1.70"
license.workspace = true license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -40,14 +40,12 @@ 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" }
nym-statistics-common = { path = "../statistics" } nym-statistics-common = { path = "../statistics" }
nym-pemstore = { path = "../pemstore" } nym-pemstore = { path = "../pemstore" }
nym-topology = { path = "../topology", features = ["persistence"] } nym-topology = { path = "../topology", features = ["serializable"] }
nym-mixnet-client = { path = "../client-libs/mixnet-client", default-features = false }
nym-validator-client = { path = "../client-libs/validator-client", default-features = false } nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
nym-task = { path = "../task" } nym-task = { path = "../task" }
nym-credentials-interface = { path = "../credentials-interface" } nym-credentials-interface = { path = "../credentials-interface" }
+1 -88
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.
@@ -555,18 +550,6 @@ pub struct Topology {
/// Specifies a minimum performance of a gateway that is used on route construction. /// Specifies a minimum performance of a gateway that is used on route construction.
/// This setting is only applicable when `NymApi` topology is used. /// This setting is only applicable when `NymApi` topology is used.
pub minimum_gateway_performance: u8, pub minimum_gateway_performance: u8,
/// Specifies whether this client should attempt to retrieve all available network nodes
/// as opposed to just active mixnodes/gateways.
pub use_extended_topology: bool,
/// Specifies whether this client should ignore the current epoch role of the target egress node
/// when constructing the final hop packets.
pub ignore_egress_epoch_role: bool,
/// Specifies whether this client should ignore the current epoch role of the ingress node
/// when attempting to establish new connection
pub ignore_ingress_epoch_role: bool,
} }
#[allow(clippy::large_enum_variant)] #[allow(clippy::large_enum_variant)]
@@ -603,10 +586,6 @@ impl Default for Topology {
topology_structure: TopologyStructure::default(), topology_structure: TopologyStructure::default(),
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,
ignore_egress_epoch_role: true,
ignore_ingress_epoch_role: true,
} }
} }
} }
@@ -724,9 +703,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 +725,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(),
}, },
} }
} }
@@ -8,10 +8,7 @@ use crate::{
}, },
}; };
use log::{debug, error}; use log::{debug, error};
use sqlx::{ use sqlx::ConnectOptions;
sqlite::{SqliteAutoVacuum, SqliteSynchronous},
ConnectOptions,
};
use std::path::Path; use std::path::Path;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -33,9 +30,6 @@ impl StorageManager {
} }
let opts = sqlx::sqlite::SqliteConnectOptions::new() let opts = sqlx::sqlite::SqliteConnectOptions::new()
.journal_mode(sqlx::sqlite::SqliteJournalMode::Wal)
.synchronous(SqliteSynchronous::Normal)
.auto_vacuum(SqliteAutoVacuum::Incremental)
.filename(database_path) .filename(database_path)
.create_if_missing(true) .create_if_missing(true)
.disable_statement_logging(); .disable_statement_logging();
@@ -116,7 +110,7 @@ impl StorageManager {
) -> Result<(), sqlx::Error> { ) -> Result<(), sqlx::Error> {
sqlx::query!( sqlx::query!(
r#" r#"
INSERT INTO registered_gateway(gateway_id_bs58, registration_timestamp, gateway_type) INSERT INTO registered_gateway(gateway_id_bs58, registration_timestamp, gateway_type)
VALUES (?, ?, ?) VALUES (?, ?, ?)
"#, "#,
registered_gateway.gateway_id_bs58, registered_gateway.gateway_id_bs58,
@@ -230,7 +224,7 @@ impl StorageManager {
) -> Result<(), sqlx::Error> { ) -> Result<(), sqlx::Error> {
sqlx::query!( sqlx::query!(
r#" r#"
INSERT INTO custom_gateway_details(gateway_id_bs58, data) INSERT INTO custom_gateway_details(gateway_id_bs58, data)
VALUES (?, ?) VALUES (?, ?)
"#, "#,
custom.gateway_id_bs58, custom.gateway_id_bs58,
@@ -15,7 +15,6 @@ pub mod error;
mod manager; mod manager;
mod models; mod models;
#[derive(Clone)]
pub struct OnDiskGatewaysDetails { pub struct OnDiskGatewaysDetails {
manager: StorageManager, manager: StorageManager,
} }
@@ -20,12 +20,12 @@ pub enum InMemStorageError {
MalformedGateway(#[from] BadGateway), MalformedGateway(#[from] BadGateway),
} }
#[derive(Clone, Debug, Default)] #[derive(Debug, Default)]
pub struct InMemGatewaysDetails { pub struct InMemGatewaysDetails {
inner: Arc<RwLock<InMemStorageInner>>, inner: Arc<RwLock<InMemStorageInner>>,
} }
#[derive(Clone, Debug, Default)] #[derive(Debug, Default)]
struct InMemStorageInner { struct InMemStorageInner {
active_gateway: Option<String>, active_gateway: Option<String>,
gateways: HashMap<String, GatewayRegistration>, gateways: HashMap<String, GatewayRegistration>,
@@ -112,24 +112,18 @@ where
source, source,
} }
})?; })?;
hardcoded_topology.entry_capable_nodes().cloned().collect() hardcoded_topology.get_gateways()
} else { } else {
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
crate::init::helpers::gateways_for_init( crate::init::helpers::current_gateways(&mut rng, &core.client.nym_api_urls, user_agent)
&mut rng, .await?
&core.client.nym_api_urls,
user_agent,
core.debug.topology.minimum_gateway_performance,
core.debug.topology.ignore_ingress_epoch_role,
)
.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
let available_gateways = available_gateways let available_gateways = available_gateways
.into_iter() .into_iter()
.filter(|g| !registered_gateways.contains(&g.identity())) .filter(|g| !registered_gateways.contains(g.identity()))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if available_gateways.is_empty() { if available_gateways.is_empty() {
@@ -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> {
@@ -171,17 +167,11 @@ where
source, source,
} }
})?; })?;
hardcoded_topology.entry_capable_nodes().cloned().collect() hardcoded_topology.get_gateways()
} else { } else {
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
crate::init::helpers::gateways_for_init( crate::init::helpers::current_gateways(&mut rng, &core.client.nym_api_urls, user_agent)
&mut rng, .await?
&core.client.nym_api_urls,
user_agent,
core.debug.topology.minimum_gateway_performance,
core.debug.topology.ignore_ingress_epoch_role,
)
.await?
}; };
let gateway_setup = GatewaySetup::New { let gateway_setup = GatewaySetup::New {
@@ -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,
} }
+46 -104
View File
@@ -1,9 +1,9 @@
// 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 super::topology_control::geo_aware_provider::GeoAwareTopologyProvider;
use crate::client::base_client::storage::helpers::store_client_keys; use crate::client::base_client::storage::helpers::store_client_keys;
use crate::client::base_client::storage::MixnetClientStorage; use crate::client::base_client::storage::MixnetClientStorage;
use crate::client::cover_traffic_stream::LoopCoverTrafficStream; use crate::client::cover_traffic_stream::LoopCoverTrafficStream;
@@ -24,7 +24,7 @@ use crate::client::replies::reply_storage::{
}; };
use crate::client::topology_control::nym_api_provider::NymApiTopologyProvider; use crate::client::topology_control::nym_api_provider::NymApiTopologyProvider;
use crate::client::topology_control::{ use crate::client::topology_control::{
TopologyAccessor, TopologyRefresher, TopologyRefresherConfig, nym_api_provider, TopologyAccessor, TopologyRefresher, TopologyRefresherConfig,
}; };
use crate::config::{Config, DebugConfig}; use crate::config::{Config, DebugConfig};
use crate::error::ClientCoreError; use crate::error::ClientCoreError;
@@ -36,11 +36,9 @@ use crate::{config, spawn_future};
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 +176,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>,
@@ -190,23 +188,18 @@ pub struct BaseClientBuilder<C, S: MixnetClientStorage> {
user_agent: Option<UserAgent>, user_agent: Option<UserAgent>,
setup_method: GatewaySetup, setup_method: GatewaySetup,
#[cfg(unix)]
connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
derivation_material: Option<DerivationMaterial>,
} }
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,
@@ -217,27 +210,9 @@ where
shutdown: None, shutdown: None,
user_agent: None, user_agent: None,
setup_method: GatewaySetup::MustLoad { gateway_id: None }, setup_method: GatewaySetup::MustLoad { gateway_id: None },
#[cfg(unix)]
connection_fd_callback: None,
derivation_material: None,
} }
} }
#[must_use]
pub fn with_derivation_material(
mut self,
derivation_material: Option<DerivationMaterial>,
) -> Self {
self.derivation_material = derivation_material;
self
}
#[must_use]
pub fn with_forget_me(mut self, forget_me: &ForgetMe) -> Self {
self.config.debug.forget_me = *forget_me;
self
}
#[must_use] #[must_use]
pub fn with_gateway_setup(mut self, setup: GatewaySetup) -> Self { pub fn with_gateway_setup(mut self, setup: GatewaySetup) -> Self {
self.setup_method = setup; self.setup_method = setup;
@@ -286,15 +261,6 @@ where
Ok(self) Ok(self)
} }
#[cfg(unix)]
pub fn with_connection_fd_callback(
mut self,
callback: Arc<dyn Fn(RawFd) + Send + Sync>,
) -> Self {
self.connection_fd_callback = Some(callback);
self
}
// note: do **NOT** make this method public as its only valid usage is from within `start_base` // note: do **NOT** make this method public as its only valid usage is from within `start_base`
// because it relies on the crypto keys being already loaded // because it relies on the crypto keys being already loaded
fn mix_address(details: &InitialisationResult) -> Recipient { fn mix_address(details: &InitialisationResult) -> Recipient {
@@ -310,7 +276,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 +289,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 +306,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 +324,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,12 +348,10 @@ 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)]
async fn start_gateway_client( async fn start_gateway_client(
config: &Config, config: &Config,
initialisation_result: InitialisationResult, initialisation_result: InitialisationResult,
@@ -397,7 +359,6 @@ where
details_store: &S::GatewaysDetailsStore, details_store: &S::GatewaysDetailsStore,
packet_router: PacketRouter, packet_router: PacketRouter,
stats_reporter: ClientStatsSender, stats_reporter: ClientStatsSender,
#[cfg(unix)] connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
shutdown: TaskClient, shutdown: TaskClient,
) -> Result<GatewayClient<C, S::CredentialStore>, ClientCoreError> ) -> Result<GatewayClient<C, S::CredentialStore>, ClientCoreError>
where where
@@ -440,8 +401,6 @@ where
packet_router, packet_router,
bandwidth_controller, bandwidth_controller,
stats_reporter, stats_reporter,
#[cfg(unix)]
connection_fd_callback,
shutdown, shutdown,
) )
}; };
@@ -478,8 +437,8 @@ where
details_store details_store
.upgrade_stored_remote_gateway_key(gateway_client.gateway_identity(), &updated_key) .upgrade_stored_remote_gateway_key(gateway_client.gateway_identity(), &updated_key)
.await.map_err(|err| { .await.map_err(|err| {
error!("failed to store upgraded gateway key! this connection might be forever broken now: {err}"); error!("failed to store upgraded gateway key! this connection might be forever broken now: {err}");
ClientCoreError::GatewaysDetailsStoreError { source: Box::new(err) } ClientCoreError::GatewaysDetailsStoreError { source: Box::new(err) }
})? })?
} }
@@ -487,7 +446,6 @@ where
.claim_initial_bandwidth() .claim_initial_bandwidth()
.await .await
.map_err(gateway_failure)?; .map_err(gateway_failure)?;
gateway_client gateway_client
.start_listening_for_mixnet_messages() .start_listening_for_mixnet_messages()
.map_err(gateway_failure)?; .map_err(gateway_failure)?;
@@ -504,7 +462,6 @@ where
details_store: &S::GatewaysDetailsStore, details_store: &S::GatewaysDetailsStore,
packet_router: PacketRouter, packet_router: PacketRouter,
stats_reporter: ClientStatsSender, stats_reporter: ClientStatsSender,
#[cfg(unix)] connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
mut shutdown: TaskClient, mut shutdown: TaskClient,
) -> Result<Box<dyn GatewayTransceiver + Send>, ClientCoreError> ) -> Result<Box<dyn GatewayTransceiver + Send>, ClientCoreError>
where where
@@ -536,8 +493,6 @@ where
details_store, details_store,
packet_router, packet_router,
stats_reporter, stats_reporter,
#[cfg(unix)]
connection_fd_callback,
shutdown, shutdown,
) )
.await?; .await?;
@@ -554,15 +509,20 @@ where
// if no custom provider was ... provided ..., create one using nym-api // if no custom provider was ... provided ..., create one using nym-api
custom_provider.unwrap_or_else(|| match config_topology.topology_structure { custom_provider.unwrap_or_else(|| match config_topology.topology_structure {
config::TopologyStructure::NymApi => Box::new(NymApiTopologyProvider::new( config::TopologyStructure::NymApi => Box::new(NymApiTopologyProvider::new(
config_topology, nym_api_provider::Config {
min_mixnode_performance: config_topology.minimum_mixnode_performance,
min_gateway_performance: config_topology.minimum_gateway_performance,
},
nym_api_urls, nym_api_urls,
env!("CARGO_PKG_VERSION").to_string(),
user_agent, user_agent,
)), )),
config::TopologyStructure::GeoAware(group_by) => { config::TopologyStructure::GeoAware(group_by) => {
warn!("using deprecated 'GeoAware' topology provider - this option will be removed very soon"); Box::new(GeoAwareTopologyProvider::new(
nym_api_urls,
#[allow(deprecated)] env!("CARGO_PKG_VERSION").to_string(),
Box::new(crate::client::topology_control::GeoAwareTopologyProvider::new(nym_api_urls, group_by)) group_by,
))
} }
}) })
} }
@@ -573,24 +533,17 @@ where
topology_provider: Box<dyn TopologyProvider + Send + Sync>, topology_provider: Box<dyn TopologyProvider + Send + Sync>,
topology_config: config::Topology, topology_config: config::Topology,
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
@@ -612,7 +565,7 @@ where
}; };
if let Err(err) = topology_refresher if let Err(err) = topology_refresher
.ensure_contains_routable_egress(local_gateway) .ensure_contains_gateway(local_gateway)
.await .await
{ {
if let Some(waiting_timeout) = gateway_wait_timeout { if let Some(waiting_timeout) = gateway_wait_timeout {
@@ -631,11 +584,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 +603,28 @@ 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) { ) -> 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);
MixTrafficController::new(gateway_transceiver, shutdown); mix_traffic_controller.start_with_shutdown(shutdown);
mix_traffic_controller.start(); mix_tx
(mix_tx, client_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 +659,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 +668,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 +690,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?;
@@ -764,8 +713,7 @@ where
// channels responsible for controlling ack messages // channels responsible for controlling ack messages
let (ack_sender, ack_receiver) = mpsc::unbounded(); let (ack_sender, ack_receiver) = mpsc::unbounded();
let shared_topology_accessor = let shared_topology_accessor = TopologyAccessor::new();
TopologyAccessor::new(self.config.debug.topology.ignore_egress_epoch_role);
// Shutdown notifier for signalling tasks to stop // Shutdown notifier for signalling tasks to stop
let shutdown = self let shutdown = self
@@ -797,7 +745,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,14 +771,12 @@ 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,
gateway_packet_router, gateway_packet_router,
stats_reporter.clone(), stats_reporter.clone(),
#[cfg(unix)]
self.connection_fd_callback,
shutdown.fork("gateway_transceiver"), shutdown.fork("gateway_transceiver"),
) )
.await?; .await?;
@@ -856,8 +802,7 @@ where
// that are to be sent to the mixnet. They are used by cover traffic stream and real // that are to be sent to the mixnet. They are used by cover traffic stream and real
// traffic stream. // traffic stream.
// The MixTrafficController then sends the actual traffic // The MixTrafficController then sends the actual traffic
let message_sender = Self::start_mix_traffic_controller(
let (message_sender, client_request_sender) = Self::start_mix_traffic_controller(
gateway_transceiver, gateway_transceiver,
shutdown.fork("mix_traffic_controller"), shutdown.fork("mix_traffic_controller"),
); );
@@ -934,8 +879,6 @@ where
}, },
stats_reporter, stats_reporter,
task_handle: shutdown, task_handle: shutdown,
client_request_sender,
forget_me: self.config.debug.forget_me,
}) })
} }
} }
@@ -947,7 +890,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(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(),
}
} }
} }
@@ -121,7 +114,6 @@ impl MixnetClientStorage for Ephemeral {
} }
} }
#[derive(Clone)]
#[cfg(all( #[cfg(all(
not(target_arch = "wasm32"), not(target_arch = "wasm32"),
feature = "fs-surb-storage", feature = "fs-surb-storage",
@@ -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,
} }
} }
@@ -168,7 +163,6 @@ impl LoopCoverTrafficStream<OsRng> {
// poisson delay, but is it really a problem? // poisson delay, but is it really a problem?
let topology_permit = self.topology_access.get_read_permit().await; let topology_permit = self.topology_access.get_read_permit().await;
// the ack is sent back to ourselves (and then ignored) // the ack is sent back to ourselves (and then ignored)
let topology_ref = match topology_permit.try_get_valid_topology_ref( let topology_ref = match topology_permit.try_get_valid_topology_ref(
&self.our_full_destination, &self.our_full_destination,
Some(&self.our_full_destination), Some(&self.our_full_destination),
@@ -180,7 +174,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 +183,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 +216,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 +230,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");
@@ -28,6 +28,7 @@ pub enum InputMessage {
recipient: Recipient, recipient: Recipient,
data: Vec<u8>, data: Vec<u8>,
lane: TransmissionLane, lane: TransmissionLane,
mix_hops: Option<u8>,
}, },
/// Creates a message used for a duplex anonymous communication where the recipient /// Creates a message used for a duplex anonymous communication where the recipient
@@ -43,6 +44,7 @@ pub enum InputMessage {
data: Vec<u8>, data: Vec<u8>,
reply_surbs: u32, reply_surbs: u32,
lane: TransmissionLane, lane: TransmissionLane,
mix_hops: Option<u8>,
}, },
/// Attempt to use our internally received and stored `ReplySurb` to send the message back /// Attempt to use our internally received and stored `ReplySurb` to send the message back
@@ -92,6 +94,29 @@ impl InputMessage {
recipient, recipient,
data, data,
lane, lane,
mix_hops: None,
};
if let Some(packet_type) = packet_type {
InputMessage::new_wrapper(message, packet_type)
} else {
message
}
}
// IMHO `new_regular` should take `mix_hops: Option<u8>` as an argument instead of creating
// this function, but that would potentially break backwards compatibility with the current API
pub fn new_regular_with_custom_hops(
recipient: Recipient,
data: Vec<u8>,
lane: TransmissionLane,
packet_type: Option<PacketType>,
mix_hops: Option<u8>,
) -> Self {
let message = InputMessage::Regular {
recipient,
data,
lane,
mix_hops,
}; };
if let Some(packet_type) = packet_type { if let Some(packet_type) = packet_type {
InputMessage::new_wrapper(message, packet_type) InputMessage::new_wrapper(message, packet_type)
@@ -112,6 +137,7 @@ impl InputMessage {
data, data,
reply_surbs, reply_surbs,
lane, lane,
mix_hops: None,
}; };
if let Some(packet_type) = packet_type { if let Some(packet_type) = packet_type {
InputMessage::new_wrapper(message, packet_type) InputMessage::new_wrapper(message, packet_type)
@@ -128,12 +154,14 @@ impl InputMessage {
reply_surbs: u32, reply_surbs: u32,
lane: TransmissionLane, lane: TransmissionLane,
packet_type: Option<PacketType>, packet_type: Option<PacketType>,
mix_hops: Option<u8>,
) -> Self { ) -> Self {
let message = InputMessage::Anonymous { let message = InputMessage::Anonymous {
recipient, recipient,
data, data,
reply_surbs, reply_surbs,
lane, lane,
mix_hops,
}; };
if let Some(packet_type) = packet_type { if let Some(packet_type) = packet_type {
InputMessage::new_wrapper(message, packet_type) InputMessage::new_wrapper(message, packet_type)
@@ -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,9 +3,7 @@
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 tokio::sync::Mutex; use tokio::sync::Mutex;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
@@ -66,7 +64,6 @@ pub enum OnDiskKeysError {
}, },
} }
#[derive(Clone)]
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
pub struct OnDiskKeys { pub struct OnDiskKeys {
paths: ClientKeysPaths, paths: ClientKeysPaths,
@@ -196,20 +193,9 @@ impl KeyStore for OnDiskKeys {
} }
} }
#[derive(Clone)] #[derive(Default)]
pub struct InMemEphemeralKeys { pub struct InMemEphemeralKeys {
keys: Arc<Mutex<ClientKeys>>, keys: 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 +208,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,12 @@
// 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; use crate::spawn_future;
use log::*; use log::*;
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 +22,45 @@ 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,
task_client: TaskClient,
} }
impl MixTrafficController { impl MixTrafficController {
pub fn new<T>( pub fn new<T>(gateway_transceiver: T) -> (MixTrafficController, BatchMixMessageSender)
gateway_transceiver: T,
task_client: TaskClient,
) -> (
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,
}, },
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, ) -> (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,
}, },
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,62 +72,46 @@ 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;
log::debug!("MixTrafficController: Exiting"); log::debug!("MixTrafficController: Exiting");
}); })
} }
} }
@@ -5,10 +5,8 @@ use async_trait::async_trait;
use log::{debug, error}; use log::{debug, error};
use nym_credential_storage::storage::Storage as CredentialStorage; use nym_credential_storage::storage::Storage as CredentialStorage;
use nym_crypto::asymmetric::identity; use nym_crypto::asymmetric::identity;
use nym_gateway_client::error::GatewayClientError;
use nym_gateway_client::GatewayClient; use nym_gateway_client::GatewayClient;
pub use nym_gateway_client::{GatewayPacketRouter, PacketRouter}; pub use nym_gateway_client::{GatewayPacketRouter, PacketRouter};
use nym_gateway_requests::ClientRequest;
use nym_sphinx::forwarding::packet::MixPacket; use nym_sphinx::forwarding::packet::MixPacket;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient; use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
use std::fmt::Debug; use std::fmt::Debug;
@@ -16,7 +14,7 @@ use std::os::raw::c_int as RawFd;
use thiserror::Error; use thiserror::Error;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use futures::channel::oneshot; use futures::channel::{mpsc, oneshot};
// we need to type erase the error type since we can't have dynamic associated types alongside dynamic dispatch // we need to type erase the error type since we can't have dynamic associated types alongside dynamic dispatch
#[derive(Debug, Error)] #[derive(Debug, Error)]
@@ -28,14 +26,9 @@ fn erase_err<E: std::error::Error + Send + Sync + 'static>(err: E) -> ErasedGate
} }
/// This combines combines the functionalities of being able to send and receive mix packets. /// This combines combines the functionalities of being able to send and receive mix packets.
#[async_trait]
pub trait GatewayTransceiver: GatewaySender + GatewayReceiver { pub trait GatewayTransceiver: GatewaySender + GatewayReceiver {
fn gateway_identity(&self) -> identity::PublicKey; fn gateway_identity(&self) -> identity::PublicKey;
fn ws_fd(&self) -> Option<RawFd>; fn ws_fd(&self) -> Option<RawFd>;
async fn send_client_request(
&mut self,
message: ClientRequest,
) -> Result<(), GatewayClientError>;
} }
/// This trait defines the functionality of sending `MixPacket` into the mixnet, /// This trait defines the functionality of sending `MixPacket` into the mixnet,
@@ -72,7 +65,6 @@ pub trait GatewayReceiver {
} }
// to allow for dynamic dispatch // to allow for dynamic dispatch
#[async_trait]
impl<G: GatewayTransceiver + ?Sized + Send> GatewayTransceiver for Box<G> { impl<G: GatewayTransceiver + ?Sized + Send> GatewayTransceiver for Box<G> {
#[inline] #[inline]
fn gateway_identity(&self) -> identity::PublicKey { fn gateway_identity(&self) -> identity::PublicKey {
@@ -81,15 +73,6 @@ impl<G: GatewayTransceiver + ?Sized + Send> GatewayTransceiver for Box<G> {
fn ws_fd(&self) -> Option<RawFd> { fn ws_fd(&self) -> Option<RawFd> {
(**self).ws_fd() (**self).ws_fd()
} }
async fn send_client_request(
&mut self,
message: ClientRequest,
) -> Result<(), GatewayClientError> {
let _ = (**self).send_client_request(message.clone()).await?;
log::debug!("Sent client request: {:?}", message);
Ok(())
}
} }
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
@@ -108,6 +91,7 @@ impl<G: GatewaySender + ?Sized + Send> GatewaySender for Box<G> {
(**self).batch_send_mix_packets(packets).await (**self).batch_send_mix_packets(packets).await
} }
} }
impl<G: GatewayReceiver + ?Sized> GatewayReceiver for Box<G> { impl<G: GatewayReceiver + ?Sized> GatewayReceiver for Box<G> {
#[inline] #[inline]
fn set_packet_router(&mut self, packet_router: PacketRouter) -> Result<(), ErasedGatewayError> { fn set_packet_router(&mut self, packet_router: PacketRouter) -> Result<(), ErasedGatewayError> {
@@ -127,7 +111,6 @@ impl<C, St> RemoteGateway<C, St> {
} }
} }
#[async_trait]
impl<C, St> GatewayTransceiver for RemoteGateway<C, St> impl<C, St> GatewayTransceiver for RemoteGateway<C, St>
where where
C: DkgQueryClient + Send + Sync, C: DkgQueryClient + Send + Sync,
@@ -140,13 +123,6 @@ where
fn ws_fd(&self) -> Option<RawFd> { fn ws_fd(&self) -> Option<RawFd> {
self.gateway_client.ws_fd() self.gateway_client.ws_fd()
} }
async fn send_client_request(
&mut self,
message: ClientRequest,
) -> Result<(), GatewayClientError> {
self.gateway_client.send_client_request(message).await
}
} }
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
@@ -194,7 +170,7 @@ pub struct LocalGateway {
// 'sender' part // 'sender' part
/// Channel responsible for taking mix packets and forwarding them further into the further mixnet layers. /// Channel responsible for taking mix packets and forwarding them further into the further mixnet layers.
packet_forwarder: nym_mixnet_client::forwarder::MixForwardingSender, packet_forwarder: mpsc::UnboundedSender<MixPacket>,
// 'receiver' part // 'receiver' part
packet_router_tx: Option<oneshot::Sender<PacketRouter>>, packet_router_tx: Option<oneshot::Sender<PacketRouter>>,
@@ -204,7 +180,7 @@ pub struct LocalGateway {
impl LocalGateway { impl LocalGateway {
pub fn new( pub fn new(
local_identity: identity::PublicKey, local_identity: identity::PublicKey,
packet_forwarder: nym_mixnet_client::forwarder::MixForwardingSender, packet_forwarder: mpsc::UnboundedSender<MixPacket>,
packet_router_tx: oneshot::Sender<PacketRouter>, packet_router_tx: oneshot::Sender<PacketRouter>,
) -> Self { ) -> Self {
LocalGateway { LocalGateway {
@@ -219,7 +195,6 @@ impl LocalGateway {
mod nonwasm_sealed { mod nonwasm_sealed {
use super::*; use super::*;
#[async_trait]
impl GatewayTransceiver for LocalGateway { impl GatewayTransceiver for LocalGateway {
fn gateway_identity(&self) -> identity::PublicKey { fn gateway_identity(&self) -> identity::PublicKey {
self.local_identity self.local_identity
@@ -227,20 +202,14 @@ mod nonwasm_sealed {
fn ws_fd(&self) -> Option<RawFd> { fn ws_fd(&self) -> Option<RawFd> {
None None
} }
async fn send_client_request(
&mut self,
_message: ClientRequest,
) -> Result<(), GatewayClientError> {
Ok(())
}
} }
#[async_trait] #[async_trait]
impl GatewaySender for LocalGateway { impl GatewaySender for LocalGateway {
async fn send_mix_packet(&mut self, packet: MixPacket) -> Result<(), ErasedGatewayError> { async fn send_mix_packet(&mut self, packet: MixPacket) -> Result<(), ErasedGatewayError> {
self.packet_forwarder self.packet_forwarder
.forward_packet(packet) .unbounded_send(packet)
.map_err(|err| err.into_send_error())
.map_err(erase_err) .map_err(erase_err)
} }
} }
@@ -301,7 +270,6 @@ impl GatewaySender for MockGateway {
} }
} }
#[async_trait]
impl GatewayTransceiver for MockGateway { impl GatewayTransceiver for MockGateway {
fn gateway_identity(&self) -> identity::PublicKey { fn gateway_identity(&self) -> identity::PublicKey {
self.dummy_identity self.dummy_identity
@@ -309,11 +277,4 @@ impl GatewayTransceiver for MockGateway {
fn ws_fd(&self) -> Option<RawFd> { fn ws_fd(&self) -> Option<RawFd> {
None None
} }
async fn send_client_request(
&mut self,
_message: ClientRequest,
) -> Result<(), GatewayClientError> {
Ok(())
}
} }
@@ -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(
@@ -83,10 +73,11 @@ where
content: Vec<u8>, content: Vec<u8>,
lane: TransmissionLane, lane: TransmissionLane,
packet_type: PacketType, packet_type: PacketType,
mix_hops: Option<u8>,
) { ) {
if let Err(err) = self if let Err(err) = self
.message_handler .message_handler
.try_send_plain_message(recipient, content, lane, packet_type) .try_send_plain_message(recipient, content, lane, packet_type, mix_hops)
.await .await
{ {
warn!("failed to send a plain message - {err}") warn!("failed to send a plain message - {err}")
@@ -100,10 +91,18 @@ where
reply_surbs: u32, reply_surbs: u32,
lane: TransmissionLane, lane: TransmissionLane,
packet_type: PacketType, packet_type: PacketType,
mix_hops: Option<u8>,
) { ) {
if let Err(err) = self if let Err(err) = self
.message_handler .message_handler
.try_send_message_with_reply_surbs(recipient, content, reply_surbs, lane, packet_type) .try_send_message_with_reply_surbs(
recipient,
content,
reply_surbs,
lane,
packet_type,
mix_hops,
)
.await .await
{ {
warn!("failed to send a repliable message - {err}") warn!("failed to send a repliable message - {err}")
@@ -116,8 +115,9 @@ where
recipient, recipient,
data, data,
lane, lane,
mix_hops,
} => { } => {
self.handle_plain_message(recipient, data, lane, PacketType::Mix) self.handle_plain_message(recipient, data, lane, PacketType::Mix, mix_hops)
.await .await
} }
InputMessage::Anonymous { InputMessage::Anonymous {
@@ -125,9 +125,17 @@ where
data, data,
reply_surbs, reply_surbs,
lane, lane,
mix_hops,
} => { } => {
self.handle_repliable_message(recipient, data, reply_surbs, lane, PacketType::Mix) self.handle_repliable_message(
.await recipient,
data,
reply_surbs,
lane,
PacketType::Mix,
mix_hops,
)
.await
} }
InputMessage::Reply { InputMessage::Reply {
recipient_tag, recipient_tag,
@@ -145,8 +153,9 @@ where
recipient, recipient,
data, data,
lane, lane,
mix_hops,
} => { } => {
self.handle_plain_message(recipient, data, lane, packet_type) self.handle_plain_message(recipient, data, lane, packet_type, mix_hops)
.await .await
} }
InputMessage::Anonymous { InputMessage::Anonymous {
@@ -154,9 +163,17 @@ where
data, data,
reply_surbs, reply_surbs,
lane, lane,
mix_hops,
} => { } => {
self.handle_repliable_message(recipient, data, reply_surbs, lane, packet_type) self.handle_repliable_message(
.await recipient,
data,
reply_surbs,
lane,
packet_type,
mix_hops,
)
.await
} }
InputMessage::Reply { InputMessage::Reply {
recipient_tag, recipient_tag,
@@ -174,10 +191,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 +205,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,10 +66,11 @@ 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,
mix_hops: Option<u8>,
retransmissions: u32, retransmissions: u32,
} }
@@ -80,11 +80,13 @@ impl PendingAcknowledgement {
message_chunk: Fragment, message_chunk: Fragment,
delay: SphinxDelay, delay: SphinxDelay,
recipient: Recipient, recipient: Recipient,
mix_hops: Option<u8>,
) -> Self { ) -> Self {
PendingAcknowledgement { PendingAcknowledgement {
message_chunk, message_chunk,
delay, delay,
destination: PacketDestination::KnownRecipient(recipient.into()), destination: PacketDestination::KnownRecipient(recipient.into()),
mix_hops,
retransmissions: 0, retransmissions: 0,
} }
} }
@@ -102,6 +104,9 @@ impl PendingAcknowledgement {
recipient_tag, recipient_tag,
extra_surb_request, extra_surb_request,
}, },
// Messages sent using SURBs are using the number of mix hops set by the recipient when
// they provided the SURBs, so it doesn't make sense to include it here.
mix_hops: None,
retransmissions: 0, retransmissions: 0,
} }
} }
@@ -217,7 +222,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 +231,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 +239,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 +246,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 +255,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 +271,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,
} }
} }
@@ -55,12 +52,18 @@ where
packet_recipient: Recipient, packet_recipient: Recipient,
chunk_data: Fragment, chunk_data: Fragment,
packet_type: PacketType, packet_type: PacketType,
mix_hops: Option<u8>,
) -> Result<PreparedFragment, PreparationError> { ) -> Result<PreparedFragment, PreparationError> {
debug!("retransmitting normal packet..."); debug!("retransmitting normal packet...");
// TODO: Figure out retransmission packet type signaling // TODO: Figure out retransmission packet type signaling
self.message_handler self.message_handler
.try_prepare_single_chunk_for_sending(packet_recipient, chunk_data, packet_type) .try_prepare_single_chunk_for_sending(
packet_recipient,
chunk_data,
packet_type,
mix_hops,
)
.await .await
} }
@@ -82,12 +85,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,22 +99,18 @@ 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(
**recipient, **recipient,
timed_out_ack.message_chunk.clone(), timed_out_ack.message_chunk.clone(),
packet_type, packet_type,
timed_out_ack.mix_hops,
) )
.await .await
} }
@@ -125,12 +121,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 +148,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 +164,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 +180,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");
} }
} }
@@ -15,12 +15,11 @@ use nym_sphinx::anonymous_replies::requests::{AnonymousSenderTag, RepliableMessa
use nym_sphinx::anonymous_replies::{ReplySurb, SurbEncryptionKey}; use nym_sphinx::anonymous_replies::{ReplySurb, SurbEncryptionKey};
use nym_sphinx::chunking::fragment::{Fragment, FragmentIdentifier}; use nym_sphinx::chunking::fragment::{Fragment, FragmentIdentifier};
use nym_sphinx::message::NymMessage; use nym_sphinx::message::NymMessage;
use nym_sphinx::params::{PacketSize, PacketType}; use nym_sphinx::params::{PacketSize, PacketType, DEFAULT_NUM_MIX_HOPS};
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::{NymTopology, NymTopologyError};
use nym_topology::{NymRouteProvider, NymTopologyError};
use rand::{CryptoRng, Rng}; use rand::{CryptoRng, Rng};
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
@@ -101,6 +100,10 @@ pub(crate) struct Config {
/// Average delay an acknowledgement packet is going to get delay at a single mixnode. /// Average delay an acknowledgement packet is going to get delay at a single mixnode.
average_ack_delay: Duration, average_ack_delay: Duration,
/// Number of mix hops each packet ('real' message, ack, reply) is expected to take.
/// Note that it does not include gateway hops.
num_mix_hops: u8,
/// Primary predefined packet size used for the encapsulated messages. /// Primary predefined packet size used for the encapsulated messages.
primary_packet_size: PacketSize, primary_packet_size: PacketSize,
@@ -122,11 +125,19 @@ impl Config {
deterministic_route_selection, deterministic_route_selection,
average_packet_delay, average_packet_delay,
average_ack_delay, average_ack_delay,
num_mix_hops: DEFAULT_NUM_MIX_HOPS,
primary_packet_size: PacketSize::default(), primary_packet_size: PacketSize::default(),
secondary_packet_size: None, secondary_packet_size: None,
} }
} }
/// Allows setting non-default number of expected mix hops in the network.
#[allow(dead_code)]
pub fn with_mix_hops(mut self, hops: u8) -> Self {
self.num_mix_hops = hops;
self
}
/// Allows setting non-default size of the sphinx packets sent out. /// Allows setting non-default size of the sphinx packets sent out.
pub fn with_custom_primary_packet_size(mut self, packet_size: PacketSize) -> Self { pub fn with_custom_primary_packet_size(mut self, packet_size: PacketSize) -> Self {
self.primary_packet_size = packet_size; self.primary_packet_size = packet_size;
@@ -150,14 +161,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 +175,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,
@@ -177,7 +185,9 @@ where
config.sender_address, config.sender_address,
config.average_packet_delay, config.average_packet_delay,
config.average_ack_delay, config.average_ack_delay,
); )
.with_mix_hops(config.num_mix_hops);
MessageHandler { MessageHandler {
config, config,
rng, rng,
@@ -187,7 +197,6 @@ where
topology_access, topology_access,
reply_key_storage, reply_key_storage,
tag_storage, tag_storage,
task_client,
} }
} }
@@ -207,7 +216,7 @@ where
fn get_topology<'a>( fn get_topology<'a>(
&self, &self,
permit: &'a TopologyReadPermit<'a>, permit: &'a TopologyReadPermit<'a>,
) -> Result<&'a NymRouteProvider, PreparationError> { ) -> Result<&'a NymTopology, PreparationError> {
match permit.try_get_valid_topology_ref(&self.config.sender_address, None) { match permit.try_get_valid_topology_ref(&self.config.sender_address, None) {
Ok(topology_ref) => Ok(topology_ref), Ok(topology_ref) => Ok(topology_ref),
Err(err) => { Err(err) => {
@@ -224,8 +233,9 @@ where
return self.config.primary_packet_size; return self.config.primary_packet_size;
}; };
let primary_count = msg.required_packets(self.config.primary_packet_size); let primary_count =
let secondary_count = msg.required_packets(secondary_packet); msg.required_packets(self.config.primary_packet_size, self.config.num_mix_hops);
let secondary_count = msg.required_packets(secondary_packet, self.config.num_mix_hops);
trace!("This message would require: {primary_count} primary packets or {secondary_count} secondary packets..."); trace!("This message would require: {primary_count} primary packets or {secondary_count} secondary packets...");
// if there would be no benefit in using the secondary packet - use the primary (duh) // if there would be no benefit in using the secondary packet - use the primary (duh)
@@ -414,9 +424,10 @@ where
message: Vec<u8>, message: Vec<u8>,
lane: TransmissionLane, lane: TransmissionLane,
packet_type: PacketType, packet_type: PacketType,
mix_hops: Option<u8>,
) -> Result<(), PreparationError> { ) -> Result<(), PreparationError> {
let message = NymMessage::new_plain(message); let message = NymMessage::new_plain(message);
self.try_split_and_send_non_reply_message(message, recipient, lane, packet_type) self.try_split_and_send_non_reply_message(message, recipient, lane, packet_type, mix_hops)
.await .await
} }
@@ -426,6 +437,7 @@ where
recipient: Recipient, recipient: Recipient,
lane: TransmissionLane, lane: TransmissionLane,
packet_type: PacketType, packet_type: PacketType,
mix_hops: Option<u8>,
) -> Result<(), PreparationError> { ) -> Result<(), PreparationError> {
debug!("Sending non-reply message with packet type {packet_type}"); debug!("Sending non-reply message with packet type {packet_type}");
// TODO: I really dislike existence of this assertion, it implies code has to be re-organised // TODO: I really dislike existence of this assertion, it implies code has to be re-organised
@@ -458,6 +470,7 @@ where
&self.config.ack_key, &self.config.ack_key,
&recipient, &recipient,
packet_type, packet_type,
mix_hops,
)?; )?;
let real_message = RealMessage::new( let real_message = RealMessage::new(
@@ -465,7 +478,8 @@ where
Some(fragment.fragment_identifier()), Some(fragment.fragment_identifier()),
); );
let delay = prepared_fragment.total_delay; let delay = prepared_fragment.total_delay;
let pending_ack = PendingAcknowledgement::new_known(fragment, delay, recipient); let pending_ack =
PendingAcknowledgement::new_known(fragment, delay, recipient, mix_hops);
real_messages.push(real_message); real_messages.push(real_message);
pending_acks.push(pending_ack); pending_acks.push(pending_ack);
@@ -482,6 +496,7 @@ where
recipient: Recipient, recipient: Recipient,
amount: u32, amount: u32,
packet_type: PacketType, packet_type: PacketType,
mix_hops: Option<u8>,
) -> Result<(), PreparationError> { ) -> Result<(), PreparationError> {
debug!("Sending additional reply SURBs with packet type {packet_type}"); debug!("Sending additional reply SURBs with packet type {packet_type}");
let sender_tag = self.get_or_create_sender_tag(&recipient); let sender_tag = self.get_or_create_sender_tag(&recipient);
@@ -498,6 +513,7 @@ where
recipient, recipient,
TransmissionLane::AdditionalReplySurbs, TransmissionLane::AdditionalReplySurbs,
packet_type, packet_type,
mix_hops,
) )
.await?; .await?;
@@ -514,6 +530,7 @@ where
num_reply_surbs: u32, num_reply_surbs: u32,
lane: TransmissionLane, lane: TransmissionLane,
packet_type: PacketType, packet_type: PacketType,
mix_hops: Option<u8>,
) -> Result<(), SurbWrappedPreparationError> { ) -> Result<(), SurbWrappedPreparationError> {
debug!("Sending message with reply SURBs with packet type {packet_type}"); debug!("Sending message with reply SURBs with packet type {packet_type}");
let sender_tag = self.get_or_create_sender_tag(&recipient); let sender_tag = self.get_or_create_sender_tag(&recipient);
@@ -524,7 +541,7 @@ where
let message = let message =
NymMessage::new_repliable(RepliableMessage::new_data(message, sender_tag, reply_surbs)); NymMessage::new_repliable(RepliableMessage::new_data(message, sender_tag, reply_surbs));
self.try_split_and_send_non_reply_message(message, recipient, lane, packet_type) self.try_split_and_send_non_reply_message(message, recipient, lane, packet_type, mix_hops)
.await?; .await?;
log::trace!("storing {} reply keys", reply_keys.len()); log::trace!("storing {} reply keys", reply_keys.len());
@@ -538,18 +555,23 @@ where
recipient: Recipient, recipient: Recipient,
chunk: Fragment, chunk: Fragment,
packet_type: PacketType, packet_type: PacketType,
mix_hops: Option<u8>,
) -> Result<PreparedFragment, PreparationError> { ) -> Result<PreparedFragment, PreparationError> {
debug!("Sending single chunk with packet type {packet_type}"); debug!("Sending single chunk with packet type {packet_type}");
let topology_permit = self.topology_access.get_read_permit().await; let topology_permit = self.topology_access.get_read_permit().await;
let topology = self.get_topology(&topology_permit)?; let topology = self.get_topology(&topology_permit)?;
let prepared_fragment = self.message_preparer.prepare_chunk_for_sending( let prepared_fragment = self
chunk, .message_preparer
topology, .prepare_chunk_for_sending(
&self.config.ack_key, chunk,
&recipient, topology,
packet_type, &self.config.ack_key,
)?; &recipient,
packet_type,
mix_hops,
)
.unwrap();
Ok(prepared_fragment) Ok(prepared_fragment)
} }
@@ -602,37 +624,30 @@ where
Err(err) => return Err(err.return_surbs(vec![reply_surb])), Err(err) => return Err(err.return_surbs(vec![reply_surb])),
}; };
let prepared_fragment = self.message_preparer.prepare_reply_chunk_for_sending( let prepared_fragment = self
chunk, .message_preparer
topology, .prepare_reply_chunk_for_sending(
&self.config.ack_key, chunk,
reply_surb, topology,
PacketType::Mix, &self.config.ack_key,
)?; reply_surb,
PacketType::Mix,
)
.unwrap();
Ok(prepared_fragment) Ok(prepared_fragment)
} }
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 +656,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}");
}
}
} }
} }
@@ -9,12 +9,10 @@ use self::{
acknowledgement_control::AcknowledgementController, real_traffic_stream::OutQueueControl, acknowledgement_control::AcknowledgementController, real_traffic_stream::OutQueueControl,
}; };
use crate::client::real_messages_control::message_handler::MessageHandler; use crate::client::real_messages_control::message_handler::MessageHandler;
use crate::client::replies::reply_controller;
use crate::client::replies::reply_controller::{ use crate::client::replies::reply_controller::{
ReplyController, ReplyControllerReceiver, ReplyControllerSender, ReplyController, ReplyControllerReceiver, ReplyControllerSender,
}; };
use crate::client::replies::reply_storage::CombinedReplyStorage; use crate::client::replies::reply_storage::CombinedReplyStorage;
use crate::config;
use crate::{ use crate::{
client::{ client::{
inbound_messages::InputMessageReceiver, mix_traffic::BatchMixMessageSender, inbound_messages::InputMessageReceiver, mix_traffic::BatchMixMessageSender,
@@ -29,14 +27,16 @@ use nym_gateway_client::AcknowledgementReceiver;
use nym_sphinx::acknowledgements::AckKey; use nym_sphinx::acknowledgements::AckKey;
use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::params::PacketType; use nym_sphinx::params::PacketType;
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;
use crate::client::replies::reply_controller;
use crate::config;
pub(crate) use acknowledgement_control::{AckActionSender, Action}; pub(crate) use acknowledgement_control::{AckActionSender, Action};
use nym_statistics_common::clients::ClientStatsSender;
pub(crate) mod acknowledgement_control; pub(crate) mod acknowledgement_control;
pub(crate) mod message_handler; pub(crate) mod message_handler;
pub(crate) mod real_traffic_stream; pub(crate) mod real_traffic_stream;
@@ -148,7 +148,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 +178,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 +187,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 +194,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 +206,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 +215,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 {
@@ -237,7 +230,6 @@ where
// poisson delay, but is it really a problem? // poisson delay, but is it really a problem?
let topology_permit = self.topology_access.get_read_permit().await; let topology_permit = self.topology_access.get_read_permit().await;
// the ack is sent back to ourselves (and then ignored) // the ack is sent back to ourselves (and then ignored)
let topology_ref = match topology_permit.try_get_valid_topology_ref( let topology_ref = match topology_permit.try_get_valid_topology_ref(
&self.config.our_full_destination, &self.config.our_full_destination,
Some(&self.config.our_full_destination), Some(&self.config.our_full_destination),
@@ -278,9 +270,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 +503,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 +534,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;
} }
@@ -70,10 +70,7 @@ impl SendingDelayController {
lower_bound, lower_bound,
multiplier_elevated_counter: 0, multiplier_elevated_counter: 0,
time_when_logged_about_elevated_multiplier: now time_when_logged_about_elevated_multiplier: now
.checked_sub(Duration::from_secs( - Duration::from_secs(INTERVAL_BETWEEN_WARNING_ABOUT_ELEVATED_MULTIPLIER_SECS),
INTERVAL_BETWEEN_WARNING_ABOUT_ELEVATED_MULTIPLIER_SECS,
))
.unwrap_or(now),
time_when_changed: now, time_when_changed: now,
time_when_backpressure_detected: now, time_when_backpressure_detected: now,
} }
@@ -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,
} }
} }
@@ -522,6 +516,7 @@ where
recipient, recipient,
to_send, to_send,
nym_sphinx::params::PacketType::Mix, nym_sphinx::params::PacketType::Mix,
self.config.reply_surbs.surb_mix_hops,
) )
.await .await
{ {
@@ -852,11 +847,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 +861,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>,
@@ -16,14 +16,14 @@
#![warn(clippy::todo)] #![warn(clippy::todo)]
#![warn(clippy::dbg_macro)] #![warn(clippy::dbg_macro)]
use futures::StreamExt; use std::time::Duration;
use nym_client_core_config_types::StatsReporting; use nym_client_core_config_types::StatsReporting;
use nym_sphinx::addressing::Recipient; 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 crate::{ use crate::{
client::inbound_messages::{InputMessage, InputMessageSender}, client::inbound_messages::{InputMessage, InputMessageSender},
@@ -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,43 +91,16 @@ 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"))] let mut stats_report_interval =
let mut stats_report_interval = tokio_stream::wrappers::IntervalStream::new( tokio::time::interval(self.reporting_config.reporting_interval);
tokio::time::interval(self.reporting_config.reporting_interval), let mut local_report_interval = tokio::time::interval(LOCAL_REPORT_INTERVAL);
); let mut snapshot_interval = tokio::time::interval(SNAPSHOT_INTERVAL);
#[cfg(not(target_arch = "wasm32"))] loop {
let mut local_report_interval = tokio_stream::wrappers::IntervalStream::new(
tokio::time::interval(LOCAL_REPORT_INTERVAL),
);
#[cfg(not(target_arch = "wasm32"))]
let mut snapshot_interval =
tokio_stream::wrappers::IntervalStream::new(tokio::time::interval(SNAPSHOT_INTERVAL));
#[cfg(target_arch = "wasm32")]
let mut stats_report_interval = gloo_timers::future::IntervalStream::new(
self.reporting_config.reporting_interval.as_millis() as u32,
);
#[cfg(target_arch = "wasm32")]
let mut local_report_interval =
gloo_timers::future::IntervalStream::new(LOCAL_REPORT_INTERVAL.as_millis() as u32);
#[cfg(target_arch = "wasm32")]
let mut snapshot_interval =
gloo_timers::future::IntervalStream::new(SNAPSHOT_INTERVAL.as_millis() as u32);
while !self.task_client.is_shutdown() {
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 => {
@@ -143,48 +108,44 @@ impl StatisticsControl {
break; break;
} }
}, },
_ = snapshot_interval.next() => { _ = snapshot_interval.tick() => {
self.stats.snapshot(); self.stats.snapshot();
} }
_ = stats_report_interval.next() => { _ = stats_report_interval.tick(), if self.reporting_config.enabled && self.reporting_config.provider_address.is_some() => {
let Some(recipient) = self.reporting_config.provider_address else { // SAFTEY : this branch executes only if reporting is not none, so unwrapp is fine
continue #[allow(clippy::unwrap_used)]
}; self.report_stats(self.reporting_config.provider_address.unwrap()).await;
if self.reporting_config.enabled {
self.report_stats(recipient).await;
}
} }
_ = local_report_interval.next() => { _ = local_report_interval.tick() => {
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
} }
} }
@@ -2,7 +2,8 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::addressing::clients::Recipient;
use nym_topology::{NymRouteProvider, NymTopology, NymTopologyError}; use nym_sphinx::params::DEFAULT_NUM_MIX_HOPS;
use nym_topology::{NymTopology, NymTopologyError};
use std::ops::Deref; use std::ops::Deref;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc; use std::sync::Arc;
@@ -16,36 +17,29 @@ pub struct TopologyAccessorInner {
// few seconds, while reads are needed every single packet generated. // few seconds, while reads are needed every single packet generated.
// However, proper benchmarks will be needed to determine if `RwLock` is indeed a better // However, proper benchmarks will be needed to determine if `RwLock` is indeed a better
// approach than a `Mutex` // approach than a `Mutex`
topology: RwLock<NymRouteProvider>, topology: RwLock<Option<NymTopology>>,
} }
impl TopologyAccessorInner { impl TopologyAccessorInner {
fn new(initial: NymRouteProvider) -> Self { fn new() -> Self {
TopologyAccessorInner { TopologyAccessorInner {
controlled_manually: AtomicBool::new(false), controlled_manually: AtomicBool::new(false),
released_manual_control: Notify::new(), released_manual_control: Notify::new(),
topology: RwLock::new(initial), topology: RwLock::new(None),
} }
} }
async fn update(&self, new: Option<NymTopology>) { async fn update(&self, new: Option<NymTopology>) {
let mut guard = self.topology.write().await; *self.topology.write().await = new;
match new {
Some(updated) => {
guard.update(updated);
}
None => guard.clear_topology(),
}
} }
} }
pub struct TopologyReadPermit<'a> { pub struct TopologyReadPermit<'a> {
permit: RwLockReadGuard<'a, NymRouteProvider>, permit: RwLockReadGuard<'a, Option<NymTopology>>,
} }
impl Deref for TopologyReadPermit<'_> { impl<'a> Deref for TopologyReadPermit<'a> {
type Target = NymRouteProvider; type Target = Option<NymTopology>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.permit &self.permit
@@ -59,31 +53,43 @@ impl<'a> TopologyReadPermit<'a> {
&'a self, &'a self,
ack_recipient: &Recipient, ack_recipient: &Recipient,
packet_recipient: Option<&Recipient>, packet_recipient: Option<&Recipient>,
) -> Result<&'a NymRouteProvider, NymTopologyError> { ) -> Result<&'a NymTopology, NymTopologyError> {
let route_provider = self.permit.deref();
let topology = &route_provider.topology;
// 1. Have we managed to get anything from the refresher, i.e. have the nym-api queries gone through? // 1. Have we managed to get anything from the refresher, i.e. have the nym-api queries gone through?
topology.ensure_not_empty()?; let topology = self
.permit
.as_ref()
.ok_or(NymTopologyError::EmptyNetworkTopology)?;
// 2. does the topology have a node on each mixing layer? // 2. does it have any mixnode at all?
topology.ensure_minimally_routable()?; // 3. does it have any gateways at all?
// 4. does it have a mixnode on each layer?
topology.ensure_can_construct_path_through(DEFAULT_NUM_MIX_HOPS)?;
// 3. does it contain OUR gateway (so that we could create an ack packet)? // 5. does it contain OUR gateway (so that we could create an ack packet)?
let _ = route_provider.egress_by_identity(ack_recipient.gateway())?; if !topology.gateway_exists(ack_recipient.gateway()) {
return Err(NymTopologyError::NonExistentGatewayError {
// 4. for our target recipient, does it contain THEIR gateway (so that we send anything over?) identity_key: ack_recipient.gateway().to_base58_string(),
if let Some(recipient) = packet_recipient { });
let _ = route_provider.egress_by_identity(recipient.gateway())?;
} }
Ok(route_provider) // 6. for our target recipient, does it contain THEIR gateway (so that we could create
if let Some(recipient) = packet_recipient {
if !topology.gateway_exists(recipient.gateway()) {
return Err(NymTopologyError::NonExistentGatewayError {
identity_key: recipient.gateway().to_base58_string(),
});
}
}
Ok(topology)
} }
} }
impl<'a> From<RwLockReadGuard<'a, NymRouteProvider>> for TopologyReadPermit<'a> { impl<'a> From<RwLockReadGuard<'a, Option<NymTopology>>> for TopologyReadPermit<'a> {
fn from(permit: RwLockReadGuard<'a, NymRouteProvider>) -> Self { fn from(read_permit: RwLockReadGuard<'a, Option<NymTopology>>) -> Self {
TopologyReadPermit { permit } TopologyReadPermit {
permit: read_permit,
}
} }
} }
@@ -93,11 +99,9 @@ pub struct TopologyAccessor {
} }
impl TopologyAccessor { impl TopologyAccessor {
pub fn new(ignore_egress_epoch_roles: bool) -> Self { pub fn new() -> Self {
TopologyAccessor { TopologyAccessor {
inner: Arc::new(TopologyAccessorInner::new(NymRouteProvider::new_empty( inner: Arc::new(TopologyAccessorInner::new()),
ignore_egress_epoch_roles,
))),
} }
} }
@@ -117,21 +121,8 @@ impl TopologyAccessor {
self.inner.released_manual_control.notified().await self.inner.released_manual_control.notified().await
} }
#[deprecated(note = "use .current_route_provider instead")]
pub async fn current_topology(&self) -> Option<NymTopology> { pub async fn current_topology(&self) -> Option<NymTopology> {
self.current_route_provider() self.inner.topology.read().await.clone()
.await
.as_ref()
.map(|p| p.topology.clone())
}
pub async fn current_route_provider(&self) -> Option<RwLockReadGuard<NymRouteProvider>> {
let provider = self.inner.topology.read().await;
if provider.topology.is_empty() {
None
} else {
Some(provider)
}
} }
pub async fn manually_change_topology(&self, new_topology: NymTopology) { pub async fn manually_change_topology(&self, new_topology: NymTopology) {
@@ -149,11 +140,15 @@ impl TopologyAccessor {
// only used by the client at startup to get a slightly more reasonable error message // only used by the client at startup to get a slightly more reasonable error message
// (currently displays as unused because health checker is disabled due to required changes) // (currently displays as unused because health checker is disabled due to required changes)
pub async fn ensure_is_routable(&self) -> Result<(), NymTopologyError> { pub async fn ensure_is_routable(&self) -> Result<(), NymTopologyError> {
self.inner match self.inner.topology.read().await.deref() {
.topology None => Err(NymTopologyError::EmptyNetworkTopology),
.read() Some(ref topology) => topology.ensure_can_construct_path_through(DEFAULT_NUM_MIX_HOPS),
.await }
.topology }
.ensure_minimally_routable() }
impl Default for TopologyAccessor {
fn default() -> Self {
TopologyAccessor::new()
} }
} }
@@ -3,6 +3,7 @@ use log::{debug, error};
use nym_explorer_client::{ExplorerClient, PrettyDetailedMixNodeBond}; use nym_explorer_client::{ExplorerClient, PrettyDetailedMixNodeBond};
use nym_network_defaults::var_names::EXPLORER_API; use nym_network_defaults::var_names::EXPLORER_API;
use nym_topology::{ use nym_topology::{
nym_topology_from_basic_info,
provider_trait::{async_trait, TopologyProvider}, provider_trait::{async_trait, TopologyProvider},
NymTopology, NymTopology,
}; };
@@ -14,6 +15,8 @@ use url::Url;
pub use nym_country_group::CountryGroup; pub use nym_country_group::CountryGroup;
const MIN_NODES_PER_LAYER: usize = 1;
fn create_explorer_client() -> Option<ExplorerClient> { fn create_explorer_client() -> Option<ExplorerClient> {
let Ok(explorer_api_url) = std::env::var(EXPLORER_API) else { let Ok(explorer_api_url) = std::env::var(EXPLORER_API) else {
error!("Missing EXPLORER_API"); error!("Missing EXPLORER_API");
@@ -60,22 +63,37 @@ fn log_mixnode_distribution(mixnodes: &HashMap<CountryGroup, Vec<NodeId>>) {
} }
fn check_layer_integrity(topology: NymTopology) -> Result<(), ()> { fn check_layer_integrity(topology: NymTopology) -> Result<(), ()> {
if topology.ensure_minimally_routable().is_err() { let mixes = topology.mixes();
if mixes.keys().len() < 3 {
error!("Layer is missing in topology!"); error!("Layer is missing in topology!");
return Err(()); return Err(());
} }
for (layer, mixnodes) in mixes {
debug!("Layer {:?} has {} mixnodes", layer, mixnodes.len());
if mixnodes.len() < MIN_NODES_PER_LAYER {
error!(
"There are only {} mixnodes in layer {:?}",
mixnodes.len(),
layer
);
return Err(());
}
}
Ok(()) Ok(())
} }
#[deprecated(note = "use NymApiTopologyProvider instead as explorer API will soon be removed")]
pub struct GeoAwareTopologyProvider { pub struct GeoAwareTopologyProvider {
validator_client: nym_validator_client::client::NymApiClient, validator_client: nym_validator_client::client::NymApiClient,
filter_on: GroupBy, filter_on: GroupBy,
client_version: String,
} }
#[allow(deprecated)]
impl GeoAwareTopologyProvider { impl GeoAwareTopologyProvider {
pub fn new(mut nym_api_urls: Vec<Url>, filter_on: GroupBy) -> GeoAwareTopologyProvider { pub fn new(
mut nym_api_urls: Vec<Url>,
client_version: String,
filter_on: GroupBy,
) -> GeoAwareTopologyProvider {
log::info!( log::info!(
"Creating geo-aware topology provider with filter on {}", "Creating geo-aware topology provider with filter on {}",
filter_on filter_on
@@ -87,22 +105,14 @@ impl GeoAwareTopologyProvider {
nym_api_urls[0].clone(), nym_api_urls[0].clone(),
), ),
filter_on, filter_on,
client_version,
} }
} }
async fn get_topology(&self) -> Option<NymTopology> { async fn get_topology(&self) -> Option<NymTopology> {
let rewarded_set = self
.validator_client
.get_current_rewarded_set()
.await
.inspect_err(|err| error!("failed to get current rewarded set: {err}"))
.ok()?;
let mut topology = NymTopology::new_empty(rewarded_set);
let mixnodes = match self let mixnodes = match self
.validator_client .validator_client
.get_all_basic_active_mixing_assigned_nodes() .get_all_basic_active_mixing_assigned_nodes(Some(self.client_version.clone()))
.await .await
{ {
Err(err) => { Err(err) => {
@@ -114,7 +124,7 @@ impl GeoAwareTopologyProvider {
let gateways = match self let gateways = match self
.validator_client .validator_client
.get_all_basic_entry_assigned_nodes() .get_all_basic_entry_assigned_nodes(Some(self.client_version.clone()))
.await .await
{ {
Err(err) => { Err(err) => {
@@ -183,8 +193,7 @@ impl GeoAwareTopologyProvider {
.filter(|m| filtered_mixnode_ids.contains(&m.node_id)) .filter(|m| filtered_mixnode_ids.contains(&m.node_id))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
topology.add_skimmed_nodes(&mixnodes); let topology = nym_topology_from_basic_info(&mixnodes, &gateways);
topology.add_skimmed_nodes(&gateways);
// TODO: return real error type // TODO: return real error type
check_layer_integrity(topology.clone()).ok()?; check_layer_integrity(topology.clone()).ok()?;
@@ -193,7 +202,6 @@ impl GeoAwareTopologyProvider {
} }
} }
#[allow(deprecated)]
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
#[async_trait] #[async_trait]
impl TopologyProvider for GeoAwareTopologyProvider { impl TopologyProvider for GeoAwareTopologyProvider {
@@ -203,7 +211,6 @@ impl TopologyProvider for GeoAwareTopologyProvider {
} }
} }
#[allow(deprecated)]
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
#[async_trait(?Send)] #[async_trait(?Send)]
impl TopologyProvider for GeoAwareTopologyProvider { impl TopologyProvider for GeoAwareTopologyProvider {
@@ -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;
@@ -20,7 +19,6 @@ mod accessor;
pub mod geo_aware_provider; pub mod geo_aware_provider;
pub mod nym_api_provider; pub mod nym_api_provider;
#[allow(deprecated)]
pub use geo_aware_provider::GeoAwareTopologyProvider; pub use geo_aware_provider::GeoAwareTopologyProvider;
pub use nym_api_provider::{Config as NymApiTopologyProviderConfig, NymApiTopologyProvider}; pub use nym_api_provider::{Config as NymApiTopologyProviderConfig, NymApiTopologyProvider};
pub use nym_topology::provider_trait::TopologyProvider; pub use nym_topology::provider_trait::TopologyProvider;
@@ -29,7 +27,7 @@ pub use nym_topology::provider_trait::TopologyProvider;
const MAX_FAILURE_COUNT: usize = 10; const MAX_FAILURE_COUNT: usize = 10;
pub struct TopologyRefresherConfig { pub struct TopologyRefresherConfig {
pub refresh_rate: Duration, refresh_rate: Duration,
} }
impl TopologyRefresherConfig { impl TopologyRefresherConfig {
@@ -44,8 +42,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 +49,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,
} }
} }
@@ -102,24 +96,28 @@ impl TopologyRefresher {
self.topology_accessor.ensure_is_routable().await self.topology_accessor.ensure_is_routable().await
} }
pub async fn ensure_contains_routable_egress( pub async fn ensure_contains_gateway(
&self, &self,
egress: NodeIdentity, gateway: &NodeIdentity,
) -> Result<(), NymTopologyError> { ) -> Result<(), NymTopologyError> {
let topology = self let topology = self
.topology_accessor .topology_accessor
.current_route_provider() .current_topology()
.await .await
.ok_or(NymTopologyError::EmptyNetworkTopology)?; .ok_or(NymTopologyError::EmptyNetworkTopology)?;
let _ = topology.egress_by_identity(egress)?; if !topology.gateway_exists(gateway) {
return Err(NymTopologyError::NonExistentGatewayError {
identity_key: gateway.to_base58_string(),
});
}
Ok(()) Ok(())
} }
pub async fn wait_for_gateway( pub async fn wait_for_gateway(
&mut self, &mut self,
gateway: NodeIdentity, gateway: &NodeIdentity,
timeout_duration: Duration, timeout_duration: Duration,
) -> Result<(), NymTopologyError> { ) -> Result<(), NymTopologyError> {
info!( info!(
@@ -137,7 +135,7 @@ impl TopologyRefresher {
}) })
} }
_ = self.try_refresh() => { _ = self.try_refresh() => {
if self.ensure_contains_routable_egress(gateway).await.is_ok() { if self.ensure_contains_gateway(gateway).await.is_ok() {
return Ok(()) return Ok(())
} }
info!("gateway '{gateway}' is still not online..."); info!("gateway '{gateway}' is still not online...");
@@ -147,7 +145,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 +158,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");
}) })
} }
@@ -4,51 +4,47 @@
use async_trait::async_trait; use async_trait::async_trait;
use log::{debug, error, warn}; use log::{debug, error, warn};
use nym_topology::provider_trait::TopologyProvider; use nym_topology::provider_trait::TopologyProvider;
use nym_topology::NymTopology; use nym_topology::{NymTopology, NymTopologyError};
use nym_validator_client::UserAgent; use nym_validator_client::UserAgent;
use rand::prelude::SliceRandom; use rand::prelude::SliceRandom;
use rand::thread_rng; use rand::thread_rng;
use std::cmp::min;
use url::Url; use url::Url;
// the same values as our current (10.06.24) blacklist
pub const DEFAULT_MIN_MIXNODE_PERFORMANCE: u8 = 50;
pub const DEFAULT_MIN_GATEWAY_PERFORMANCE: u8 = 50;
#[derive(Debug)] #[derive(Debug)]
pub struct Config { pub struct Config {
pub min_mixnode_performance: u8, pub min_mixnode_performance: u8,
pub min_gateway_performance: u8, pub min_gateway_performance: u8,
pub use_extended_topology: bool,
pub ignore_egress_epoch_role: bool,
} }
impl From<nym_client_core_config_types::Topology> for Config { impl Default for Config {
fn from(value: nym_client_core_config_types::Topology) -> Self { fn default() -> Self {
// old values that decided on blacklist membership
Config { Config {
min_mixnode_performance: value.minimum_mixnode_performance, min_mixnode_performance: DEFAULT_MIN_MIXNODE_PERFORMANCE,
min_gateway_performance: value.minimum_gateway_performance, min_gateway_performance: DEFAULT_MIN_GATEWAY_PERFORMANCE,
use_extended_topology: value.use_extended_topology,
ignore_egress_epoch_role: value.ignore_egress_epoch_role,
} }
} }
} }
impl Config {
// if we're using 'extended' topology, filter the nodes based on the lowest set performance
fn min_node_performance(&self) -> u8 {
min(self.min_mixnode_performance, self.min_gateway_performance)
}
}
pub struct NymApiTopologyProvider { pub struct NymApiTopologyProvider {
config: Config, config: Config,
validator_client: nym_validator_client::client::NymApiClient, validator_client: nym_validator_client::client::NymApiClient,
nym_api_urls: Vec<Url>, nym_api_urls: Vec<Url>,
client_version: String,
currently_used_api: usize, currently_used_api: usize,
} }
impl NymApiTopologyProvider { impl NymApiTopologyProvider {
pub fn new( pub fn new(
config: impl Into<Config>, config: Config,
mut nym_api_urls: Vec<Url>, mut nym_api_urls: Vec<Url>,
client_version: String,
user_agent: Option<UserAgent>, user_agent: Option<UserAgent>,
) -> Self { ) -> Self {
nym_api_urls.shuffle(&mut thread_rng()); nym_api_urls.shuffle(&mut thread_rng());
@@ -63,9 +59,10 @@ impl NymApiTopologyProvider {
}; };
NymApiTopologyProvider { NymApiTopologyProvider {
config: config.into(), config,
validator_client, validator_client,
nym_api_urls, nym_api_urls,
client_version,
currently_used_api: 0, currently_used_api: 0,
} }
} }
@@ -81,69 +78,70 @@ impl NymApiTopologyProvider {
.change_nym_api(self.nym_api_urls[self.currently_used_api].clone()) .change_nym_api(self.nym_api_urls[self.currently_used_api].clone())
} }
/// Verifies whether nodes a reasonably distributed among all mix layers.
///
/// In ideal world we would have 33% nodes on layer 1, 33% on layer 2 and 33% on layer 3.
/// However, this is a rather unrealistic expectation, instead we check whether there exists
/// a layer with more than 66% of nodes or with fewer than 15% and if so, we trigger a failure.
///
/// # Arguments
///
/// * `topology`: active topology constructed from validator api data
fn check_layer_distribution(
&self,
active_topology: &NymTopology,
) -> Result<(), NymTopologyError> {
let lower_threshold = 0.15;
let upper_threshold = 0.66;
active_topology.ensure_even_layer_distribution(lower_threshold, upper_threshold)
}
async fn get_current_compatible_topology(&mut self) -> Option<NymTopology> { async fn get_current_compatible_topology(&mut self) -> Option<NymTopology> {
let rewarded_set = self let mixnodes = match self
.validator_client .validator_client
.get_current_rewarded_set() .get_all_basic_active_mixing_assigned_nodes(Some(self.client_version.clone()))
.await .await
.inspect_err(|err| error!("failed to get current rewarded set: {err}")) {
.ok()?; Err(err) => {
error!("failed to get network mixnodes - {err}");
let mut topology = NymTopology::new_empty(rewarded_set); return None;
}
if self.config.use_extended_topology { Ok(mixes) => mixes,
let all_nodes = self
.validator_client
.get_all_basic_nodes()
.await
.inspect_err(|err| error!("failed to get network nodes: {err}"))
.ok()?;
debug!(
"there are {} nodes on the network (before filtering)",
all_nodes.len()
);
topology.add_additional_nodes(all_nodes.iter().filter(|n| {
n.performance.round_to_integer() >= self.config.min_node_performance()
}));
} else {
// if we're not using extended topology, we're only getting active set mixnodes and gateways
let mixnodes = self
.validator_client
.get_all_basic_active_mixing_assigned_nodes()
.await
.inspect_err(|err| error!("failed to get network mixnodes: {err}"))
.ok()?;
// TODO: we really should be getting ACTIVE gateways only
let gateways = self
.validator_client
.get_all_basic_entry_assigned_nodes()
.await
.inspect_err(|err| error!("failed to get network gateways: {err}"))
.ok()?;
debug!(
"there are {} mixnodes and {} gateways in total (before performance filtering)",
mixnodes.len(),
gateways.len()
);
topology.add_additional_nodes(mixnodes.iter().filter(|m| {
m.performance.round_to_integer() >= self.config.min_mixnode_performance
}));
topology.add_additional_nodes(gateways.iter().filter(|m| {
m.performance.round_to_integer() >= self.config.min_gateway_performance
}));
}; };
if !topology.is_minimally_routable() { let gateways = match self
error!("the current filtered active topology can't be used to construct any packets"); .validator_client
return None; .get_all_basic_entry_assigned_nodes(Some(self.client_version.clone()))
} .await
{
Err(err) => {
error!("failed to get network gateways - {err}");
return None;
}
Ok(gateways) => gateways,
};
Some(topology) debug!(
"there are {} mixnodes and {} gateways in total (before performance filtering)",
mixnodes.len(),
gateways.len()
);
let topology = NymTopology::from_unordered(
mixnodes.iter().filter(|m| {
m.performance.round_to_integer() >= self.config.min_mixnode_performance
}),
gateways.iter().filter(|g| {
g.performance.round_to_integer() >= self.config.min_gateway_performance
}),
);
if let Err(err) = self.check_layer_distribution(&topology) {
warn!("The current filtered active topology has extremely skewed layer distribution. It cannot be used: {err}");
self.use_next_nym_api();
None
} else {
Some(topology)
}
} }
} }
@@ -152,11 +150,7 @@ impl NymApiTopologyProvider {
#[async_trait] #[async_trait]
impl TopologyProvider for NymApiTopologyProvider { impl TopologyProvider for NymApiTopologyProvider {
async fn get_new_topology(&mut self) -> Option<NymTopology> { async fn get_new_topology(&mut self) -> Option<NymTopology> {
let Some(topology) = self.get_current_compatible_topology().await else { self.get_current_compatible_topology().await
self.use_next_nym_api();
return None;
};
Some(topology)
} }
} }
@@ -164,10 +158,6 @@ impl TopologyProvider for NymApiTopologyProvider {
#[async_trait(?Send)] #[async_trait(?Send)]
impl TopologyProvider for NymApiTopologyProvider { impl TopologyProvider for NymApiTopologyProvider {
async fn get_new_topology(&mut self) -> Option<NymTopology> { async fn get_new_topology(&mut self) -> Option<NymTopology> {
let Some(topology) = self.get_current_compatible_topology().await else { self.get_current_compatible_topology().await
self.use_next_nym_api();
return None;
};
Some(topology)
} }
} }
+4 -20
View File
@@ -4,8 +4,8 @@
use crate::client::mix_traffic::transceiver::ErasedGatewayError; use crate::client::mix_traffic::transceiver::ErasedGatewayError;
use nym_crypto::asymmetric::identity::Ed25519RecoveryError; use nym_crypto::asymmetric::identity::Ed25519RecoveryError;
use nym_gateway_client::error::GatewayClientError; use nym_gateway_client::error::GatewayClientError;
use nym_topology::node::RoutingNodeError; use nym_topology::gateway::GatewayConversionError;
use nym_topology::{NodeId, NymTopologyError}; use nym_topology::NymTopologyError;
use nym_validator_client::ValidatorClientError; use nym_validator_client::ValidatorClientError;
use std::error::Error; use std::error::Error;
use std::path::PathBuf; use std::path::PathBuf;
@@ -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,
@@ -81,10 +74,10 @@ pub enum ClientCoreError {
#[error("the gateway id is invalid - {0}")] #[error("the gateway id is invalid - {0}")]
UnableToCreatePublicKeyFromGatewayId(Ed25519RecoveryError), UnableToCreatePublicKeyFromGatewayId(Ed25519RecoveryError),
#[error("the node is malformed: {source}")] #[error("The gateway is malformed: {source}")]
MalformedGateway { MalformedGateway {
#[from] #[from]
source: Box<RoutingNodeError>, source: GatewayConversionError,
}, },
#[error("failed to establish connection to gateway: {source}")] #[error("failed to establish connection to gateway: {source}")]
@@ -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 },
@@ -169,9 +159,6 @@ pub enum ClientCoreError {
#[error("the specified gateway '{gateway}' does not support the wss protocol")] #[error("the specified gateway '{gateway}' does not support the wss protocol")]
UnsupportedWssProtocol { gateway: String }, UnsupportedWssProtocol { gateway: String },
#[error("node {id} ({identity}) does not support mixnet entry mode")]
UnsupportedEntry { id: NodeId, identity: String },
#[error( #[error(
"failed to load custom topology using path '{}'. detailed message: {source}", file_path.display() "failed to load custom topology using path '{}'. detailed message: {source}", file_path.display()
)] )]
@@ -222,9 +209,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
+49 -58
View File
@@ -7,7 +7,7 @@ use futures::{SinkExt, StreamExt};
use log::{debug, info, trace, warn}; use log::{debug, info, trace, warn};
use nym_crypto::asymmetric::identity; use nym_crypto::asymmetric::identity;
use nym_gateway_client::GatewayClient; use nym_gateway_client::GatewayClient;
use nym_topology::node::RoutingNode; use nym_topology::{gateway, mix};
use nym_validator_client::client::IdentityKeyRef; use nym_validator_client::client::IdentityKeyRef;
use nym_validator_client::UserAgent; use nym_validator_client::UserAgent;
use rand::{seq::SliceRandom, Rng}; use rand::{seq::SliceRandom, Rng};
@@ -15,10 +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;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use tokio::net::TcpStream; use tokio::net::TcpStream;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
@@ -26,7 +22,10 @@ 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;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
@@ -49,30 +48,22 @@ const PING_TIMEOUT: Duration = Duration::from_millis(1000);
// The abstraction that some of these helpers use // The abstraction that some of these helpers use
pub trait ConnectableGateway { pub trait ConnectableGateway {
fn node_id(&self) -> NodeId; fn identity(&self) -> &identity::PublicKey;
fn identity(&self) -> identity::PublicKey; fn clients_address(&self) -> String;
fn clients_address(&self, prefer_ipv6: bool) -> Option<String>;
fn is_wss(&self) -> bool; fn is_wss(&self) -> bool;
} }
impl ConnectableGateway for RoutingNode { impl ConnectableGateway for gateway::LegacyNode {
fn node_id(&self) -> NodeId { fn identity(&self) -> &identity::PublicKey {
self.node_id self.identity()
} }
fn identity(&self) -> identity::PublicKey { fn clients_address(&self) -> String {
self.identity_key self.clients_address()
}
fn clients_address(&self, prefer_ipv6: bool) -> Option<String> {
self.ws_entry_address(prefer_ipv6)
} }
fn is_wss(&self) -> bool { fn is_wss(&self) -> bool {
self.entry self.clients_wss_port.is_some()
.as_ref()
.map(|e| e.clients_wss_port.is_some())
.unwrap_or_default()
} }
} }
@@ -87,13 +78,11 @@ impl<'a, G: ConnectableGateway> GatewayWithLatency<'a, G> {
} }
} }
pub async fn gateways_for_init<R: Rng>( pub async fn current_gateways<R: Rng>(
rng: &mut R, rng: &mut R,
nym_apis: &[Url], nym_apis: &[Url],
user_agent: Option<UserAgent>, user_agent: Option<UserAgent>,
minimum_performance: u8, ) -> Result<Vec<gateway::LegacyNode>, ClientCoreError> {
ignore_epoch_roles: bool,
) -> Result<Vec<RoutingNode>, ClientCoreError> {
let nym_api = nym_apis let nym_api = nym_apis
.choose(rng) .choose(rng)
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?; .ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
@@ -105,35 +94,49 @@ pub async fn gateways_for_init<R: Rng>(
log::debug!("Fetching list of gateways from: {nym_api}"); log::debug!("Fetching list of gateways from: {nym_api}");
let gateways = client.get_all_basic_entry_assigned_nodes().await?; let gateways = client.get_all_basic_entry_assigned_nodes(None).await?;
info!("nym api reports {} gateways", gateways.len()); log::debug!("Found {} gateways", gateways.len());
log::trace!("Gateways: {:#?}", gateways); log::trace!("Gateways: {:#?}", gateways);
// filter out gateways below minimum performance and ones that could operate as a mixnode
// (we don't want instability)
let valid_gateways = gateways let valid_gateways = gateways
.iter() .iter()
.filter(|g| ignore_epoch_roles || !g.supported_roles.mixnode)
.filter(|g| g.performance.round_to_integer() >= minimum_performance)
.filter_map(|gateway| gateway.try_into().ok()) .filter_map(|gateway| gateway.try_into().ok())
.collect::<Vec<_>>(); .collect::<Vec<gateway::LegacyNode>>();
log::debug!("After checking validity: {}", valid_gateways.len()); log::debug!("After checking validity: {}", valid_gateways.len());
log::trace!("Valid gateways: {:#?}", valid_gateways); log::trace!("Valid gateways: {:#?}", valid_gateways);
log::info!( log::info!("nym-api reports {} valid gateways", valid_gateways.len());
"and {} after validity and performance filtering",
valid_gateways.len()
);
Ok(valid_gateways) Ok(valid_gateways)
} }
pub async fn current_mixnodes<R: Rng>(
rng: &mut R,
nym_apis: &[Url],
) -> Result<Vec<mix::LegacyNode>, ClientCoreError> {
let nym_api = nym_apis
.choose(rng)
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
let client = nym_validator_client::client::NymApiClient::new(nym_api.clone());
log::trace!("Fetching list of mixnodes from: {nym_api}");
let mixnodes = client
.get_all_basic_active_mixing_assigned_nodes(None)
.await?;
let valid_mixnodes = mixnodes
.iter()
.filter_map(|mixnode| mixnode.try_into().ok())
.collect::<Vec<mix::LegacyNode>>();
Ok(valid_mixnodes)
}
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
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),
} }
} }
@@ -147,12 +150,7 @@ async fn measure_latency<G>(gateway: &G) -> Result<GatewayWithLatency<G>, Client
where where
G: ConnectableGateway, G: ConnectableGateway,
{ {
let Some(addr) = gateway.clients_address(false) else { let addr = gateway.clients_address();
return Err(ClientCoreError::UnsupportedEntry {
id: gateway.node_id(),
identity: gateway.identity().to_string(),
});
};
trace!( trace!(
"establishing connection to {} ({addr})...", "establishing connection to {} ({addr})...",
gateway.identity(), gateway.identity(),
@@ -208,7 +206,7 @@ where
Ok(GatewayWithLatency::new(gateway, avg)) Ok(GatewayWithLatency::new(gateway, avg))
} }
pub async fn choose_gateway_by_latency<R: Rng, G: ConnectableGateway + Clone>( pub async fn choose_gateway_by_latency<'a, R: Rng, G: ConnectableGateway + Clone>(
rng: &mut R, rng: &mut R,
gateways: &[G], gateways: &[G],
must_use_tls: bool, must_use_tls: bool,
@@ -223,7 +221,7 @@ pub async fn choose_gateway_by_latency<R: Rng, G: ConnectableGateway + Clone>(
let gateways_with_latency = Arc::new(tokio::sync::Mutex::new(Vec::new())); let gateways_with_latency = Arc::new(tokio::sync::Mutex::new(Vec::new()));
futures::stream::iter(gateways) futures::stream::iter(gateways)
.for_each_concurrent(CONCURRENT_GATEWAYS_MEASURED, |gateway| async { .for_each_concurrent(CONCURRENT_GATEWAYS_MEASURED, |gateway| async {
let id = gateway.identity(); let id = *gateway.identity();
trace!("measuring latency to {id}..."); trace!("measuring latency to {id}...");
match measure_latency(gateway).await { match measure_latency(gateway).await {
Ok(with_latency) => { Ok(with_latency) => {
@@ -270,9 +268,9 @@ fn filter_by_tls<G: ConnectableGateway>(
pub(super) fn uniformly_random_gateway<R: Rng>( pub(super) fn uniformly_random_gateway<R: Rng>(
rng: &mut R, rng: &mut R,
gateways: &[RoutingNode], gateways: &[gateway::LegacyNode],
must_use_tls: bool, must_use_tls: bool,
) -> Result<RoutingNode, ClientCoreError> { ) -> Result<gateway::LegacyNode, ClientCoreError> {
filter_by_tls(gateways, must_use_tls)? filter_by_tls(gateways, must_use_tls)?
.choose(rng) .choose(rng)
.ok_or(ClientCoreError::NoGatewaysOnNetwork) .ok_or(ClientCoreError::NoGatewaysOnNetwork)
@@ -281,9 +279,9 @@ pub(super) fn uniformly_random_gateway<R: Rng>(
pub(super) fn get_specified_gateway( pub(super) fn get_specified_gateway(
gateway_identity: IdentityKeyRef, gateway_identity: IdentityKeyRef,
gateways: &[RoutingNode], gateways: &[gateway::LegacyNode],
must_use_tls: bool, must_use_tls: bool,
) -> Result<RoutingNode, ClientCoreError> { ) -> Result<gateway::LegacyNode, ClientCoreError> {
log::debug!("Requesting specified gateway: {}", gateway_identity); log::debug!("Requesting specified gateway: {}", gateway_identity);
let user_gateway = identity::PublicKey::from_base58_string(gateway_identity) let user_gateway = identity::PublicKey::from_base58_string(gateway_identity)
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?; .map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
@@ -293,14 +291,7 @@ pub(super) fn get_specified_gateway(
.find(|gateway| gateway.identity_key == user_gateway) .find(|gateway| gateway.identity_key == user_gateway)
.ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_identity.to_string()))?; .ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_identity.to_string()))?;
let Some(entry_details) = gateway.entry.as_ref() else { if must_use_tls && gateway.clients_wss_port.is_none() {
return Err(ClientCoreError::UnsupportedEntry {
id: gateway.node_id,
identity: gateway.identity().to_string(),
});
};
if must_use_tls && entry_details.clients_wss_port.is_none() {
return Err(ClientCoreError::UnsupportedWssProtocol { return Err(ClientCoreError::UnsupportedWssProtocol {
gateway: gateway_identity.to_string(), gateway: gateway_identity.to_string(),
}); });

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