Compare commits

..

3 Commits

Author SHA1 Message Date
Tommy Verrall 01f52153bc Update ci-build-upload-binaries.yml
add features console
2025-07-23 11:12:48 +02:00
Tommy Verrall 18593539f6 Update ci-build-upload-binaries.yml 2025-07-22 16:56:26 +02:00
Tommy Verrall 2580676c76 add tokio console 2025-07-22 16:48:43 +02:00
1185 changed files with 33033 additions and 61803 deletions
@@ -21,7 +21,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
platform: [ arc-linux-latest ] platform: [ arc-ubuntu-22.04 ]
runs-on: ${{ matrix.platform }} runs-on: ${{ matrix.platform }}
env: env:
@@ -45,7 +45,7 @@ jobs:
if: github.event_name == 'workflow_dispatch' && inputs.add_tokio_unstable == true if: github.event_name == 'workflow_dispatch' && inputs.add_tokio_unstable == true
run: | run: |
echo "RUSTFLAGS=--cfg tokio_unstable" >> $GITHUB_ENV echo "RUSTFLAGS=--cfg tokio_unstable" >> $GITHUB_ENV
echo "CARGO_FEATURES=--features tokio-console" >> $GITHUB_ENV echo "CARGO_FEATURES=--features console" >> $GITHUB_ENV
- name: Install Rust stable - name: Install Rust stable
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
+1 -1
View File
@@ -9,7 +9,7 @@ on:
jobs: jobs:
wasm: wasm:
runs-on: arc-linux-latest runs-on: arc-ubuntu-22.04
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
RUSTUP_PERMIT_COPY_RENAME: 1 RUSTUP_PERMIT_COPY_RENAME: 1
+8 -17
View File
@@ -38,7 +38,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [ arc-linux-latest, custom-windows-11, custom-macos-15 ] os: [ arc-ubuntu-22.04, custom-windows-11, custom-macos-15 ]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
@@ -46,9 +46,9 @@ jobs:
RUSTUP_PERMIT_COPY_RENAME: 1 RUSTUP_PERMIT_COPY_RENAME: 1
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 cmake 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
continue-on-error: true continue-on-error: true
if: contains(matrix.os, 'linux') if: contains(matrix.os, 'ubuntu')
- name: Check out repository code - name: Check out repository code
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -63,7 +63,7 @@ jobs:
# To avoid running out of disk space, skip generating debug symbols # To avoid running out of disk space, skip generating debug symbols
- name: Set debug to false (unix) - name: Set debug to false (unix)
if: contains(matrix.os, 'linux') || contains(matrix.os, 'mac') if: contains(matrix.os, 'ubuntu') || contains(matrix.os, 'mac')
run: | run: |
sed -i.bak 's/\[profile.dev\]/\[profile.dev\]\ndebug = false/' Cargo.toml sed -i.bak 's/\[profile.dev\]/\[profile.dev\]\ndebug = false/' Cargo.toml
git diff git diff
@@ -81,35 +81,26 @@ jobs:
command: fmt command: fmt
args: --all -- --check args: --all -- --check
- name: Clippy (macos) - name: Clippy
if: contains(matrix.os, 'mac')
uses: actions-rs/cargo@v1
with:
command: clippy
args: --workspace --all-targets --exclude nym-gateway-probe -- -D warnings
- name: Clippy (non-macos)
if: contains(matrix.os, 'linux') || contains(matrix.os, 'windows')
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: clippy command: clippy
args: --workspace --all-targets -- -D warnings args: --workspace --all-targets -- -D warnings
- name: Build all binaries - name: Build all binaries
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: build command: build
- name: Build all examples - name: Build all examples
if: contains(matrix.os, 'linux') if: contains(matrix.os, 'ubuntu')
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: build command: build
args: --workspace --examples args: --workspace --examples
- name: Run all tests - name: Run all tests
if: contains(matrix.os, 'linux') if: contains(matrix.os, 'ubuntu')
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
env: env:
NYM_API: https://sandbox-nym-api1.nymtech.net/api NYM_API: https://sandbox-nym-api1.nymtech.net/api
@@ -118,7 +109,7 @@ jobs:
args: --workspace args: --workspace
- name: Run expensive tests - name: Run expensive tests
if: (github.ref == 'refs/heads/develop' || github.event.pull_request.base.ref == 'develop' || github.event.pull_request.base.ref == 'master') && contains(matrix.os, 'linux') if: (github.ref == 'refs/heads/develop' || github.event.pull_request.base.ref == 'develop' || github.event.pull_request.base.ref == 'master') && contains(matrix.os, 'ubuntu')
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: test command: test
@@ -10,13 +10,13 @@ env:
jobs: jobs:
check-if-tag-exists: check-if-tag-exists:
runs-on: arc-linux-latest-dind runs-on: arc-ubuntu-22.04-dind
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Get version from cargo.toml - name: Get version from cargo.toml
uses: mikefarah/yq@v4.47.1 uses: mikefarah/yq@v4.45.4
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
@@ -16,7 +16,7 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Get version from cargo.toml - name: Get version from cargo.toml
uses: mikefarah/yq@v4.47.1 uses: mikefarah/yq@v4.45.4
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
@@ -11,7 +11,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
platform: [ arc-linux-latest-dind ] platform: [ arc-ubuntu-22.04 ]
runs-on: ${{ matrix.platform }} runs-on: ${{ matrix.platform }}
env: env:
@@ -28,11 +28,18 @@ jobs:
mkdir -p $OUTPUT_DIR mkdir -p $OUTPUT_DIR
echo $OUTPUT_DIR echo $OUTPUT_DIR
- name: Build contracts - name: Install Rust stable
run: make optimize-contracts uses: actions-rs/toolchain@v1
with:
toolchain: stable
target: wasm32-unknown-unknown
override: true
- name: Check optimized contracts - name: Install cosmwasm-check
run: make docker-check-contracts run: cargo install cosmwasm-check
- name: Build release contracts
run: make publish-contracts
- name: Prepare build output - name: Prepare build output
shell: bash shell: bash
+2 -2
View File
@@ -17,7 +17,7 @@ jobs:
build: build:
# since it's going to be compiled into wasm, there's absolutely # since it's going to be compiled into wasm, there's absolutely
# no point in running CI on different OS-es # no point in running CI on different OS-es
runs-on: arc-linux-latest runs-on: ubuntu-22.04
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
RUSTUP_PERMIT_COPY_RENAME: 1 RUSTUP_PERMIT_COPY_RENAME: 1
@@ -54,7 +54,7 @@ jobs:
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: test command: test
args: --lib --manifest-path contracts/Cargo.toml --all-features args: --lib --manifest-path contracts/Cargo.toml
- name: Check formatting - name: Check formatting
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
+1 -1
View File
@@ -10,7 +10,7 @@ on:
jobs: jobs:
build: build:
runs-on: arc-linux-latest runs-on: arc-ubuntu-22.04
env: env:
RUSTUP_PERMIT_COPY_RENAME: 1 RUSTUP_PERMIT_COPY_RENAME: 1
defaults: defaults:
+1 -1
View File
@@ -11,7 +11,7 @@ on:
jobs: jobs:
build: build:
runs-on: arc-linux-latest runs-on: arc-ubuntu-22.04
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
RUSTUP_PERMIT_COPY_RENAME: 1 RUSTUP_PERMIT_COPY_RENAME: 1
+7 -7
View File
@@ -4,14 +4,14 @@ on:
workflow_dispatch: workflow_dispatch:
pull_request: pull_request:
paths: paths:
- "wasm/**" - 'wasm/**'
- "clients/client-core/**" - 'clients/client-core/**'
- "common/**" - 'common/**'
- ".github/workflows/ci-sdk-wasm.yml" - '.github/workflows/ci-sdk-wasm.yml'
jobs: jobs:
wasm: wasm:
runs-on: arc-linux-latest runs-on: arc-ubuntu-22.04
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
RUSTUP_PERMIT_COPY_RENAME: 1 RUSTUP_PERMIT_COPY_RENAME: 1
@@ -33,7 +33,7 @@ jobs:
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: "1.24.6" go-version: "1.23.7"
- name: Install wasm-pack - name: Install wasm-pack
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
@@ -41,7 +41,7 @@ jobs:
- name: Install wasm-opt - name: Install wasm-opt
uses: ./.github/actions/install-wasm-opt uses: ./.github/actions/install-wasm-opt
with: with:
version: "116" version: '116'
- name: Install wasm-bindgen-cli - name: Install wasm-bindgen-cli
run: cargo install wasm-bindgen-cli run: cargo install wasm-bindgen-cli
+1 -1
View File
@@ -6,7 +6,7 @@ jobs:
greeting: greeting:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/first-interaction@v3 - uses: actions/first-interaction@v1
with: with:
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
issue-message: 'Thank you for raising this issue' issue-message: 'Thank you for raising this issue'
+1 -1
View File
@@ -31,7 +31,7 @@ jobs:
- name: Check out repository code - name: Check out repository code
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Download report from previous job - name: Download report from previous job
uses: actions/download-artifact@v5 uses: actions/download-artifact@v4
with: with:
name: report name: report
path: .github/workflows/support-files/notifications path: .github/workflows/support-files/notifications
+2 -2
View File
@@ -21,7 +21,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- os: arc-linux-latest - os: arc-ubuntu-22.04
target: x86_64-unknown-linux-gnu target: x86_64-unknown-linux-gnu
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
@@ -56,7 +56,7 @@ jobs:
- name: Install Rust stable - name: Install Rust stable
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
toolchain: 1.88.0 toolchain: 1.86.0
override: true override: true
- name: Build all binaries - name: Build all binaries
@@ -25,7 +25,7 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install Java - name: Install Java
uses: actions/setup-java@v5 uses: actions/setup-java@v4
with: with:
distribution: "temurin" distribution: "temurin"
java-version: "17" java-version: "17"
@@ -91,7 +91,7 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Download binary artifact - name: Download binary artifact
uses: actions/download-artifact@v5 uses: actions/download-artifact@v4
with: with:
name: nyms5-apk-arch64 name: nyms5-apk-arch64
path: apk path: apk
+2 -2
View File
@@ -8,7 +8,7 @@ env:
jobs: jobs:
build-container: build-container:
runs-on: arc-linux-latest-dind runs-on: arc-ubuntu-22.04-dind
steps: steps:
- name: Login to Harbor - name: Login to Harbor
uses: docker/login-action@v3 uses: docker/login-action@v3
@@ -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.47.1 uses: mikefarah/yq@v4.45.4
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.47.1 uses: mikefarah/yq@v4.45.4
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.47.1 uses: mikefarah/yq@v4.45.4
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
@@ -20,7 +20,7 @@ env:
jobs: jobs:
build-container: build-container:
runs-on: arc-linux-latest-dind runs-on: arc-ubuntu-22.04-dind
steps: steps:
- name: Login to Harbor - name: Login to Harbor
uses: docker/login-action@v3 uses: docker/login-action@v3
@@ -40,8 +40,7 @@ jobs:
- name: Get version from cargo.toml - name: Get version from cargo.toml
id: get_version id: get_version
run: | run: |
VERSION=$(yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml) yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
echo "result=$VERSION" >> $GITHUB_OUTPUT
- name: cleanup-gateway-probe-ref - name: cleanup-gateway-probe-ref
id: cleanup_gateway_probe_ref id: cleanup_gateway_probe_ref
@@ -53,16 +52,13 @@ jobs:
- name: Set GIT_TAG variable - name: Set GIT_TAG variable
run: echo "GIT_TAG=${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}-${{ steps.cleanup_gateway_probe_ref.outputs.git_ref }}" >> $GITHUB_ENV run: echo "GIT_TAG=${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}-${{ steps.cleanup_gateway_probe_ref.outputs.git_ref }}" >> $GITHUB_ENV
- name: Initialize RELEASE_TAG - name: Set RELEASE_TAG variable
run: echo "RELEASE_TAG=" >> $GITHUB_ENV
- name: Set RELEASE_TAG for release
if: github.event.inputs.release_image == 'true' if: github.event.inputs.release_image == 'true'
run: echo "RELEASE_TAG=golden-" >> $GITHUB_ENV run: echo "RELEASE_TAG=golden-" >> $GITHUB_ENV
- name: Set IMAGE_NAME_AND_TAGS variable - name: Set IMAGE_NAME_AND_TAGS variable
run: echo "IMAGE_NAME_AND_TAGS=${{ env.CONTAINER_NAME }}:${{ env.RELEASE_TAG }}${{ steps.get_version.outputs.result }}-${{ steps.cleanup_gateway_probe_ref.outputs.git_ref }}" >> $GITHUB_ENV run: echo "IMAGE_NAME_AND_TAGS=${{ env.CONTAINER_NAME }}:${{ env.RELEASE_TAG }}${{ steps.get_version.outputs.result }}-${{ steps.cleanup_gateway_probe_ref.outputs.git_ref }}" >> $GITHUB_ENV
- name: New env vars - name: New env vars
run: echo "RELEASE_TAG='$RELEASE_TAG' GIT_TAG='$GIT_TAG' IMAGE_NAME_AND_TAGS='$IMAGE_NAME_AND_TAGS'" run: echo "RELEASE_TAG='$RELEASE_TAG' GIT_TAG='$GIT_TAG' IMAGE_NAME_AND_TAGS='$IMAGE_NAME_AND_TAGS'"
+5 -9
View File
@@ -14,7 +14,7 @@ env:
jobs: jobs:
build-container: build-container:
runs-on: arc-linux-latest-dind runs-on: arc-ubuntu-22.04-dind
steps: steps:
- name: Login to Harbor - name: Login to Harbor
uses: docker/login-action@v3 uses: docker/login-action@v3
@@ -34,22 +34,18 @@ jobs:
- name: Get version from cargo.toml - name: Get version from cargo.toml
id: get_version id: get_version
run: | run: |
VERSION=$(yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml) yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
echo "result=$VERSION" >> $GITHUB_OUTPUT
- name: Set GIT_TAG variable - name: Set GIT_TAG variable
run: echo "GIT_TAG=${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}" >> $GITHUB_ENV run: echo "GIT_TAG=${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}" >> $GITHUB_ENV
- name: Initialise RELEASE_TAG - name: Set RELEASE_TAG variable
run: echo "RELEASE_TAG=" >> $GITHUB_ENV
- name: Set RELEASE_TAG for release
if: github.event.inputs.release_image == 'true' if: github.event.inputs.release_image == 'true'
run: echo "RELEASE_TAG=golden-" >> $GITHUB_ENV run: echo "RELEASE_TAG=golden-" >> $GITHUB_ENV
- name: Set IMAGE_NAME_AND_TAGS variable - name: Set IMAGE_NAME_AND_TAGS variable
run: echo "IMAGE_NAME_AND_TAGS=${{ env.CONTAINER_NAME }}:${{ env.RELEASE_TAG }}${{ steps.get_version.outputs.result }}" >> $GITHUB_ENV run: echo "IMAGE_NAME_AND_TAGS=${{ env.CONTAINER_NAME }}:${{ env.RELEASE_TAG }}${{ steps.get_version.outputs.result }}" >> $GITHUB_ENV
- name: New env vars - name: New env vars
run: echo "RELEASE_TAG='$RELEASE_TAG' GIT_TAG='$GIT_TAG' IMAGE_NAME_AND_TAGS='$IMAGE_NAME_AND_TAGS'" run: echo "RELEASE_TAG='$RELEASE_TAG' GIT_TAG='$GIT_TAG' IMAGE_NAME_AND_TAGS='$IMAGE_NAME_AND_TAGS'"
+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.47.1 uses: mikefarah/yq@v4.45.4
id: get_version id: get_version
with: with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-api/Cargo.toml cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-api/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.47.1 uses: mikefarah/yq@v4.45.4
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
@@ -8,7 +8,7 @@ env:
jobs: jobs:
build-container: build-container:
runs-on: arc-linux-latest-dind runs-on: arc-ubuntu-22.04-dind
steps: steps:
- name: Login to Harbor - name: Login to Harbor
uses: docker/login-action@v3 uses: docker/login-action@v3
@@ -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.47.1 uses: mikefarah/yq@v4.45.4
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
@@ -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.47.1 uses: mikefarah/yq@v4.45.4
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
@@ -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.47.1 uses: mikefarah/yq@v4.45.4
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
-200
View File
@@ -4,206 +4,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased] ## [Unreleased]
## [2025.18-jarlsberg] (2025-10-14)
- ns-api: add descriptions to dVPN gateway responses ([#6102])
- NS API: use new probe download filesize and milliseconds field ([#6097])
- ns-api: use download files size from probes instead of parsing filenames ([#6095])
- ns-api: add new fields for probe output for query_metadata and download file size and duration in ms ([#6091])
- Bugfix/bloomfilters purge ([#6089])
- Hotfix: Update API source in node ping tester script ([#6082])
- Get wireguard keypair as arg instead of reading it from disk ([#6078])
- Feature: Ping probe all nodes /described nodes from a server ([#6074])
- Node Status API: add bridge information to dVPN endpoint ([#6069])
- frontdoor typo fix ([#6067])
- Feature: Node rewards tracker ([#6064])
- [chore] Clippy fix ([#6060])
- Registration Client ([#6059])
- Bugfix: Nym node CLI download nym-node exception ([#6058])
- Feature: Nym node html landing page ([#6053])
- feat: DKG contract method for updating announce address ([#6050])
- feat: NS ticket faucet ([#6047])
- Bridge proto client params in Self-Described ([#6035])
- Node Status API: remove sqlite support ([#6004])
- Benny/ci contract fix ([#5962])
[#6102]: https://github.com/nymtech/nym/pull/6102
[#6097]: https://github.com/nymtech/nym/pull/6097
[#6095]: https://github.com/nymtech/nym/pull/6095
[#6091]: https://github.com/nymtech/nym/pull/6091
[#6089]: https://github.com/nymtech/nym/pull/6089
[#6082]: https://github.com/nymtech/nym/pull/6082
[#6078]: https://github.com/nymtech/nym/pull/6078
[#6074]: https://github.com/nymtech/nym/pull/6074
[#6069]: https://github.com/nymtech/nym/pull/6069
[#6067]: https://github.com/nymtech/nym/pull/6067
[#6064]: https://github.com/nymtech/nym/pull/6064
[#6060]: https://github.com/nymtech/nym/pull/6060
[#6059]: https://github.com/nymtech/nym/pull/6059
[#6058]: https://github.com/nymtech/nym/pull/6058
[#6053]: https://github.com/nymtech/nym/pull/6053
[#6050]: https://github.com/nymtech/nym/pull/6050
[#6047]: https://github.com/nymtech/nym/pull/6047
[#6035]: https://github.com/nymtech/nym/pull/6035
[#6004]: https://github.com/nymtech/nym/pull/6004
[#5962]: https://github.com/nymtech/nym/pull/5962
## [2025.17-isabirra] (2025-09-29)
- Bugfix | Fix the registration handshake ([#6062])
- Convenience for ShutdownTracker ([#6038])
- chore: made http-api-client-macro doctest compile ([#6037])
- feat: refresh mixnet contract on epoch progression ([#6023])
- chore: remove legacy nodes from nym api [and kinda-ish from node status api] ([#6021])
- Feature/credential proxy crate ([#6018])
- Moving clients crate from vpn-client repo to here ([#6015])
- Feature/cancellation migration ([#6014])
- Use default value for the ports until api is deployed ([#6007])
- bugfix: return from MixTrafficController if client request channel has closed ([#6002])
- Revert "Create an axum_test client for more integrated unit testing (… ([#5999])
- chore: upgraded syn to 2.0 and removed nym-execute ([#5998])
- feat: use `ShutdownToken` (`CancellationToken` inside) for nym-api ([#5997])
- bugfix: Recipient deserialisation for deserialisers missing bytes specialisation ([#5991])
- chore: use updated version of simulate endpoint ([#5988])
- chore: purge temp databases on build ([#5984])
- Bump sha.js from 2.4.11 to 2.4.12 ([#5983])
- Feature: Delegation program stake checker and adjuster ([#5980])
- build(deps): bump actions/setup-java from 4 to 5 ([#5975])
- Domain fronting integration ([#5974])
- chore: internal hidden command to force advance nyx epoch ([#5964])
- Create an axum_test client for more integrated unit testing ([#5956])
- feat: shared library for attempting to retrieve update mode attestation ([#5954])
- Bump slab from 0.4.10 to 0.4.11 ([#5952])
- build(deps): bump actions/first-interaction from 1 to 3 ([#5950])
- fix: use WASM compatible time API in client ([#5948])
- feat: credential proxy deposit pool ([#5945])
- build(deps): bump actions/download-artifact from 4 to 5 ([#5939])
- feat: nym signers monitor ([#5933])
- Bump console from 0.15.11 to 0.16.0 ([#5931])
- Bump mock_instant from 0.5.3 to 0.6.0 ([#5930])
- Bump tokio from 1.46.1 to 1.47.1 ([#5929])
- Bump defguard_wireguard_rs from v0.4.7 to v0.7.5 ([#5928])
- Bump indicatif from 0.17.11 to 0.18.0 ([#5924])
- Feature: Nym node autorun CLI ([#5916])
- build(deps): bump mikefarah/yq from 4.45.4 to 4.47.1 ([#5911])
- build(deps): bump pbkdf2 from 3.1.2 to 3.1.3 ([#5869])
[#6062]: https://github.com/nymtech/nym/pull/6062
[#6038]: https://github.com/nymtech/nym/pull/6038
[#6037]: https://github.com/nymtech/nym/pull/6037
[#6023]: https://github.com/nymtech/nym/pull/6023
[#6021]: https://github.com/nymtech/nym/pull/6021
[#6018]: https://github.com/nymtech/nym/pull/6018
[#6015]: https://github.com/nymtech/nym/pull/6015
[#6014]: https://github.com/nymtech/nym/pull/6014
[#6007]: https://github.com/nymtech/nym/pull/6007
[#6002]: https://github.com/nymtech/nym/pull/6002
[#5999]: https://github.com/nymtech/nym/pull/5999
[#5998]: https://github.com/nymtech/nym/pull/5998
[#5997]: https://github.com/nymtech/nym/pull/5997
[#5991]: https://github.com/nymtech/nym/pull/5991
[#5988]: https://github.com/nymtech/nym/pull/5988
[#5984]: https://github.com/nymtech/nym/pull/5984
[#5983]: https://github.com/nymtech/nym/pull/5983
[#5980]: https://github.com/nymtech/nym/pull/5980
[#5975]: https://github.com/nymtech/nym/pull/5975
[#5974]: https://github.com/nymtech/nym/pull/5974
[#5964]: https://github.com/nymtech/nym/pull/5964
[#5956]: https://github.com/nymtech/nym/pull/5956
[#5954]: https://github.com/nymtech/nym/pull/5954
[#5952]: https://github.com/nymtech/nym/pull/5952
[#5950]: https://github.com/nymtech/nym/pull/5950
[#5948]: https://github.com/nymtech/nym/pull/5948
[#5945]: https://github.com/nymtech/nym/pull/5945
[#5939]: https://github.com/nymtech/nym/pull/5939
[#5933]: https://github.com/nymtech/nym/pull/5933
[#5931]: https://github.com/nymtech/nym/pull/5931
[#5930]: https://github.com/nymtech/nym/pull/5930
[#5929]: https://github.com/nymtech/nym/pull/5929
[#5928]: https://github.com/nymtech/nym/pull/5928
[#5924]: https://github.com/nymtech/nym/pull/5924
[#5916]: https://github.com/nymtech/nym/pull/5916
[#5911]: https://github.com/nymtech/nym/pull/5911
[#5869]: https://github.com/nymtech/nym/pull/5869
## [2025.16-halloumi] (2025-09-16)
- Backport metadata endpoint ([#6010])
- bugfix: make sure tables are removed in correct order to not trigger FK constraint issue ([#5987])
- chore: move authenticator into gateway crate ([#5982])
- Fix the ns api ci workflow ([#5981])
- Remove freshness check on testrun submit ([#5977])
- Update sysinfo to the latest ([#5976])
- bugfix: manually calculate per node work on rewarded set changes ([#5972])
- fixing the ci for ns agent ([#5965])
- Feature/testing utils ([#5963])
- bugfix: fix ci-build for linux (and use updated runner) ([#5958])
- chore: updated refs to cheddar rev of nym repo ([#5955])
- http api client adjustment ([#5953])
- chore: fix rust 1.89 clippy issues ([#5944])
- Wireguard metadata client library ([#5943])
- chore: remove unused import ([#5942])
- feat: introduce additional checks when attempting to send to bounded channels ([#5941])
- Move credential verifier in peer controller ([#5938])
- change PK/FK on expiration date signatures tables ([#5934])
- Wireguard private metadata ([#5915])
[#6010]: https://github.com/nymtech/nym/pull/6010
[#5987]: https://github.com/nymtech/nym/pull/5987
[#5982]: https://github.com/nymtech/nym/pull/5982
[#5981]: https://github.com/nymtech/nym/pull/5981
[#5977]: https://github.com/nymtech/nym/pull/5977
[#5976]: https://github.com/nymtech/nym/pull/5976
[#5972]: https://github.com/nymtech/nym/pull/5972
[#5965]: https://github.com/nymtech/nym/pull/5965
[#5963]: https://github.com/nymtech/nym/pull/5963
[#5958]: https://github.com/nymtech/nym/pull/5958
[#5955]: https://github.com/nymtech/nym/pull/5955
[#5953]: https://github.com/nymtech/nym/pull/5953
[#5944]: https://github.com/nymtech/nym/pull/5944
[#5943]: https://github.com/nymtech/nym/pull/5943
[#5942]: https://github.com/nymtech/nym/pull/5942
[#5941]: https://github.com/nymtech/nym/pull/5941
[#5938]: https://github.com/nymtech/nym/pull/5938
[#5934]: https://github.com/nymtech/nym/pull/5934
[#5915]: https://github.com/nymtech/nym/pull/5915
## [2025.15-gruyere] (2025-08-20)
- Migrate strum to 0.27.2 ([#5960])
- WG exit policy scripts update ([#5921])
- Make DNS Resolver fallback optional ([#5920])
- nym-node debug command to reset providers db ([#5914])
- basic zulip client for sending messages ([#5913])
- chore: allow compatibility with 'CDLA-Permissive-2.0' ([#5910])
- feat: ecash liveness check ([#5890])
- Remove old free credential handle ([#5864])
[#5960]: https://github.com/nymtech/nym/pull/5960
[#5921]: https://github.com/nymtech/nym/pull/5921
[#5920]: https://github.com/nymtech/nym/pull/5920
[#5914]: https://github.com/nymtech/nym/pull/5914
[#5913]: https://github.com/nymtech/nym/pull/5913
[#5910]: https://github.com/nymtech/nym/pull/5910
[#5890]: https://github.com/nymtech/nym/pull/5890
[#5864]: https://github.com/nymtech/nym/pull/5864
## [2025.14-feta] (2025-08-05)
- chore: nym node tokio console ([#5909])
- Feature/dkg snapshot epoch ([#5900])
- Feature/dkg epoch dealers query ([#5899])
- sqlx-pool-guard: allocate more memory on windows ([#5896])
- Support mnemonic in the NS agent ([#5883])
- Allow PG database backend ([#5880])
[#5909]: https://github.com/nymtech/nym/pull/5909
[#5900]: https://github.com/nymtech/nym/pull/5900
[#5899]: https://github.com/nymtech/nym/pull/5899
[#5896]: https://github.com/nymtech/nym/pull/5896
[#5883]: https://github.com/nymtech/nym/pull/5883
[#5880]: https://github.com/nymtech/nym/pull/5880
## [2025.13-emmental] (2025-07-22) ## [2025.13-emmental] (2025-07-22)
- fix: don't allow mixnode running in exit mode ([#5898]) - fix: don't allow mixnode running in exit mode ([#5898])
Generated
+1739 -1448
View File
File diff suppressed because it is too large Load Diff
+18 -47
View File
@@ -31,7 +31,6 @@ members = [
"common/client-libs/mixnet-client", "common/client-libs/mixnet-client",
"common/client-libs/validator-client", "common/client-libs/validator-client",
"common/commands", "common/commands",
"common/nym-common",
"common/config", "common/config",
"common/cosmwasm-smart-contracts/coconut-dkg", "common/cosmwasm-smart-contracts/coconut-dkg",
"common/cosmwasm-smart-contracts/contracts-common", "common/cosmwasm-smart-contracts/contracts-common",
@@ -40,11 +39,9 @@ members = [
"common/cosmwasm-smart-contracts/ecash-contract", "common/cosmwasm-smart-contracts/ecash-contract",
"common/cosmwasm-smart-contracts/group-contract", "common/cosmwasm-smart-contracts/group-contract",
"common/cosmwasm-smart-contracts/mixnet-contract", "common/cosmwasm-smart-contracts/mixnet-contract",
"common/cosmwasm-smart-contracts/multisig-contract", "common/cosmwasm-smart-contracts/multisig-contract", "common/cosmwasm-smart-contracts/nym-performance-contract",
"common/cosmwasm-smart-contracts/nym-performance-contract",
"common/cosmwasm-smart-contracts/nym-pool-contract", "common/cosmwasm-smart-contracts/nym-pool-contract",
"common/cosmwasm-smart-contracts/vesting-contract", "common/cosmwasm-smart-contracts/vesting-contract",
"common/credential-proxy",
"common/credential-storage", "common/credential-storage",
"common/credential-utils", "common/credential-utils",
"common/credential-verification", "common/credential-verification",
@@ -52,15 +49,13 @@ members = [
"common/credentials-interface", "common/credentials-interface",
"common/crypto", "common/crypto",
"common/dkg", "common/dkg",
"common/ecash-signer-check",
"common/ecash-signer-check-types",
"common/ecash-time", "common/ecash-time",
"common/execute",
"common/exit-policy", "common/exit-policy",
"common/gateway-requests", "common/gateway-requests",
"common/gateway-stats-storage", "common/gateway-stats-storage",
"common/gateway-storage", "common/gateway-storage",
"common/http-api-client", "common/http-api-client",
"common/http-api-client-macro",
"common/http-api-common", "common/http-api-common",
"common/inclusion-probability", "common/inclusion-probability",
"common/ip-packet-requests", "common/ip-packet-requests",
@@ -69,8 +64,6 @@ members = [
"common/network-defaults", "common/network-defaults",
"common/node-tester-utils", "common/node-tester-utils",
"common/nonexhaustive-delayqueue", "common/nonexhaustive-delayqueue",
"common/nym-cache",
"common/nym-connection-monitor",
"common/nym-id", "common/nym-id",
"common/nym-metrics", "common/nym-metrics",
"common/nym_offline_compact_ecash", "common/nym_offline_compact_ecash",
@@ -89,7 +82,6 @@ members = [
"common/nymsphinx/types", "common/nymsphinx/types",
"common/nyxd-scraper", "common/nyxd-scraper",
"common/pemstore", "common/pemstore",
"common/registration",
"common/serde-helpers", "common/serde-helpers",
"common/service-provider-requests-common", "common/service-provider-requests-common",
"common/socks5-client-core", "common/socks5-client-core",
@@ -98,33 +90,24 @@ members = [
"common/statistics", "common/statistics",
"common/store-cipher", "common/store-cipher",
"common/task", "common/task",
"common/test-utils",
"common/ticketbooks-merkle", "common/ticketbooks-merkle",
"common/topology", "common/topology",
"common/tun", "common/tun",
"common/types", "common/types",
"common/upgrade-mode-check",
"common/verloc", "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-private-metadata/client",
"common/wireguard-private-metadata/server",
"common/wireguard-private-metadata/shared",
"common/wireguard-private-metadata/tests",
"common/wireguard-types", "common/wireguard-types",
"common/zulip-client",
"documentation/autodoc", "documentation/autodoc",
"gateway", "gateway",
"nym-api", "nym-api",
"nym-api/nym-api-requests", "nym-api/nym-api-requests",
"nym-authenticator-client",
"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-ip-packet-client",
"nym-network-monitor", "nym-network-monitor",
"nym-node", "nym-node",
"nym-node-status-api/nym-node-status-agent", "nym-node-status-api/nym-node-status-agent",
@@ -133,8 +116,6 @@ members = [
"nym-node/nym-node-metrics", "nym-node/nym-node-metrics",
"nym-node/nym-node-requests", "nym-node/nym-node-requests",
"nym-outfox", "nym-outfox",
"nym-registration-client",
"nym-signers-monitor",
"nym-statistics-api", "nym-statistics-api",
"nym-validator-rewarder", "nym-validator-rewarder",
"nyx-chain-watcher", "nyx-chain-watcher",
@@ -142,6 +123,7 @@ members = [
"sdk/ffi/go", "sdk/ffi/go",
"sdk/ffi/shared", "sdk/ffi/shared",
"sdk/rust/nym-sdk", "sdk/rust/nym-sdk",
"service-providers/authenticator",
"service-providers/common", "service-providers/common",
"service-providers/ip-packet-router", "service-providers/ip-packet-router",
"service-providers/network-requester", "service-providers/network-requester",
@@ -153,6 +135,7 @@ members = [
# "tools/internal/sdk-version-bump", # "tools/internal/sdk-version-bump",
"tools/internal/ssl-inject", "tools/internal/ssl-inject",
"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/validator-status-check", "tools/internal/validator-status-check",
"tools/nym-cli", "tools/nym-cli",
@@ -165,7 +148,6 @@ members = [
"wasm/mix-fetch", "wasm/mix-fetch",
"wasm/node-tester", "wasm/node-tester",
"wasm/zknym-lib", "wasm/zknym-lib",
"nym-gateway-probe"
] ]
default-members = [ default-members = [
@@ -179,21 +161,22 @@ default-members = [
"nym-statistics-api", "nym-statistics-api",
"nym-validator-rewarder", "nym-validator-rewarder",
"nyx-chain-watcher", "nyx-chain-watcher",
"service-providers/authenticator",
"service-providers/ip-packet-router", "service-providers/ip-packet-router",
"service-providers/network-requester", "service-providers/network-requester",
"tools/nymvisor", "tools/nymvisor",
] ]
exclude = ["contracts", "nym-wallet", "cpu-cycles"] exclude = ["explorer", "contracts", "nym-wallet", "cpu-cycles"]
[workspace.package] [workspace.package]
authors = ["Nym Technologies SA"] authors = ["Nym Technologies SA"]
repository = "https://github.com/nymtech/nym" repository = "https://github.com/nymtech/nym"
homepage = "https://nymtech.net" homepage = "https://nymtech.net"
documentation = "https://nymtech.net" documentation = "https://nymtech.net"
edition = "2024" edition = "2021"
license = "Apache-2.0" license = "Apache-2.0"
rust-version = "1.85" rust-version = "1.80"
readme = "README.md" readme = "README.md"
[workspace.dependencies] [workspace.dependencies]
@@ -234,8 +217,8 @@ clap_complete = "4.5"
clap_complete_fig = "4.5" clap_complete_fig = "4.5"
colored = "2.2" colored = "2.2"
comfy-table = "7.1.4" comfy-table = "7.1.4"
console = "0.16.0" console = "0.15.11"
console-subscriber = "0.4.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.34"
@@ -248,10 +231,9 @@ dashmap = "5.5.3"
# We want https://github.com/DefGuard/wireguard-rs/pull/64 , but there's no crates.io release being pushed out anymore # We want https://github.com/DefGuard/wireguard-rs/pull/64 , but there's no crates.io release being pushed out anymore
defguard_wireguard_rs = { git = "https://github.com/DefGuard/wireguard-rs.git", rev = "v0.4.7" } defguard_wireguard_rs = { git = "https://github.com/DefGuard/wireguard-rs.git", rev = "v0.4.7" }
digest = "0.10.7" digest = "0.10.7"
dirs = "6.0" dirs = "5.0"
doc-comment = "0.3" doc-comment = "0.3"
dotenvy = "0.15.6" dotenvy = "0.15.6"
dyn-clone = "1.0.19"
ecdsa = "0.16" ecdsa = "0.16"
ed25519-dalek = "2.1" ed25519-dalek = "2.1"
encoding_rs = "0.8.35" encoding_rs = "0.8.35"
@@ -281,13 +263,11 @@ humantime = "2.2.0"
humantime-serde = "1.1.1" humantime-serde = "1.1.1"
hyper = "1.6.0" hyper = "1.6.0"
hyper-util = "0.1" hyper-util = "0.1"
indicatif = "0.18.0" indicatif = "0.17.11"
inquire = "0.6.2" inquire = "0.6.2"
inventory = "0.3.21"
ip_network = "0.4.1" ip_network = "0.4.1"
ipnetwork = "0.20" ipnetwork = "0.20"
itertools = "0.14.0" itertools = "0.14.0"
jwt-simple = { version = "0.12.12", default-features = false, features = ["pure-rust"] }
k256 = "0.13" k256 = "0.13"
lazy_static = "1.5.0" lazy_static = "1.5.0"
ledger-transport = "0.10.0" ledger-transport = "0.10.0"
@@ -306,7 +286,6 @@ 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"
pnet_packet = "0.35.0"
pin-project-lite = "0.2.16" pin-project-lite = "0.2.16"
publicsuffix = "2.3.0" publicsuffix = "2.3.0"
proc_pidinfo = "0.1.3" proc_pidinfo = "0.1.3"
@@ -332,24 +311,22 @@ serde_json_path = "0.7.2"
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"
serde_plain = "1.0.2"
sha2 = "0.10.9" sha2 = "0.10.9"
si-scale = "0.2.3" si-scale = "0.2.3"
snow = "0.9.6" snow = "0.9.6"
sphinx-packet = "=0.6.0" sphinx-packet = "=0.6.0"
sqlx = "0.8.6" sqlx = "0.8.6"
strum = "0.27.2" strum = "0.26"
strum_macros = "0.27.2" strum_macros = "0.26"
subtle-encoding = "0.5" subtle-encoding = "0.5"
syn = "2" syn = "1"
sysinfo = "0.37.0" sysinfo = "0.33.0"
tap = "1.0.1" tap = "1.0.1"
tar = "0.4.44" tar = "0.4.44"
test-with = { version = "0.15.4", default-features = false }
tempfile = "3.20" tempfile = "3.20"
thiserror = "2.0" thiserror = "2.0"
time = "0.3.41" time = "0.3.41"
tokio = "1.47" tokio = "1.45"
tokio-postgres = "0.7" tokio-postgres = "0.7"
tokio-stream = "0.1.17" tokio-stream = "0.1.17"
tokio-test = "0.4.4" tokio-test = "0.4.4"
@@ -365,7 +342,6 @@ tracing-opentelemetry = "0.19.0"
tracing-subscriber = "0.3.19" tracing-subscriber = "0.3.19"
tracing-tree = "0.2.2" tracing-tree = "0.2.2"
tracing-indicatif = "0.3.9" tracing-indicatif = "0.3.9"
tracing-test = "0.2.5"
ts-rs = "10.1.0" ts-rs = "10.1.0"
tungstenite = { version = "0.20.1", default-features = false } tungstenite = { version = "0.20.1", default-features = false }
uniffi = "0.29.2" uniffi = "0.29.2"
@@ -376,7 +352,6 @@ utoipa-swagger-ui = "8.1"
utoipauto = "0.2" utoipauto = "0.2"
uuid = "*" uuid = "*"
vergen = { version = "=8.3.1", default-features = false } vergen = { version = "=8.3.1", default-features = false }
vergen-gitcl = { version = "1.0.8", default-features = false }
walkdir = "2" walkdir = "2"
x25519-dalek = "2.0.0" x25519-dalek = "2.0.0"
zeroize = "1.7.0" zeroize = "1.7.0"
@@ -416,19 +391,18 @@ prost = { version = "0.13", 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.6.0"
gloo-timers = "0.3.0"
indexed_db_futures = "0.6.4" indexed_db_futures = "0.6.4"
js-sys = "0.3.76" js-sys = "0.3.76"
serde-wasm-bindgen = "0.6.5" serde-wasm-bindgen = "0.6.5"
tsify = "0.4.5" tsify = "0.4.5"
tokio_with_wasm = { version = "0.8.7" }
wasm-bindgen = "0.2.99" wasm-bindgen = "0.2.99"
wasm-bindgen-futures = "0.4.49" wasm-bindgen-futures = "0.4.49"
wasm-bindgen-test = "0.3.49" wasm-bindgen-test = "0.3.49"
wasmtimer = "0.4.1" wasmtimer = "0.4.1"
web-sys = "0.3.76" web-sys = "0.3.76"
# for local development: # for local development:
#[patch.crates-io] #[patch.crates-io]
#sphinx-packet = { path = "../sphinx" } #sphinx-packet = { path = "../sphinx" }
@@ -460,9 +434,6 @@ opt-level = 'z'
# lto = true # lto = true
opt-level = 'z' opt-level = 'z'
[workspace.lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(tokio_unstable)'] }
[workspace.lints.clippy] [workspace.lints.clippy]
unwrap_used = "deny" unwrap_used = "deny"
expect_used = "deny" expect_used = "deny"
+5 -13
View File
@@ -107,16 +107,16 @@ sdk-wasm-build:
$(MAKE) -C nym-browser-extension/storage wasm-pack $(MAKE) -C nym-browser-extension/storage wasm-pack
$(MAKE) -C wasm/client $(MAKE) -C wasm/client
$(MAKE) -C wasm/node-tester $(MAKE) -C wasm/node-tester
# $(MAKE) -C wasm/mix-fetch $(MAKE) -C wasm/mix-fetch
$(MAKE) -C wasm/zknym-lib $(MAKE) -C wasm/zknym-lib
# $(MAKE) -C wasm/full-nym-wasm #$(MAKE) -C wasm/full-nym-wasm
# run this from npm/yarn to ensure tools are in the path, e.g. yarn build:sdk from root of repo # run this from npm/yarn to ensure tools are in the path, e.g. yarn build:sdk from root of repo
sdk-typescript-build: sdk-typescript-build:
npx lerna run --scope @nymproject/sdk build --stream npx lerna run --scope @nymproject/sdk build --stream
# npx lerna run --scope @nymproject/mix-fetch build --stream npx lerna run --scope @nymproject/mix-fetch build --stream
# npx lerna run --scope @nymproject/node-tester build --stream npx lerna run --scope @nymproject/node-tester build --stream
# yarn --cwd sdk/typescript/codegen/contract-clients build yarn --cwd sdk/typescript/codegen/contract-clients build
# NOTE: These targets are part of the main workspace (but not as wasm32-unknown-unknown) # NOTE: These targets are part of the main workspace (but not as wasm32-unknown-unknown)
WASM_CRATES = extension-storage nym-client-wasm nym-node-tester-wasm zknym-lib WASM_CRATES = extension-storage nym-client-wasm nym-node-tester-wasm zknym-lib
@@ -154,7 +154,6 @@ CONTRACTS_OUT_DIR = contracts/artifacts
# #
COSMWASM_OPTIMIZER_IMAGE ?= cosmwasm/optimizer:0.17.0 COSMWASM_OPTIMIZER_IMAGE ?= cosmwasm/optimizer:0.17.0
COSMWASM_OPTIMIZER_PLATFORM ?= linux/amd64 COSMWASM_OPTIMIZER_PLATFORM ?= linux/amd64
COSMWASM_CHECK_IMAGE ?= rust:1.88
# Ensure clean build environment and run the optimizer # Ensure clean build environment and run the optimizer
optimize-contracts: optimize-contracts:
@@ -180,13 +179,6 @@ optimize-contracts:
# Cleanup temporary artefacts directory # Cleanup temporary artefacts directory
@rm -rf artifacts 2>/dev/null || true @rm -rf artifacts 2>/dev/null || true
# Check artifacts with cosmwasm-check inside the optimizer image
docker-check-contracts:
@docker run --rm --platform $(COSMWASM_OPTIMIZER_PLATFORM) \
-v $(CURDIR):/code --workdir /code \
--entrypoint /bin/sh \
$(COSMWASM_CHECK_IMAGE) -lc 'apt-get update && apt-get install -y --no-install-recommends llvm-dev libclang-dev pkg-config && export PATH="/usr/local/cargo/bin:/usr/local/rustup/bin:$$PATH" && cargo install cosmwasm-check --locked && WASMER_ENGINE=universal WASMER_COMPILER=singlepass cosmwasm-check contracts/artifacts/*.wasm'
wasm-opt-contracts: wasm-opt-contracts:
@for WASM in $(WASM_CONTRACT_DIR)/*.wasm; do \ @for WASM in $(WASM_CONTRACT_DIR)/*.wasm; do \
echo "Running wasm-opt on $$WASM"; \ echo "Running wasm-opt on $$WASM"; \
+2 -2
View File
@@ -1,10 +1,10 @@
[package] [package]
name = "nym-client" name = "nym-client"
version = "1.1.64" version = "1.1.59"
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"
rust-version = "1.85" 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
+11 -16
View File
@@ -11,7 +11,7 @@ use nym_client_core::client::base_client::{
BaseClientBuilder, ClientInput, ClientOutput, ClientState, BaseClientBuilder, ClientInput, ClientOutput, ClientState,
}; };
use nym_sphinx::params::PacketType; use nym_sphinx::params::PacketType;
use nym_task::ShutdownManager; use nym_task::TaskHandle;
use nym_validator_client::QueryHttpRpcNyxdClient; use nym_validator_client::QueryHttpRpcNyxdClient;
use std::error::Error; use std::error::Error;
use std::path::PathBuf; use std::path::PathBuf;
@@ -29,8 +29,6 @@ pub struct SocketClient {
/// Optional path to a .json file containing standalone network details. /// Optional path to a .json file containing standalone network details.
custom_mixnet: Option<PathBuf>, custom_mixnet: Option<PathBuf>,
shutdown_manager: ShutdownManager,
} }
impl SocketClient { impl SocketClient {
@@ -42,7 +40,6 @@ impl SocketClient {
SocketClient { SocketClient {
config, config,
custom_mixnet, custom_mixnet,
shutdown_manager: Default::default(),
} }
} }
@@ -52,7 +49,7 @@ impl SocketClient {
client_output: ClientOutput, client_output: ClientOutput,
client_state: ClientState, client_state: ClientState,
self_address: &Recipient, self_address: &Recipient,
shutdown_token: nym_task::ShutdownToken, task_client: nym_task::TaskClient,
packet_type: PacketType, packet_type: PacketType,
) { ) {
info!("Starting websocket listener..."); info!("Starting websocket listener...");
@@ -60,7 +57,6 @@ impl SocketClient {
let ClientInput { let ClientInput {
connection_command_sender, connection_command_sender,
input_sender, input_sender,
..
} = client_input; } = client_input;
let ClientOutput { let ClientOutput {
@@ -81,24 +77,24 @@ impl SocketClient {
shared_lane_queue_lengths, shared_lane_queue_lengths,
reply_controller_sender, reply_controller_sender,
Some(packet_type), Some(packet_type),
shutdown_token.clone(), task_client.fork("websocket_handler"),
); );
websocket::Listener::new( websocket::Listener::new(
config.socket.host, config.socket.host,
config.socket.listening_port, config.socket.listening_port,
shutdown_token.child_token(), task_client.with_suffix("websocket_listener"),
) )
.start(websocket_handler); .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)
pub async fn run_socket_forever(self) -> Result<(), Box<dyn Error + Send + Sync>> { pub async fn run_socket_forever(self) -> Result<(), Box<dyn Error + Send + Sync>> {
let mut shutdown = self.start_socket().await?; let shutdown = self.start_socket().await?;
shutdown.run_until_shutdown().await; let res = shutdown.wait_for_shutdown().await;
log::info!("Stopping nym-client"); log::info!("Stopping nym-client");
Ok(()) res
} }
async fn initialise_storage(&self) -> Result<OnDiskPersistent, ClientError> { async fn initialise_storage(&self) -> Result<OnDiskPersistent, ClientError> {
@@ -115,7 +111,7 @@ impl SocketClient {
let dkg_query_client = if self.config.base.client.disabled_credentials_mode { let dkg_query_client = if self.config.base.client.disabled_credentials_mode {
None None
} else { } else {
Some(default_query_dkg_client_from_config(&self.config.base)?) Some(default_query_dkg_client_from_config(&self.config.base))
}; };
let storage = self.initialise_storage().await?; let storage = self.initialise_storage().await?;
@@ -123,7 +119,6 @@ impl SocketClient {
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_shutdown(self.shutdown_manager.shutdown_tracker_owned())
.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 {
@@ -133,7 +128,7 @@ impl SocketClient {
Ok(base_client) Ok(base_client)
} }
pub async fn start_socket(self) -> Result<ShutdownManager, ClientError> { pub async fn start_socket(self) -> Result<TaskHandle, ClientError> {
if !self.config.socket.socket_type.is_websocket() { if !self.config.socket.socket_type.is_websocket() {
return Err(ClientError::InvalidSocketMode); return Err(ClientError::InvalidSocketMode);
} }
@@ -152,13 +147,13 @@ impl SocketClient {
client_output, client_output,
client_state, client_state,
&self_address, &self_address,
self.shutdown_manager.child_shutdown_token(), started_client.task_handle.get_handle(),
packet_type, packet_type,
); );
info!("Client startup finished!"); info!("Client startup finished!");
info!("The address of this client is: {self_address}"); info!("The address of this client is: {self_address}");
Ok(self.shutdown_manager) Ok(started_client.task_handle)
} }
} }
+27 -21
View File
@@ -19,7 +19,7 @@ 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::ShutdownToken; 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,7 +44,7 @@ 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>,
shutdown_token: ShutdownToken, task_client: TaskClient,
} }
impl HandlerBuilder { impl HandlerBuilder {
@@ -57,7 +57,7 @@ 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>,
shutdown_token: ShutdownToken, task_client: TaskClient,
) -> Self { ) -> Self {
Self { Self {
msg_input, msg_input,
@@ -67,13 +67,14 @@ impl HandlerBuilder {
lane_queue_lengths, lane_queue_lengths,
reply_controller_sender, reply_controller_sender,
packet_type, packet_type,
shutdown_token, 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 shutdown_token = self.shutdown_token.clone(); 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(),
@@ -84,7 +85,7 @@ 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,
shutdown_token, task_client,
} }
} }
} }
@@ -99,14 +100,19 @@ 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>,
shutdown_token: ShutdownToken, task_client: TaskClient,
} }
impl Drop for Handler { impl Drop for Handler {
fn drop(&mut self) { fn drop(&mut self) {
let _ = self if let Err(err) = self
.buffer_requester .buffer_requester
.unbounded_send(ReceivedBufferMessage::ReceiverDisconnect); .unbounded_send(ReceivedBufferMessage::ReceiverDisconnect)
{
if !self.task_client.is_shutdown_poll() {
error!("failed to disconnect the receiver from the buffer: {err}");
}
}
} }
} }
@@ -136,7 +142,7 @@ impl Handler {
{ {
Ok(length) => length, Ok(length) => length,
Err(err) => { Err(err) => {
if !self.shutdown_token.is_cancelled() { if !self.task_client.is_shutdown_poll() {
error!( error!(
"Failed to get reply queue length for connection {connection_id}: {err}" "Failed to get reply queue length for connection {connection_id}: {err}"
); );
@@ -186,7 +192,7 @@ 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 { if let Err(err) = self.msg_input.send(input_msg).await {
if !self.shutdown_token.is_cancelled() { if !self.task_client.is_shutdown_poll() {
error!("Failed to send message to the input buffer: {err}"); error!("Failed to send message to the input buffer: {err}");
} }
} }
@@ -219,7 +225,7 @@ 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 { if let Err(err) = self.msg_input.send(input_msg).await {
if !self.shutdown_token.is_cancelled() { if !self.task_client.is_shutdown_poll() {
error!("Failed to send anonymous message to the input buffer: {err}"); error!("Failed to send anonymous message to the input buffer: {err}");
} }
} }
@@ -247,7 +253,7 @@ 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 { if let Err(err) = self.msg_input.send(input_msg).await {
if !self.shutdown_token.is_cancelled() { if !self.task_client.is_shutdown_poll() {
error!("Failed to send reply message to the input buffer: {err}"); error!("Failed to send reply message to the input buffer: {err}");
} }
} }
@@ -269,7 +275,7 @@ impl Handler {
.client_connection_tx .client_connection_tx
.unbounded_send(ConnectionCommand::Close(connection_id)) .unbounded_send(ConnectionCommand::Close(connection_id))
{ {
if !self.shutdown_token.is_cancelled() { if !self.task_client.is_shutdown_poll() {
error!("Failed to send close connection command: {err}"); error!("Failed to send close connection command: {err}");
} }
} }
@@ -388,14 +394,11 @@ impl Handler {
} }
async fn listen_for_requests(&mut self, mut msg_receiver: ReconstructedMessagesReceiver) { async fn listen_for_requests(&mut self, mut msg_receiver: ReconstructedMessagesReceiver) {
let shutdown_token = self.shutdown_token.clone(); let mut task_client = self.task_client.fork("select");
task_client.disarm();
loop { while !task_client.is_shutdown() {
tokio::select! { tokio::select! {
_ = shutdown_token.cancelled() => {
log::trace!("Websocket handler: Received shutdown");
break;
}
// we can either get a client request from the websocket // we can either get a client request from the websocket
socket_msg = self.next_websocket_request() => { socket_msg = self.next_websocket_request() => {
if socket_msg.is_none() { if socket_msg.is_none() {
@@ -433,6 +436,9 @@ impl Handler {
break; break;
} }
} }
_ = task_client.recv() => {
log::trace!("Websocket handler: Received shutdown");
}
} }
} }
log::debug!("Websocket handler: Exiting"); log::debug!("Websocket handler: Exiting");
@@ -458,7 +464,7 @@ impl Handler {
reconstructed_sender, reconstructed_sender,
)) ))
{ {
if !self.shutdown_token.is_cancelled() { if !self.task_client.is_shutdown_poll() {
error!("failed to announce the receiver to the buffer: {err}"); error!("failed to announce the receiver to the buffer: {err}");
} }
} }
+7 -7
View File
@@ -3,7 +3,7 @@
use super::handler::HandlerBuilder; use super::handler::HandlerBuilder;
use log::*; use log::*;
use nym_task::ShutdownToken; 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,15 +23,15 @@ impl State {
pub(crate) struct Listener { pub(crate) struct Listener {
address: SocketAddr, address: SocketAddr,
state: State, state: State,
shutdown_token: ShutdownToken, task_client: TaskClient,
} }
impl Listener { impl Listener {
pub(crate) fn new(host: IpAddr, port: u16, shutdown_token: ShutdownToken) -> Self { pub(crate) fn new(host: IpAddr, port: u16, task_client: TaskClient) -> Self {
Listener { Listener {
address: SocketAddr::new(host, port), address: SocketAddr::new(host, port),
state: State::AwaitingConnection, state: State::AwaitingConnection,
shutdown_token, task_client,
} }
} }
@@ -46,11 +46,11 @@ impl Listener {
let notify = Arc::new(Notify::new()); let notify = Arc::new(Notify::new());
while !self.shutdown_token.is_cancelled() { while !self.task_client.is_shutdown() {
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.shutdown_token.is_cancelled() { if self.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 +59,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.shutdown_token.cancelled() => { _ = self.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;
+2 -2
View File
@@ -1,10 +1,10 @@
[package] [package]
name = "nym-socks5-client" name = "nym-socks5-client"
version = "1.1.64" version = "1.1.59"
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"
rust-version = "1.85" rust-version = "1.70"
license.workspace = true license.workspace = true
[dependencies] [dependencies]
+5 -5
View File
@@ -1,8 +1,8 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use futures::StreamExt;
use futures::channel::mpsc; use futures::channel::mpsc;
use futures::StreamExt;
use notify::event::{DataChange, MetadataKind, ModifyKind}; use notify::event::{DataChange, MetadataKind, ModifyKind};
use notify::{Config, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher}; use notify::{Config, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
use std::collections::HashMap; use std::collections::HashMap;
@@ -96,10 +96,10 @@ impl AsyncFileWatcher {
// when testing I was consistently getting two `Modify(Data(Any))` events in quick succession // when testing I was consistently getting two `Modify(Data(Any))` events in quick succession
// (probably to modify content and metadata). // (probably to modify content and metadata).
// we really only want to propagate one of them // we really only want to propagate one of them
if let Some(previous) = self.last_received.get(&event.kind) if let Some(previous) = self.last_received.get(&event.kind) {
&& now.duration_since(*previous) < self.tick_duration if now.duration_since(*previous) < self.tick_duration {
{ return false;
return false; }
} }
let Some(filters) = &self.filters else { let Some(filters) = &self.filters else {
-2
View File
@@ -13,8 +13,6 @@ base64 = { workspace = true }
bincode = { workspace = true } bincode = { workspace = true }
rand = { workspace = true } rand = { workspace = true }
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
semver = { workspace = true }
strum_macros = { workspace = true }
thiserror = { workspace = true } thiserror = { workspace = true }
nym-credentials-interface = { path = "../credentials-interface" } nym-credentials-interface = { path = "../credentials-interface" }
@@ -1,273 +0,0 @@
// Copyright 2025 Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use nym_sphinx::addressing::Recipient;
use nym_wireguard_types::PeerPublicKey;
use crate::{
AuthenticatorVersion, Error,
latest::registration::IpPair,
traits::{FinalMessage, InitMessage, QueryBandwidthMessage, TopUpMessage, Versionable},
v2, v3, v4, v5,
};
// This is very redundant with AuthenticatorRequest and I reckon they could be smooshed.
// It is a bit out of scope for me at the moment though
#[derive(Debug)]
pub enum ClientMessage {
Initial(Box<dyn InitMessage + Send + Sync + 'static>),
Final(Box<dyn FinalMessage + Send + Sync + 'static>),
Query(Box<dyn QueryBandwidthMessage + Send + Sync + 'static>),
TopUp(Box<dyn TopUpMessage + Send + Sync + 'static>),
}
impl ClientMessage {
// check if message is wasteful e.g. contains a credential
pub fn is_wasteful(&self) -> bool {
match self {
Self::Final(msg) => msg.credential().is_some(),
Self::TopUp(_) => true,
Self::Initial(_) | Self::Query(_) => false,
}
}
fn version(&self) -> AuthenticatorVersion {
match self {
ClientMessage::Initial(msg) => msg.version(),
ClientMessage::Final(msg) => msg.version(),
ClientMessage::Query(msg) => msg.version(),
ClientMessage::TopUp(msg) => msg.version(),
}
}
pub fn bytes(&self, reply_to: Recipient) -> Result<(Vec<u8>, u64), Error> {
match self.version() {
AuthenticatorVersion::V1 => Err(Error::UnsupportedVersion),
AuthenticatorVersion::V2 => {
use v2::{
registration::{ClientMac, FinalMessage, GatewayClient, InitMessage},
request::AuthenticatorRequest,
};
match self {
ClientMessage::Initial(init_message) => {
let (req, id) = AuthenticatorRequest::new_initial_request(
InitMessage {
pub_key: init_message.pub_key(),
},
reply_to,
);
Ok((req.to_bytes()?, id))
}
ClientMessage::Final(final_message) => {
let (req, id) = AuthenticatorRequest::new_final_request(
FinalMessage {
gateway_client: GatewayClient {
pub_key: final_message.gateway_client_pub_key(),
private_ip: final_message
.gateway_client_ipv4()
.ok_or(Error::UnsupportedMessage)?
.into(),
mac: ClientMac::new(final_message.gateway_client_mac()),
},
credential: final_message.credential(),
},
reply_to,
);
Ok((req.to_bytes()?, id))
}
ClientMessage::Query(query_message) => {
let (req, id) = AuthenticatorRequest::new_query_request(
query_message.pub_key(),
reply_to,
);
Ok((req.to_bytes()?, id))
}
_ => Err(Error::UnsupportedMessage),
}
}
AuthenticatorVersion::V3 => {
use v3::{
registration::{ClientMac, FinalMessage, GatewayClient, InitMessage},
request::AuthenticatorRequest,
topup::TopUpMessage,
};
match self {
ClientMessage::Initial(init_message) => {
let (req, id) = AuthenticatorRequest::new_initial_request(
InitMessage {
pub_key: init_message.pub_key(),
},
reply_to,
);
Ok((req.to_bytes()?, id))
}
ClientMessage::Final(final_message) => {
let (req, id) = AuthenticatorRequest::new_final_request(
FinalMessage {
gateway_client: GatewayClient {
pub_key: final_message.gateway_client_pub_key(),
private_ip: final_message
.gateway_client_ipv4()
.ok_or(Error::UnsupportedMessage)?
.into(),
mac: ClientMac::new(final_message.gateway_client_mac()),
},
credential: final_message.credential(),
},
reply_to,
);
Ok((req.to_bytes()?, id))
}
ClientMessage::Query(query_message) => {
let (req, id) = AuthenticatorRequest::new_query_request(
query_message.pub_key(),
reply_to,
);
Ok((req.to_bytes()?, id))
}
ClientMessage::TopUp(top_up_message) => {
let (req, id) = AuthenticatorRequest::new_topup_request(
TopUpMessage {
pub_key: top_up_message.pub_key(),
credential: top_up_message.credential(),
},
reply_to,
);
Ok((req.to_bytes()?, id))
}
}
}
AuthenticatorVersion::V4 => {
use v4::{
registration::{ClientMac, FinalMessage, GatewayClient, InitMessage},
request::AuthenticatorRequest,
topup::TopUpMessage,
};
match self {
ClientMessage::Initial(init_message) => {
let (req, id) = AuthenticatorRequest::new_initial_request(
InitMessage {
pub_key: init_message.pub_key(),
},
reply_to,
);
Ok((req.to_bytes()?, id))
}
ClientMessage::Final(final_message) => {
let (req, id) = AuthenticatorRequest::new_final_request(
FinalMessage {
gateway_client: GatewayClient {
pub_key: final_message.gateway_client_pub_key(),
private_ips: IpPair {
ipv4: final_message
.gateway_client_ipv4()
.ok_or(Error::UnsupportedMessage)?,
ipv6: final_message
.gateway_client_ipv6()
.ok_or(Error::UnsupportedMessage)?,
}
.into(),
mac: ClientMac::new(final_message.gateway_client_mac()),
},
credential: final_message.credential(),
},
reply_to,
);
Ok((req.to_bytes()?, id))
}
ClientMessage::Query(query_message) => {
let (req, id) = AuthenticatorRequest::new_query_request(
query_message.pub_key(),
reply_to,
);
Ok((req.to_bytes()?, id))
}
ClientMessage::TopUp(top_up_message) => {
let (req, id) = AuthenticatorRequest::new_topup_request(
TopUpMessage {
pub_key: top_up_message.pub_key(),
credential: top_up_message.credential(),
},
reply_to,
);
Ok((req.to_bytes()?, id))
}
}
}
AuthenticatorVersion::V5 => {
use v5::{
registration::{ClientMac, FinalMessage, GatewayClient, InitMessage},
request::AuthenticatorRequest,
topup::TopUpMessage,
};
match self {
ClientMessage::Initial(init_message) => {
let (req, id) = AuthenticatorRequest::new_initial_request(InitMessage {
pub_key: init_message.pub_key(),
});
Ok((req.to_bytes()?, id))
}
ClientMessage::Final(final_message) => {
let (req, id) = AuthenticatorRequest::new_final_request(FinalMessage {
gateway_client: GatewayClient {
pub_key: final_message.gateway_client_pub_key(),
private_ips: IpPair {
ipv4: final_message
.gateway_client_ipv4()
.ok_or(Error::UnsupportedMessage)?,
ipv6: final_message
.gateway_client_ipv6()
.ok_or(Error::UnsupportedMessage)?,
},
mac: ClientMac::new(final_message.gateway_client_mac()),
},
credential: final_message.credential(),
});
Ok((req.to_bytes()?, id))
}
ClientMessage::Query(query_message) => {
let (req, id) =
AuthenticatorRequest::new_query_request(query_message.pub_key());
Ok((req.to_bytes()?, id))
}
ClientMessage::TopUp(top_up_message) => {
let (req, id) = AuthenticatorRequest::new_topup_request(TopUpMessage {
pub_key: top_up_message.pub_key(),
credential: top_up_message.credential(),
});
Ok((req.to_bytes()?, id))
}
}
}
AuthenticatorVersion::UNKNOWN => Err(Error::UnknownVersion),
}
}
pub fn use_surbs(&self) -> bool {
use AuthenticatorVersion::*;
match self.version() {
V1 | V2 | V3 | V4 => false,
V5 => true,
UNKNOWN => true,
}
}
}
// Same comment as above struct
#[derive(Debug)]
pub struct QueryMessageImpl {
pub pub_key: PeerPublicKey,
pub version: AuthenticatorVersion,
}
impl Versionable for QueryMessageImpl {
fn version(&self) -> AuthenticatorVersion {
self.version
}
}
impl QueryBandwidthMessage for QueryMessageImpl {
fn pub_key(&self) -> PeerPublicKey {
self.pub_key
}
}
+2 -13
View File
@@ -23,17 +23,6 @@ pub enum Error {
#[error("conversion: {0}")] #[error("conversion: {0}")]
Conversion(String), Conversion(String),
// TODO add version number for debugging #[error("failed to serialize response packet: {source}")]
#[error("unknown version number")] FailedToSerializeResponsePacket { source: Box<bincode::ErrorKind> },
UnknownVersion,
// TODO add version number for debugging
#[error("unsupported request version")]
UnsupportedVersion,
#[error("gateway doesn't support this type of message")]
UnsupportedMessage,
#[error(transparent)]
Bincode(#[from] bincode::Error),
} }
+1 -6
View File
@@ -1,9 +1,6 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net> // Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
pub mod client_message;
pub mod request;
pub mod response;
pub mod traits; pub mod traits;
pub mod v1; pub mod v1;
pub mod v2; pub mod v2;
@@ -13,13 +10,11 @@ pub mod v5;
mod error; mod error;
mod util; mod util;
mod version;
pub use error::Error; pub use error::Error;
pub use v5 as latest; pub use v5 as latest;
pub use version::AuthenticatorVersion;
pub const CURRENT_VERSION: u8 = latest::VERSION; pub const CURRENT_VERSION: u8 = 5;
fn make_bincode_serializer() -> impl bincode::Options { fn make_bincode_serializer() -> impl bincode::Options {
use bincode::Options; use bincode::Options;
@@ -1,204 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
use nym_sphinx::addressing::Recipient;
use crate::traits::{FinalMessage, InitMessage, QueryBandwidthMessage, TopUpMessage};
use crate::{v1, v2, v3, v4, v5};
#[derive(Debug)]
pub enum AuthenticatorRequest {
Initial {
msg: Box<dyn InitMessage + Send + Sync + 'static>,
protocol: Protocol,
reply_to: Option<Recipient>,
request_id: u64,
},
Final {
msg: Box<dyn FinalMessage + Send + Sync + 'static>,
protocol: Protocol,
reply_to: Option<Recipient>,
request_id: u64,
},
QueryBandwidth {
msg: Box<dyn QueryBandwidthMessage + Send + Sync + 'static>,
protocol: Protocol,
reply_to: Option<Recipient>,
request_id: u64,
},
TopUpBandwidth {
msg: Box<dyn TopUpMessage + Send + Sync + 'static>,
protocol: Protocol,
reply_to: Option<Recipient>,
request_id: u64,
},
}
impl From<v1::request::AuthenticatorRequest> for AuthenticatorRequest {
fn from(value: v1::request::AuthenticatorRequest) -> Self {
match value.data {
v1::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
msg: Box::new(init_message),
protocol: Protocol {
version: value.version,
service_provider_type: ServiceProviderType::Authenticator,
},
reply_to: Some(value.reply_to),
request_id: value.request_id,
},
v1::request::AuthenticatorRequestData::Final(gateway_client) => Self::Final {
msg: Box::new(gateway_client),
protocol: Protocol {
version: value.version,
service_provider_type: ServiceProviderType::Authenticator,
},
reply_to: Some(value.reply_to),
request_id: value.request_id,
},
v1::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
Self::QueryBandwidth {
msg: Box::new(peer_public_key),
protocol: Protocol {
version: value.version,
service_provider_type: ServiceProviderType::Authenticator,
},
reply_to: Some(value.reply_to),
request_id: value.request_id,
}
}
}
}
}
impl From<v2::request::AuthenticatorRequest> for AuthenticatorRequest {
fn from(value: v2::request::AuthenticatorRequest) -> Self {
match value.data {
v2::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
msg: Box::new(init_message),
protocol: value.protocol,
reply_to: Some(value.reply_to),
request_id: value.request_id,
},
v2::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
msg: final_message,
protocol: value.protocol,
reply_to: Some(value.reply_to),
request_id: value.request_id,
},
v2::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
Self::QueryBandwidth {
msg: Box::new(peer_public_key),
protocol: value.protocol,
reply_to: Some(value.reply_to),
request_id: value.request_id,
}
}
}
}
}
impl From<v3::request::AuthenticatorRequest> for AuthenticatorRequest {
fn from(value: v3::request::AuthenticatorRequest) -> Self {
match value.data {
v3::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
msg: Box::new(init_message),
protocol: value.protocol,
reply_to: Some(value.reply_to),
request_id: value.request_id,
},
v3::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
msg: final_message,
protocol: value.protocol,
reply_to: Some(value.reply_to),
request_id: value.request_id,
},
v3::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
Self::QueryBandwidth {
msg: Box::new(peer_public_key),
protocol: value.protocol,
reply_to: Some(value.reply_to),
request_id: value.request_id,
}
}
v3::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => {
Self::TopUpBandwidth {
msg: top_up_message,
protocol: value.protocol,
reply_to: Some(value.reply_to),
request_id: value.request_id,
}
}
}
}
}
impl From<v4::request::AuthenticatorRequest> for AuthenticatorRequest {
fn from(value: v4::request::AuthenticatorRequest) -> Self {
match value.data {
v4::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
msg: Box::new(init_message),
protocol: value.protocol,
reply_to: Some(value.reply_to),
request_id: value.request_id,
},
v4::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
msg: final_message,
protocol: value.protocol,
reply_to: Some(value.reply_to),
request_id: value.request_id,
},
v4::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
Self::QueryBandwidth {
msg: Box::new(peer_public_key),
protocol: value.protocol,
reply_to: Some(value.reply_to),
request_id: value.request_id,
}
}
v4::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => {
Self::TopUpBandwidth {
msg: top_up_message,
protocol: value.protocol,
reply_to: Some(value.reply_to),
request_id: value.request_id,
}
}
}
}
}
impl From<v5::request::AuthenticatorRequest> for AuthenticatorRequest {
fn from(value: v5::request::AuthenticatorRequest) -> Self {
match value.data {
v5::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
msg: Box::new(init_message),
protocol: value.protocol,
reply_to: None,
request_id: value.request_id,
},
v5::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
msg: final_message,
protocol: value.protocol,
reply_to: None,
request_id: value.request_id,
},
v5::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
Self::QueryBandwidth {
msg: Box::new(peer_public_key),
protocol: value.protocol,
reply_to: None,
request_id: value.request_id,
}
}
v5::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => {
Self::TopUpBandwidth {
msg: top_up_message,
protocol: value.protocol,
reply_to: None,
request_id: value.request_id,
}
}
}
}
}
@@ -1,106 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::traits::{
Id, PendingRegistrationResponse, RegisteredResponse, RemainingBandwidthResponse,
TopUpBandwidthResponse,
};
use crate::{v2, v3, v4, v5};
#[derive(Debug)]
pub enum AuthenticatorResponse {
PendingRegistration(Box<dyn PendingRegistrationResponse + Send + Sync + 'static>),
Registered(Box<dyn RegisteredResponse + Send + Sync + 'static>),
RemainingBandwidth(Box<dyn RemainingBandwidthResponse + Send + Sync + 'static>),
TopUpBandwidth(Box<dyn TopUpBandwidthResponse + Send + Sync + 'static>),
}
impl Id for AuthenticatorResponse {
fn id(&self) -> u64 {
match self {
AuthenticatorResponse::PendingRegistration(pending_registration_response) => {
pending_registration_response.id()
}
AuthenticatorResponse::Registered(registered_response) => registered_response.id(),
AuthenticatorResponse::RemainingBandwidth(remaining_bandwidth_response) => {
remaining_bandwidth_response.id()
}
AuthenticatorResponse::TopUpBandwidth(top_up_bandwidth_response) => {
top_up_bandwidth_response.id()
}
}
}
}
impl From<v2::response::AuthenticatorResponse> for AuthenticatorResponse {
fn from(value: v2::response::AuthenticatorResponse) -> Self {
match value.data {
v2::response::AuthenticatorResponseData::PendingRegistration(
pending_registration_response,
) => Self::PendingRegistration(Box::new(pending_registration_response)),
v2::response::AuthenticatorResponseData::Registered(registered_response) => {
Self::Registered(Box::new(registered_response))
}
v2::response::AuthenticatorResponseData::RemainingBandwidth(
remaining_bandwidth_response,
) => Self::RemainingBandwidth(Box::new(remaining_bandwidth_response)),
}
}
}
impl From<v3::response::AuthenticatorResponse> for AuthenticatorResponse {
fn from(value: v3::response::AuthenticatorResponse) -> Self {
match value.data {
v3::response::AuthenticatorResponseData::PendingRegistration(
pending_registration_response,
) => Self::PendingRegistration(Box::new(pending_registration_response)),
v3::response::AuthenticatorResponseData::Registered(registered_response) => {
Self::Registered(Box::new(registered_response))
}
v3::response::AuthenticatorResponseData::RemainingBandwidth(
remaining_bandwidth_response,
) => Self::RemainingBandwidth(Box::new(remaining_bandwidth_response)),
v3::response::AuthenticatorResponseData::TopUpBandwidth(top_up_bandwidth_response) => {
Self::TopUpBandwidth(Box::new(top_up_bandwidth_response))
}
}
}
}
impl From<v4::response::AuthenticatorResponse> for AuthenticatorResponse {
fn from(value: v4::response::AuthenticatorResponse) -> Self {
match value.data {
v4::response::AuthenticatorResponseData::PendingRegistration(
pending_registration_response,
) => Self::PendingRegistration(Box::new(pending_registration_response)),
v4::response::AuthenticatorResponseData::Registered(registered_response) => {
Self::Registered(Box::new(registered_response))
}
v4::response::AuthenticatorResponseData::RemainingBandwidth(
remaining_bandwidth_response,
) => Self::RemainingBandwidth(Box::new(remaining_bandwidth_response)),
v4::response::AuthenticatorResponseData::TopUpBandwidth(top_up_bandwidth_response) => {
Self::TopUpBandwidth(Box::new(top_up_bandwidth_response))
}
}
}
}
impl From<v5::response::AuthenticatorResponse> for AuthenticatorResponse {
fn from(value: v5::response::AuthenticatorResponse) -> Self {
match value.data {
v5::response::AuthenticatorResponseData::PendingRegistration(
pending_registration_response,
) => Self::PendingRegistration(Box::new(pending_registration_response)),
v5::response::AuthenticatorResponseData::Registered(registered_response) => {
Self::Registered(Box::new(registered_response))
}
v5::response::AuthenticatorResponseData::RemainingBandwidth(
remaining_bandwidth_response,
) => Self::RemainingBandwidth(Box::new(remaining_bandwidth_response)),
v5::response::AuthenticatorResponseData::TopUpBandwidth(top_up_bandwidth_response) => {
Self::TopUpBandwidth(Box::new(top_up_bandwidth_response))
}
}
}
}
+220 -437
View File
@@ -1,105 +1,49 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net> // Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use std::fmt;
use std::net::{Ipv4Addr, Ipv6Addr};
use nym_credentials_interface::CredentialSpendingData; use nym_credentials_interface::CredentialSpendingData;
use nym_crypto::asymmetric::x25519::PrivateKey; use nym_crypto::asymmetric::x25519::PrivateKey;
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
use nym_sphinx::addressing::clients::Recipient;
use nym_wireguard_types::PeerPublicKey; use nym_wireguard_types::PeerPublicKey;
use crate::latest::registration::IpPair; use crate::{
use crate::{AuthenticatorVersion, Error, v1, v2, v3, v4, v5}; v1, v2, v3, v4,
v5::{self, registration::IpPair},
Error,
};
pub trait Versionable { #[derive(Copy, Clone, Debug)]
fn version(&self) -> AuthenticatorVersion; pub enum AuthenticatorVersion {
V1,
V2,
V3,
V4,
V5,
UNKNOWN,
} }
impl Versionable for v1::GatewayClient { impl From<Protocol> for AuthenticatorVersion {
fn version(&self) -> AuthenticatorVersion { fn from(value: Protocol) -> Self {
AuthenticatorVersion::V1 if value.service_provider_type != ServiceProviderType::Authenticator {
AuthenticatorVersion::UNKNOWN
} else if value.version == v1::VERSION {
AuthenticatorVersion::V1
} else if value.version == v2::VERSION {
AuthenticatorVersion::V2
} else if value.version == v3::VERSION {
AuthenticatorVersion::V3
} else if value.version == v4::VERSION {
AuthenticatorVersion::V4
} else if value.version == v5::VERSION {
AuthenticatorVersion::V5
} else {
AuthenticatorVersion::UNKNOWN
}
} }
} }
impl Versionable for v1::registration::InitMessage { pub trait InitMessage {
fn version(&self) -> AuthenticatorVersion {
AuthenticatorVersion::V1
}
}
impl Versionable for v2::registration::InitMessage {
fn version(&self) -> AuthenticatorVersion {
AuthenticatorVersion::V2
}
}
impl Versionable for v3::registration::InitMessage {
fn version(&self) -> AuthenticatorVersion {
AuthenticatorVersion::V3
}
}
impl Versionable for v4::registration::InitMessage {
fn version(&self) -> AuthenticatorVersion {
AuthenticatorVersion::V4
}
}
impl Versionable for v5::registration::InitMessage {
fn version(&self) -> AuthenticatorVersion {
AuthenticatorVersion::V5
}
}
impl Versionable for v2::registration::FinalMessage {
fn version(&self) -> AuthenticatorVersion {
AuthenticatorVersion::V2
}
}
impl Versionable for v3::registration::FinalMessage {
fn version(&self) -> AuthenticatorVersion {
AuthenticatorVersion::V3
}
}
impl Versionable for v4::registration::FinalMessage {
fn version(&self) -> AuthenticatorVersion {
AuthenticatorVersion::V4
}
}
impl Versionable for v5::registration::FinalMessage {
fn version(&self) -> AuthenticatorVersion {
AuthenticatorVersion::V5
}
}
impl Versionable for PeerPublicKey {
fn version(&self) -> AuthenticatorVersion {
AuthenticatorVersion::V3
}
}
impl Versionable for v3::topup::TopUpMessage {
fn version(&self) -> AuthenticatorVersion {
AuthenticatorVersion::V3
}
}
impl Versionable for v4::topup::TopUpMessage {
fn version(&self) -> AuthenticatorVersion {
AuthenticatorVersion::V4
}
}
impl Versionable for v5::topup::TopUpMessage {
fn version(&self) -> AuthenticatorVersion {
AuthenticatorVersion::V5
}
}
pub trait InitMessage: Versionable + fmt::Debug {
fn pub_key(&self) -> PeerPublicKey; fn pub_key(&self) -> PeerPublicKey;
} }
@@ -133,18 +77,15 @@ impl InitMessage for v5::registration::InitMessage {
} }
} }
pub trait FinalMessage: Versionable + fmt::Debug { pub trait FinalMessage {
fn gateway_client_pub_key(&self) -> PeerPublicKey; fn pub_key(&self) -> PeerPublicKey;
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error>; fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error>;
fn private_ips(&self) -> IpPair; fn private_ips(&self) -> IpPair;
fn gateway_client_ipv4(&self) -> Option<Ipv4Addr>;
fn gateway_client_ipv6(&self) -> Option<Ipv6Addr>;
fn gateway_client_mac(&self) -> Vec<u8>;
fn credential(&self) -> Option<CredentialSpendingData>; fn credential(&self) -> Option<CredentialSpendingData>;
} }
impl FinalMessage for v1::GatewayClient { impl FinalMessage for v1::GatewayClient {
fn gateway_client_pub_key(&self) -> PeerPublicKey { fn pub_key(&self) -> PeerPublicKey {
self.pub_key self.pub_key
} }
@@ -156,28 +97,13 @@ impl FinalMessage for v1::GatewayClient {
self.private_ip.into() self.private_ip.into()
} }
fn gateway_client_ipv4(&self) -> Option<Ipv4Addr> {
match self.private_ip {
std::net::IpAddr::V4(ipv4_addr) => Some(ipv4_addr),
std::net::IpAddr::V6(_) => None,
}
}
fn gateway_client_ipv6(&self) -> Option<Ipv6Addr> {
None
}
fn gateway_client_mac(&self) -> Vec<u8> {
self.mac.to_vec()
}
fn credential(&self) -> Option<CredentialSpendingData> { fn credential(&self) -> Option<CredentialSpendingData> {
None None
} }
} }
impl FinalMessage for v2::registration::FinalMessage { impl FinalMessage for v2::registration::FinalMessage {
fn gateway_client_pub_key(&self) -> PeerPublicKey { fn pub_key(&self) -> PeerPublicKey {
self.gateway_client.pub_key self.gateway_client.pub_key
} }
@@ -189,28 +115,13 @@ impl FinalMessage for v2::registration::FinalMessage {
self.gateway_client.private_ip.into() self.gateway_client.private_ip.into()
} }
fn gateway_client_ipv4(&self) -> Option<Ipv4Addr> {
match self.gateway_client.private_ip {
std::net::IpAddr::V4(ipv4_addr) => Some(ipv4_addr),
std::net::IpAddr::V6(_) => None,
}
}
fn gateway_client_ipv6(&self) -> Option<Ipv6Addr> {
None
}
fn gateway_client_mac(&self) -> Vec<u8> {
self.gateway_client.mac.to_vec()
}
fn credential(&self) -> Option<CredentialSpendingData> { fn credential(&self) -> Option<CredentialSpendingData> {
self.credential.clone() self.credential.clone()
} }
} }
impl FinalMessage for v3::registration::FinalMessage { impl FinalMessage for v3::registration::FinalMessage {
fn gateway_client_pub_key(&self) -> PeerPublicKey { fn pub_key(&self) -> PeerPublicKey {
self.gateway_client.pub_key self.gateway_client.pub_key
} }
@@ -222,28 +133,13 @@ impl FinalMessage for v3::registration::FinalMessage {
self.gateway_client.private_ip.into() self.gateway_client.private_ip.into()
} }
fn gateway_client_ipv4(&self) -> Option<Ipv4Addr> {
match self.gateway_client.private_ip {
std::net::IpAddr::V4(ipv4_addr) => Some(ipv4_addr),
std::net::IpAddr::V6(_) => None,
}
}
fn gateway_client_ipv6(&self) -> Option<Ipv6Addr> {
None
}
fn gateway_client_mac(&self) -> Vec<u8> {
self.gateway_client.mac.to_vec()
}
fn credential(&self) -> Option<CredentialSpendingData> { fn credential(&self) -> Option<CredentialSpendingData> {
self.credential.clone() self.credential.clone()
} }
} }
impl FinalMessage for v4::registration::FinalMessage { impl FinalMessage for v4::registration::FinalMessage {
fn gateway_client_pub_key(&self) -> PeerPublicKey { fn pub_key(&self) -> PeerPublicKey {
self.gateway_client.pub_key self.gateway_client.pub_key
} }
@@ -255,25 +151,13 @@ impl FinalMessage for v4::registration::FinalMessage {
self.gateway_client.private_ips.into() self.gateway_client.private_ips.into()
} }
fn gateway_client_ipv4(&self) -> Option<Ipv4Addr> {
Some(self.gateway_client.private_ips.ipv4)
}
fn gateway_client_ipv6(&self) -> Option<Ipv6Addr> {
Some(self.gateway_client.private_ips.ipv6)
}
fn gateway_client_mac(&self) -> Vec<u8> {
self.gateway_client.mac.to_vec()
}
fn credential(&self) -> Option<CredentialSpendingData> { fn credential(&self) -> Option<CredentialSpendingData> {
self.credential.clone() self.credential.clone()
} }
} }
impl FinalMessage for v5::registration::FinalMessage { impl FinalMessage for v5::registration::FinalMessage {
fn gateway_client_pub_key(&self) -> PeerPublicKey { fn pub_key(&self) -> PeerPublicKey {
self.gateway_client.pub_key self.gateway_client.pub_key
} }
@@ -285,24 +169,12 @@ impl FinalMessage for v5::registration::FinalMessage {
self.gateway_client.private_ips self.gateway_client.private_ips
} }
fn gateway_client_ipv4(&self) -> Option<Ipv4Addr> {
Some(self.gateway_client.private_ips.ipv4)
}
fn gateway_client_ipv6(&self) -> Option<Ipv6Addr> {
Some(self.gateway_client.private_ips.ipv6)
}
fn gateway_client_mac(&self) -> Vec<u8> {
self.gateway_client.mac.to_vec()
}
fn credential(&self) -> Option<CredentialSpendingData> { fn credential(&self) -> Option<CredentialSpendingData> {
self.credential.clone() self.credential.clone()
} }
} }
pub trait QueryBandwidthMessage: Versionable + fmt::Debug { pub trait QueryBandwidthMessage {
fn pub_key(&self) -> PeerPublicKey; fn pub_key(&self) -> PeerPublicKey;
} }
@@ -312,7 +184,7 @@ impl QueryBandwidthMessage for PeerPublicKey {
} }
} }
pub trait TopUpMessage: Versionable + fmt::Debug { pub trait TopUpMessage {
fn pub_key(&self) -> PeerPublicKey; fn pub_key(&self) -> PeerPublicKey;
fn credential(&self) -> CredentialSpendingData; fn credential(&self) -> CredentialSpendingData;
} }
@@ -347,286 +219,197 @@ impl TopUpMessage for v5::topup::TopUpMessage {
} }
} }
pub trait Id { pub enum AuthenticatorRequest {
fn id(&self) -> u64; Initial {
msg: Box<dyn InitMessage + Send + Sync + 'static>,
protocol: Protocol,
reply_to: Option<Recipient>,
request_id: u64,
},
Final {
msg: Box<dyn FinalMessage + Send + Sync + 'static>,
protocol: Protocol,
reply_to: Option<Recipient>,
request_id: u64,
},
QueryBandwidth {
msg: Box<dyn QueryBandwidthMessage + Send + Sync + 'static>,
protocol: Protocol,
reply_to: Option<Recipient>,
request_id: u64,
},
TopUpBandwidth {
msg: Box<dyn TopUpMessage + Send + Sync + 'static>,
protocol: Protocol,
reply_to: Option<Recipient>,
request_id: u64,
},
} }
impl Id for v2::response::PendingRegistrationResponse { impl From<v1::request::AuthenticatorRequest> for AuthenticatorRequest {
fn id(&self) -> u64 { fn from(value: v1::request::AuthenticatorRequest) -> Self {
self.request_id match value.data {
v1::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
msg: Box::new(init_message),
protocol: Protocol {
version: value.version,
service_provider_type: ServiceProviderType::Authenticator,
},
reply_to: Some(value.reply_to),
request_id: value.request_id,
},
v1::request::AuthenticatorRequestData::Final(gateway_client) => Self::Final {
msg: Box::new(gateway_client),
protocol: Protocol {
version: value.version,
service_provider_type: ServiceProviderType::Authenticator,
},
reply_to: Some(value.reply_to),
request_id: value.request_id,
},
v1::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
Self::QueryBandwidth {
msg: Box::new(peer_public_key),
protocol: Protocol {
version: value.version,
service_provider_type: ServiceProviderType::Authenticator,
},
reply_to: Some(value.reply_to),
request_id: value.request_id,
}
}
}
} }
} }
impl Id for v3::response::PendingRegistrationResponse { impl From<v2::request::AuthenticatorRequest> for AuthenticatorRequest {
fn id(&self) -> u64 { fn from(value: v2::request::AuthenticatorRequest) -> Self {
self.request_id match value.data {
v2::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
msg: Box::new(init_message),
protocol: value.protocol,
reply_to: Some(value.reply_to),
request_id: value.request_id,
},
v2::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
msg: final_message,
protocol: value.protocol,
reply_to: Some(value.reply_to),
request_id: value.request_id,
},
v2::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
Self::QueryBandwidth {
msg: Box::new(peer_public_key),
protocol: value.protocol,
reply_to: Some(value.reply_to),
request_id: value.request_id,
}
}
}
} }
} }
impl Id for v4::response::PendingRegistrationResponse { impl From<v3::request::AuthenticatorRequest> for AuthenticatorRequest {
fn id(&self) -> u64 { fn from(value: v3::request::AuthenticatorRequest) -> Self {
self.request_id match value.data {
v3::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
msg: Box::new(init_message),
protocol: value.protocol,
reply_to: Some(value.reply_to),
request_id: value.request_id,
},
v3::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
msg: final_message,
protocol: value.protocol,
reply_to: Some(value.reply_to),
request_id: value.request_id,
},
v3::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
Self::QueryBandwidth {
msg: Box::new(peer_public_key),
protocol: value.protocol,
reply_to: Some(value.reply_to),
request_id: value.request_id,
}
}
v3::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => {
Self::TopUpBandwidth {
msg: top_up_message,
protocol: value.protocol,
reply_to: Some(value.reply_to),
request_id: value.request_id,
}
}
}
} }
} }
impl Id for v5::response::PendingRegistrationResponse { impl From<v4::request::AuthenticatorRequest> for AuthenticatorRequest {
fn id(&self) -> u64 { fn from(value: v4::request::AuthenticatorRequest) -> Self {
self.request_id match value.data {
v4::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
msg: Box::new(init_message),
protocol: value.protocol,
reply_to: Some(value.reply_to),
request_id: value.request_id,
},
v4::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
msg: final_message,
protocol: value.protocol,
reply_to: Some(value.reply_to),
request_id: value.request_id,
},
v4::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
Self::QueryBandwidth {
msg: Box::new(peer_public_key),
protocol: value.protocol,
reply_to: Some(value.reply_to),
request_id: value.request_id,
}
}
v4::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => {
Self::TopUpBandwidth {
msg: top_up_message,
protocol: value.protocol,
reply_to: Some(value.reply_to),
request_id: value.request_id,
}
}
}
} }
} }
impl Id for v2::response::RegisteredResponse { impl From<v5::request::AuthenticatorRequest> for AuthenticatorRequest {
fn id(&self) -> u64 { fn from(value: v5::request::AuthenticatorRequest) -> Self {
self.request_id match value.data {
} v5::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
} msg: Box::new(init_message),
protocol: value.protocol,
impl Id for v3::response::RegisteredResponse { reply_to: None,
fn id(&self) -> u64 { request_id: value.request_id,
self.request_id },
} v5::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
} msg: final_message,
protocol: value.protocol,
impl Id for v4::response::RegisteredResponse { reply_to: None,
fn id(&self) -> u64 { request_id: value.request_id,
self.request_id },
} v5::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
} Self::QueryBandwidth {
msg: Box::new(peer_public_key),
impl Id for v5::response::RegisteredResponse { protocol: value.protocol,
fn id(&self) -> u64 { reply_to: None,
self.request_id request_id: value.request_id,
} }
} }
v5::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => {
impl Id for v2::response::RemainingBandwidthResponse { Self::TopUpBandwidth {
fn id(&self) -> u64 { msg: top_up_message,
self.request_id protocol: value.protocol,
} reply_to: None,
} request_id: value.request_id,
}
impl Id for v3::response::RemainingBandwidthResponse { }
fn id(&self) -> u64 { }
self.request_id
}
}
impl Id for v4::response::RemainingBandwidthResponse {
fn id(&self) -> u64 {
self.request_id
}
}
impl Id for v5::response::RemainingBandwidthResponse {
fn id(&self) -> u64 {
self.request_id
}
}
impl Id for v3::response::TopUpBandwidthResponse {
fn id(&self) -> u64 {
self.request_id
}
}
impl Id for v4::response::TopUpBandwidthResponse {
fn id(&self) -> u64 {
self.request_id
}
}
impl Id for v5::response::TopUpBandwidthResponse {
fn id(&self) -> u64 {
self.request_id
}
}
pub trait PendingRegistrationResponse: Id + fmt::Debug {
fn nonce(&self) -> u64;
fn verify(&self, gateway_key: &PrivateKey) -> std::result::Result<(), Error>;
fn pub_key(&self) -> PeerPublicKey;
fn private_ips(&self) -> IpPair;
}
impl PendingRegistrationResponse for v2::response::PendingRegistrationResponse {
fn nonce(&self) -> u64 {
self.reply.nonce
}
fn verify(&self, gateway_key: &PrivateKey) -> std::result::Result<(), Error> {
self.reply.gateway_data.verify(gateway_key, self.nonce())
}
fn pub_key(&self) -> PeerPublicKey {
self.reply.gateway_data.pub_key
}
fn private_ips(&self) -> IpPair {
self.reply.gateway_data.private_ip.into()
}
}
impl PendingRegistrationResponse for v3::response::PendingRegistrationResponse {
fn nonce(&self) -> u64 {
self.reply.nonce
}
fn verify(&self, gateway_key: &PrivateKey) -> std::result::Result<(), Error> {
self.reply.gateway_data.verify(gateway_key, self.nonce())
}
fn pub_key(&self) -> PeerPublicKey {
self.reply.gateway_data.pub_key
}
fn private_ips(&self) -> IpPair {
self.reply.gateway_data.private_ip.into()
}
}
impl PendingRegistrationResponse for v4::response::PendingRegistrationResponse {
fn nonce(&self) -> u64 {
self.reply.nonce
}
fn verify(&self, gateway_key: &PrivateKey) -> std::result::Result<(), Error> {
self.reply.gateway_data.verify(gateway_key, self.nonce())
}
fn pub_key(&self) -> PeerPublicKey {
self.reply.gateway_data.pub_key
}
fn private_ips(&self) -> IpPair {
self.reply.gateway_data.private_ips.into()
}
}
impl PendingRegistrationResponse for v5::response::PendingRegistrationResponse {
fn nonce(&self) -> u64 {
self.reply.nonce
}
fn verify(&self, gateway_key: &PrivateKey) -> std::result::Result<(), Error> {
self.reply.gateway_data.verify(gateway_key, self.nonce())
}
fn pub_key(&self) -> PeerPublicKey {
self.reply.gateway_data.pub_key
}
fn private_ips(&self) -> IpPair {
self.reply.gateway_data.private_ips
}
}
pub trait RegisteredResponse: Id + fmt::Debug {
fn private_ips(&self) -> IpPair;
fn pub_key(&self) -> PeerPublicKey;
fn wg_port(&self) -> u16;
}
impl RegisteredResponse for v2::response::RegisteredResponse {
fn private_ips(&self) -> IpPair {
self.reply.private_ip.into()
}
fn pub_key(&self) -> PeerPublicKey {
self.reply.pub_key
}
fn wg_port(&self) -> u16 {
self.reply.wg_port
}
}
impl RegisteredResponse for v3::response::RegisteredResponse {
fn private_ips(&self) -> IpPair {
self.reply.private_ip.into()
}
fn pub_key(&self) -> PeerPublicKey {
self.reply.pub_key
}
fn wg_port(&self) -> u16 {
self.reply.wg_port
}
}
impl RegisteredResponse for v4::response::RegisteredResponse {
fn private_ips(&self) -> IpPair {
self.reply.private_ips.into()
}
fn pub_key(&self) -> PeerPublicKey {
self.reply.pub_key
}
fn wg_port(&self) -> u16 {
self.reply.wg_port
}
}
impl RegisteredResponse for v5::response::RegisteredResponse {
fn private_ips(&self) -> IpPair {
self.reply.private_ips
}
fn pub_key(&self) -> PeerPublicKey {
self.reply.pub_key
}
fn wg_port(&self) -> u16 {
self.reply.wg_port
}
}
pub trait RemainingBandwidthResponse: Id + fmt::Debug {
fn available_bandwidth(&self) -> Option<i64>;
}
impl RemainingBandwidthResponse for v2::response::RemainingBandwidthResponse {
fn available_bandwidth(&self) -> Option<i64> {
self.reply.as_ref().map(|r| r.available_bandwidth)
}
}
impl RemainingBandwidthResponse for v3::response::RemainingBandwidthResponse {
fn available_bandwidth(&self) -> Option<i64> {
self.reply.as_ref().map(|r| r.available_bandwidth)
}
}
impl RemainingBandwidthResponse for v4::response::RemainingBandwidthResponse {
fn available_bandwidth(&self) -> Option<i64> {
self.reply.as_ref().map(|r| r.available_bandwidth)
}
}
impl RemainingBandwidthResponse for v5::response::RemainingBandwidthResponse {
fn available_bandwidth(&self) -> Option<i64> {
self.reply.as_ref().map(|r| r.available_bandwidth)
}
}
pub trait TopUpBandwidthResponse: Id + fmt::Debug {
fn available_bandwidth(&self) -> i64;
}
impl TopUpBandwidthResponse for v3::response::TopUpBandwidthResponse {
fn available_bandwidth(&self) -> i64 {
self.reply.available_bandwidth
}
}
impl TopUpBandwidthResponse for v4::response::TopUpBandwidthResponse {
fn available_bandwidth(&self) -> i64 {
self.reply.available_bandwidth
}
}
impl TopUpBandwidthResponse for v5::response::TopUpBandwidthResponse {
fn available_bandwidth(&self) -> i64 {
self.reply.available_bandwidth
} }
} }
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::error::Error; use crate::error::Error;
use base64::{Engine, engine::general_purpose}; use base64::{engine::general_purpose, Engine};
use nym_wireguard_types::PeerPublicKey; use nym_wireguard_types::PeerPublicKey;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::HashMap;
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::error::Error; use crate::error::Error;
use base64::{Engine, engine::general_purpose}; use base64::{engine::general_purpose, Engine};
use nym_credentials_interface::CredentialSpendingData; use nym_credentials_interface::CredentialSpendingData;
use nym_wireguard_types::PeerPublicKey; use nym_wireguard_types::PeerPublicKey;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::error::Error; use crate::error::Error;
use base64::{Engine, engine::general_purpose}; use base64::{engine::general_purpose, Engine};
use nym_credentials_interface::CredentialSpendingData; use nym_credentials_interface::CredentialSpendingData;
use nym_wireguard_types::PeerPublicKey; use nym_wireguard_types::PeerPublicKey;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::error::Error; use crate::error::Error;
use base64::{Engine, engine::general_purpose}; use base64::{engine::general_purpose, Engine};
use nym_credentials_interface::CredentialSpendingData; use nym_credentials_interface::CredentialSpendingData;
use nym_network_defaults::constants::{WG_TUN_DEVICE_IP_ADDRESS_V4, WG_TUN_DEVICE_IP_ADDRESS_V6}; use nym_network_defaults::constants::{WG_TUN_DEVICE_IP_ADDRESS_V4, WG_TUN_DEVICE_IP_ADDRESS_V6};
use nym_wireguard_types::PeerPublicKey; use nym_wireguard_types::PeerPublicKey;
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::error::Error; use crate::error::Error;
use base64::{Engine, engine::general_purpose}; use base64::{engine::general_purpose, Engine};
use nym_credentials_interface::CredentialSpendingData; use nym_credentials_interface::CredentialSpendingData;
use nym_network_defaults::constants::{WG_TUN_DEVICE_IP_ADDRESS_V4, WG_TUN_DEVICE_IP_ADDRESS_V6}; use nym_network_defaults::constants::{WG_TUN_DEVICE_IP_ADDRESS_V4, WG_TUN_DEVICE_IP_ADDRESS_V6};
use nym_wireguard_types::PeerPublicKey; use nym_wireguard_types::PeerPublicKey;
@@ -28,6 +28,8 @@ 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
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct IpPair { pub struct IpPair {
pub ipv4: Ipv4Addr, pub ipv4: Ipv4Addr,
@@ -1,195 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::{v1, v2, v3, v4, v5};
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
#[derive(Copy, Clone, Debug, PartialEq, strum_macros::Display)]
#[strum(serialize_all = "snake_case")]
pub enum AuthenticatorVersion {
/// introduced in wispa release (1.1.5)
V1,
/// introduced in aero release (1.1.9)
V2,
/// introduced in magura release (1.1.10)
V3,
/// introduced in crunch release (1.2.0)
V4,
/// introduced in dorina-patched release (1.6.1)
V5,
UNKNOWN,
}
impl AuthenticatorVersion {
pub const LATEST: Self = Self::V5;
pub const fn release_version(&self) -> semver::Version {
match self {
AuthenticatorVersion::V1 => semver::Version::new(1, 1, 5),
AuthenticatorVersion::V2 => semver::Version::new(1, 1, 9),
AuthenticatorVersion::V3 => semver::Version::new(1, 1, 10),
AuthenticatorVersion::V4 => semver::Version::new(1, 2, 0),
AuthenticatorVersion::V5 => semver::Version::new(1, 6, 1),
AuthenticatorVersion::UNKNOWN => semver::Version::new(0, 0, 0),
}
}
}
impl From<Protocol> for AuthenticatorVersion {
fn from(value: Protocol) -> Self {
if value.service_provider_type != ServiceProviderType::Authenticator {
AuthenticatorVersion::UNKNOWN
} else if value.version == v1::VERSION {
AuthenticatorVersion::V1
} else if value.version == v2::VERSION {
AuthenticatorVersion::V2
} else if value.version == v3::VERSION {
AuthenticatorVersion::V3
} else if value.version == v4::VERSION {
AuthenticatorVersion::V4
} else if value.version == v5::VERSION {
AuthenticatorVersion::V5
} else {
AuthenticatorVersion::UNKNOWN
}
}
}
impl From<u8> for AuthenticatorVersion {
fn from(value: u8) -> Self {
if value == v1::VERSION {
AuthenticatorVersion::V1
} else if value == v2::VERSION {
AuthenticatorVersion::V2
} else if value == v3::VERSION {
AuthenticatorVersion::V3
} else if value == v4::VERSION {
AuthenticatorVersion::V4
} else if value == v5::VERSION {
AuthenticatorVersion::V5
} else {
AuthenticatorVersion::UNKNOWN
}
}
}
impl From<&str> for AuthenticatorVersion {
fn from(value: &str) -> Self {
let Ok(semver) = semver::Version::parse(value) else {
return Self::UNKNOWN;
};
semver.into()
}
}
impl From<Option<&String>> for AuthenticatorVersion {
fn from(value: Option<&String>) -> Self {
match value {
None => Self::UNKNOWN,
Some(value) => value.as_str().into(),
}
}
}
impl From<String> for AuthenticatorVersion {
fn from(value: String) -> Self {
Self::from(value.as_str())
}
}
impl From<Option<String>> for AuthenticatorVersion {
fn from(value: Option<String>) -> Self {
value.as_ref().into()
}
}
impl From<semver::Version> for AuthenticatorVersion {
fn from(semver: semver::Version) -> Self {
if semver < AuthenticatorVersion::V1.release_version() {
return Self::UNKNOWN;
}
if semver < AuthenticatorVersion::V2.release_version() {
return Self::V1;
}
if semver < AuthenticatorVersion::V3.release_version() {
return Self::V2;
}
if semver < AuthenticatorVersion::V4.release_version() {
return Self::V3;
}
if semver < AuthenticatorVersion::V5.release_version() {
return Self::V4;
}
// if provided version is higher (or equal) to release version of V5,
// we return the latest (i.e. v5)
debug_assert_eq!(
Self::V5,
Self::LATEST,
"a new AuthenticatorVersion variant has been introduced without adjusting the `From<semver::Version>` trait"
);
Self::LATEST
}
}
#[cfg(test)]
mod tests {
use super::super::latest;
use super::*;
#[test]
fn strum_display() {
// sanity check on formatting and casing
assert_eq!("v1", AuthenticatorVersion::V1.to_string());
assert_eq!("v2", AuthenticatorVersion::V2.to_string());
assert_eq!("unknown", AuthenticatorVersion::UNKNOWN.to_string());
}
#[test]
fn u8_conversion() {
assert_eq!(AuthenticatorVersion::V1, AuthenticatorVersion::from(1u8));
assert_eq!(AuthenticatorVersion::V2, AuthenticatorVersion::from(2u8));
assert_eq!(
AuthenticatorVersion::UNKNOWN,
AuthenticatorVersion::from(latest::VERSION + 1)
);
assert_eq!(
AuthenticatorVersion::UNKNOWN,
AuthenticatorVersion::from(0u8)
);
assert_eq!(
AuthenticatorVersion::UNKNOWN,
AuthenticatorVersion::from(255u8)
);
}
#[test]
fn semver_checks() {
assert_eq!(AuthenticatorVersion::UNKNOWN, "1.1.4".into());
assert_eq!(AuthenticatorVersion::UNKNOWN, "0.1.0".into());
assert_eq!(AuthenticatorVersion::UNKNOWN, "1.0.4".into());
assert_eq!(AuthenticatorVersion::V1, "1.1.5".into());
assert_eq!(AuthenticatorVersion::V1, "1.1.6".into());
assert_eq!(AuthenticatorVersion::V1, "1.1.8".into());
assert_eq!(AuthenticatorVersion::V2, "1.1.9".into());
assert_eq!(AuthenticatorVersion::V3, "1.1.10".into());
assert_eq!(AuthenticatorVersion::V3, "1.1.11".into());
assert_eq!(AuthenticatorVersion::V3, "1.1.60".into());
assert_eq!(AuthenticatorVersion::V4, "1.2.0".into());
assert_eq!(AuthenticatorVersion::V4, "1.2.1".into());
assert_eq!(AuthenticatorVersion::V4, "1.5.1".into());
assert_eq!(AuthenticatorVersion::V4, "1.6.0".into());
assert_eq!(AuthenticatorVersion::V5, "1.6.1".into());
assert_eq!(AuthenticatorVersion::V5, "1.6.11".into());
assert_eq!(AuthenticatorVersion::V5, "1.7.0".into());
assert_eq!(AuthenticatorVersion::V5, "1.16.11".into());
assert_eq!(AuthenticatorVersion::V5, "1.17.0".into());
}
}
-1
View File
@@ -7,7 +7,6 @@ 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
[dependencies] [dependencies]
async-trait = { workspace = true }
bip39 = { workspace = true } bip39 = { workspace = true }
log = { workspace = true } log = { workspace = true }
rand = { workspace = true } rand = { workspace = true }
-2
View File
@@ -23,12 +23,10 @@ use nym_validator_client::nym_api::EpochId;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient; use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
pub use event::BandwidthStatusMessage; pub use event::BandwidthStatusMessage;
pub use traits::{BandwidthTicketProvider, DEFAULT_TICKETS_TO_SPEND};
pub mod acquire; pub mod acquire;
pub mod error; pub mod error;
mod event; mod event;
mod traits;
mod utils; mod utils;
#[derive(Debug)] #[derive(Debug)]
-42
View File
@@ -1,42 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use async_trait::async_trait;
use nym_credential_storage::storage::Storage;
use nym_credentials_interface::TicketType;
use nym_crypto::asymmetric::ed25519;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
use crate::{error::BandwidthControllerError, BandwidthController, PreparedCredential};
pub const DEFAULT_TICKETS_TO_SPEND: u32 = 1;
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait BandwidthTicketProvider: Send + Sync {
async fn get_ecash_ticket(
&self,
ticket_type: TicketType,
gateway_id: ed25519::PublicKey,
tickets_to_spend: u32,
) -> Result<PreparedCredential, BandwidthControllerError>;
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl<C, St> BandwidthTicketProvider for BandwidthController<C, St>
where
C: DkgQueryClient + Sync + Send,
St: nym_credential_storage::storage::Storage,
<St as Storage>::StorageError: Send + Sync + 'static,
{
async fn get_ecash_ticket(
&self,
ticket_type: TicketType,
gateway_id: ed25519::PublicKey,
tickets_to_spend: u32,
) -> Result<PreparedCredential, BandwidthControllerError> {
self.prepare_ecash_ticket(ticket_type, gateway_id.to_bytes(), tickets_to_spend)
.await
}
}
+3 -3
View File
@@ -13,7 +13,7 @@ use nym_credentials_interface::{
}; };
use nym_ecash_time::Date; use nym_ecash_time::Date;
use nym_validator_client::coconut::all_ecash_api_clients; use nym_validator_client::coconut::all_ecash_api_clients;
use nym_validator_client::nym_api::{EpochId, NymApiClientExt}; use nym_validator_client::nym_api::EpochId;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient; use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
use nym_validator_client::EcashApiClient; use nym_validator_client::EcashApiClient;
use rand::prelude::SliceRandom; use rand::prelude::SliceRandom;
@@ -207,7 +207,7 @@ where
<St as Storage>::StorageError: Send + Sync + 'static, <St as Storage>::StorageError: Send + Sync + 'static,
{ {
if let Some(stored) = storage if let Some(stored) = storage
.get_expiration_date_signatures(expiration_date, epoch_id) .get_expiration_date_signatures(expiration_date)
.await .await
.map_err(BandwidthControllerError::credential_storage_error)? .map_err(BandwidthControllerError::credential_storage_error)?
{ {
@@ -220,7 +220,7 @@ where
ecash_apis, ecash_apis,
|api| async move { |api| async move {
api.api_client api.api_client
.global_expiration_date_signatures(Some(expiration_date), Some(epoch_id)) .global_expiration_date_signatures(Some(expiration_date))
.await .await
}, },
format!("aggregated coin index signatures for date {expiration_date}"), format!("aggregated coin index signatures for date {expiration_date}"),
+2 -2
View File
@@ -1,8 +1,8 @@
use clap::Args;
use clap::builder::Command; use clap::builder::Command;
use clap::clap_derive::ValueEnum; use clap::clap_derive::ValueEnum;
use clap_complete::Shell as ClapShell; use clap::Args;
use clap_complete::generator::generate; use clap_complete::generator::generate;
use clap_complete::Shell as ClapShell;
use std::io; use std::io;
pub fn fig_generate(command: &mut Command, name: &str) { pub fn fig_generate(command: &mut Command, name: &str) {
+3 -11
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.85" rust-version = "1.76"
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
@@ -13,7 +13,6 @@ async-trait = { workspace = true }
base64 = { workspace = true } base64 = { workspace = true }
bs58 = { workspace = true } bs58 = { workspace = true }
clap = { workspace = true, optional = true } clap = { workspace = true, optional = true }
cfg-if = { workspace = true }
comfy-table = { workspace = true, optional = true } comfy-table = { workspace = true, optional = true }
futures = { workspace = true } futures = { workspace = true }
humantime = { workspace = true } humantime = { workspace = true }
@@ -53,7 +52,6 @@ nym-client-core-config-types = { path = "./config-types", features = [
nym-client-core-surb-storage = { path = "./surb-storage" } nym-client-core-surb-storage = { path = "./surb-storage" }
nym-client-core-gateways-storage = { path = "./gateways-storage" } nym-client-core-gateways-storage = { path = "./gateways-storage" }
nym-ecash-time = { path = "../ecash-time" } nym-ecash-time = { path = "../ecash-time" }
nym-mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" }
[target."cfg(not(target_arch = \"wasm32\"))".dependencies] [target."cfg(not(target_arch = \"wasm32\"))".dependencies]
nym-mixnet-client = { path = "../client-libs/mixnet-client", default-features = false } nym-mixnet-client = { path = "../client-libs/mixnet-client", default-features = false }
@@ -69,6 +67,7 @@ workspace = true
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.hyper-util] [target."cfg(not(target_arch = \"wasm32\"))".dependencies.hyper-util]
workspace = true workspace = true
features = ["tokio"] features = ["tokio"]
###
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-stream] [target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-stream]
workspace = true workspace = true
@@ -102,7 +101,7 @@ workspace = true
features = ["tokio"] features = ["tokio"]
[target."cfg(target_arch = \"wasm32\")".dependencies.gloo-timers] [target."cfg(target_arch = \"wasm32\")".dependencies.gloo-timers]
workspace = true version = "0.3.0"
features = ["futures"] features = ["futures"]
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-utils] [target."cfg(target_arch = \"wasm32\")".dependencies.wasm-utils]
@@ -113,10 +112,6 @@ features = ["websocket"]
workspace = true workspace = true
features = ["wasm-bindgen"] features = ["wasm-bindgen"]
[target."cfg(target_arch = \"wasm32\")".dependencies.tokio_with_wasm]
workspace = true
features = ["full"]
[dev-dependencies] [dev-dependencies]
tempfile = { workspace = true } tempfile = { workspace = true }
@@ -128,6 +123,3 @@ fs-surb-storage = ["nym-client-core-surb-storage/fs-surb-storage"]
fs-gateways-storage = ["nym-client-core-gateways-storage/fs-gateways-storage"] fs-gateways-storage = ["nym-client-core-gateways-storage/fs-gateways-storage"]
wasm = ["nym-gateway-client/wasm"] wasm = ["nym-gateway-client/wasm"]
metrics-server = [] metrics-server = []
[lints]
workspace = true
@@ -707,10 +707,13 @@ pub struct DebugConfig {
/// Defines all configuration options related to reply SURBs. /// Defines all configuration options related to reply SURBs.
pub reply_surbs: ReplySurbs, pub reply_surbs: ReplySurbs,
/// 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. /// Defines all configuration options related to the forget me flag.
pub forget_me: ForgetMe, pub forget_me: ForgetMe,
/// Defines all configuration options related to the remember me flag. /// Defines all configuration options related to the remember me flag.
pub remember_me: RememberMe, pub remember_me: RememberMe,
} }
@@ -543,8 +543,10 @@ pub struct DebugConfigV6 {
/// Defines all configuration options related to reply SURBs. /// Defines all configuration options related to reply SURBs.
pub reply_surbs: ReplySurbsV6, pub reply_surbs: ReplySurbsV6,
/// Defines all configuration options related to stats reporting. /// Defines all configuration options related to stats reporting.
pub stats_reporting: StatsReportingV6, pub stats_reporting: StatsReportingV6,
/// Defines all configuration options related to the forget me flag. /// Defines all configuration options related to the forget me flag.
pub forget_me: ForgetMeV6, pub forget_me: ForgetMeV6,
@@ -3,7 +3,6 @@ name = "nym-client-core-gateways-storage"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
license.workspace = true license.workspace = true
rust-version.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
@@ -27,7 +26,6 @@ features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate", "time"]
optional = true optional = true
[build-dependencies] [build-dependencies]
anyhow = { workspace = true }
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
sqlx = { workspace = true, features = [ sqlx = { workspace = true, features = [
"runtime-tokio-rustls", "runtime-tokio-rustls",
+4 -13
View File
@@ -2,30 +2,23 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#[tokio::main] #[tokio::main]
async fn main() -> anyhow::Result<()> { async fn main() {
#[cfg(feature = "fs-gateways-storage")] #[cfg(feature = "fs-gateways-storage")]
{ {
use anyhow::Context;
use sqlx::{Connection, SqliteConnection}; use sqlx::{Connection, SqliteConnection};
use std::env; use std::env;
let out_dir = env::var("OUT_DIR")?; let out_dir = env::var("OUT_DIR").unwrap();
let database_path = format!("{out_dir}/gateways-storage-example.sqlite"); let database_path = format!("{out_dir}/gateways-storage-example.sqlite");
// remove the db file if it already existed from previous build
// in case it was from a different branch
if std::fs::exists(&database_path)? {
std::fs::remove_file(&database_path)?;
}
let mut conn = SqliteConnection::connect(&format!("sqlite://{database_path}?mode=rwc")) let mut conn = SqliteConnection::connect(&format!("sqlite://{database_path}?mode=rwc"))
.await .await
.context("Failed to create SQLx database connection")?; .expect("Failed to create SQLx database connection");
sqlx::migrate!("./fs_gateways_migrations") sqlx::migrate!("./fs_gateways_migrations")
.run(&mut conn) .run(&mut conn)
.await .await
.context("Failed to perform SQLx migrations")?; .expect("Failed to perform SQLx migrations");
#[cfg(target_family = "unix")] #[cfg(target_family = "unix")]
println!("cargo:rustc-env=DATABASE_URL=sqlite://{}", &database_path); println!("cargo:rustc-env=DATABASE_URL=sqlite://{}", &database_path);
@@ -35,6 +28,4 @@ async fn main() -> anyhow::Result<()> {
// not a valid windows path... but hey, it works... // not a valid windows path... but hey, it works...
println!("cargo:rustc-env=DATABASE_URL=sqlite:///{}", &database_path); println!("cargo:rustc-env=DATABASE_URL=sqlite:///{}", &database_path);
} }
Ok(())
} }
@@ -31,6 +31,7 @@ impl StorageManager {
} }
})?; })?;
} }
let opts = sqlx::sqlite::SqliteConnectOptions::new() let opts = sqlx::sqlite::SqliteConnectOptions::new()
.journal_mode(sqlx::sqlite::SqliteJournalMode::Wal) .journal_mode(sqlx::sqlite::SqliteJournalMode::Wal)
.synchronous(SqliteSynchronous::Normal) .synchronous(SqliteSynchronous::Normal)
@@ -114,12 +114,13 @@ where
})?; })?;
hardcoded_topology.entry_capable_nodes().cloned().collect() hardcoded_topology.entry_capable_nodes().cloned().collect()
} else { } else {
let mut rng = rand::thread_rng();
crate::init::helpers::gateways_for_init( crate::init::helpers::gateways_for_init(
&mut rng,
&core.client.nym_api_urls, &core.client.nym_api_urls,
user_agent, user_agent,
core.debug.topology.minimum_gateway_performance, core.debug.topology.minimum_gateway_performance,
core.debug.topology.ignore_ingress_epoch_role, core.debug.topology.ignore_ingress_epoch_role,
None,
) )
.await? .await?
}; };
@@ -58,7 +58,6 @@ where
Some(data) => data, Some(data) => data,
None => { None => {
// SAFETY: one of those arguments must have been set // SAFETY: one of those arguments must have been set
#[allow(clippy::unwrap_used)]
fs::read(common_args.signatures_path.unwrap())? fs::read(common_args.signatures_path.unwrap())?
} }
}; };
@@ -64,7 +64,6 @@ where
Some(data) => data, Some(data) => data,
None => { None => {
// SAFETY: one of those arguments must have been set // SAFETY: one of those arguments must have been set
#[allow(clippy::unwrap_used)]
fs::read(common_args.credential_path.unwrap())? fs::read(common_args.credential_path.unwrap())?
} }
}; };
@@ -58,7 +58,6 @@ where
Some(data) => data, Some(data) => data,
None => { None => {
// SAFETY: one of those arguments must have been set // SAFETY: one of those arguments must have been set
#[allow(clippy::unwrap_used)]
fs::read(common_args.signatures_path.unwrap())? fs::read(common_args.signatures_path.unwrap())?
} }
}; };
@@ -58,7 +58,6 @@ where
Some(data) => data, Some(data) => data,
None => { None => {
// SAFETY: one of those arguments must have been set // SAFETY: one of those arguments must have been set
#[allow(clippy::unwrap_used)]
fs::read(common_args.key_path.unwrap())? fs::read(common_args.key_path.unwrap())?
} }
}; };
@@ -173,12 +173,13 @@ where
})?; })?;
hardcoded_topology.entry_capable_nodes().cloned().collect() hardcoded_topology.entry_capable_nodes().cloned().collect()
} else { } else {
let mut rng = rand::thread_rng();
crate::init::helpers::gateways_for_init( crate::init::helpers::gateways_for_init(
&mut rng,
&core.client.nym_api_urls, &core.client.nym_api_urls,
user_agent, user_agent,
core.debug.topology.minimum_gateway_performance, core.debug.topology.minimum_gateway_performance,
core.debug.topology.ignore_ingress_epoch_role, core.debug.topology.ignore_ingress_epoch_role,
None,
) )
.await? .await?
}; };
+98 -242
View File
@@ -7,12 +7,11 @@ use super::statistics_control::StatisticsControl;
use crate::client::base_client::storage::helpers::store_client_keys; use crate::client::base_client::storage::helpers::store_client_keys;
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;
use crate::client::event_control::EventControl;
use crate::client::inbound_messages::{InputMessage, InputMessageReceiver, InputMessageSender}; use crate::client::inbound_messages::{InputMessage, InputMessageReceiver, InputMessageSender};
use crate::client::key_manager::persistence::KeyStore; use crate::client::key_manager::persistence::KeyStore;
use crate::client::key_manager::ClientKeys; use crate::client::key_manager::ClientKeys;
use crate::client::mix_traffic::transceiver::{GatewayReceiver, GatewayTransceiver, RemoteGateway}; use crate::client::mix_traffic::transceiver::{GatewayReceiver, GatewayTransceiver, RemoteGateway};
use crate::client::mix_traffic::{BatchMixMessageSender, MixTrafficController, MixTrafficEvent}; use crate::client::mix_traffic::{BatchMixMessageSender, MixTrafficController};
use crate::client::real_messages_control; use crate::client::real_messages_control;
use crate::client::real_messages_control::RealMessagesController; use crate::client::real_messages_control::RealMessagesController;
use crate::client::received_buffer::{ use crate::client::received_buffer::{
@@ -28,13 +27,13 @@ use crate::client::topology_control::nym_api_provider::NymApiTopologyProvider;
use crate::client::topology_control::{ use crate::client::topology_control::{
TopologyAccessor, TopologyRefresher, TopologyRefresherConfig, TopologyAccessor, TopologyRefresher, TopologyRefresherConfig,
}; };
use crate::config;
use crate::config::{Config, DebugConfig}; use crate::config::{Config, DebugConfig};
use crate::error::ClientCoreError; use crate::error::ClientCoreError;
use crate::init::{ use crate::init::{
setup_gateway, setup_gateway,
types::{GatewaySetup, InitialisationResult}, types::{GatewaySetup, InitialisationResult},
}; };
use crate::{config, spawn_future};
use futures::channel::mpsc; use futures::channel::mpsc;
use nym_bandwidth_controller::BandwidthController; use nym_bandwidth_controller::BandwidthController;
use nym_client_core_config_types::{ForgetMe, RememberMe}; use nym_client_core_config_types::{ForgetMe, RememberMe};
@@ -49,15 +48,16 @@ use nym_gateway_client::{
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::addressing::nodes::NodeIdentity; use nym_sphinx::addressing::nodes::NodeIdentity;
use nym_sphinx::params::PacketType;
use nym_sphinx::receiver::{ReconstructedMessage, SphinxMessageReceiver}; use nym_sphinx::receiver::{ReconstructedMessage, SphinxMessageReceiver};
use nym_statistics_common::clients::ClientStatsSender; use nym_statistics_common::clients::ClientStatsSender;
use nym_statistics_common::generate_client_stats_id; use nym_statistics_common::generate_client_stats_id;
use nym_task::connections::{ConnectionCommandReceiver, ConnectionCommandSender, LaneQueueLengths}; use nym_task::connections::{ConnectionCommandReceiver, ConnectionCommandSender, LaneQueueLengths};
use nym_task::ShutdownTracker; use nym_task::{TaskClient, TaskHandle};
use nym_topology::provider_trait::TopologyProvider; use nym_topology::provider_trait::TopologyProvider;
use nym_topology::HardcodedTopologyProvider; use nym_topology::HardcodedTopologyProvider;
use nym_validator_client::nym_api::NymApiClientExt; use nym_validator_client::nym_api::NymApiClientExt;
use nym_validator_client::{nyxd::contract_traits::DkgQueryClient, UserAgent}; use nym_validator_client::{nyxd::contract_traits::DkgQueryClient, NymApiClient, UserAgent};
use rand::prelude::SliceRandom; use rand::prelude::SliceRandom;
use rand::rngs::OsRng; use rand::rngs::OsRng;
use rand::thread_rng; use rand::thread_rng;
@@ -67,12 +67,9 @@ use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
use time::OffsetDateTime; use time::OffsetDateTime;
use tokio::sync::mpsc::Sender; use tokio::sync::mpsc::Sender;
use tracing::*;
use url::Url; use url::Url;
#[cfg(target_arch = "wasm32")]
#[cfg(debug_assertions)]
use wasm_utils::console_log;
#[cfg(all( #[cfg(all(
not(target_arch = "wasm32"), not(target_arch = "wasm32"),
feature = "fs-surb-storage", feature = "fs-surb-storage",
@@ -83,28 +80,10 @@ pub mod non_wasm_helpers;
pub mod helpers; pub mod helpers;
pub mod storage; pub mod storage;
#[derive(Clone, Copy, Debug)]
pub enum MixnetClientEvent {
Traffic(MixTrafficEvent),
}
pub type EventReceiver = mpsc::UnboundedReceiver<MixnetClientEvent>;
#[derive(Clone)]
pub struct EventSender(pub mpsc::UnboundedSender<MixnetClientEvent>);
impl EventSender {
pub fn send(&self, event: MixnetClientEvent) {
if let Err(err) = self.0.unbounded_send(event) {
tracing::warn!("Failed to send error event. The caller event reader was closed: {err}");
}
}
}
#[derive(Clone)] #[derive(Clone)]
pub struct ClientInput { pub struct ClientInput {
pub connection_command_sender: ConnectionCommandSender, pub connection_command_sender: ConnectionCommandSender,
pub input_sender: InputMessageSender, pub input_sender: InputMessageSender,
pub client_request_sender: ClientRequestSender,
} }
impl ClientInput { impl ClientInput {
@@ -116,6 +95,7 @@ impl ClientInput {
} }
} }
#[derive(Clone)]
pub struct ClientOutput { pub struct ClientOutput {
pub received_buffer_request_sender: ReceivedBufferRequestSender, pub received_buffer_request_sender: ReceivedBufferRequestSender,
} }
@@ -155,11 +135,9 @@ pub enum ClientInputStatus {
} }
impl ClientInputStatus { impl ClientInputStatus {
#[allow(clippy::panic)]
pub fn register_producer(&mut self) -> ClientInput { pub fn register_producer(&mut self) -> ClientInput {
match std::mem::replace(self, ClientInputStatus::Connected) { match std::mem::replace(self, ClientInputStatus::Connected) {
ClientInputStatus::AwaitingProducer { client_input } => client_input, ClientInputStatus::AwaitingProducer { client_input } => client_input,
// critical failure implying misuse of software
ClientInputStatus::Connected => panic!("producer was already registered before"), ClientInputStatus::Connected => panic!("producer was already registered before"),
} }
} }
@@ -171,11 +149,9 @@ pub enum ClientOutputStatus {
} }
impl ClientOutputStatus { impl ClientOutputStatus {
#[allow(clippy::panic)]
pub fn register_consumer(&mut self) -> ClientOutput { pub fn register_consumer(&mut self) -> ClientOutput {
match std::mem::replace(self, ClientOutputStatus::Connected) { match std::mem::replace(self, ClientOutputStatus::Connected) {
ClientOutputStatus::AwaitingConsumer { client_output } => client_output, ClientOutputStatus::AwaitingConsumer { client_output } => client_output,
// critical failure implying misuse of software
ClientOutputStatus::Connected => panic!("consumer was already registered before"), ClientOutputStatus::Connected => panic!("consumer was already registered before"),
} }
} }
@@ -215,8 +191,7 @@ pub struct BaseClientBuilder<C, S: MixnetClientStorage> {
wait_for_gateway: bool, wait_for_gateway: bool,
custom_topology_provider: Option<Box<dyn TopologyProvider + Send + Sync>>, custom_topology_provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
custom_gateway_transceiver: Option<Box<dyn GatewayTransceiver + Send>>, custom_gateway_transceiver: Option<Box<dyn GatewayTransceiver + Send>>,
shutdown: Option<ShutdownTracker>, shutdown: Option<TaskClient>,
event_tx: Option<EventSender>,
user_agent: Option<UserAgent>, user_agent: Option<UserAgent>,
setup_method: GatewaySetup, setup_method: GatewaySetup,
@@ -245,7 +220,6 @@ where
custom_topology_provider: None, custom_topology_provider: None,
custom_gateway_transceiver: None, custom_gateway_transceiver: None,
shutdown: None, shutdown: None,
event_tx: None,
user_agent: None, user_agent: None,
setup_method: GatewaySetup::MustLoad { gateway_id: None }, setup_method: GatewaySetup::MustLoad { gateway_id: None },
#[cfg(unix)] #[cfg(unix)]
@@ -303,17 +277,11 @@ where
} }
#[must_use] #[must_use]
pub fn with_shutdown(mut self, shutdown: ShutdownTracker) -> Self { pub fn with_shutdown(mut self, shutdown: TaskClient) -> Self {
self.shutdown = Some(shutdown); self.shutdown = Some(shutdown);
self self
} }
#[must_use]
pub fn with_event_tx(mut self, event_tx: EventSender) -> Self {
self.event_tx = Some(event_tx);
self
}
#[must_use] #[must_use]
pub fn with_user_agent(mut self, user_agent: UserAgent) -> Self { pub fn with_user_agent(mut self, user_agent: UserAgent) -> Self {
self.user_agent = Some(user_agent); self.user_agent = Some(user_agent);
@@ -344,18 +312,6 @@ where
details.client_address() details.client_address()
} }
fn start_event_control(
parent_event_tx: Option<EventSender>,
children_event_rx: EventReceiver,
shutdown_tracker: &ShutdownTracker,
) {
let event_control = EventControl::new(parent_event_tx, children_event_rx);
shutdown_tracker.try_spawn_named_with_shutdown(
async move { event_control.run().await },
"EventControl",
);
}
// future constantly pumping loop cover traffic at some specified average rate // future constantly pumping loop cover traffic at some specified average rate
// the pumped traffic goes to the MixTrafficController // the pumped traffic goes to the MixTrafficController
fn start_cover_traffic_stream( fn start_cover_traffic_stream(
@@ -365,11 +321,11 @@ where
topology_accessor: TopologyAccessor, topology_accessor: TopologyAccessor,
mix_tx: BatchMixMessageSender, mix_tx: BatchMixMessageSender,
stats_tx: ClientStatsSender, stats_tx: ClientStatsSender,
shutdown_tracker: &ShutdownTracker, task_client: TaskClient,
) { ) {
tracing::info!("Starting loop cover traffic stream..."); info!("Starting loop cover traffic stream...");
let mut stream = LoopCoverTrafficStream::new( let stream = LoopCoverTrafficStream::new(
ack_key, ack_key,
debug_config.acknowledgements.average_ack_delay, debug_config.acknowledgements.average_ack_delay,
mix_tx, mix_tx,
@@ -378,9 +334,10 @@ where
debug_config.traffic, debug_config.traffic,
debug_config.cover_traffic, debug_config.cover_traffic,
stats_tx, stats_tx,
task_client,
); );
shutdown_tracker
.try_spawn_named_with_shutdown(async move { stream.run().await }, "CoverTrafficStream"); stream.start();
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
@@ -396,12 +353,13 @@ 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,
packet_type: PacketType,
stats_tx: ClientStatsSender, stats_tx: ClientStatsSender,
shutdown_tracker: &ShutdownTracker,
) { ) {
tracing::info!("Starting real traffic stream..."); info!("Starting real traffic stream...");
let real_messages_controller = RealMessagesController::new( RealMessagesController::new(
controller_config, controller_config,
key_rotation_config, key_rotation_config,
ack_receiver, ack_receiver,
@@ -414,63 +372,9 @@ where
lane_queue_lengths, lane_queue_lengths,
client_connection_rx, client_connection_rx,
stats_tx, stats_tx,
shutdown_tracker.clone_shutdown_token(), task_client,
); )
.start(packet_type);
// break out all the subtasks
let (mut out_queue_control, mut reply_controller, ack_controller) =
real_messages_controller.into_tasks();
let (
mut ack_listener,
mut input_listener,
mut retransmission_listener,
mut sent_notification_listener,
mut ack_action_controller,
) = ack_controller.into_tasks();
shutdown_tracker.try_spawn_named(
async move { out_queue_control.run().await },
"RealMessagesController::OutQueueControl",
);
let shutdown_token = shutdown_tracker.clone_shutdown_token();
shutdown_tracker.try_spawn_named(
async move { reply_controller.run(shutdown_token).await },
"RealMessagesController::ReplyController",
);
let shutdown_token = shutdown_tracker.clone_shutdown_token();
shutdown_tracker.try_spawn_named(
async move { ack_listener.run(shutdown_token).await },
"AcknowledgementController::AcknowledgementListener",
);
let shutdown_token = shutdown_tracker.clone_shutdown_token();
shutdown_tracker.try_spawn_named(
async move { input_listener.run(shutdown_token).await },
"AcknowledgementController::InputMessageListener",
);
let shutdown_token = shutdown_tracker.clone_shutdown_token();
shutdown_tracker.try_spawn_named(
async move { retransmission_listener.run(shutdown_token).await },
"AcknowledgementController::RetransmissionRequestListener",
);
shutdown_tracker.try_spawn_named_with_shutdown(
async move {
sent_notification_listener.run().await;
},
"AcknowledgementController::SentNotificationListener",
);
let shutdown_token = shutdown_tracker.clone_shutdown_token();
shutdown_tracker.try_spawn_named(
async move { ack_action_controller.run(shutdown_token).await },
"AcknowledgementController::ActionController",
);
// .start(packet_type);
} }
// buffer controlling all messages fetched from provider // buffer controlling all messages fetched from provider
@@ -481,29 +385,21 @@ where
mixnet_receiver: MixnetMessageReceiver, mixnet_receiver: MixnetMessageReceiver,
reply_key_storage: SentReplyKeys, reply_key_storage: SentReplyKeys,
reply_controller_sender: ReplyControllerSender, reply_controller_sender: ReplyControllerSender,
shutdown: TaskClient,
metrics_reporter: ClientStatsSender, metrics_reporter: ClientStatsSender,
shutdown_tracker: &ShutdownTracker,
) { ) {
tracing::info!("Starting received messages buffer controller..."); info!("Starting received messages buffer controller...");
let controller = ReceivedMessagesBufferController::<SphinxMessageReceiver>::new( let controller: ReceivedMessagesBufferController<SphinxMessageReceiver> =
local_encryption_keypair, ReceivedMessagesBufferController::new(
query_receiver, local_encryption_keypair,
mixnet_receiver, query_receiver,
reply_key_storage, mixnet_receiver,
reply_controller_sender, reply_key_storage,
metrics_reporter, reply_controller_sender,
shutdown_tracker.clone_shutdown_token(), metrics_reporter,
); shutdown,
let (mut msg_receiver, mut req_receiver) = controller.into_tasks(); );
controller.start()
shutdown_tracker.try_spawn_named(
async move { msg_receiver.run().await },
"ReceivedMessagesBufferController::FragmentedMessageReceiver",
);
shutdown_tracker.try_spawn_named(
async move { req_receiver.run().await },
"ReceivedMessagesBufferController::RequestReceiver",
);
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
@@ -515,7 +411,7 @@ where
packet_router: PacketRouter, packet_router: PacketRouter,
stats_reporter: ClientStatsSender, stats_reporter: ClientStatsSender,
#[cfg(unix)] connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>, #[cfg(unix)] connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
shutdown_tracker: &ShutdownTracker, shutdown: TaskClient,
) -> Result<GatewayClient<C, S::CredentialStore>, ClientCoreError> ) -> Result<GatewayClient<C, S::CredentialStore>, ClientCoreError>
where where
<S::KeyStore as KeyStore>::StorageError: Send + Sync + 'static, <S::KeyStore as KeyStore>::StorageError: Send + Sync + 'static,
@@ -534,7 +430,7 @@ where
packet_router, packet_router,
bandwidth_controller, bandwidth_controller,
stats_reporter, stats_reporter,
shutdown_tracker.clone_shutdown_token(), shutdown,
) )
} else { } else {
let cfg = GatewayConfig::new( let cfg = GatewayConfig::new(
@@ -559,7 +455,7 @@ where
stats_reporter, stats_reporter,
#[cfg(unix)] #[cfg(unix)]
connection_fd_callback, connection_fd_callback,
shutdown_tracker.clone_shutdown_token(), shutdown,
) )
}; };
@@ -595,7 +491,7 @@ 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| {
tracing::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) }
})? })?
} }
@@ -622,7 +518,7 @@ where
packet_router: PacketRouter, packet_router: PacketRouter,
stats_reporter: ClientStatsSender, stats_reporter: ClientStatsSender,
#[cfg(unix)] connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>, #[cfg(unix)] connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
shutdown_tracker: &ShutdownTracker, mut shutdown: TaskClient,
) -> Result<Box<dyn GatewayTransceiver + Send>, ClientCoreError> ) -> Result<Box<dyn GatewayTransceiver + Send>, ClientCoreError>
where where
<S::KeyStore as KeyStore>::StorageError: Send + Sync + 'static, <S::KeyStore as KeyStore>::StorageError: Send + Sync + 'static,
@@ -639,6 +535,7 @@ where
Err(ClientCoreError::CustomGatewaySelectionExpected) Err(ClientCoreError::CustomGatewaySelectionExpected)
} else { } else {
// and make sure to invalidate the task client, so we wouldn't cause premature shutdown // and make sure to invalidate the task client, so we wouldn't cause premature shutdown
shutdown.disarm();
custom_gateway_transceiver.set_packet_router(packet_router)?; custom_gateway_transceiver.set_packet_router(packet_router)?;
Ok(custom_gateway_transceiver) Ok(custom_gateway_transceiver)
}; };
@@ -654,7 +551,7 @@ where
stats_reporter, stats_reporter,
#[cfg(unix)] #[cfg(unix)]
connection_fd_callback, connection_fd_callback,
shutdown_tracker, shutdown,
) )
.await?; .await?;
@@ -665,7 +562,7 @@ where
custom_provider: Option<Box<dyn TopologyProvider + Send + Sync>>, custom_provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
config_topology: config::Topology, config_topology: config::Topology,
nym_api_urls: Vec<Url>, nym_api_urls: Vec<Url>,
nym_api_client: nym_http_api_client::Client, nym_api_client: NymApiClient,
) -> Box<dyn TopologyProvider + Send + Sync> { ) -> Box<dyn TopologyProvider + Send + Sync> {
// 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(|| { custom_provider.unwrap_or_else(|| {
@@ -685,24 +582,26 @@ where
topology_accessor: TopologyAccessor, topology_accessor: TopologyAccessor,
local_gateway: NodeIdentity, local_gateway: NodeIdentity,
wait_for_gateway: bool, wait_for_gateway: bool,
shutdown_tracker: &ShutdownTracker, mut task_client: 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 topology_config.disable_refreshing {
// if we're not spawning the refresher, don't cause shutdown immediately // if we're not spawning the refresher, don't cause shutdown immediately
tracing::info!("The background topology refresher is not going to be started"); 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
tracing::info!("Obtaining initial network topology"); info!("Obtaining initial network topology");
topology_refresher.try_refresh().await; topology_refresher.try_refresh().await;
if let Err(err) = topology_refresher.ensure_topology_is_routable().await { if let Err(err) = topology_refresher.ensure_topology_is_routable().await {
@@ -728,13 +627,13 @@ where
.wait_for_gateway(local_gateway, waiting_timeout) .wait_for_gateway(local_gateway, waiting_timeout)
.await .await
{ {
tracing::error!( error!(
"the gateway did not come back online within the specified timeout: {err}" "the gateway did not come back online within the specified timeout: {err}"
); );
return Err(err.into()); return Err(err.into());
} }
} else { } else {
tracing::error!("the gateway we're supposedly connected to does not exist. We'll not be able to send any packets to ourselves: {err}"); error!("the gateway we're supposedly connected to does not exist. We'll not be able to send any packets to ourselves: {err}");
return Err(err.into()); return Err(err.into());
} }
} }
@@ -742,11 +641,8 @@ where
if !topology_config.disable_refreshing { if !topology_config.disable_refreshing {
// 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
tracing::info!("Starting topology refresher..."); info!("Starting topology refresher...");
shutdown_tracker.try_spawn_named_with_shutdown( topology_refresher.start();
async move { topology_refresher.run().await },
"TopologyRefresher",
);
} }
Ok(()) Ok(())
@@ -757,9 +653,9 @@ 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>,
shutdown_tracker: &ShutdownTracker, task_client: TaskClient,
) -> ClientStatsSender { ) -> ClientStatsSender {
tracing::info!("Starting statistics control..."); info!("Starting statistics control...");
StatisticsControl::create_and_start( StatisticsControl::create_and_start(
config.debug.stats_reporting, config.debug.stats_reporting,
user_agent user_agent
@@ -767,30 +663,18 @@ where
.unwrap_or("unknown".to_string()), .unwrap_or("unknown".to_string()),
client_stats_id, client_stats_id,
input_sender.clone(), input_sender.clone(),
shutdown_tracker, task_client,
) )
} }
fn start_mix_traffic_controller( fn start_mix_traffic_controller(
gateway_transceiver: Box<dyn GatewayTransceiver + Send>, gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
shutdown_tracker: &ShutdownTracker, shutdown: TaskClient,
event_tx: EventSender,
) -> (BatchMixMessageSender, ClientRequestSender) { ) -> (BatchMixMessageSender, ClientRequestSender) {
tracing::info!("Starting mix traffic controller..."); info!("Starting mix traffic controller...");
let mut mix_traffic_controller = MixTrafficController::new( let (mix_traffic_controller, mix_tx, client_tx) =
gateway_transceiver, MixTrafficController::new(gateway_transceiver, shutdown);
shutdown_tracker.clone_shutdown_token(), mix_traffic_controller.start();
event_tx,
);
let mix_tx = mix_traffic_controller.mix_rx();
let client_tx = mix_traffic_controller.client_tx();
shutdown_tracker.try_spawn_named(
async move { mix_traffic_controller.run().await },
"MixTrafficController",
);
(mix_tx, client_tx) (mix_tx, client_tx)
} }
@@ -798,7 +682,7 @@ where
async fn setup_persistent_reply_storage( async fn setup_persistent_reply_storage(
backend: S::ReplyStore, backend: S::ReplyStore,
key_rotation_config: KeyRotationConfig, key_rotation_config: KeyRotationConfig,
shutdown_tracker: &ShutdownTracker, shutdown: TaskClient,
) -> Result<CombinedReplyStorage, ClientCoreError> ) -> Result<CombinedReplyStorage, ClientCoreError>
where where
<S::ReplyStore as ReplyStorageBackend>::StorageError: Sync + Send, <S::ReplyStore as ReplyStorageBackend>::StorageError: Sync + Send,
@@ -823,15 +707,11 @@ where
})?; })?;
let store_clone = mem_store.clone(); let store_clone = mem_store.clone();
let shutdown_token = shutdown_tracker.clone_shutdown_token(); spawn_future(async move {
shutdown_tracker.try_spawn_named( persistent_storage
async move { .flush_on_shutdown(store_clone, shutdown)
persistent_storage .await
.flush_on_shutdown(store_clone, shutdown_token) });
.await
},
"PersistentReplyStorage::flush_on_shutdown",
);
Ok(mem_store) Ok(mem_store)
} }
@@ -848,11 +728,11 @@ where
{ {
// if client keys do not exist already, create and persist them // if client keys do not exist already, create and persist them
if key_store.load_keys().await.is_err() { if key_store.load_keys().await.is_err() {
tracing::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 = if let Some(derivation_material) = derivation_material {
ClientKeys::from_master_key(&mut rng, &derivation_material) ClientKeys::from_master_key(&mut rng, &derivation_material)
.map_err(|_| ClientCoreError::HkdfDerivationError)? .map_err(|_| ClientCoreError::HkdfDerivationError {})?
} else { } else {
ClientKeys::generate_new(&mut rng) ClientKeys::generate_new(&mut rng)
}; };
@@ -862,29 +742,21 @@ where
setup_gateway(setup_method, key_store, details_store).await setup_gateway(setup_method, key_store, details_store).await
} }
fn construct_nym_api_client( fn construct_nym_api_client(config: &Config, user_agent: Option<UserAgent>) -> NymApiClient {
config: &Config,
user_agent: Option<UserAgent>,
) -> Result<nym_http_api_client::Client, ClientCoreError> {
let mut nym_api_urls = config.get_nym_api_endpoints(); let mut nym_api_urls = config.get_nym_api_endpoints();
nym_api_urls.shuffle(&mut thread_rng()); nym_api_urls.shuffle(&mut thread_rng());
let mut builder = nym_http_api_client::Client::builder(nym_api_urls[0].clone())
.map_err(ClientCoreError::from)?;
if let Some(user_agent) = user_agent { if let Some(user_agent) = user_agent {
builder = builder.with_user_agent(user_agent); NymApiClient::new_with_user_agent(nym_api_urls[0].clone(), user_agent)
} else {
NymApiClient::new(nym_api_urls[0].clone())
} }
builder = builder.with_bincode();
builder.build().map_err(ClientCoreError::from)
} }
async fn determine_key_rotation_state( async fn determine_key_rotation_state(
client: &nym_http_api_client::Client, client: &NymApiClient,
) -> Result<KeyRotationConfig, ClientCoreError> { ) -> Result<KeyRotationConfig, ClientCoreError> {
Ok(client.get_key_rotation_info().await?.into()) Ok(client.nym_api.get_key_rotation_info().await?.into())
} }
pub async fn start_base(mut self) -> Result<BaseClient, ClientCoreError> pub async fn start_base(mut self) -> Result<BaseClient, ClientCoreError>
@@ -895,12 +767,7 @@ where
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static, <S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static,
<S::GatewaysDetailsStore as GatewaysDetailsStore>::StorageError: Sync + Send, <S::GatewaysDetailsStore as GatewaysDetailsStore>::StorageError: Sync + Send,
{ {
tracing::info!("Starting nym client"); info!("Starting nym client");
#[cfg(debug_assertions)]
#[cfg(target_arch = "wasm32")]
{
console_log!("Starting base Nym Client");
}
// derive (or load) client keys and gateway configuration // derive (or load) client keys and gateway configuration
let init_res = Self::initialise_keys_and_gateway( let init_res = Self::initialise_keys_and_gateway(
@@ -929,22 +796,17 @@ where
// channels responsible for controlling real messages // channels responsible for controlling real messages
let (input_sender, input_receiver) = tokio::sync::mpsc::channel::<InputMessage>(1); let (input_sender, input_receiver) = tokio::sync::mpsc::channel::<InputMessage>(1);
// channels responsible for event management
let (event_sender, event_receiver) = mpsc::unbounded();
// 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(self.config.debug.topology.ignore_egress_epoch_role); TopologyAccessor::new(self.config.debug.topology.ignore_egress_epoch_role);
// Create a shutdown tracker for this client - either as a child of provided tracker // Shutdown notifier for signalling tasks to stop
// or get one from the registry let shutdown = self
let shutdown_tracker = match self.shutdown { .shutdown
Some(parent_tracker) => parent_tracker.child_tracker(), .map(Into::<TaskHandle>::into)
None => nym_task::get_sdk_shutdown_tracker()?, .unwrap_or_default()
}; .name_if_unnamed("BaseNymClient");
Self::start_event_control(self.event_tx, event_receiver, &shutdown_tracker);
// channels responsible for dealing with reply-related fun // channels responsible for dealing with reply-related fun
let (reply_controller_sender, reply_controller_receiver) = let (reply_controller_sender, reply_controller_receiver) =
@@ -961,7 +823,7 @@ where
.dkg_query_client .dkg_query_client
.map(|client| BandwidthController::new(credential_store, client)); .map(|client| BandwidthController::new(credential_store, client));
let nym_api_client = Self::construct_nym_api_client(&self.config, self.user_agent.clone())?; let nym_api_client = Self::construct_nym_api_client(&self.config, self.user_agent.clone());
let key_rotation_config = Self::determine_key_rotation_state(&nym_api_client).await?; let key_rotation_config = Self::determine_key_rotation_state(&nym_api_client).await?;
let topology_provider = Self::setup_topology_provider( let topology_provider = Self::setup_topology_provider(
@@ -976,7 +838,7 @@ where
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(),
&shutdown_tracker.child_tracker(), shutdown.fork("statistics_control"),
); );
// needs to be started as the first thing to block if required waiting for the gateway // needs to be started as the first thing to block if required waiting for the gateway
@@ -986,14 +848,14 @@ where
shared_topology_accessor.clone(), shared_topology_accessor.clone(),
self_address.gateway(), self_address.gateway(),
self.wait_for_gateway, self.wait_for_gateway,
&shutdown_tracker.child_tracker(), shutdown.fork("topology_refresher"),
) )
.await?; .await?;
let gateway_packet_router = PacketRouter::new( let gateway_packet_router = PacketRouter::new(
ack_sender, ack_sender,
mixnet_messages_sender, mixnet_messages_sender,
shutdown_tracker.clone_shutdown_token(), shutdown.get_handle().named("gateway-packet-router"),
); );
let gateway_transceiver = Self::setup_gateway_transceiver( let gateway_transceiver = Self::setup_gateway_transceiver(
@@ -1006,7 +868,7 @@ where
stats_reporter.clone(), stats_reporter.clone(),
#[cfg(unix)] #[cfg(unix)]
self.connection_fd_callback, self.connection_fd_callback,
&shutdown_tracker.child_tracker(), shutdown.fork("gateway_transceiver"),
) )
.await?; .await?;
let gateway_ws_fd = gateway_transceiver.ws_fd(); let gateway_ws_fd = gateway_transceiver.ws_fd();
@@ -1014,7 +876,7 @@ where
let reply_storage = Self::setup_persistent_reply_storage( let reply_storage = Self::setup_persistent_reply_storage(
reply_storage_backend, reply_storage_backend,
key_rotation_config, key_rotation_config,
&shutdown_tracker.child_tracker(), shutdown.fork("persistent_reply_storage"),
) )
.await?; .await?;
@@ -1024,8 +886,8 @@ where
mixnet_messages_receiver, mixnet_messages_receiver,
reply_storage.key_storage(), reply_storage.key_storage(),
reply_controller_sender.clone(), reply_controller_sender.clone(),
shutdown.fork("received_messages_buffer"),
stats_reporter.clone(), stats_reporter.clone(),
&shutdown_tracker.child_tracker(),
); );
// The message_sender is the transmitter for any component generating sphinx packets // The message_sender is the transmitter for any component generating sphinx packets
@@ -1035,8 +897,7 @@ where
let (message_sender, client_request_sender) = Self::start_mix_traffic_controller( let (message_sender, client_request_sender) = Self::start_mix_traffic_controller(
gateway_transceiver, gateway_transceiver,
&shutdown_tracker.child_tracker(), shutdown.fork("mix_traffic_controller"),
EventSender(event_sender),
); );
// Channels that the websocket listener can use to signal downstream to the real traffic // Channels that the websocket listener can use to signal downstream to the real traffic
@@ -1065,8 +926,9 @@ where
reply_controller_receiver, reply_controller_receiver,
shared_lane_queue_lengths.clone(), shared_lane_queue_lengths.clone(),
client_connection_rx, client_connection_rx,
shutdown.fork("real_traffic_controller"),
self.config.debug.traffic.packet_type,
stats_reporter.clone(), stats_reporter.clone(),
&shutdown_tracker.child_tracker(),
); );
if !self if !self
@@ -1082,19 +944,12 @@ where
shared_topology_accessor.clone(), shared_topology_accessor.clone(),
message_sender, message_sender,
stats_reporter.clone(), stats_reporter.clone(),
&shutdown_tracker.child_tracker(), shutdown.fork("cover_traffic_stream"),
); );
} }
tracing::debug!("Core client startup finished!"); debug!("Core client startup finished!");
tracing::debug!("The address of this client is: {self_address}"); debug!("The address of this client is: {self_address}");
#[cfg(debug_assertions)]
#[cfg(target_arch = "wasm32")]
{
console_log!("Core client startup finished!");
console_log!("Rust::start_base: the address of this client is: {self_address}");
}
Ok(BaseClient { Ok(BaseClient {
address: self_address, address: self_address,
@@ -1103,7 +958,6 @@ where
client_input: ClientInput { client_input: ClientInput {
connection_command_sender: client_connection_tx, connection_command_sender: client_connection_tx,
input_sender, input_sender,
client_request_sender,
}, },
}, },
client_output: ClientOutputStatus::AwaitingConsumer { client_output: ClientOutputStatus::AwaitingConsumer {
@@ -1118,7 +972,8 @@ where
gateway_connection: GatewayConnection { gateway_ws_fd }, gateway_connection: GatewayConnection { gateway_ws_fd },
}, },
stats_reporter, stats_reporter,
shutdown_handle: shutdown_tracker, // The primary tracker for this client task_handle: shutdown,
client_request_sender,
forget_me: self.config.debug.forget_me, forget_me: self.config.debug.forget_me,
remember_me: self.config.debug.remember_me, remember_me: self.config.debug.remember_me,
}) })
@@ -1132,7 +987,8 @@ 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 shutdown_handle: ShutdownTracker, pub client_request_sender: ClientRequestSender,
pub task_handle: TaskHandle,
pub forget_me: ForgetMe, pub forget_me: ForgetMe,
pub remember_me: RememberMe, pub remember_me: RememberMe,
} }
@@ -114,32 +114,41 @@ pub async fn setup_fs_gateways_storage<P: AsRef<Path>>(
}) })
} }
pub fn create_bandwidth_controller_with_urls<St: CredentialStorage>( pub fn create_bandwidth_controller<St: CredentialStorage>(
nyxd_url: Url,
storage: St,
) -> Result<BandwidthController<QueryHttpRpcNyxdClient, St>, ClientCoreError> {
let client = default_query_dkg_client(nyxd_url)?;
Ok(BandwidthController::new(storage, client))
}
pub fn default_query_dkg_client_from_config(
config: &Config, config: &Config,
) -> Result<QueryHttpRpcNyxdClient, ClientCoreError> { storage: St,
) -> BandwidthController<QueryHttpRpcNyxdClient, St> {
let nyxd_url = config let nyxd_url = config
.get_validator_endpoints() .get_validator_endpoints()
.pop() .pop()
.ok_or(ClientCoreError::RpcClientMissingUrl)?; .expect("No nyxd validator endpoint provided");
create_bandwidth_controller_with_urls(nyxd_url, storage)
}
pub fn create_bandwidth_controller_with_urls<St: CredentialStorage>(
nyxd_url: Url,
storage: St,
) -> BandwidthController<QueryHttpRpcNyxdClient, St> {
let client = default_query_dkg_client(nyxd_url);
BandwidthController::new(storage, client)
}
pub fn default_query_dkg_client_from_config(config: &Config) -> QueryHttpRpcNyxdClient {
let nyxd_url = config
.get_validator_endpoints()
.pop()
.expect("No nyxd validator endpoint provided");
default_query_dkg_client(nyxd_url) default_query_dkg_client(nyxd_url)
} }
pub fn default_query_dkg_client(nyxd_url: Url) -> Result<QueryHttpRpcNyxdClient, ClientCoreError> { pub fn default_query_dkg_client(nyxd_url: Url) -> QueryHttpRpcNyxdClient {
let details = nym_network_defaults::NymNetworkDetails::new_from_env(); let details = nym_network_defaults::NymNetworkDetails::new_from_env();
let client_config = nyxd::Config::try_from_nym_network_details(&details) let client_config = nyxd::Config::try_from_nym_network_details(&details)
.map_err(|source| ClientCoreError::InvalidNetworkDetails { source })?; .expect("failed to construct validator client config");
// overwrite env configuration with config URLs // overwrite env configuration with config URLs
QueryHttpRpcNyxdClient::connect(client_config, nyxd_url.as_str()) QueryHttpRpcNyxdClient::connect(client_config, nyxd_url.as_str())
.map_err(|source| ClientCoreError::RpcClientCreationFailure { source }) .expect("Could not construct query client")
} }
@@ -3,7 +3,7 @@
use crate::client::mix_traffic::BatchMixMessageSender; use crate::client::mix_traffic::BatchMixMessageSender;
use crate::client::topology_control::TopologyAccessor; use crate::client::topology_control::TopologyAccessor;
use crate::config; use crate::{config, spawn_future};
use futures::task::{Context, Poll}; use futures::task::{Context, Poll};
use futures::{Future, Stream, StreamExt}; use futures::{Future, Stream, StreamExt};
use nym_sphinx::acknowledgements::AckKey; use nym_sphinx::acknowledgements::AckKey;
@@ -12,6 +12,7 @@ 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;
@@ -68,6 +69,8 @@ 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>
@@ -114,6 +117,7 @@ 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;
@@ -133,6 +137,7 @@ impl LoopCoverTrafficStream<OsRng> {
use_legacy_sphinx_format: traffic_config.use_legacy_sphinx_format, use_legacy_sphinx_format: traffic_config.use_legacy_sphinx_format,
packet_type: traffic_config.packet_type, packet_type: traffic_config.packet_type,
stats_tx, stats_tx,
task_client,
} }
} }
@@ -225,24 +230,16 @@ impl LoopCoverTrafficStream<OsRng> {
// JS: due to identical logical structure to OutQueueControl::on_message(), this is also // JS: due to identical logical structure to OutQueueControl::on_message(), this is also
// presumably required to prevent bugs in the future. Exact reason is still unknown to me. // presumably required to prevent bugs in the future. Exact reason is still unknown to me.
// TODO: temporary and BAD workaround for wasm (we should find a way to yield here in wasm)
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
{ tokio::task::yield_now().await;
tokio::task::yield_now().await;
}
#[cfg(target_arch = "wasm32")]
{
tokio_with_wasm::task::yield_now().await;
}
} }
// it's fine if cover traffic stream task gets killed whilst processing next message pub fn start(mut self) {
#[allow(clippy::panic)]
pub async fn run(&mut self) {
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
panic!("attempted to run LoopCoverTrafficStream while config explicitly disabled it.") panic!("attempted to start LoopCoverTrafficStream while config explicitly disabled it.")
} }
// we should set initial delay only when we actually start the stream // we should set initial delay only when we actually start the stream
@@ -252,11 +249,29 @@ impl LoopCoverTrafficStream<OsRng> {
); );
self.set_next_delay(sampled); self.set_next_delay(sampled);
while self.next().await.is_some() { let mut shutdown = self.task_client.fork("select");
self.on_new_message().await;
}
// this should never get triggered spawn_future(async move {
error!("cover traffic stream has been exhausted!") debug!("Started LoopCoverTrafficStream with graceful shutdown support");
while !shutdown.is_shutdown() {
tokio::select! {
biased;
_ = shutdown.recv() => {
tracing::trace!("LoopCoverTrafficStream: Received shutdown");
}
next = self.next() => {
if next.is_some() {
self.on_new_message().await;
} else {
tracing::trace!("LoopCoverTrafficStream: Stopping since channel closed");
break;
}
}
}
}
shutdown.recv_timeout().await;
tracing::debug!("LoopCoverTrafficStream: Exiting");
})
} }
} }
@@ -1,40 +0,0 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use futures::StreamExt;
use crate::client::base_client::{EventReceiver, EventSender, MixnetClientEvent};
/// Launches and manages task events, propagating upwards what is not strictly internal.
pub(crate) struct EventControl {
parent_event_tx: Option<EventSender>,
children_event_rx: EventReceiver,
}
impl EventControl {
pub(crate) fn new(
parent_event_tx: Option<EventSender>,
children_event_rx: EventReceiver,
) -> Self {
EventControl {
parent_event_tx,
children_event_rx,
}
}
fn is_internal(event: MixnetClientEvent) -> bool {
match event {
MixnetClientEvent::Traffic(_) => false,
}
}
pub(crate) async fn run(mut self) {
while let Some(event) = self.children_event_rx.next().await {
if let Some(parent_event_tx) = &self.parent_event_tx {
if !Self::is_internal(event) {
parent_event_tx.send(event);
}
}
}
}
}
@@ -2,9 +2,9 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#![allow(unused_imports)] #![allow(unused_imports)]
use std::time::Duration; use std::time::Duration;
#[cfg(target_arch = "wasm32")]
pub use wasmtimer::{std::Instant, tokio::*}; pub use wasmtimer::{std::Instant, tokio::*};
pub type IntervalStream = gloo_timers::future::IntervalStream; pub type IntervalStream = gloo_timers::future::IntervalStream;
+96 -100
View File
@@ -1,13 +1,12 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::client::{ use crate::client::mix_traffic::transceiver::GatewayTransceiver;
base_client::{EventSender, MixnetClientEvent}, use crate::error::ClientCoreError;
mix_traffic::transceiver::GatewayTransceiver, use crate::spawn_future;
};
use nym_gateway_requests::ClientRequest; use nym_gateway_requests::ClientRequest;
use nym_sphinx::forwarding::packet::MixPacket; use nym_sphinx::forwarding::packet::MixPacket;
use nym_task::ShutdownToken; use nym_task::TaskClient;
use tracing::*; use tracing::*;
use transceiver::ErasedGatewayError; use transceiver::ErasedGatewayError;
@@ -25,33 +24,28 @@ const MAX_FAILURE_COUNT: usize = 100;
// that's also disgusting. // that's also disgusting.
pub struct Empty; pub struct Empty;
#[derive(Clone, Copy, Debug)]
pub enum MixTrafficEvent {
FailedSendingSphinx,
}
pub struct MixTrafficController { pub struct MixTrafficController {
gateway_transceiver: Box<dyn GatewayTransceiver + Send>, gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
mix_tx: BatchMixMessageSender,
mix_rx: BatchMixMessageReceiver, mix_rx: BatchMixMessageReceiver,
client_rx: ClientRequestReceiver, client_rx: ClientRequestReceiver,
client_tx: ClientRequestSender,
// 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,
shutdown_token: ShutdownToken, task_client: TaskClient,
event_tx: EventSender,
} }
impl MixTrafficController { impl MixTrafficController {
pub fn new<T>( pub fn new<T>(
gateway_transceiver: T, gateway_transceiver: T,
shutdown_token: ShutdownToken, task_client: TaskClient,
event_tx: EventSender, ) -> (
) -> MixTrafficController MixTrafficController,
BatchMixMessageSender,
ClientRequestSender,
)
where where
T: GatewayTransceiver + Send + 'static, T: GatewayTransceiver + Send + 'static,
{ {
@@ -60,32 +54,41 @@ impl MixTrafficController {
let (client_sender, client_receiver) = tokio::sync::mpsc::channel(8); let (client_sender, client_receiver) = tokio::sync::mpsc::channel(8);
MixTrafficController { (
gateway_transceiver: Box::new(gateway_transceiver), MixTrafficController {
mix_tx: message_sender, gateway_transceiver: Box::new(gateway_transceiver),
mix_rx: message_receiver, mix_rx: message_receiver,
client_rx: client_receiver, client_rx: client_receiver,
client_tx: client_sender, consecutive_gateway_failure_count: 0,
consecutive_gateway_failure_count: 0, task_client,
shutdown_token, },
event_tx, message_sender,
} client_sender,
)
} }
pub fn new_dynamic( pub fn new_dynamic(
gateway_transceiver: Box<dyn GatewayTransceiver + Send>, gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
shutdown_token: ShutdownToken, task_client: TaskClient,
event_tx: EventSender, ) -> (
) -> MixTrafficController { MixTrafficController,
Self::new(gateway_transceiver, shutdown_token, event_tx) BatchMixMessageSender,
} ClientRequestSender,
) {
pub fn client_tx(&self) -> ClientRequestSender { let (message_sender, message_receiver) =
self.client_tx.clone() tokio::sync::mpsc::channel(MIX_MESSAGE_RECEIVER_BUFFER_SIZE);
} let (client_sender, client_receiver) = tokio::sync::mpsc::channel(8);
(
pub fn mix_rx(&self) -> BatchMixMessageSender { MixTrafficController {
self.mix_tx.clone() gateway_transceiver,
mix_rx: message_receiver,
client_rx: client_receiver,
consecutive_gateway_failure_count: 0,
task_client,
},
message_sender,
client_sender,
)
} }
async fn on_messages( async fn on_messages(
@@ -93,79 +96,72 @@ impl MixTrafficController {
mut mix_packets: Vec<MixPacket>, mut mix_packets: Vec<MixPacket>,
) -> Result<(), ErasedGatewayError> { ) -> Result<(), ErasedGatewayError> {
debug_assert!(!mix_packets.is_empty()); debug_assert!(!mix_packets.is_empty());
let send_future = if mix_packets.len() == 1 {
// SAFETY: we just checked we have one packet let result = if mix_packets.len() == 1 {
#[allow(clippy::unwrap_used)]
let mix_packet = mix_packets.pop().unwrap(); let mix_packet = mix_packets.pop().unwrap();
self.gateway_transceiver.send_mix_packet(mix_packet) self.gateway_transceiver.send_mix_packet(mix_packet).await
} else { } else {
self.gateway_transceiver.batch_send_mix_packets(mix_packets) self.gateway_transceiver
.batch_send_mix_packets(mix_packets)
.await
}; };
tokio::select! { if result.is_err() {
biased; self.consecutive_gateway_failure_count += 1;
_ = self.shutdown_token.cancelled() => { } else {
trace!("received shutdown while handling messages"); trace!("We *might* have managed to forward sphinx packet(s) to the gateway!");
Ok(()) self.consecutive_gateway_failure_count = 0;
}
result = send_future => {
if result.is_err() {
self.consecutive_gateway_failure_count += 1;
} else {
trace!("We *might* have managed to forward sphinx packet(s) to the gateway!");
self.consecutive_gateway_failure_count = 0;
}
result
}
} }
result
} }
async fn on_client_request(&mut self, client_request: ClientRequest) { pub fn start(mut self) {
tokio::select! { spawn_future(async move {
biased; debug!("Started MixTrafficController with graceful shutdown support");
_ = self.shutdown_token.cancelled() => {
trace!("received shutdown while handling client request");
}
result = self.gateway_transceiver.send_client_request(client_request) => {
if let Err(err) = result {
error!("Failed to send client request: {err}")
}
}
}
}
pub async fn run(&mut self) { while !self.task_client.is_shutdown() {
debug!("Started MixTrafficController with graceful shutdown support"); tokio::select! {
loop { mix_packets = self.mix_rx.recv() => match mix_packets {
tokio::select! { Some(mix_packets) => {
biased; if let Err(err) = self.on_messages(mix_packets).await {
_ = self.shutdown_token.cancelled() => { error!("Failed to send sphinx packet(s) to the gateway: {err}");
trace!("MixTrafficController: Received shutdown"); if self.consecutive_gateway_failure_count == MAX_FAILURE_COUNT {
break; // Disconnect from the gateway. If we should try to re-connect
} // is handled at a higher layer.
// mix_rx should never error out as we're holding one instance of the sender 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
Some(mix_packets) = self.mix_rx.recv() => { // separately?
if let Err(err) = self.on_messages(mix_packets).await { self.task_client.send_we_stopped(Box::new(ClientCoreError::GatewayFailedToForwardMessages));
error!("Failed to send sphinx packet(s) to the gateway: {err}"); break;
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"); None => {
// Do we need to handle the embedded mixnet client case tracing::trace!("MixTrafficController: Stopping since channel closed");
// separately?
self.event_tx.send(MixnetClientEvent::Traffic(MixTrafficEvent::FailedSendingSphinx));
break; break;
} }
},
client_request = self.client_rx.recv() => match client_request {
Some(client_request) => {
match self.gateway_transceiver.send_client_request(client_request).await {
Ok(_) => (),
Err(e) => error!("Failed to send client request: {e}"),
};
},
None => {
tracing::trace!("MixTrafficController, client request channel closed");
}
},
_ = self.task_client.recv() => {
tracing::trace!("MixTrafficController: Received shutdown");
break;
} }
},
// client_rx should never error out as we're holding one instance of the sender
Some(client_request) = self.client_rx.recv() => {
self.on_client_request(client_request).await;
} }
} }
} self.task_client.recv_timeout().await;
debug!("MixTrafficController: Exiting");
tracing::debug!("MixTrafficController: Exiting");
});
} }
} }
@@ -269,8 +269,6 @@ pub struct MockGateway {
} }
impl Default for MockGateway { impl Default for MockGateway {
// test code
#[allow(clippy::unwrap_used)]
fn default() -> Self { fn default() -> Self {
MockGateway { MockGateway {
dummy_identity: "3ebjp1Fb9hdcS1AR6AZihgeJiMHkB5jjJUsvqNnfQwU7" dummy_identity: "3ebjp1Fb9hdcS1AR6AZihgeJiMHkB5jjJUsvqNnfQwU7"
-1
View File
@@ -3,7 +3,6 @@
pub mod base_client; pub mod base_client;
pub mod cover_traffic_stream; pub mod cover_traffic_stream;
pub(crate) mod event_control;
pub(crate) mod helpers; pub(crate) mod helpers;
pub mod inbound_messages; pub mod inbound_messages;
pub mod key_manager; pub mod key_manager;
@@ -10,17 +10,18 @@ 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::ShutdownToken; use nym_task::TaskClient;
use std::sync::Arc; use std::sync::Arc;
use tracing::*; use tracing::*;
/// Module responsible for listening for any data resembling acknowledgements from the network /// Module responsible for listening for any data resembling acknowledgements from the network
/// and firing actions to remove them from the 'Pending' state. /// and firing actions to remove them from the 'Pending' state.
pub(crate) struct AcknowledgementListener { pub(super) struct AcknowledgementListener {
ack_key: Arc<AckKey>, ack_key: Arc<AckKey>,
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 {
@@ -29,12 +30,14 @@ 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,
} }
} }
@@ -65,9 +68,14 @@ impl AcknowledgementListener {
trace!("Received {frag_id} from the mix network"); trace!("Received {frag_id} from the mix network");
self.stats_tx self.stats_tx
.report(PacketStatisticsEvent::RealAckReceived(ack_content.len()).into()); .report(PacketStatisticsEvent::RealAckReceived(ack_content.len()).into());
let _ = self if let Err(err) = self
.action_sender .action_sender
.unbounded_send(Action::new_remove(frag_id)); .unbounded_send(Action::new_remove(frag_id))
{
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>>) {
@@ -77,16 +85,11 @@ impl AcknowledgementListener {
} }
} }
pub(crate) async fn run(&mut self, shutdown_token: ShutdownToken) { pub(super) async fn run(&mut self) {
debug!("Started AcknowledgementListener with graceful shutdown support"); debug!("Started AcknowledgementListener with graceful shutdown support");
loop { while !self.task_client.is_shutdown() {
tokio::select! { tokio::select! {
biased;
_ = shutdown_token.cancelled() => {
tracing::trace!("AcknowledgementListener: Received shutdown");
break;
}
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,
None => { None => {
@@ -94,9 +97,12 @@ impl AcknowledgementListener {
break; break;
} }
}, },
_ = self.task_client.recv() => {
tracing::trace!("AcknowledgementListener: Received shutdown");
}
} }
} }
self.task_client.recv_timeout().await;
tracing::debug!("AcknowledgementListener: Exiting"); tracing::debug!("AcknowledgementListener: Exiting");
} }
} }
@@ -8,7 +8,7 @@ use futures::StreamExt;
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::ShutdownToken; 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;
@@ -82,7 +82,7 @@ impl Config {
} }
} }
pub(crate) struct ActionController { pub(super) struct ActionController {
/// Configurable parameters of the `ActionController` /// Configurable parameters of the `ActionController`
config: Config, config: Config,
@@ -102,6 +102,8 @@ pub(crate) 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 {
@@ -109,6 +111,7 @@ 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,
@@ -116,6 +119,7 @@ impl ActionController {
pending_acks_timers: NonExhaustiveDelayQueue::new(), pending_acks_timers: NonExhaustiveDelayQueue::new(),
incoming_actions, incoming_actions,
retransmission_sender, retransmission_sender,
task_client,
} }
} }
@@ -190,11 +194,10 @@ impl ActionController {
trace!("{frag_id} is updating its delay"); trace!("{frag_id} is updating its delay");
// TODO: is it possible to solve this without either locking or temporarily removing the value? // TODO: is it possible to solve this without either locking or temporarily removing the value?
if let Some((pending_ack_data, queue_key)) = self.pending_acks_data.remove(&frag_id) { if let Some((pending_ack_data, queue_key)) = self.pending_acks_data.remove(&frag_id) {
// SAFETY: this Action is triggered by `RetransmissionRequestListener` (for 'normal' packets) // this Action is triggered by `RetransmissionRequestListener` (for 'normal' packets)
// or `ReplyController` (for 'reply' packets) which held the other potential // or `ReplyController` (for 'reply' packets) which held the other potential
// reference to this Arc. HOWEVER, before the Action was pushed onto the queue, the reference // reference to this Arc. HOWEVER, before the Action was pushed onto the queue, the reference
// was dropped hence this unwrap is safe. // was dropped hence this unwrap is safe.
#[allow(clippy::unwrap_used)]
let mut inner_data = Arc::try_unwrap(pending_ack_data).unwrap(); let mut inner_data = Arc::try_unwrap(pending_ack_data).unwrap();
inner_data.update_retransmitted(delay); inner_data.update_retransmitted(delay);
@@ -206,7 +209,6 @@ 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
#[allow(clippy::panic)]
fn handle_expired_ack_timer(&mut self, expired_ack: Expired<FragmentIdentifier>) { fn handle_expired_ack_timer(&mut self, expired_ack: Expired<FragmentIdentifier>) {
let frag_id = expired_ack.into_inner(); let frag_id = expired_ack.into_inner();
@@ -222,9 +224,14 @@ 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)
let _ = self if let Err(err) = self
.retransmission_sender .retransmission_sender
.unbounded_send(Arc::downgrade(pending_ack_data)); .unbounded_send(Arc::downgrade(pending_ack_data))
{
if !self.task_client.is_shutdown_poll() {
tracing::error!("Failed to send pending ack for retransmission: {err}");
}
}
} 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!
error!("An already removed pending ack has expired") error!("An already removed pending ack has expired")
@@ -242,16 +249,11 @@ impl ActionController {
} }
} }
pub(crate) async fn run(&mut self, shutdown_token: ShutdownToken) { pub(super) async fn run(&mut self) {
debug!("Started ActionController with graceful shutdown support"); debug!("Started ActionController with graceful shutdown support");
loop { while !self.task_client.is_shutdown() {
tokio::select! { tokio::select! {
biased;
_ = shutdown_token.cancelled() => {
tracing::trace!("ActionController: Received shutdown");
break;
}
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),
None => { None => {
@@ -268,8 +270,13 @@ impl ActionController {
break; break;
} }
}, },
_ = self.task_client.recv() => {
tracing::trace!("ActionController: Received shutdown");
break;
}
} }
} }
self.task_client.recv_timeout().await;
tracing::debug!("ActionController: Exiting"); tracing::debug!("ActionController: Exiting");
} }
} }
@@ -10,20 +10,21 @@ 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::ShutdownToken; use nym_task::TaskClient;
use rand::{CryptoRng, Rng}; use rand::{CryptoRng, Rng};
use tracing::*; use tracing::*;
/// Module responsible for dealing with the received messages: splitting them, creating acknowledgements, /// Module responsible for dealing with the received messages: splitting them, creating acknowledgements,
/// putting everything into sphinx packets, etc. /// putting everything into sphinx packets, etc.
/// It also makes an initial sending attempt for said messages. /// It also makes an initial sending attempt for said messages.
pub(crate) struct InputMessageListener<R> pub(super) struct InputMessageListener<R>
where where
R: CryptoRng + Rng, R: CryptoRng + Rng,
{ {
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>
@@ -37,11 +38,13 @@ 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,
} }
} }
@@ -65,9 +68,14 @@ where
max_retransmissions: Option<u32>, max_retransmissions: Option<u32>,
) { ) {
// offload reply handling to the dedicated task // offload reply handling to the dedicated task
let _ = if let Err(err) =
self.reply_controller_sender self.reply_controller_sender
.send_reply(recipient_tag, data, lane, max_retransmissions); .send_reply(recipient_tag, data, lane, max_retransmissions)
{
if !self.task_client.is_shutdown_poll() {
error!("failed to send a reply - {err}");
}
}
} }
async fn handle_plain_message( async fn handle_plain_message(
@@ -112,7 +120,6 @@ where
} }
} }
#[allow(clippy::panic)]
async fn on_input_message(&mut self, msg: InputMessage) { async fn on_input_message(&mut self, msg: InputMessage) {
match msg { match msg {
InputMessage::Regular { InputMessage::Regular {
@@ -206,23 +213,16 @@ where
self.handle_premade_packets(msgs, lane).await self.handle_premade_packets(msgs, lane).await
} }
// MessageWrappers can't be nested // MessageWrappers can't be nested
InputMessage::MessageWrapper { .. } => { InputMessage::MessageWrapper { .. } => unimplemented!(),
panic!("attempted to use nested MessageWrapper")
}
}, },
}; };
} }
pub(crate) async fn run(&mut self, shutdown_token: ShutdownToken) { pub(super) async fn run(&mut self) {
debug!("Started InputMessageListener with graceful shutdown support"); debug!("Started InputMessageListener with graceful shutdown support");
loop { while !self.task_client.is_shutdown() {
tokio::select! { tokio::select! {
biased;
_ = shutdown_token.cancelled() => {
tracing::trace!("InputMessageListener: Received shutdown");
break;
}
input_msg = self.input_receiver.recv() => match input_msg { input_msg = self.input_receiver.recv() => match input_msg {
Some(input_msg) => { Some(input_msg) => {
self.on_input_message(input_msg).await; self.on_input_message(input_msg).await;
@@ -232,9 +232,12 @@ where
break; break;
} }
}, },
_ = self.task_client.recv() => {
tracing::trace!("InputMessageListener: Received shutdown");
}
} }
} }
self.task_client.recv_timeout().await;
tracing::debug!("InputMessageListener: Exiting"); tracing::debug!("InputMessageListener: Exiting");
} }
} }
@@ -10,6 +10,7 @@ use self::{
use crate::client::inbound_messages::InputMessageReceiver; use crate::client::inbound_messages::InputMessageReceiver;
use crate::client::real_messages_control::message_handler::MessageHandler; use crate::client::real_messages_control::message_handler::MessageHandler;
use crate::client::replies::reply_controller::ReplyControllerSender; use crate::client::replies::reply_controller::ReplyControllerSender;
use crate::spawn_future;
use action_controller::AckActionReceiver; use action_controller::AckActionReceiver;
use futures::channel::mpsc; use futures::channel::mpsc;
use nym_gateway_client::AcknowledgementReceiver; use nym_gateway_client::AcknowledgementReceiver;
@@ -22,11 +23,13 @@ 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},
time::Duration, time::Duration,
}; };
use tracing::*;
pub(crate) use action_controller::{AckActionSender, Action}; pub(crate) use action_controller::{AckActionSender, Action};
@@ -187,9 +190,6 @@ pub(super) struct Config {
/// Predefined packet size used for the encapsulated messages. /// Predefined packet size used for the encapsulated messages.
packet_size: PacketSize, packet_size: PacketSize,
/// Type of packets used for retransmissions
packet_type: PacketType,
} }
impl Config { impl Config {
@@ -197,14 +197,12 @@ impl Config {
maximum_retransmissions: Option<u32>, maximum_retransmissions: Option<u32>,
ack_wait_addition: Duration, ack_wait_addition: Duration,
ack_wait_multiplier: f64, ack_wait_multiplier: f64,
packet_type: PacketType,
) -> Self { ) -> Self {
Config { Config {
maximum_retransmissions, maximum_retransmissions,
ack_wait_addition, ack_wait_addition,
ack_wait_multiplier, ack_wait_multiplier,
packet_size: Default::default(), packet_size: Default::default(),
packet_type,
} }
} }
@@ -214,7 +212,7 @@ impl Config {
} }
} }
pub(crate) struct AcknowledgementController<R> pub(super) struct AcknowledgementController<R>
where where
R: CryptoRng + Rng, R: CryptoRng + Rng,
{ {
@@ -236,6 +234,7 @@ 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();
@@ -245,6 +244,7 @@ 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
@@ -253,6 +253,7 @@ 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
@@ -260,6 +261,7 @@ 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
@@ -269,13 +271,16 @@ where
message_handler, message_handler,
retransmission_rx, retransmission_rx,
reply_controller_sender, reply_controller_sender,
config.packet_type, 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 = let sent_notification_listener = SentNotificationListener::new(
SentNotificationListener::new(connectors.sent_notifier, connectors.ack_action_sender); connectors.sent_notifier,
connectors.ack_action_sender,
task_client.with_suffix("sent_notification_listener"),
);
AcknowledgementController { AcknowledgementController {
acknowledgement_listener, acknowledgement_listener,
@@ -286,21 +291,36 @@ where
} }
} }
pub(crate) fn into_tasks( pub(super) fn start(self, packet_type: PacketType) {
self, let mut acknowledgement_listener = self.acknowledgement_listener;
) -> ( let mut input_message_listener = self.input_message_listener;
AcknowledgementListener, let mut retransmission_request_listener = self.retransmission_request_listener;
InputMessageListener<R>, let mut sent_notification_listener = self.sent_notification_listener;
RetransmissionRequestListener<R>, let mut action_controller = self.action_controller;
SentNotificationListener,
ActionController, spawn_future(async move {
) { acknowledgement_listener.run().await;
( debug!("The acknowledgement listener has finished execution!");
self.acknowledgement_listener, });
self.input_message_listener,
self.retransmission_request_listener, spawn_future(async move {
self.sent_notification_listener, input_message_listener.run().await;
self.action_controller, debug!("The input listener has finished execution!");
) });
spawn_future(async move {
retransmission_request_listener.run(packet_type).await;
debug!("The retransmission request listener has finished execution!");
});
spawn_future(async move {
sent_notification_listener.run().await;
debug!("The sent notification listener has finished execution!");
});
spawn_future(async move {
action_controller.run().await;
debug!("The controller has finished execution!");
});
} }
} }
@@ -13,19 +13,19 @@ use futures::StreamExt;
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, ShutdownToken}; use nym_task::{connections::TransmissionLane, TaskClient};
use rand::{CryptoRng, Rng}; use rand::{CryptoRng, Rng};
use std::sync::{Arc, Weak}; use std::sync::{Arc, Weak};
use tracing::*; use tracing::*;
// responsible for packet retransmission upon fired timer // responsible for packet retransmission upon fired timer
pub(crate) struct RetransmissionRequestListener<R> { pub(super) struct RetransmissionRequestListener<R> {
maximum_retransmissions: Option<u32>, maximum_retransmissions: Option<u32>,
action_sender: AckActionSender, action_sender: AckActionSender,
message_handler: MessageHandler<R>, message_handler: MessageHandler<R>,
request_receiver: RetransmissionRequestReceiver, request_receiver: RetransmissionRequestReceiver,
reply_controller_sender: ReplyControllerSender, reply_controller_sender: ReplyControllerSender,
packet_type: PacketType, task_client: TaskClient,
} }
impl<R> RetransmissionRequestListener<R> impl<R> RetransmissionRequestListener<R>
@@ -38,7 +38,7 @@ where
message_handler: MessageHandler<R>, message_handler: MessageHandler<R>,
request_receiver: RetransmissionRequestReceiver, request_receiver: RetransmissionRequestReceiver,
reply_controller_sender: ReplyControllerSender, reply_controller_sender: ReplyControllerSender,
packet_type: PacketType, task_client: TaskClient,
) -> Self { ) -> Self {
RetransmissionRequestListener { RetransmissionRequestListener {
maximum_retransmissions, maximum_retransmissions,
@@ -46,7 +46,7 @@ where
message_handler, message_handler,
request_receiver, request_receiver,
reply_controller_sender, reply_controller_sender,
packet_type, task_client,
} }
} }
@@ -67,6 +67,7 @@ where
async fn on_retransmission_request( async fn on_retransmission_request(
&mut self, &mut self,
weak_timed_out_ack: Weak<PendingAcknowledgement>, weak_timed_out_ack: Weak<PendingAcknowledgement>,
packet_type: PacketType,
) { ) {
let timed_out_ack = match weak_timed_out_ack.upgrade() { let timed_out_ack = match weak_timed_out_ack.upgrade() {
Some(timed_out_ack) => timed_out_ack, Some(timed_out_ack) => timed_out_ack,
@@ -96,18 +97,22 @@ 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
let _ = self.reply_controller_sender.send_retransmission_data( if let Err(err) = 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; 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(),
self.packet_type, packet_type,
) )
.await .await
} }
@@ -148,9 +153,14 @@ 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`
let _ = self if let Err(err) = 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))
{
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
@@ -164,26 +174,24 @@ where
.await .await
} }
pub(crate) async fn run(&mut self, shutdown_token: ShutdownToken) { pub(super) async fn run(&mut self, packet_type: PacketType) {
debug!("Started RetransmissionRequestListener with graceful shutdown support"); debug!("Started RetransmissionRequestListener with graceful shutdown support");
loop { while !self.task_client.is_shutdown() {
tokio::select! { tokio::select! {
biased;
_ = shutdown_token.cancelled() => {
tracing::trace!("RetransmissionRequestListener: Received shutdown");
break;
}
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).await, Some(timed_out_ack) => self.on_retransmission_request(timed_out_ack, packet_type).await,
None => { None => {
tracing::trace!("RetransmissionRequestListener: Stopping since channel closed"); tracing::trace!("RetransmissionRequestListener: Stopping since channel closed");
break; break;
} }
}, },
_ = self.task_client.recv() => {
tracing::trace!("RetransmissionRequestListener: Received shutdown");
}
} }
} }
self.task_client.recv_timeout().await;
tracing::debug!("RetransmissionRequestListener: Exiting"); tracing::debug!("RetransmissionRequestListener: Exiting");
} }
} }
@@ -5,25 +5,29 @@ use super::action_controller::{AckActionSender, Action};
use super::SentPacketNotificationReceiver; use super::SentPacketNotificationReceiver;
use futures::StreamExt; use futures::StreamExt;
use nym_sphinx::chunking::fragment::{FragmentIdentifier, COVER_FRAG_ID}; use nym_sphinx::chunking::fragment::{FragmentIdentifier, COVER_FRAG_ID};
use nym_task::TaskClient;
use tracing::*; use tracing::*;
/// 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
/// by a poisson timer, there's no guarantee the message will be sent immediately, so we might /// by a poisson timer, there's no guarantee the message will be sent immediately, so we might
/// accidentally fire retransmission way quicker than we should have. /// accidentally fire retransmission way quicker than we should have.
pub(crate) 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,
} }
} }
@@ -32,18 +36,37 @@ 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;
} }
let _ = self if let Err(err) = self
.action_sender .action_sender
.unbounded_send(Action::new_start_timer(frag_id)); .unbounded_send(Action::new_start_timer(frag_id))
{
if !self.task_client.is_shutdown_poll() {
error!("Failed to send start timer action to action controller: {err}");
}
}
} }
pub(crate) async fn run(&mut self) { pub(super) async fn run(&mut self) {
debug!("Started SentNotificationListener with graceful shutdown support"); debug!("Started SentNotificationListener with graceful shutdown support");
while let Some(frag_id) = self.sent_notifier.next().await { while !self.task_client.is_shutdown() {
self.on_sent_message(frag_id).await; tokio::select! {
frag_id = self.sent_notifier.next() => match frag_id {
Some(frag_id) => {
self.on_sent_message(frag_id).await;
}
None => {
tracing::trace!("SentNotificationListener: Stopping since channel closed");
break;
}
},
_ = self.task_client.recv() => {
tracing::trace!("SentNotificationListener: Received shutdown");
break;
}
}
} }
assert!(self.task_client.is_shutdown_poll());
tracing::debug!("SentNotificationListener: Exiting"); tracing::debug!("SentNotificationListener: Exiting");
} }
} }
@@ -20,7 +20,7 @@ use nym_sphinx::params::{PacketSize, PacketType};
use nym_sphinx::preparer::{MessagePreparer, PreparedFragment}; use nym_sphinx::preparer::{MessagePreparer, PreparedFragment};
use nym_sphinx::Delay; use nym_sphinx::Delay;
use nym_task::connections::TransmissionLane; use nym_task::connections::TransmissionLane;
use nym_task::ShutdownToken; use nym_task::TaskClient;
use nym_topology::{NymRouteProvider, NymTopologyError}; use nym_topology::{NymRouteProvider, NymTopologyError};
use rand::{CryptoRng, Rng}; use rand::{CryptoRng, Rng};
use std::collections::HashMap; use std::collections::HashMap;
@@ -35,9 +35,6 @@ pub enum PreparationError {
#[error(transparent)] #[error(transparent)]
NymTopologyError(#[from] NymTopologyError), NymTopologyError(#[from] NymTopologyError),
#[error("message wasn't split into any fragments!")]
EmptyFragments,
#[error("message too long for a single SURB, splitting into {fragments} fragments.")] #[error("message too long for a single SURB, splitting into {fragments} fragments.")]
MessageTooLongForSingleSurb { fragments: usize }, MessageTooLongForSingleSurb { fragments: usize },
@@ -189,7 +186,7 @@ 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,
shutdown_token: ShutdownToken, task_client: TaskClient,
} }
impl<R> MessageHandler<R> impl<R> MessageHandler<R>
@@ -205,7 +202,7 @@ where
topology_access: TopologyAccessor, topology_access: TopologyAccessor,
reply_key_storage: SentReplyKeys, reply_key_storage: SentReplyKeys,
tag_storage: UsedSenderTags, tag_storage: UsedSenderTags,
shutdown_token: ShutdownToken, task_client: TaskClient,
) -> Self ) -> Self
where where
R: Copy, R: Copy,
@@ -228,7 +225,7 @@ where
topology_access, topology_access,
reply_key_storage, reply_key_storage,
tag_storage, tag_storage,
shutdown_token, task_client,
} }
} }
@@ -323,16 +320,6 @@ where
}); });
} }
if fragment.is_empty() {
error!("CRITICAL FAILURE: our split message didn't result in any sendable fragments");
return Err(SurbWrappedPreparationError {
source: PreparationError::EmptyFragments,
returned_surbs: Some(vec![reply_surb]),
});
}
// SAFETY: we just checked we have one fragment
#[allow(clippy::unwrap_used)]
let chunk = fragment.pop().unwrap(); let chunk = fragment.pop().unwrap();
let chunk_clone = chunk.clone(); let chunk_clone = chunk.clone();
let prepared_fragment = self let prepared_fragment = self
@@ -548,7 +535,6 @@ where
pending_acks.push(pending_ack); pending_acks.push(pending_ack);
} }
drop(topology_permit);
self.insert_pending_acks(pending_acks); self.insert_pending_acks(pending_acks);
self.forward_messages(real_messages, lane).await; self.forward_messages(real_messages, lane).await;
@@ -671,7 +657,6 @@ where
.zip(reply_surbs.into_iter()) .zip(reply_surbs.into_iter())
.map(|(fragment, reply_surb)| { .map(|(fragment, reply_surb)| {
// unwrap here is fine as we know we have a valid topology // unwrap here is fine as we know we have a valid topology
#[allow(clippy::unwrap_used)]
self.message_preparer self.message_preparer
.prepare_reply_chunk_for_sending( .prepare_reply_chunk_for_sending(
fragment, fragment,
@@ -712,7 +697,7 @@ where
.action_sender .action_sender
.unbounded_send(Action::UpdatePendingAck(id, new_delay)) .unbounded_send(Action::UpdatePendingAck(id, new_delay))
{ {
if !self.shutdown_token.is_cancelled() { if !self.task_client.is_shutdown_poll() {
error!("Failed to send update action to the controller: {err}"); error!("Failed to send update action to the controller: {err}");
} }
} }
@@ -723,7 +708,7 @@ where
.action_sender .action_sender
.unbounded_send(Action::new_insert(pending_acks)) .unbounded_send(Action::new_insert(pending_acks))
{ {
if !self.shutdown_token.is_cancelled() { if !self.task_client.is_shutdown_poll() {
error!("Failed to send insert action to the controller: {err}"); error!("Failed to send insert action to the controller: {err}");
} }
} }
@@ -731,21 +716,17 @@ where
// 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
pub(crate) async fn forward_messages( pub(crate) async fn forward_messages(
&mut self, &self,
messages: Vec<RealMessage>, messages: Vec<RealMessage>,
transmission_lane: TransmissionLane, transmission_lane: TransmissionLane,
) { ) {
tokio::select! { if let Err(err) = self
biased; .real_message_sender
_ = self.shutdown_token.cancelled() => { .send((messages, transmission_lane))
trace!("received shutdown while attempting to forward mixnet messages"); .await
} {
sending_res = self.real_message_sender.send((messages, transmission_lane)) => { if !self.task_client.is_shutdown_poll() {
if sending_res.is_err() { error!("Failed to forward messages to the real message sender: {err}");
error!(
"failed to forward mixnet messages due to closed channel (outside of shutdown!)"
);
}
} }
} }
} }
@@ -14,21 +14,26 @@ 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::client::{
inbound_messages::InputMessageReceiver, mix_traffic::BatchMixMessageSender,
real_messages_control::acknowledgement_control::AcknowledgementControllerConnectors,
topology_control::TopologyAccessor,
};
use crate::config; use crate::config;
use crate::{
client::{
inbound_messages::InputMessageReceiver, mix_traffic::BatchMixMessageSender,
real_messages_control::acknowledgement_control::AcknowledgementControllerConnectors,
topology_control::TopologyAccessor,
},
spawn_future,
};
use futures::channel::mpsc; use futures::channel::mpsc;
use nym_gateway_client::AcknowledgementReceiver; 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_statistics_common::clients::ClientStatsSender; use nym_statistics_common::clients::ClientStatsSender;
use nym_task::connections::{ConnectionCommandReceiver, LaneQueueLengths}; use nym_task::connections::{ConnectionCommandReceiver, LaneQueueLengths};
use nym_task::ShutdownToken; 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 tracing::*;
use crate::client::replies::reply_controller::key_rotation_helpers::KeyRotationConfig; use crate::client::replies::reply_controller::key_rotation_helpers::KeyRotationConfig;
pub(crate) use acknowledgement_control::{AckActionSender, Action}; pub(crate) use acknowledgement_control::{AckActionSender, Action};
@@ -64,7 +69,6 @@ impl<'a> From<&'a Config> for acknowledgement_control::Config {
cfg.traffic.maximum_number_of_retransmissions, cfg.traffic.maximum_number_of_retransmissions,
cfg.acks.ack_wait_addition, cfg.acks.ack_wait_addition,
cfg.acks.ack_wait_multiplier, cfg.acks.ack_wait_multiplier,
cfg.traffic.packet_type,
) )
.with_custom_packet_size(cfg.traffic.primary_packet_size) .with_custom_packet_size(cfg.traffic.primary_packet_size)
} }
@@ -142,7 +146,7 @@ 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,
shutdown_token: ShutdownToken, task_client: TaskClient,
) -> Self { ) -> Self {
let rng = OsRng; let rng = OsRng;
@@ -174,7 +178,7 @@ 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(),
shutdown_token.clone(), task_client.fork("message_handler"),
); );
let ack_control = AcknowledgementController::new( let ack_control = AcknowledgementController::new(
@@ -184,6 +188,7 @@ 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(
@@ -191,6 +196,7 @@ 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(
@@ -203,7 +209,7 @@ impl RealMessagesController<OsRng> {
lane_queue_lengths, lane_queue_lengths,
client_connection_rx, client_connection_rx,
stats_tx, stats_tx,
shutdown_token.clone(), task_client.with_suffix("out_queue_control"),
); );
RealMessagesController { RealMessagesController {
@@ -213,13 +219,20 @@ impl RealMessagesController<OsRng> {
} }
} }
pub fn into_tasks( pub fn start(self, packet_type: PacketType) {
self, let mut out_queue_control = self.out_queue_control;
) -> ( let ack_control = self.ack_control;
OutQueueControl<OsRng>, let mut reply_control = self.reply_control;
ReplyController<OsRng>,
AcknowledgementController<OsRng>, spawn_future(async move {
) { out_queue_control.run().await;
(self.out_queue_control, self.reply_control, self.ack_control) debug!("The out queue controller has finished execution!");
});
spawn_future(async move {
reply_control.run().await;
debug!("The reply controller has finished execution!");
});
ack_control.start(packet_type);
} }
} }
@@ -21,7 +21,7 @@ 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::ShutdownToken; 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;
@@ -31,9 +31,9 @@ use tracing::*;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use tokio::time::{sleep, Sleep}; use tokio::time::{sleep, Sleep};
// use wasm_utils::console_log;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
use wasmtimer::tokio::{sleep, Sleep}; use wasmtimer::tokio::{sleep, Sleep};
mod sending_delay_controller; mod sending_delay_controller;
/// Configurable parameters of the `OutQueueControl` /// Configurable parameters of the `OutQueueControl`
@@ -119,7 +119,7 @@ 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,
shutdown_token: ShutdownToken, task_client: TaskClient,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -179,7 +179,7 @@ where
lane_queue_lengths: LaneQueueLengths, lane_queue_lengths: LaneQueueLengths,
client_connection_rx: ConnectionCommandReceiver, client_connection_rx: ConnectionCommandReceiver,
stats_tx: ClientStatsSender, stats_tx: ClientStatsSender,
shutdown_token: ShutdownToken, task_client: TaskClient,
) -> Self { ) -> Self {
OutQueueControl { OutQueueControl {
config, config,
@@ -194,7 +194,7 @@ where
client_connection_rx, client_connection_rx,
lane_queue_lengths, lane_queue_lengths,
stats_tx, stats_tx,
shutdown_token, task_client,
} }
} }
@@ -249,8 +249,6 @@ where
} }
}; };
// SAFETY: our topology must be valid at this point
#[allow(clippy::expect_used)]
( (
generate_loop_cover_packet( generate_loop_cover_packet(
&mut self.rng, &mut self.rng,
@@ -280,33 +278,17 @@ where
} }
}; };
let sending_res = tokio::select! { if let Err(err) = self.mix_tx.send(vec![next_message]).await {
biased; if !self.task_client.is_shutdown_poll() {
_ = self.shutdown_token.cancelled() => { tracing::error!("Failed to send: {err}");
trace!("received shutdown signal while attempting to send mix message");
return
}
sending_res = self.mix_tx.send(vec![next_message]) => {
sending_res
}
};
match sending_res {
Err(_) => {
if !self.shutdown_token.is_cancelled() {
tracing::error!(
"failed to send mixnet packet due to closed channel (outside of shutdown!)"
);
}
}
Ok(_) => {
let event = if fragment_id.is_some() {
PacketStatisticsEvent::RealPacketSent(packet_size)
} else {
PacketStatisticsEvent::CoverPacketSent(packet_size)
};
self.stats_tx.report(event.into());
} }
} else {
let event = if fragment_id.is_some() {
PacketStatisticsEvent::RealPacketSent(packet_size)
} else {
PacketStatisticsEvent::CoverPacketSent(packet_size)
};
self.stats_tx.report(event.into());
} }
// notify ack controller about sending our message only after we actually managed to push it // notify ack controller about sending our message only after we actually managed to push it
@@ -325,15 +307,9 @@ where
// ready and hence was immediately re-scheduled causing other tasks to be starved; // ready and hence was immediately re-scheduled causing other tasks to be starved;
// yield makes it go back the scheduling queue regardless of its value availability // yield makes it go back the scheduling queue regardless of its value availability
// TODO: temporary and BAD workaround for wasm (we should find a way to yield here in wasm)
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
{ tokio::task::yield_now().await;
tokio::task::yield_now().await;
}
#[cfg(target_arch = "wasm32")]
{
tokio_with_wasm::task::yield_now().await;
}
} }
fn on_close_connection(&mut self, connection_id: ConnectionId) { fn on_close_connection(&mut self, connection_id: ConnectionId) {
@@ -463,8 +439,6 @@ where
tracing::trace!("handling real_messages: size: {}", real_messages.len()); tracing::trace!("handling real_messages: size: {}", real_messages.len());
self.transmission_buffer.store(&conn_id, real_messages); self.transmission_buffer.store(&conn_id, real_messages);
// SAFETY: we just stored the message
#[allow(clippy::expect_used)]
let real_next = self.pop_next_message().expect("Just stored one"); let real_next = self.pop_next_message().expect("Just stored one");
Poll::Ready(Some(StreamMessage::Real(Box::new(real_next)))) Poll::Ready(Some(StreamMessage::Real(Box::new(real_next))))
@@ -513,8 +487,6 @@ where
// First store what we got for the given connection id // First store what we got for the given connection id
self.transmission_buffer.store(&conn_id, real_messages); self.transmission_buffer.store(&conn_id, real_messages);
// SAFETY: we just stored the message
#[allow(clippy::expect_used)]
let real_next = self.pop_next_message().expect("we just added one"); let real_next = self.pop_next_message().expect("we just added one");
Poll::Ready(Some(StreamMessage::Real(Box::new(real_next)))) Poll::Ready(Some(StreamMessage::Real(Box::new(real_next))))
@@ -542,7 +514,9 @@ where
} }
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
fn log_status(&self) { fn log_status(&self, shutdown: &mut TaskClient) {
use crate::error::ClientCoreStatusMessage;
let packets = self.transmission_buffer.total_size(); let packets = self.transmission_buffer.total_size();
let lanes = self.transmission_buffer.lanes(); let lanes = self.transmission_buffer.lanes();
let mult = self.sending_delay_controller.current_multiplier(); let mult = self.sending_delay_controller.current_multiplier();
@@ -571,33 +545,32 @@ where
tracing::debug!("{status_str}"); tracing::debug!("{status_str}");
} }
// leave the code commented in case somebody wanted to restore this logic with a different channel // Send status message to whoever is listening (possibly UI)
// // Send status message to whoever is listening (possibly UI) if mult == self.sending_delay_controller.max_multiplier() {
// if mult == self.sending_delay_controller.max_multiplier() { shutdown.send_status_msg(Box::new(ClientCoreStatusMessage::GatewayIsVerySlow));
// shutdown.send_status_msg(Box::new(ClientCoreStatusMessage::GatewayIsVerySlow)); } else if mult > self.sending_delay_controller.min_multiplier() {
// } else if mult > self.sending_delay_controller.min_multiplier() { shutdown.send_status_msg(Box::new(ClientCoreStatusMessage::GatewayIsSlow));
// shutdown.send_status_msg(Box::new(ClientCoreStatusMessage::GatewayIsSlow)); }
// }
} }
pub(crate) async fn run(&mut self) { pub(super) async fn run(&mut self) {
debug!("Started OutQueueControl with graceful shutdown support"); debug!("Started OutQueueControl with graceful shutdown support");
// avoid borrow on self let mut shutdown = self.task_client.fork("select");
let shutdown_token = self.shutdown_token.clone();
#[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));
loop { while !shutdown.is_shutdown() {
tokio::select! { tokio::select! {
biased; biased;
_ = shutdown_token.cancelled() => { _ = shutdown.recv() => {
tracing::trace!("OutQueueControl: Received shutdown"); tracing::trace!("OutQueueControl: Received shutdown");
break; break;
} }
_ = status_timer.tick() => { _ = status_timer.tick() => {
self.log_status(); self.log_status(&mut shutdown);
} }
next_message = self.next() => if let Some(next_message) = next_message { next_message = self.next() => if let Some(next_message) = next_message {
self.on_message(next_message).await; self.on_message(next_message).await;
@@ -607,16 +580,16 @@ where
} }
} }
} }
shutdown.recv_timeout().await;
} }
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
{ {
loop { while !shutdown.is_shutdown() {
tokio::select! { tokio::select! {
biased; biased;
_ = shutdown_token.cancelled() => { _ = shutdown.recv() => {
tracing::trace!("OutQueueControl: Received shutdown"); tracing::trace!("OutQueueControl: Received shutdown");
break;
} }
next_message = self.next() => if let Some(next_message) = next_message { next_message = self.next() => if let Some(next_message) = next_message {
self.on_message(next_message).await; self.on_message(next_message).await;
@@ -83,13 +83,11 @@ impl SendingDelayController {
self.current_multiplier self.current_multiplier
} }
#[allow(dead_code)]
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
pub(crate) fn min_multiplier(&self) -> u32 { pub(crate) fn min_multiplier(&self) -> u32 {
self.lower_bound self.lower_bound
} }
#[allow(dead_code)]
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
pub(crate) fn max_multiplier(&self) -> u32 { pub(crate) fn max_multiplier(&self) -> u32 {
self.upper_bound self.upper_bound
@@ -1,10 +1,10 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::client::helpers::get_time_now;
use crate::client::replies::{ use crate::client::replies::{
reply_controller::ReplyControllerSender, reply_storage::SentReplyKeys, reply_controller::ReplyControllerSender, reply_storage::SentReplyKeys,
}; };
use crate::spawn_future;
use futures::channel::mpsc; use futures::channel::mpsc;
use futures::lock::Mutex; use futures::lock::Mutex;
use futures::StreamExt; use futures::StreamExt;
@@ -19,10 +19,10 @@ 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::ShutdownToken; use nym_task::TaskClient;
use std::collections::HashSet; use std::collections::HashSet;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::{Duration, Instant};
use tracing::*; use tracing::*;
// The interval at which we check for stale buffers // The interval at which we check for stale buffers
@@ -54,7 +54,7 @@ struct ReceivedMessagesBufferInner<R: MessageReceiver> {
stats_tx: ClientStatsSender, stats_tx: ClientStatsSender,
// Periodically check for stale buffers to clean up // Periodically check for stale buffers to clean up
last_stale_check: crate::client::helpers::Instant, last_stale_check: Instant,
} }
impl<R: MessageReceiver> ReceivedMessagesBufferInner<R> { impl<R: MessageReceiver> ReceivedMessagesBufferInner<R> {
@@ -154,7 +154,7 @@ impl<R: MessageReceiver> ReceivedMessagesBufferInner<R> {
} }
fn cleanup_stale_buffers(&mut self) { fn cleanup_stale_buffers(&mut self) {
let now = get_time_now(); let now = Instant::now();
if now - self.last_stale_check > STALE_BUFFER_CHECK_INTERVAL { if now - self.last_stale_check > STALE_BUFFER_CHECK_INTERVAL {
self.last_stale_check = now; self.last_stale_check = now;
self.message_receiver self.message_receiver
@@ -171,7 +171,7 @@ 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,
shutdown_token: ShutdownToken, task_client: TaskClient,
} }
impl<R: MessageReceiver> ReceivedMessagesBuffer<R> { impl<R: MessageReceiver> ReceivedMessagesBuffer<R> {
@@ -180,7 +180,7 @@ 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,
shutdown_token: ShutdownToken, task_client: TaskClient,
) -> Self { ) -> Self {
ReceivedMessagesBuffer { ReceivedMessagesBuffer {
inner: Arc::new(Mutex::new(ReceivedMessagesBufferInner { inner: Arc::new(Mutex::new(ReceivedMessagesBufferInner {
@@ -190,15 +190,14 @@ impl<R: MessageReceiver> ReceivedMessagesBuffer<R> {
message_sender: None, message_sender: None,
recently_reconstructed: HashSet::new(), recently_reconstructed: HashSet::new(),
stats_tx, stats_tx,
last_stale_check: get_time_now(), last_stale_check: Instant::now(),
})), })),
reply_key_storage, reply_key_storage,
reply_controller_sender, reply_controller_sender,
shutdown_token, task_client,
} }
} }
#[allow(clippy::panic)]
async fn disconnect_sender(&mut self) { async fn disconnect_sender(&mut self) {
let mut guard = self.inner.lock().await; let mut guard = self.inner.lock().await;
if guard.message_sender.is_none() { if guard.message_sender.is_none() {
@@ -209,7 +208,6 @@ impl<R: MessageReceiver> ReceivedMessagesBuffer<R> {
guard.message_sender = None; guard.message_sender = None;
} }
#[allow(clippy::panic)]
async fn connect_sender(&mut self, sender: ReconstructedMessagesSender) { async fn connect_sender(&mut self, sender: ReconstructedMessagesSender) {
let mut guard = self.inner.lock().await; let mut guard = self.inner.lock().await;
if guard.message_sender.is_some() { if guard.message_sender.is_some() {
@@ -315,7 +313,7 @@ impl<R: MessageReceiver> ReceivedMessagesBuffer<R> {
reply_surbs, reply_surbs,
from_surb_request, from_surb_request,
) { ) {
if !self.shutdown_token.is_cancelled() { if !self.task_client.is_shutdown_poll() {
error!("{err}"); error!("{err}");
} }
} }
@@ -338,7 +336,7 @@ impl<R: MessageReceiver> ReceivedMessagesBuffer<R> {
.reply_controller_sender .reply_controller_sender
.send_additional_surbs_request(*recipient, amount) .send_additional_surbs_request(*recipient, amount)
{ {
if !self.shutdown_token.is_cancelled() { if !self.task_client.is_shutdown_poll() {
error!("{err}"); error!("{err}");
} }
} }
@@ -465,22 +463,22 @@ pub enum ReceivedBufferMessage {
ReceiverDisconnect, ReceiverDisconnect,
} }
pub(crate) struct RequestReceiver<R: MessageReceiver> { struct RequestReceiver<R: MessageReceiver> {
received_buffer: ReceivedMessagesBuffer<R>, received_buffer: ReceivedMessagesBuffer<R>,
query_receiver: ReceivedBufferRequestReceiver, query_receiver: ReceivedBufferRequestReceiver,
shutdown_token: ShutdownToken, 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,
shutdown_token: ShutdownToken, task_client: TaskClient,
) -> Self { ) -> Self {
RequestReceiver { RequestReceiver {
received_buffer, received_buffer,
query_receiver, query_receiver,
shutdown_token, task_client,
} }
} }
@@ -495,70 +493,66 @@ impl<R: MessageReceiver> RequestReceiver<R> {
} }
} }
pub(crate) async fn run(&mut self) { async fn run(&mut self) {
debug!("Started RequestReceiver with graceful shutdown support"); debug!("Started RequestReceiver with graceful shutdown support");
loop { while !self.task_client.is_shutdown() {
tokio::select! { tokio::select! {
biased; biased;
_ = self.shutdown_token.cancelled() => { _ = self.task_client.recv() => {
tracing::trace!("RequestReceiver: Received shutdown"); tracing::trace!("RequestReceiver: Received shutdown");
break;
} }
request = self.query_receiver.next() => { request = self.query_receiver.next() => {
if let Some(message) = request { if let Some(message) = request {
self.handle_message(message).await self.handle_message(message).await
} else { } else {
tracing::trace!("RequestReceiver: Stopping since channel closed"); tracing::trace!("RequestReceiver: Stopping since channel closed");
self.shutdown_token.cancelled().await;
break; break;
} }
}, },
} }
} }
self.task_client.recv().await;
tracing::debug!("RequestReceiver: Exiting"); tracing::debug!("RequestReceiver: Exiting");
} }
} }
pub(crate) struct FragmentedMessageReceiver<R: MessageReceiver> { struct FragmentedMessageReceiver<R: MessageReceiver> {
received_buffer: ReceivedMessagesBuffer<R>, received_buffer: ReceivedMessagesBuffer<R>,
mixnet_packet_receiver: MixnetMessageReceiver, mixnet_packet_receiver: MixnetMessageReceiver,
shutdown_token: ShutdownToken, 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,
shutdown_token: ShutdownToken, task_client: TaskClient,
) -> Self { ) -> Self {
FragmentedMessageReceiver { FragmentedMessageReceiver {
received_buffer, received_buffer,
mixnet_packet_receiver, mixnet_packet_receiver,
shutdown_token, task_client,
} }
} }
pub(crate) async fn run(&mut self) -> Result<(), MessageRecoveryError> { async fn run(&mut self) -> Result<(), MessageRecoveryError> {
debug!("Started FragmentedMessageReceiver with graceful shutdown support"); debug!("Started FragmentedMessageReceiver with graceful shutdown support");
loop { while !self.task_client.is_shutdown() {
tokio::select! { tokio::select! {
biased;
_ = self.shutdown_token.cancelled() => {
tracing::trace!("FragmentedMessageReceiver: Received shutdown");
break;
}
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 {
self.received_buffer.handle_new_received(new_messages).await?; self.received_buffer.handle_new_received(new_messages).await?;
} else { } else {
tracing::trace!("FragmentedMessageReceiver: Stopping since channel closed"); tracing::trace!("FragmentedMessageReceiver: Stopping since channel closed");
self.shutdown_token.cancelled().await;
break; break;
} }
}, },
_ = self.task_client.recv_with_delay() => {
tracing::trace!("FragmentedMessageReceiver: Received shutdown");
}
} }
} }
self.task_client.recv_timeout().await;
tracing::debug!("FragmentedMessageReceiver: Exiting"); tracing::debug!("FragmentedMessageReceiver: Exiting");
Ok(()) Ok(())
} }
@@ -577,31 +571,42 @@ 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,
shutdown_token: ShutdownToken, 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,
shutdown_token.clone(), 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,
shutdown_token.clone(), task_client.fork("fragmented_message_receiver"),
), ),
request_receiver: RequestReceiver::new( request_receiver: RequestReceiver::new(
received_buffer, received_buffer,
query_receiver, query_receiver,
shutdown_token.clone(), task_client.with_suffix("request_receiver"),
), ),
} }
} }
pub(crate) fn into_tasks(self) -> (FragmentedMessageReceiver<R>, RequestReceiver<R>) { pub fn start(self) {
(self.fragmented_message_receiver, self.request_receiver) let mut fragmented_message_receiver = self.fragmented_message_receiver;
let mut request_receiver = self.request_receiver;
spawn_future(async move {
match fragmented_message_receiver.run().await {
Ok(_) => {}
Err(e) => error!("{e}"),
}
});
spawn_future(async move {
request_receiver.run().await;
});
} }
} }
@@ -7,7 +7,7 @@ use crate::client::replies::reply_controller::key_rotation_helpers::KeyRotationC
use crate::client::replies::reply_storage::CombinedReplyStorage; use crate::client::replies::reply_storage::CombinedReplyStorage;
use crate::config; use crate::config;
use futures::StreamExt; use futures::StreamExt;
use nym_task::ShutdownToken; use nym_task::TaskClient;
use rand::rngs::OsRng; use rand::rngs::OsRng;
use rand::{CryptoRng, Rng}; use rand::{CryptoRng, Rng};
use std::time::Duration; use std::time::Duration;
@@ -60,6 +60,9 @@ pub struct ReplyController<R> {
receiver_controller: ReceiverReplyController<R>, receiver_controller: ReceiverReplyController<R>,
request_receiver: ReplyControllerReceiver, request_receiver: ReplyControllerReceiver,
// Listen for shutdown signals
task_client: TaskClient,
} }
impl ReplyController<OsRng> { impl ReplyController<OsRng> {
@@ -68,6 +71,7 @@ impl ReplyController<OsRng> {
message_handler: MessageHandler<OsRng>, message_handler: MessageHandler<OsRng>,
full_reply_storage: CombinedReplyStorage, full_reply_storage: CombinedReplyStorage,
request_receiver: ReplyControllerReceiver, request_receiver: ReplyControllerReceiver,
task_client: TaskClient,
) -> Self { ) -> Self {
ReplyController { ReplyController {
config, config,
@@ -82,6 +86,7 @@ impl ReplyController<OsRng> {
message_handler, message_handler,
), ),
request_receiver, request_receiver,
task_client,
} }
} }
} }
@@ -143,21 +148,22 @@ where
self.sender_controller.inspect_and_clear_stale_data(now) self.sender_controller.inspect_and_clear_stale_data(now)
} }
pub(crate) async fn run(&mut self, shutdown_token: ShutdownToken) { pub(crate) async fn run(&mut self) {
debug!("Started ReplyController with graceful shutdown support"); debug!("Started ReplyController with graceful shutdown support");
let mut shutdown = self.task_client.fork("reply-controller");
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);
let polling_rate = self.config.key_rotation.epoch_duration / 8; let polling_rate = self.config.key_rotation.epoch_duration / 8;
let mut invalidation_inspection = new_interval_stream(polling_rate); let mut invalidation_inspection = new_interval_stream(polling_rate);
loop { while !shutdown.is_shutdown() {
tokio::select! { tokio::select! {
biased; biased;
_ = shutdown_token.cancelled() => { _ = shutdown.recv() => {
tracing::trace!("ReplyController: Received shutdown"); tracing::trace!("ReplyController: Received shutdown");
break;
}, },
req = self.request_receiver.next() => match req { req = self.request_receiver.next() => match req {
Some(req) => self.handle_request(req).await, Some(req) => self.handle_request(req).await,
@@ -175,6 +181,7 @@ where
} }
} }
} }
assert!(shutdown.is_shutdown_poll());
tracing::debug!("ReplyController: Exiting"); tracing::debug!("ReplyController: Exiting");
} }
} }
@@ -155,9 +155,8 @@ where
data: Vec<Arc<PendingAcknowledgement>>, data: Vec<Arc<PendingAcknowledgement>>,
) { ) {
trace!("re-inserting pending retransmissions for {recipient}"); trace!("re-inserting pending retransmissions for {recipient}");
// SAFETY: the underlying entry MUST exist as we've just got data from there // the underlying entry MUST exist as we've just got data from there
// and we hold a mut reference // and we hold a mut reference
#[allow(clippy::expect_used)]
let map_entry = &mut self let map_entry = &mut self
.surb_senders .surb_senders
.get_mut(recipient) .get_mut(recipient)
@@ -430,7 +429,6 @@ where
.pop_at_most_n_next_messages_at_random(amount) .pop_at_most_n_next_messages_at_random(amount)
} }
#[allow(clippy::panic)]
async fn try_clear_pending_queue(&mut self, target: AnonymousSenderTag) { async fn try_clear_pending_queue(&mut self, target: AnonymousSenderTag) {
trace!("trying to clear pending queue"); trace!("trying to clear pending queue");
let available_surbs = self.surbs_storage.available_surbs(&target); let available_surbs = self.surbs_storage.available_surbs(&target);
@@ -16,17 +16,21 @@
#![warn(clippy::todo)] #![warn(clippy::todo)]
#![warn(clippy::dbg_macro)] #![warn(clippy::dbg_macro)]
use crate::client::inbound_messages::{InputMessage, InputMessageSender};
use futures::StreamExt; use futures::StreamExt;
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, ShutdownToken, ShutdownTracker}; use nym_task::{connections::TransmissionLane, TaskClient};
use std::time::Duration; use std::time::Duration;
/// Time interval between reporting statistics locally (logging/shutdown_token) use crate::{
client::inbound_messages::{InputMessage, InputMessageSender},
spawn_future,
};
/// Time interval between reporting statistics locally (logging/task_client)
const LOCAL_REPORT_INTERVAL: Duration = Duration::from_secs(2); const LOCAL_REPORT_INTERVAL: Duration = Duration::from_secs(2);
/// Interval for taking snapshots of the statistics /// Interval for taking snapshots of the statistics
const SNAPSHOT_INTERVAL: Duration = Duration::from_millis(500); const SNAPSHOT_INTERVAL: Duration = Duration::from_millis(500);
@@ -47,6 +51,9 @@ 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 {
@@ -55,20 +62,24 @@ impl StatisticsControl {
client_type: String, client_type: String,
client_stats_id: String, client_stats_id: String,
report_tx: InputMessageSender, report_tx: InputMessageSender,
shutdown_token: ShutdownToken, 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), shutdown_token), ClientStatsSender::new(Some(stats_tx), task_client_stats_sender),
) )
} }
@@ -88,8 +99,7 @@ impl StatisticsControl {
} }
} }
// manually control the shutdown mechanism as we don't want to get interrupted mid-snapshot async fn run(&mut self) {
pub async fn run(&mut self, shutdown_token: ShutdownToken) {
tracing::debug!("Started StatisticsControl with graceful shutdown support"); tracing::debug!("Started StatisticsControl with graceful shutdown support");
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
@@ -119,10 +129,10 @@ impl StatisticsControl {
let mut snapshot_interval = let mut snapshot_interval =
gloo_timers::future::IntervalStream::new(SNAPSHOT_INTERVAL.as_millis() as u32); gloo_timers::future::IntervalStream::new(SNAPSHOT_INTERVAL.as_millis() as u32);
loop { while !self.task_client.is_shutdown() {
tokio::select! { tokio::select! {
biased; biased;
_ = shutdown_token.cancelled() => { _ = self.task_client.recv() => {
tracing::trace!("StatisticsControl: Received shutdown"); tracing::trace!("StatisticsControl: Received shutdown");
break; break;
}, },
@@ -147,34 +157,34 @@ impl StatisticsControl {
} }
_ = local_report_interval.next() => { _ = local_report_interval.next() => {
self.stats.local_report(); self.stats.local_report(&mut self.task_client);
} }
} }
} }
tracing::debug!("StatisticsControl: Exiting"); tracing::debug!("StatisticsControl: Exiting");
} }
pub(crate) fn start(mut self) {
spawn_future(async move {
self.run().await;
})
}
pub(crate) fn create_and_start( pub(crate) fn create_and_start(
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,
shutdown_tracker: &ShutdownTracker, task_client: TaskClient,
) -> ClientStatsSender { ) -> ClientStatsSender {
let (mut controller, sender) = Self::create( let (controller, sender) = Self::create(
reporting_config, reporting_config,
client_type, client_type,
client_stats_id, client_stats_id,
report_tx, report_tx,
shutdown_tracker.child_shutdown_token(), task_client,
);
let shutdown_token = shutdown_tracker.clone_shutdown_token();
shutdown_tracker.try_spawn_named(
async move {
controller.run(shutdown_token).await;
},
"StatisticsControl",
); );
controller.start();
sender sender
} }
} }
@@ -126,7 +126,7 @@ impl TopologyAccessor {
.map(|p| p.topology.clone()) .map(|p| p.topology.clone())
} }
pub async fn current_route_provider(&self) -> Option<RwLockReadGuard<'_, NymRouteProvider>> { pub async fn current_route_provider(&self) -> Option<RwLockReadGuard<NymRouteProvider>> {
let provider = self.inner.topology.read().await; let provider = self.inner.topology.read().await;
if provider.topology.is_empty() { if provider.topology.is_empty() {
None None
@@ -1,9 +1,11 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::spawn_future;
pub(crate) use accessor::{TopologyAccessor, TopologyReadPermit}; pub(crate) use accessor::{TopologyAccessor, TopologyReadPermit};
use futures::StreamExt; use futures::StreamExt;
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;
use tracing::*; use tracing::*;
@@ -39,6 +41,8 @@ pub struct TopologyRefresher {
refresh_rate: Duration, refresh_rate: Duration,
consecutive_failure_count: usize, consecutive_failure_count: usize,
task_client: TaskClient,
} }
impl TopologyRefresher { impl TopologyRefresher {
@@ -46,12 +50,14 @@ 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,
} }
} }
@@ -138,30 +144,37 @@ impl TopologyRefresher {
} }
} }
// it's perfectly fine if task is interrupted mid-refresh pub fn start(mut self) {
// there's no data to persist or send over spawn_future(async move {
pub async fn run(&mut self) { debug!("Started TopologyRefresher with graceful shutdown support");
debug!("Started TopologyRefresher with graceful shutdown support");
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
let mut interval = let mut interval = tokio_stream::wrappers::IntervalStream::new(tokio::time::interval(
tokio_stream::wrappers::IntervalStream::new(tokio::time::interval(self.refresh_rate)); self.refresh_rate,
));
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
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);
// We already have an initial topology, so no need to refresh it immediately. // We already have an initial topology, so no need to refresh it immediately.
// My understanding is that js setInterval does not fire immediately, so it's not // My understanding is that js setInterval does not fire immediately, so it's not
// needed there. // needed there.
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
interval.next().await; interval.next().await;
while interval.next().await.is_some() { while !self.task_client.is_shutdown() {
self.try_refresh().await; tokio::select! {
} _ = interval.next() => {
self.try_refresh().await;
// this should never get triggered },
error!("topology refresher interval has been exhausted!") _ = self.task_client.recv() => {
tracing::trace!("TopologyRefresher: Received shutdown");
},
}
}
self.task_client.recv_timeout().await;
tracing::debug!("TopologyRefresher: Exiting");
})
} }
} }
@@ -2,10 +2,8 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use async_trait::async_trait; use async_trait::async_trait;
use nym_mixnet_contract_common::EpochRewardedSet;
use nym_topology::provider_trait::{ToTopologyMetadata, TopologyProvider}; use nym_topology::provider_trait::{ToTopologyMetadata, TopologyProvider};
use nym_topology::NymTopology; use nym_topology::NymTopology;
use nym_validator_client::nym_api::NymApiClientExt;
use rand::prelude::SliceRandom; use rand::prelude::SliceRandom;
use rand::thread_rng; use rand::thread_rng;
use std::cmp::min; use std::cmp::min;
@@ -41,43 +39,30 @@ impl Config {
pub struct NymApiTopologyProvider { pub struct NymApiTopologyProvider {
config: Config, config: Config,
validator_client: nym_http_api_client::Client, validator_client: nym_validator_client::client::NymApiClient,
nym_api_urls: Vec<Url>, nym_api_urls: Vec<Url>,
currently_used_api: usize, currently_used_api: usize,
use_bincode: bool,
} }
impl NymApiTopologyProvider { impl NymApiTopologyProvider {
pub fn new( pub fn new(
config: impl Into<Config>, config: impl Into<Config>,
mut nym_api_urls: Vec<Url>, mut nym_api_urls: Vec<Url>,
validator_client: nym_http_api_client::Client, mut validator_client: nym_validator_client::client::NymApiClient,
) -> Self { ) -> Self {
nym_api_urls.shuffle(&mut thread_rng()); nym_api_urls.shuffle(&mut thread_rng());
let mut provider = NymApiTopologyProvider { validator_client.change_nym_api(nym_api_urls[0].clone());
NymApiTopologyProvider {
config: config.into(), config: config.into(),
validator_client, validator_client,
nym_api_urls, nym_api_urls,
currently_used_api: 0, currently_used_api: 0,
use_bincode: true, }
};
// Set all API URLs - the client will try them in order with automatic failover
provider.validator_client.change_base_urls(
provider
.nym_api_urls
.iter()
.map(|u| u.clone().into())
.collect(),
);
provider
} }
pub fn disable_bincode(&mut self) { pub fn disable_bincode(&mut self) {
self.use_bincode = false; self.validator_client.use_bincode = false;
// Note: The unified client doesn't support toggling bincode after creation.
// This would require recreating the client without bincode.
// For now, we'll track the preference but it won't take effect.
warn!("Disabling bincode on existing client is not currently supported");
} }
fn use_next_nym_api(&mut self) { fn use_next_nym_api(&mut self) {
@@ -87,19 +72,8 @@ impl NymApiTopologyProvider {
} }
self.currently_used_api = (self.currently_used_api + 1) % self.nym_api_urls.len(); self.currently_used_api = (self.currently_used_api + 1) % self.nym_api_urls.len();
self.validator_client
// Provide all URLs starting from the next one in rotation order .change_nym_api(self.nym_api_urls[self.currently_used_api].clone())
// This enables automatic failover to other endpoints
let rotated_urls: Vec<_> = self
.nym_api_urls
.iter()
.cycle()
.skip(self.currently_used_api)
.take(self.nym_api_urls.len())
.map(|u| u.clone().into())
.collect();
self.validator_client.change_base_urls(rotated_urls)
} }
async fn get_current_compatible_topology(&mut self) -> Option<NymTopology> { async fn get_current_compatible_topology(&mut self) -> Option<NymTopology> {
@@ -125,13 +99,8 @@ impl NymApiTopologyProvider {
.filter(|n| n.performance.round_to_integer() >= self.config.min_node_performance()) .filter(|n| n.performance.round_to_integer() >= self.config.min_node_performance())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let epoch_rewarded_set: EpochRewardedSet = rewarded_set.into(); NymTopology::new(metadata.to_topology_metadata(), rewarded_set, Vec::new())
NymTopology::new( .with_skimmed_nodes(&nodes_filtered)
metadata.to_topology_metadata(),
epoch_rewarded_set,
Vec::new(),
)
.with_skimmed_nodes(&nodes_filtered)
} else { } else {
// if we're not using extended topology, we're only getting active set mixnodes and gateways // if we're not using extended topology, we're only getting active set mixnodes and gateways
@@ -179,13 +148,8 @@ impl NymApiTopologyProvider {
} }
} }
let epoch_rewarded_set: EpochRewardedSet = rewarded_set.into(); NymTopology::new(metadata.to_topology_metadata(), rewarded_set, Vec::new())
NymTopology::new( .with_skimmed_nodes(&nodes)
metadata.to_topology_metadata(),
epoch_rewarded_set,
Vec::new(),
)
.with_skimmed_nodes(&nodes)
}; };
if !topology.is_minimally_routable() { if !topology.is_minimally_routable() {
+5 -28
View File
@@ -4,13 +4,10 @@
use crate::client::mix_traffic::transceiver::ErasedGatewayError; use crate::client::mix_traffic::transceiver::ErasedGatewayError;
use nym_crypto::asymmetric::ed25519::Ed25519RecoveryError; use nym_crypto::asymmetric::ed25519::Ed25519RecoveryError;
use nym_gateway_client::error::GatewayClientError; use nym_gateway_client::error::GatewayClientError;
use nym_task::RegistryAccessError;
use nym_topology::node::RoutingNodeError; use nym_topology::node::RoutingNodeError;
use nym_topology::{NodeId, NymTopologyError}; use nym_topology::{NodeId, NymTopologyError};
use nym_validator_client::nym_api::error::NymAPIError; use nym_validator_client::nym_api::error::NymAPIError;
use nym_validator_client::nyxd::error::NyxdError;
use nym_validator_client::ValidatorClientError; use nym_validator_client::ValidatorClientError;
use rand::distributions::WeightedError;
use std::error::Error; use std::error::Error;
use std::path::PathBuf; use std::path::PathBuf;
@@ -57,7 +54,10 @@ pub enum ClientCoreError {
ListOfNymApisIsEmpty, ListOfNymApisIsEmpty,
#[error("failed to resolve a query to nym API: {source}")] #[error("failed to resolve a query to nym API: {source}")]
NymApiQueryFailure { source: Box<NymAPIError> }, NymApiQueryFailure {
#[from]
source: NymAPIError,
},
#[error( #[error(
"the current network topology seem to be insufficient to route any packets through:\n\t{0}" "the current network topology seem to be insufficient to route any packets through:\n\t{0}"
@@ -230,22 +230,7 @@ pub enum ClientCoreError {
UnexpectedKeyUpgrade { gateway_id: String }, UnexpectedKeyUpgrade { gateway_id: String },
#[error("failed to derive keys from master key")] #[error("failed to derive keys from master key")]
HkdfDerivationError, HkdfDerivationError {},
#[error("missing url for constructing RPC client")]
RpcClientMissingUrl,
#[error("provided nym network details were malformed: {source}")]
InvalidNetworkDetails { source: NyxdError },
#[error("failed to construct RPC client: {source}")]
RpcClientCreationFailure { source: NyxdError },
#[error("failed to select valid gateway due to incomputable latency")]
GatewaySelectionFailure { source: WeightedError },
#[error("Could not access task registry, {0}")]
RegistryAccess(#[from] RegistryAccessError),
} }
impl From<tungstenite::Error> for ClientCoreError { impl From<tungstenite::Error> for ClientCoreError {
@@ -256,14 +241,6 @@ impl From<tungstenite::Error> for ClientCoreError {
} }
} }
impl From<NymAPIError> for ClientCoreError {
fn from(err: NymAPIError) -> ClientCoreError {
ClientCoreError::NymApiQueryFailure {
source: Box::new(err),
}
}
}
/// 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
#[derive(Debug)] #[derive(Debug)]
pub enum ClientCoreStatusMessage { pub enum ClientCoreStatusMessage {
+22 -134
View File
@@ -7,8 +7,7 @@ use futures::{SinkExt, StreamExt};
use nym_crypto::asymmetric::ed25519; use nym_crypto::asymmetric::ed25519;
use nym_gateway_client::GatewayClient; use nym_gateway_client::GatewayClient;
use nym_topology::node::RoutingNode; use nym_topology::node::RoutingNode;
use nym_validator_client::client::{IdentityKeyRef, NymApiClientExt}; use nym_validator_client::client::IdentityKeyRef;
use nym_validator_client::nym_nodes::SkimmedNodesWithMetadata;
use nym_validator_client::UserAgent; use nym_validator_client::UserAgent;
use rand::{seq::SliceRandom, Rng}; use rand::{seq::SliceRandom, Rng};
#[cfg(unix)] #[cfg(unix)]
@@ -45,7 +44,6 @@ type WsConn = JSWebsocket;
const CONCURRENT_GATEWAYS_MEASURED: usize = 20; const CONCURRENT_GATEWAYS_MEASURED: usize = 20;
const MEASUREMENTS: usize = 3; const MEASUREMENTS: usize = 3;
const DEFAULT_NYM_API_RETRIES: usize = 3;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
const CONN_TIMEOUT: Duration = Duration::from_millis(1500); const CONN_TIMEOUT: Duration = Duration::from_millis(1500);
@@ -85,88 +83,32 @@ struct GatewayWithLatency<'a, G: ConnectableGateway> {
latency: Duration, latency: Duration,
} }
// Helper to collect all pages of entry nodes - replicates NymApiClient's convenience method
async fn get_all_basic_entry_nodes_with_metadata(
client: &nym_http_api_client::Client,
use_bincode: bool,
) -> Result<SkimmedNodesWithMetadata, ClientCoreError> {
// Get first page to obtain metadata
let mut page = 0;
let res = client
.get_basic_entry_assigned_nodes_v2(false, Some(page), None, use_bincode)
.await?;
let mut nodes = res.nodes.data;
let metadata = res.metadata;
if res.nodes.pagination.total == nodes.len() {
return Ok(SkimmedNodesWithMetadata::new(nodes, metadata));
}
page += 1;
// Collect remaining pages
loop {
let mut res = client
.get_basic_entry_assigned_nodes_v2(false, Some(page), None, use_bincode)
.await?;
if !metadata.consistency_check(&res.metadata) {
return Err(ClientCoreError::ValidatorClientError(
nym_validator_client::ValidatorClientError::InconsistentPagedMetadata,
));
}
nodes.append(&mut res.nodes.data);
if nodes.len() < res.nodes.pagination.total {
page += 1
} else {
break;
}
}
Ok(SkimmedNodesWithMetadata::new(nodes, metadata))
}
impl<'a, G: ConnectableGateway> GatewayWithLatency<'a, G> { impl<'a, G: ConnectableGateway> GatewayWithLatency<'a, G> {
fn new(gateway: &'a G, latency: Duration) -> Self { fn new(gateway: &'a G, latency: Duration) -> Self {
GatewayWithLatency { gateway, latency } GatewayWithLatency { gateway, latency }
} }
} }
pub async fn gateways_for_init( pub async fn gateways_for_init<R: Rng>(
rng: &mut R,
nym_apis: &[Url], nym_apis: &[Url],
user_agent: Option<UserAgent>, user_agent: Option<UserAgent>,
minimum_performance: u8, minimum_performance: u8,
ignore_epoch_roles: bool, ignore_epoch_roles: bool,
retry_count: Option<usize>,
) -> Result<Vec<RoutingNode>, ClientCoreError> { ) -> Result<Vec<RoutingNode>, ClientCoreError> {
// Build client with ALL URLs for fallback support let nym_api = nym_apis
let nym_api_urls: Vec<nym_http_api_client::Url> = nym_apis .choose(rng)
.iter() .ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
.map(|url| nym_http_api_client::Url::from(url.clone())) let client = if let Some(user_agent) = user_agent {
.collect(); nym_validator_client::client::NymApiClient::new_with_user_agent(nym_api.clone(), user_agent)
} else {
nym_validator_client::client::NymApiClient::new(nym_api.clone())
};
if nym_api_urls.is_empty() { tracing::debug!("Fetching list of gateways from: {nym_api}");
return Err(ClientCoreError::ListOfNymApisIsEmpty);
}
let retry_count = retry_count.unwrap_or(DEFAULT_NYM_API_RETRIES); let gateways = client
let mut builder = nym_http_api_client::ClientBuilder::new_with_urls(nym_api_urls.clone()) .get_all_basic_entry_assigned_nodes_with_metadata()
.with_retries(retry_count)
.with_bincode();
if let Some(user_agent) = user_agent {
builder = builder.with_user_agent(user_agent);
}
let client = builder.build().map_err(|e| {
ClientCoreError::ValidatorClientError(nym_validator_client::ValidatorClientError::from(e))
})?;
tracing::debug!("Fetching list of gateways from: {:?}", nym_api_urls);
// Use our helper to handle pagination
let gateways = get_all_basic_entry_nodes_with_metadata(&client, true)
.await? .await?
.nodes; .nodes;
info!("nym api reports {} gateways", gateways.len()); info!("nym api reports {} gateways", gateways.len());
@@ -175,15 +117,17 @@ pub async fn gateways_for_init(
// filter out gateways below minimum performance and ones that could operate as a mixnode // filter out gateways below minimum performance and ones that could operate as a mixnode
// (we don't want instability) // (we don't want instability)
let valid_gateways: Vec<RoutingNode> = gateways let valid_gateways = gateways
.iter() .iter()
.filter(|g| ignore_epoch_roles || !g.supported_roles.mixnode) .filter(|g| ignore_epoch_roles || !g.supported_roles.mixnode)
.filter(|g| g.performance.round_to_integer() >= minimum_performance) .filter(|g| g.performance.round_to_integer() >= minimum_performance)
.filter_map(|gateway| gateway.try_into().ok()) .filter_map(|gateway| gateway.try_into().ok())
.collect(); .collect::<Vec<_>>();
tracing::debug!("After checking validity: {}", valid_gateways.len());
tracing::trace!("Valid gateways: {valid_gateways:#?}");
tracing::info!( tracing::info!(
"Found {} valid gateways after filtering", "and {} after validity and performance filtering",
valid_gateways.len() valid_gateways.len()
); );
@@ -204,7 +148,7 @@ async fn connect(endpoint: &str) -> Result<WsConn, ClientCoreError> {
JSWebsocket::new(endpoint).map_err(|_| ClientCoreError::GatewayJsConnectionFailure) JSWebsocket::new(endpoint).map_err(|_| ClientCoreError::GatewayJsConnectionFailure)
} }
async fn measure_latency<G>(gateway: &G) -> Result<GatewayWithLatency<'_, G>, ClientCoreError> async fn measure_latency<G>(gateway: &G) -> Result<GatewayWithLatency<G>, ClientCoreError>
where where
G: ConnectableGateway, G: ConnectableGateway,
{ {
@@ -301,7 +245,7 @@ pub async fn choose_gateway_by_latency<R: Rng, G: ConnectableGateway + Clone>(
let gateways_with_latency = gateways_with_latency.lock().await; let gateways_with_latency = gateways_with_latency.lock().await;
let chosen = gateways_with_latency let chosen = gateways_with_latency
.choose_weighted(rng, |item| 1. / item.latency.as_secs_f32()) .choose_weighted(rng, |item| 1. / item.latency.as_secs_f32())
.map_err(|source| ClientCoreError::GatewaySelectionFailure { source })?; .expect("invalid selection weight!");
info!( info!(
"chose gateway {} with average latency of {:?}", "chose gateway {} with average latency of {:?}",
@@ -346,20 +290,13 @@ pub(super) fn get_specified_gateway(
must_use_tls: bool, must_use_tls: bool,
) -> Result<RoutingNode, ClientCoreError> { ) -> Result<RoutingNode, ClientCoreError> {
tracing::debug!("Requesting specified gateway: {gateway_identity}"); tracing::debug!("Requesting specified gateway: {gateway_identity}");
let user_gateway = ed25519::PublicKey::from_base58_string(gateway_identity) let user_gateway = ed25519::PublicKey::from_base58_string(gateway_identity)
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?; .map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
let gateway = gateways let gateway = gateways
.iter() .iter()
.find(|gateway| gateway.identity_key == user_gateway) .find(|gateway| gateway.identity_key == user_gateway)
.ok_or_else(|| { .ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_identity.to_string()))?;
tracing::debug!(
"Gateway {gateway_identity} not found in {} available gateways",
gateways.len()
);
ClientCoreError::NoGatewayWithId(gateway_identity.to_string())
})?;
let Some(entry_details) = gateway.entry.as_ref() else { let Some(entry_details) = gateway.entry.as_ref() else {
return Err(ClientCoreError::UnsupportedEntry { return Err(ClientCoreError::UnsupportedEntry {
@@ -422,52 +359,3 @@ pub(super) async fn register_with_gateway(
authenticated_ephemeral_client: gateway_client, authenticated_ephemeral_client: gateway_client,
}) })
} }
#[cfg(test)]
mod tests {
use url::Url;
#[test]
fn test_single_url_builds_without_retries() {
let urls = [Url::parse("https://api.nym.com").unwrap()];
let nym_api_urls: Vec<nym_http_api_client::Url> = urls
.iter()
.map(|url| nym_http_api_client::Url::from(url.clone()))
.collect();
assert_eq!(nym_api_urls.len(), 1, "Should have exactly one URL");
}
#[test]
fn test_multiple_urls_prepared_for_retries() {
let urls = vec![
Url::parse("https://api1.nym.com").unwrap(),
Url::parse("https://api2.nym.com").unwrap(),
Url::parse("https://api3.nym.com").unwrap(),
];
let nym_api_urls: Vec<nym_http_api_client::Url> = urls
.iter()
.map(|url| nym_http_api_client::Url::from(url.clone()))
.collect();
assert_eq!(nym_api_urls.len(), 3, "Should have all three URLs");
assert!(
nym_api_urls.len() > 1,
"Multiple URLs trigger retry behavior"
);
}
#[test]
fn test_empty_url_list_is_detected() {
let urls: Vec<Url> = vec![];
let nym_api_urls: Vec<nym_http_api_client::Url> = urls
.iter()
.map(|url| nym_http_api_client::Url::from(url.clone()))
.collect();
assert!(nym_api_urls.is_empty(), "Empty list should remain empty");
}
}
+3 -9
View File
@@ -17,22 +17,16 @@ pub use nym_topology::{
HardcodedTopologyProvider, NymRouteProvider, NymTopology, NymTopologyError, TopologyProvider, HardcodedTopologyProvider, NymRouteProvider, NymTopology, NymTopologyError, TopologyProvider,
}; };
#[deprecated(note = "use spawn_future from nym_task crate instead")]
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
#[track_caller] pub(crate) fn spawn_future<F>(future: F)
pub fn spawn_future<F>(future: F)
where where
F: Future<Output = ()> + 'static, F: Future<Output = ()> + 'static,
{ {
wasm_bindgen_futures::spawn_local(async move { wasm_bindgen_futures::spawn_local(future);
future.await;
});
} }
#[deprecated(note = "use spawn_future from nym_task crate instead")]
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
#[track_caller] pub(crate) fn spawn_future<F>(future: F)
pub fn spawn_future<F>(future: F)
where where
F: Future + Send + 'static, F: Future + Send + 'static,
F::Output: Send + 'static, F::Output: Send + 'static,
@@ -30,7 +30,6 @@ optional = true
path = "../../../sqlx-pool-guard" path = "../../../sqlx-pool-guard"
[build-dependencies] [build-dependencies]
anyhow = { workspace = true }
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
sqlx = { workspace = true, features = [ sqlx = { workspace = true, features = [
"runtime-tokio-rustls", "runtime-tokio-rustls",
+4 -7
View File
@@ -2,24 +2,23 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#[tokio::main] #[tokio::main]
async fn main() -> anyhow::Result<()> { async fn main() {
#[cfg(feature = "fs-surb-storage")] #[cfg(feature = "fs-surb-storage")]
{ {
use anyhow::Context;
use sqlx::{Connection, SqliteConnection}; use sqlx::{Connection, SqliteConnection};
use std::env; use std::env;
let out_dir = env::var("OUT_DIR")?; let out_dir = env::var("OUT_DIR").unwrap();
let database_path = format!("{out_dir}/fs-surbs-example.sqlite"); let database_path = format!("{out_dir}/fs-surbs-example.sqlite");
let mut conn = SqliteConnection::connect(&format!("sqlite://{database_path}?mode=rwc")) let mut conn = SqliteConnection::connect(&format!("sqlite://{database_path}?mode=rwc"))
.await .await
.context("Failed to create SQLx database connection")?; .expect("Failed to create SQLx database connection");
sqlx::migrate!("./fs_surbs_migrations") sqlx::migrate!("./fs_surbs_migrations")
.run(&mut conn) .run(&mut conn)
.await .await
.context("Failed to perform SQLx migrations")?; .expect("Failed to perform SQLx migrations");
#[cfg(target_family = "unix")] #[cfg(target_family = "unix")]
println!("cargo:rustc-env=DATABASE_URL=sqlite://{}", &database_path); println!("cargo:rustc-env=DATABASE_URL=sqlite://{}", &database_path);
@@ -29,6 +28,4 @@ async fn main() -> anyhow::Result<()> {
// not a valid windows path... but hey, it works... // not a valid windows path... but hey, it works...
println!("cargo:rustc-env=DATABASE_URL=sqlite:///{}", &database_path); println!("cargo:rustc-env=DATABASE_URL=sqlite:///{}", &database_path);
} }
Ok(())
} }
+2 -2
View File
@@ -40,7 +40,7 @@ where
pub async fn flush_on_shutdown( pub async fn flush_on_shutdown(
mut self, mut self,
mem_state: CombinedReplyStorage, mem_state: CombinedReplyStorage,
shutdown: nym_task::ShutdownToken, mut shutdown: nym_task::TaskClient,
) { ) {
use tracing::{debug, error, info}; use tracing::{debug, error, info};
@@ -50,7 +50,7 @@ where
return; return;
} }
shutdown.cancelled().await; shutdown.recv().await;
info!("PersistentReplyStorage is flushing all reply-related data to underlying storage"); info!("PersistentReplyStorage is flushing all reply-related data to underlying storage");
if let Err(err) = self.backend.flush_surb_storage(&mem_state).await { if let Err(err) = self.backend.flush_surb_storage(&mem_state).await {
@@ -12,7 +12,7 @@ use crate::socket_state::{ws_fd, PartiallyDelegatedHandle, SocketState};
use crate::traits::GatewayPacketRouter; use crate::traits::GatewayPacketRouter;
use crate::{cleanup_socket_message, try_decrypt_binary_message}; use crate::{cleanup_socket_message, try_decrypt_binary_message};
use futures::{SinkExt, StreamExt}; use futures::{SinkExt, StreamExt};
use nym_bandwidth_controller::BandwidthController; use nym_bandwidth_controller::{BandwidthController, BandwidthStatusMessage};
use nym_credential_storage::ephemeral_storage::EphemeralStorage as EphemeralCredentialStorage; use nym_credential_storage::ephemeral_storage::EphemeralStorage as EphemeralCredentialStorage;
use nym_credential_storage::storage::Storage as CredentialStorage; use nym_credential_storage::storage::Storage as CredentialStorage;
use nym_credentials::CredentialSpendingData; use nym_credentials::CredentialSpendingData;
@@ -27,7 +27,7 @@ use nym_gateway_requests::{
use nym_sphinx::forwarding::packet::MixPacket; use nym_sphinx::forwarding::packet::MixPacket;
use nym_statistics_common::clients::connection::ConnectionStatsEvent; use nym_statistics_common::clients::connection::ConnectionStatsEvent;
use nym_statistics_common::clients::ClientStatsSender; use nym_statistics_common::clients::ClientStatsSender;
use nym_task::ShutdownToken; use nym_task::TaskClient;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient; use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
use rand::rngs::OsRng; use rand::rngs::OsRng;
use std::sync::Arc; use std::sync::Arc;
@@ -109,7 +109,7 @@ pub struct GatewayClient<C, St = EphemeralCredentialStorage> {
connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>, connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
/// Listen to shutdown messages and send notifications back to the task manager /// Listen to shutdown messages and send notifications back to the task manager
shutdown_token: ShutdownToken, task_client: TaskClient,
} }
impl<C, St> GatewayClient<C, St> { impl<C, St> GatewayClient<C, St> {
@@ -124,7 +124,7 @@ impl<C, St> GatewayClient<C, St> {
bandwidth_controller: Option<BandwidthController<C, St>>, bandwidth_controller: Option<BandwidthController<C, St>>,
stats_reporter: ClientStatsSender, stats_reporter: ClientStatsSender,
#[cfg(unix)] connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>, #[cfg(unix)] connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
shutdown_token: ShutdownToken, task_client: TaskClient,
) -> Self { ) -> Self {
GatewayClient { GatewayClient {
cfg, cfg,
@@ -141,7 +141,7 @@ impl<C, St> GatewayClient<C, St> {
negotiated_protocol: None, negotiated_protocol: None,
#[cfg(unix)] #[cfg(unix)]
connection_fd_callback, connection_fd_callback,
shutdown_token, task_client,
} }
} }
@@ -201,7 +201,7 @@ impl<C, St> GatewayClient<C, St> {
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
pub async fn establish_connection(&mut self) -> Result<(), GatewayClientError> { pub async fn establish_connection(&mut self) -> Result<(), GatewayClientError> {
debug!( debug!(
"Attempting to establish connection to gateway at: {}", "Attemting to establish connection to gateway at: {}",
self.gateway_address self.gateway_address
); );
let (ws_stream, _) = connect_async( let (ws_stream, _) = connect_async(
@@ -293,7 +293,7 @@ impl<C, St> GatewayClient<C, St> {
loop { loop {
tokio::select! { tokio::select! {
_ = self.shutdown_token.cancelled() => { _ = self.task_client.recv() => {
log::trace!("GatewayClient control response: Received shutdown"); log::trace!("GatewayClient control response: Received shutdown");
log::debug!("GatewayClient control response: Exiting"); log::debug!("GatewayClient control response: Exiting");
break Err(GatewayClientError::ConnectionClosedGatewayShutdown); break Err(GatewayClientError::ConnectionClosedGatewayShutdown);
@@ -514,7 +514,7 @@ impl<C, St> GatewayClient<C, St> {
self.cfg.bandwidth.require_tickets, self.cfg.bandwidth.require_tickets,
derive_aes256_gcm_siv_key, derive_aes256_gcm_siv_key,
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
self.shutdown_token.clone(), self.task_client.clone(),
) )
.await .await
.map_err(GatewayClientError::RegistrationFailure), .map_err(GatewayClientError::RegistrationFailure),
@@ -631,6 +631,9 @@ impl<C, St> GatewayClient<C, St> {
self.negotiated_protocol = protocol_version; self.negotiated_protocol = protocol_version;
log::debug!("authenticated: {status}, bandwidth remaining: {bandwidth_remaining}"); log::debug!("authenticated: {status}, bandwidth remaining: {bandwidth_remaining}");
self.task_client.send_status_msg(Box::new(
BandwidthStatusMessage::RemainingBandwidth(bandwidth_remaining),
));
Ok(()) Ok(())
} }
ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)), ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)),
@@ -1066,7 +1069,7 @@ impl<C, St> GatewayClient<C, St> {
.expect("no shared key present even though we're authenticated!"), .expect("no shared key present even though we're authenticated!"),
), ),
self.bandwidth.clone(), self.bandwidth.clone(),
self.shutdown_token.clone(), self.task_client.clone(),
) )
} }
_ => unreachable!(), _ => unreachable!(),
@@ -1140,8 +1143,8 @@ impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
// perfectly fine here, because it's not meant to be used // perfectly fine here, because it's not meant to be used
let (ack_tx, _) = mpsc::unbounded(); let (ack_tx, _) = mpsc::unbounded();
let (mix_tx, _) = mpsc::unbounded(); let (mix_tx, _) = mpsc::unbounded();
let shutdown_token = ShutdownToken::default(); let task_client = TaskClient::dummy();
let packet_router = PacketRouter::new(ack_tx, mix_tx, shutdown_token.clone()); let packet_router = PacketRouter::new(ack_tx, mix_tx, task_client.clone());
GatewayClient { GatewayClient {
cfg: GatewayClientConfig::default().with_disabled_credentials_mode(true), cfg: GatewayClientConfig::default().with_disabled_credentials_mode(true),
@@ -1154,11 +1157,11 @@ impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
connection: SocketState::NotConnected, connection: SocketState::NotConnected,
packet_router, packet_router,
bandwidth_controller: None, bandwidth_controller: None,
stats_reporter: ClientStatsSender::new(None, shutdown_token.clone()), stats_reporter: ClientStatsSender::new(None, task_client.clone()),
negotiated_protocol: None, negotiated_protocol: None,
#[cfg(unix)] #[cfg(unix)]
connection_fd_callback, connection_fd_callback,
shutdown_token, task_client,
} }
} }
@@ -1167,7 +1170,7 @@ impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
packet_router: PacketRouter, packet_router: PacketRouter,
bandwidth_controller: Option<BandwidthController<C, St>>, bandwidth_controller: Option<BandwidthController<C, St>>,
stats_reporter: ClientStatsSender, stats_reporter: ClientStatsSender,
shutdown_token: ShutdownToken, task_client: TaskClient,
) -> GatewayClient<C, St> { ) -> GatewayClient<C, St> {
// invariants that can't be broken // invariants that can't be broken
// (unless somebody decided to expose some field that wasn't meant to be exposed) // (unless somebody decided to expose some field that wasn't meant to be exposed)
@@ -1190,7 +1193,7 @@ impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
negotiated_protocol: self.negotiated_protocol, negotiated_protocol: self.negotiated_protocol,
#[cfg(unix)] #[cfg(unix)]
connection_fd_callback: self.connection_fd_callback, connection_fd_callback: self.connection_fd_callback,
shutdown_token, task_client,
} }
} }
} }
@@ -7,7 +7,7 @@
use crate::error::GatewayClientError; use crate::error::GatewayClientError;
use crate::GatewayPacketRouter; use crate::GatewayPacketRouter;
use futures::channel::mpsc; use futures::channel::mpsc;
use nym_task::ShutdownToken; use nym_task::TaskClient;
pub type MixnetMessageSender = mpsc::UnboundedSender<Vec<Vec<u8>>>; pub type MixnetMessageSender = mpsc::UnboundedSender<Vec<Vec<u8>>>;
pub type MixnetMessageReceiver = mpsc::UnboundedReceiver<Vec<Vec<u8>>>; pub type MixnetMessageReceiver = mpsc::UnboundedReceiver<Vec<Vec<u8>>>;
@@ -19,14 +19,14 @@ pub type AcknowledgementReceiver = mpsc::UnboundedReceiver<Vec<Vec<u8>>>;
pub struct PacketRouter { pub struct PacketRouter {
ack_sender: AcknowledgementSender, ack_sender: AcknowledgementSender,
mixnet_message_sender: MixnetMessageSender, mixnet_message_sender: MixnetMessageSender,
shutdown: ShutdownToken, shutdown: TaskClient,
} }
impl PacketRouter { impl PacketRouter {
pub fn new( pub fn new(
ack_sender: AcknowledgementSender, ack_sender: AcknowledgementSender,
mixnet_message_sender: MixnetMessageSender, mixnet_message_sender: MixnetMessageSender,
shutdown: ShutdownToken, shutdown: TaskClient,
) -> Self { ) -> Self {
PacketRouter { PacketRouter {
ack_sender, ack_sender,
@@ -42,7 +42,7 @@ impl PacketRouter {
if let Err(err) = self.mixnet_message_sender.unbounded_send(received_messages) { if let Err(err) = self.mixnet_message_sender.unbounded_send(received_messages) {
// check if the failure is due to the shutdown being in progress and thus the receiver channel // check if the failure is due to the shutdown being in progress and thus the receiver channel
// having already been dropped // having already been dropped
if self.shutdown.is_cancelled() { if self.shutdown.is_shutdown_poll() || self.shutdown.is_dummy() {
// This should ideally not happen, but it's ok // This should ideally not happen, but it's ok
tracing::warn!("Failed to send mixnet messages due to receiver task shutdown"); tracing::warn!("Failed to send mixnet messages due to receiver task shutdown");
return Err(GatewayClientError::ShutdownInProgress); return Err(GatewayClientError::ShutdownInProgress);
@@ -58,7 +58,7 @@ impl PacketRouter {
if let Err(err) = self.ack_sender.unbounded_send(received_acks) { if let Err(err) = self.ack_sender.unbounded_send(received_acks) {
// check if the failure is due to the shutdown being in progress and thus the receiver channel // check if the failure is due to the shutdown being in progress and thus the receiver channel
// having already been dropped // having already been dropped
if self.shutdown.is_cancelled() { if self.shutdown.is_shutdown_poll() || self.shutdown.is_dummy() {
// This should ideally not happen, but it's ok // This should ideally not happen, but it's ok
tracing::warn!("Failed to send acks due to receiver task shutdown"); tracing::warn!("Failed to send acks due to receiver task shutdown");
return Err(GatewayClientError::ShutdownInProgress); return Err(GatewayClientError::ShutdownInProgress);
@@ -69,6 +69,10 @@ impl PacketRouter {
} }
Ok(()) Ok(())
} }
pub fn disarm(&mut self) {
self.shutdown.disarm();
}
} }
impl GatewayPacketRouter for PacketRouter { impl GatewayPacketRouter for PacketRouter {
@@ -11,7 +11,7 @@ use futures::stream::{SplitSink, SplitStream};
use futures::{SinkExt, StreamExt}; use futures::{SinkExt, StreamExt};
use nym_gateway_requests::shared_key::SharedGatewayKey; use nym_gateway_requests::shared_key::SharedGatewayKey;
use nym_gateway_requests::{SensitiveServerResponse, ServerResponse, SimpleGatewayRequestsError}; use nym_gateway_requests::{SensitiveServerResponse, ServerResponse, SimpleGatewayRequestsError};
use nym_task::ShutdownToken; use nym_task::TaskClient;
use si_scale::helpers::bibytes2; use si_scale::helpers::bibytes2;
use std::os::raw::c_int as RawFd; use std::os::raw::c_int as RawFd;
use std::sync::Arc; use std::sync::Arc;
@@ -87,13 +87,13 @@ impl PartiallyDelegatedRouter {
} }
} }
async fn run(mut self, mut split_stream: SplitStream<WsConn>, shutdown_token: ShutdownToken) { async fn run(mut self, mut split_stream: SplitStream<WsConn>, mut task_client: TaskClient) {
let mut chunked_stream = (&mut split_stream).ready_chunks(8); let mut chunked_stream = (&mut split_stream).ready_chunks(8);
let ret: Result<_, GatewayClientError> = loop { let ret: Result<_, GatewayClientError> = loop {
tokio::select! { tokio::select! {
biased; biased;
// received system-wide shutdown // received system-wide shutdown
_ = shutdown_token.cancelled() => { _ = task_client.recv() => {
log::trace!("GatewayClient listener: Received shutdown"); log::trace!("GatewayClient listener: Received shutdown");
log::debug!("GatewayClient listener: Exiting"); log::debug!("GatewayClient listener: Exiting");
return; return;
@@ -118,7 +118,11 @@ impl PartiallyDelegatedRouter {
let return_res = match ret { let return_res = match ret {
Err(err) => self.stream_return.send(Err(err)), Err(err) => self.stream_return.send(Err(err)),
Ok(_) => self.stream_return.send(Ok(split_stream)), Ok(_) => {
self.packet_router.disarm();
task_client.disarm();
self.stream_return.send(Ok(split_stream))
}
}; };
if return_res.is_err() { if return_res.is_err() {
@@ -262,8 +266,8 @@ impl PartiallyDelegatedRouter {
Ok(plaintexts) Ok(plaintexts)
} }
fn spawn(self, split_stream: SplitStream<WsConn>, shutdown_token: ShutdownToken) { fn spawn(self, split_stream: SplitStream<WsConn>, task_client: TaskClient) {
let fut = async move { self.run(split_stream, shutdown_token).await }; let fut = async move { self.run(split_stream, task_client).await };
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
wasm_bindgen_futures::spawn_local(fut); wasm_bindgen_futures::spawn_local(fut);
@@ -279,7 +283,7 @@ impl PartiallyDelegatedHandle {
packet_router: PacketRouter, packet_router: PacketRouter,
shared_key: Arc<SharedGatewayKey>, shared_key: Arc<SharedGatewayKey>,
client_bandwidth: ClientBandwidth, client_bandwidth: ClientBandwidth,
shutdown: ShutdownToken, shutdown: TaskClient,
) -> Self { ) -> Self {
// when called for, it NEEDS TO yield back the stream so that we could merge it and // when called for, it NEEDS TO yield back the stream so that we could merge it and
// read control request responses. // read control request responses.
@@ -333,7 +337,7 @@ impl PartiallyDelegatedHandle {
// check if the split stream didn't error out // check if the split stream didn't error out
let receive_res = stream_receiver let receive_res = stream_receiver
.try_recv() .try_recv()
.map_err(|_| GatewayClientError::ConnectionAbruptlyClosed)?; .expect("stream sender was somehow dropped without sending anything!");
if let Some(res) = receive_res { if let Some(res) = receive_res {
let _res = res?; let _res = res?;
@@ -3,7 +3,7 @@ name = "nym-validator-client"
version = "0.1.0" version = "0.1.0"
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"] authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2021" edition = "2021"
rust-version = "1.85" rust-version = "1.56"
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
+147 -20
View File
@@ -5,8 +5,8 @@ use crate::nyxd::{self, NyxdClient};
use crate::signing::direct_wallet::DirectSecp256k1HdWallet; use crate::signing::direct_wallet::DirectSecp256k1HdWallet;
use crate::signing::signer::{NoSigner, OfflineSigner}; use crate::signing::signer::{NoSigner, OfflineSigner};
use crate::{ use crate::{
DirectSigningReqwestRpcValidatorClient, QueryReqwestRpcValidatorClient, ReqwestRpcClient, nym_api, DirectSigningReqwestRpcValidatorClient, QueryReqwestRpcValidatorClient,
ValidatorClientError, ReqwestRpcClient, ValidatorClientError,
}; };
use nym_api_requests::ecash::models::{ use nym_api_requests::ecash::models::{
AggregatedCoinIndicesSignatureResponse, AggregatedExpirationDateSignatureResponse, AggregatedCoinIndicesSignatureResponse, AggregatedExpirationDateSignatureResponse,
@@ -20,9 +20,11 @@ use nym_api_requests::ecash::{
PartialExpirationDateSignatureResponse, VerificationKeyResponse, PartialExpirationDateSignatureResponse, VerificationKeyResponse,
}; };
use nym_api_requests::models::{ use nym_api_requests::models::{
ApiHealthResponse, GatewayCoreStatusResponse, HistoricalPerformanceResponse, ApiHealthResponse, GatewayBondAnnotated, GatewayCoreStatusResponse,
MixnodeCoreStatusResponse, NymNodeDescription, HistoricalPerformanceResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse,
NymNodeDescription, RewardEstimationResponse, StakeSaturationResponse,
}; };
use nym_api_requests::models::{LegacyDescribedGateway, MixNodeBondAnnotated};
use nym_api_requests::nym_nodes::{ use nym_api_requests::nym_nodes::{
NodesByAddressesResponse, SemiSkimmedNodesWithMetadata, SkimmedNode, SkimmedNodesWithMetadata, NodesByAddressesResponse, SemiSkimmedNodesWithMetadata, SkimmedNode, SkimmedNodesWithMetadata,
}; };
@@ -151,7 +153,7 @@ impl Config {
pub struct Client<C, S = NoSigner> { pub struct Client<C, S = NoSigner> {
// ideally they would have been read-only, but unfortunately rust doesn't have such features // ideally they would have been read-only, but unfortunately rust doesn't have such features
// #[deprecated(note = "please use `nym_api_client` instead")] // #[deprecated(note = "please use `nym_api_client` instead")]
pub nym_api: nym_http_api_client::Client, pub nym_api: nym_api::Client,
// pub nym_api_client: NymApiClient, // pub nym_api_client: NymApiClient,
pub nyxd: NyxdClient<C, S>, pub nyxd: NyxdClient<C, S>,
} }
@@ -212,7 +214,7 @@ impl Client<ReqwestRpcClient> {
impl<C> Client<C> { impl<C> Client<C> {
pub fn new_with_rpc_client(config: Config, rpc_client: C) -> Self { pub fn new_with_rpc_client(config: Config, rpc_client: C) -> Self {
let nym_api_client = nym_http_api_client::Client::new(config.api_url.clone(), None); let nym_api_client = nym_api::Client::new(config.api_url.clone(), None);
Client { Client {
nym_api: nym_api_client, nym_api: nym_api_client,
@@ -226,7 +228,7 @@ impl<C, S> Client<C, S> {
where where
S: OfflineSigner, S: OfflineSigner,
{ {
let nym_api_client = nym_http_api_client::Client::new(config.api_url.clone(), None); let nym_api_client = nym_api::Client::new(config.api_url.clone(), None);
Client { Client {
nym_api: nym_api_client, nym_api: nym_api_client,
@@ -247,6 +249,65 @@ impl<C, S> Client<C, S> {
self.nym_api.change_base_urls(vec![new_endpoint.into()]) self.nym_api.change_base_urls(vec![new_endpoint.into()])
} }
#[deprecated]
pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
Ok(self.nym_api.get_mixnodes().await?)
}
#[deprecated]
pub async fn get_cached_mixnodes_detailed(
&self,
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorClientError> {
Ok(self.nym_api.get_mixnodes_detailed().await?)
}
#[deprecated]
pub async fn get_cached_mixnodes_detailed_unfiltered(
&self,
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorClientError> {
Ok(self.nym_api.get_mixnodes_detailed_unfiltered().await?)
}
#[deprecated]
pub async fn get_cached_rewarded_mixnodes(
&self,
) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
Ok(self.nym_api.get_rewarded_mixnodes().await?)
}
#[deprecated]
pub async fn get_cached_rewarded_mixnodes_detailed(
&self,
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorClientError> {
Ok(self.nym_api.get_rewarded_mixnodes_detailed().await?)
}
#[deprecated]
pub async fn get_cached_active_mixnodes(
&self,
) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
Ok(self.nym_api.get_active_mixnodes().await?)
}
#[deprecated]
pub async fn get_cached_active_mixnodes_detailed(
&self,
) -> Result<Vec<MixNodeBondAnnotated>, ValidatorClientError> {
Ok(self.nym_api.get_active_mixnodes_detailed().await?)
}
#[deprecated]
pub async fn get_cached_gateways(&self) -> Result<Vec<GatewayBond>, ValidatorClientError> {
Ok(self.nym_api.get_gateways().await?)
}
#[deprecated]
pub async fn get_cached_gateways_detailed_unfiltered(
&self,
) -> Result<Vec<GatewayBondAnnotated>, ValidatorClientError> {
Ok(self.nym_api.get_gateways_detailed_unfiltered().await?)
}
pub async fn get_full_node_performance_history( pub async fn get_full_node_performance_history(
&self, &self,
node_id: NodeId, node_id: NodeId,
@@ -324,25 +385,38 @@ impl<C, S> Client<C, S> {
} }
} }
/// DEPRECATED: Use nym_http_api_client::Client with from_network() or with_bincode() instead
#[deprecated(
since = "1.2.0",
note = "Use nym_http_api_client::Client::from_network() or ClientBuilder::with_bincode() instead"
)]
#[derive(Clone)] #[derive(Clone)]
pub struct NymApiClient { pub struct NymApiClient {
pub use_bincode: bool, pub use_bincode: bool,
pub nym_api: nym_http_api_client::Client, pub nym_api: nym_api::Client,
// TODO: perhaps if we really need it at some (currently I don't see any reasons for it) // TODO: perhaps if we really need it at some (currently I don't see any reasons for it)
// we could re-implement the communication with the REST API on port 1317 // we could re-implement the communication with the REST API on port 1317
} }
impl From<nym_api::Client> for NymApiClient {
fn from(nym_api: nym_api::Client) -> Self {
NymApiClient {
use_bincode: false,
nym_api,
}
}
}
// we have to allow the use of deprecated method here as they're calling the deprecated trait methods // we have to allow the use of deprecated method here as they're calling the deprecated trait methods
#[allow(deprecated)] #[allow(deprecated)]
impl NymApiClient { impl NymApiClient {
pub fn new(api_url: Url) -> Self {
let nym_api = nym_api::Client::new(api_url, None);
NymApiClient {
use_bincode: true,
nym_api,
}
}
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
pub fn new_with_timeout(api_url: Url, timeout: std::time::Duration) -> Self { pub fn new_with_timeout(api_url: Url, timeout: std::time::Duration) -> Self {
let nym_api = nym_http_api_client::Client::new(api_url, Some(timeout)); let nym_api = nym_api::Client::new(api_url, Some(timeout));
NymApiClient { NymApiClient {
use_bincode: true, use_bincode: true,
@@ -357,10 +431,10 @@ impl NymApiClient {
} }
pub fn new_with_user_agent(api_url: Url, user_agent: impl Into<UserAgent>) -> Self { pub fn new_with_user_agent(api_url: Url, user_agent: impl Into<UserAgent>) -> Self {
let nym_api = nym_http_api_client::Client::builder(api_url) let nym_api = nym_api::Client::builder::<_, ValidatorClientError>(api_url)
.expect("invalid api url") .expect("invalid api url")
.with_user_agent(user_agent.into()) .with_user_agent(user_agent.into())
.build() .build::<ValidatorClientError>()
.expect("failed to build nym api client"); .expect("failed to build nym api client");
NymApiClient { NymApiClient {
@@ -497,6 +571,37 @@ impl NymApiClient {
Ok(self.nym_api.health().await?) Ok(self.nym_api.health().await?)
} }
#[deprecated]
pub async fn get_cached_active_mixnodes(
&self,
) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
Ok(self.nym_api.get_active_mixnodes().await?)
}
#[deprecated]
pub async fn get_cached_rewarded_mixnodes(
&self,
) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
Ok(self.nym_api.get_rewarded_mixnodes().await?)
}
#[deprecated]
pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
Ok(self.nym_api.get_mixnodes().await?)
}
#[deprecated]
pub async fn get_cached_gateways(&self) -> Result<Vec<GatewayBond>, ValidatorClientError> {
Ok(self.nym_api.get_gateways().await?)
}
#[deprecated]
pub async fn get_cached_described_gateways(
&self,
) -> Result<Vec<LegacyDescribedGateway>, ValidatorClientError> {
Ok(self.nym_api.get_gateways_described().await?)
}
pub async fn get_all_described_nodes( pub async fn get_all_described_nodes(
&self, &self,
) -> Result<Vec<NymNodeDescription>, ValidatorClientError> { ) -> Result<Vec<NymNodeDescription>, ValidatorClientError> {
@@ -563,6 +668,30 @@ impl NymApiClient {
.await?) .await?)
} }
#[deprecated]
pub async fn get_mixnode_status(
&self,
mix_id: NodeId,
) -> Result<MixnodeStatusResponse, ValidatorClientError> {
Ok(self.nym_api.get_mixnode_status(mix_id).await?)
}
#[deprecated]
pub async fn get_mixnode_reward_estimation(
&self,
mix_id: NodeId,
) -> Result<RewardEstimationResponse, ValidatorClientError> {
Ok(self.nym_api.get_mixnode_reward_estimation(mix_id).await?)
}
#[deprecated]
pub async fn get_mixnode_stake_saturation(
&self,
mix_id: NodeId,
) -> Result<StakeSaturationResponse, ValidatorClientError> {
Ok(self.nym_api.get_mixnode_stake_saturation(mix_id).await?)
}
pub async fn blind_sign( pub async fn blind_sign(
&self, &self,
request_body: &BlindSignRequestBody, request_body: &BlindSignRequestBody,
@@ -590,11 +719,10 @@ impl NymApiClient {
pub async fn partial_expiration_date_signatures( pub async fn partial_expiration_date_signatures(
&self, &self,
expiration_date: Option<Date>, expiration_date: Option<Date>,
epoch_id: Option<EpochId>,
) -> Result<PartialExpirationDateSignatureResponse, ValidatorClientError> { ) -> Result<PartialExpirationDateSignatureResponse, ValidatorClientError> {
Ok(self Ok(self
.nym_api .nym_api
.partial_expiration_date_signatures(expiration_date, epoch_id) .partial_expiration_date_signatures(expiration_date)
.await?) .await?)
} }
@@ -611,11 +739,10 @@ impl NymApiClient {
pub async fn global_expiration_date_signatures( pub async fn global_expiration_date_signatures(
&self, &self,
expiration_date: Option<Date>, expiration_date: Option<Date>,
epoch_id: Option<EpochId>,
) -> Result<AggregatedExpirationDateSignatureResponse, ValidatorClientError> { ) -> Result<AggregatedExpirationDateSignatureResponse, ValidatorClientError> {
Ok(self Ok(self
.nym_api .nym_api
.global_expiration_date_signatures(expiration_date, epoch_id) .global_expiration_date_signatures(expiration_date)
.await?) .await?)
} }

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