Compare commits
157 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2c717c0ebd | |||
| d73b7b7127 | |||
| 440aadf124 | |||
| d126d8e5a0 | |||
| 6b2bb3029b | |||
| 4dcc568ec2 | |||
| 468835e3a2 | |||
| 28a866e26d | |||
| 350d244032 | |||
| 17ca000782 | |||
| aac983d922 | |||
| 577675bab3 | |||
| ec015618cd | |||
| fa40acbeca | |||
| 386e1790dd | |||
| d07f9c8fad | |||
| 0dc071daeb | |||
| babf113fe5 | |||
| 10951d4cd3 | |||
| 872c25bfcc | |||
| 5acce42c64 | |||
| 4848d081d0 | |||
| b3452ede77 | |||
| a44819b14c | |||
| 5455110810 | |||
| a0178d28f7 | |||
| 3e42160426 | |||
| 2f752a6c42 | |||
| 806f807f02 | |||
| 1400db6156 | |||
| 673a3e45d3 | |||
| d9c2f6ebda | |||
| e24e094711 | |||
| 0d7487f530 | |||
| 378f32e6d7 | |||
| 3e9b8d237f | |||
| f5a4dbc555 | |||
| 4480534e4d | |||
| d355f9d752 | |||
| 9f3a370496 | |||
| e4adc5d954 | |||
| 00373b70e2 | |||
| 65f2017422 | |||
| 192f258463 | |||
| a5eee7444b | |||
| 6abd7e7352 | |||
| 3306ca5357 | |||
| 9c2ccead0e | |||
| b7aeb51362 | |||
| e9e725defe | |||
| c74494a21d | |||
| 54f6c98c22 | |||
| 846484bbb4 | |||
| fb3f5501ba | |||
| e8a607f520 | |||
| f5f6df9eaf | |||
| c647ab5605 | |||
| 416c21a42e | |||
| fd5a95fa4d | |||
| c61df79182 | |||
| 08559a7660 | |||
| 6dce55a99b | |||
| bc0b89b31c | |||
| 67c32faa11 | |||
| aa0d15ee67 | |||
| 4f0974fcf1 | |||
| bd2174641e | |||
| 59b62fabc9 | |||
| e6f4bae895 | |||
| d71742af32 | |||
| 3b7c07e249 | |||
| 3b429dde69 | |||
| 3a29c296da | |||
| 8544c54f8f | |||
| 9f9639950b | |||
| 111a0b20b6 | |||
| 67b300d0b8 | |||
| 88c4e0ce6c | |||
| f6800aff0a | |||
| 0c7c927ca2 | |||
| a69c8b1660 | |||
| f3ea310a46 | |||
| 92f9ff035d | |||
| 5a817e1df1 | |||
| a07a24db00 | |||
| a0cb812eff | |||
| 923c1fa184 | |||
| 35ea7e4926 | |||
| d1cb9afaf0 | |||
| 79d4b4b2e3 | |||
| 8460b33946 | |||
| ae6539e07c | |||
| 18cebdfedc | |||
| c448ec823a | |||
| a266137278 | |||
| 6f4dfd1dab | |||
| 57719787db | |||
| 29a57bf172 | |||
| 17d11f201e | |||
| fef7e42eb4 | |||
| ceeeb6211b | |||
| cd77b1032f | |||
| 6bbb14f12f | |||
| de8030d85a | |||
| e18e64bf21 | |||
| a50c9ac3fb | |||
| db813b6e3e | |||
| 1be5ba310a | |||
| 41ff3f7824 | |||
| c9d4d62446 | |||
| e839a0d80e | |||
| cd61f930bf | |||
| 0674f31227 | |||
| 3e4f563dce | |||
| edcf2b1204 | |||
| b07fb18113 | |||
| 017dea4afd | |||
| 5a9ce13beb | |||
| 514cf25c68 | |||
| 49ee0636e4 | |||
| bb971ce99c | |||
| 54de369c1e | |||
| 6d6ce284df | |||
| 56ad1c6c8e | |||
| 10b4a288c8 | |||
| bbbb9486ce | |||
| 16e86e1a07 | |||
| ca0c9898f0 | |||
| 8b73d4e615 | |||
| 6a9a767ab4 | |||
| e03a9fa16f | |||
| a0fbd57d5b | |||
| 9856198356 | |||
| 5c33846e57 | |||
| cfa7635ae1 | |||
| 5d45544c27 | |||
| aa6a79cb3e | |||
| b3a940770a | |||
| e980f76a81 | |||
| 9b38fef28f | |||
| 43910ca635 | |||
| d3ccd7575a | |||
| 422f889df7 | |||
| c9e96edc35 | |||
| 7768317046 | |||
| 0ebbb1a540 | |||
| 827c13b69e | |||
| 18ff09608c | |||
| 8cc996bc0d | |||
| 83a598907f | |||
| bb06a1b7a8 | |||
| e783a5fced | |||
| 8a24b45b5d | |||
| 10e4eba727 | |||
| 8ebf482f36 | |||
| 6940ca427e | |||
| 24f877fda5 |
@@ -8,10 +8,13 @@ on:
|
||||
- 'gateway/**'
|
||||
- 'integrations/**'
|
||||
- 'nym-api/**'
|
||||
- 'nym-authenticator-client/**'
|
||||
- 'nym-credential-proxy/**'
|
||||
- 'nym-ip-packet-client/**'
|
||||
- 'nym-network-monitor/**'
|
||||
- 'nym-node/**'
|
||||
- 'nym-node-status-api/**'
|
||||
- 'nym-registration-client/**'
|
||||
- 'nym-statistics-api/**'
|
||||
- 'nym-outfox/**'
|
||||
- 'nym-validator-rewarder/**'
|
||||
@@ -81,12 +84,21 @@ jobs:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
|
||||
- name: Clippy
|
||||
- name: Clippy (macos)
|
||||
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
|
||||
with:
|
||||
command: clippy
|
||||
args: --workspace --all-targets -- -D warnings
|
||||
|
||||
|
||||
- name: Build all binaries
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --lib --manifest-path contracts/Cargo.toml
|
||||
args: --lib --manifest-path contracts/Cargo.toml --all-features
|
||||
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
|
||||
@@ -10,7 +10,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: arc-ubuntu-22.04
|
||||
runs-on: arc-linux-latest
|
||||
env:
|
||||
RUSTUP_PERMIT_COPY_RENAME: 1
|
||||
defaults:
|
||||
|
||||
@@ -6,16 +6,14 @@ on:
|
||||
paths:
|
||||
- "ts-packages/**"
|
||||
- "sdk/typescript/**"
|
||||
- "nym-connect/desktop/src/**"
|
||||
- "nym-connect/desktop/package.json"
|
||||
- "nym-wallet/src/**"
|
||||
- "nym-wallet/package.json"
|
||||
- "explorer/**"
|
||||
- "explorer-v2/**"
|
||||
- ".github/workflows/ci-lint-typescript.yml"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: arc-linux-latest
|
||||
env:
|
||||
RUSTUP_PERMIT_COPY_RENAME: 1
|
||||
steps:
|
||||
@@ -25,6 +23,7 @@ jobs:
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Setup yarn
|
||||
run: npm install -g yarn
|
||||
|
||||
@@ -37,14 +36,12 @@ jobs:
|
||||
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||||
|
||||
- name: Install wasm-opt
|
||||
uses: ./.github/actions/install-wasm-opt
|
||||
with:
|
||||
version: '116'
|
||||
run: cargo install wasm-opt
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: "1.23.7"
|
||||
go-version: "1.24.6"
|
||||
|
||||
- name: Install
|
||||
run: yarn
|
||||
@@ -52,7 +49,11 @@ jobs:
|
||||
- name: Build packages
|
||||
run: yarn build:ci
|
||||
|
||||
- name: Install again
|
||||
run: yarn
|
||||
|
||||
- name: Lint
|
||||
run: yarn lint
|
||||
|
||||
- name: Typecheck with tsc
|
||||
run: yarn tsc
|
||||
|
||||
@@ -8,7 +8,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: custom-linux
|
||||
runs-on: arc-linux-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@ on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'wasm/**'
|
||||
- 'clients/client-core/**'
|
||||
- 'common/**'
|
||||
- '.github/workflows/ci-sdk-wasm.yml'
|
||||
- "wasm/**"
|
||||
- "clients/client-core/**"
|
||||
- "common/**"
|
||||
- ".github/workflows/ci-sdk-wasm.yml"
|
||||
|
||||
jobs:
|
||||
wasm:
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.23.7"
|
||||
go-version: "1.24.6"
|
||||
|
||||
- name: Install wasm-pack
|
||||
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
- name: Install wasm-opt
|
||||
uses: ./.github/actions/install-wasm-opt
|
||||
with:
|
||||
version: '116'
|
||||
version: "116"
|
||||
|
||||
- name: Install wasm-bindgen-cli
|
||||
run: cargo install wasm-bindgen-cli
|
||||
|
||||
@@ -4,7 +4,7 @@ on:
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: arc-ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@@ -17,10 +17,13 @@ jobs:
|
||||
- name: Setup yarn
|
||||
run: npm install -g yarn
|
||||
|
||||
- name: Install Rust stable
|
||||
- name: Install rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Install wasm-pack
|
||||
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||||
@@ -29,9 +32,9 @@ jobs:
|
||||
run: cargo install wasm-opt
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: "1.23.7"
|
||||
go-version: "1.24.6"
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
@@ -3,11 +3,6 @@ name: Build and upload Node Status agent container to harbor.nymte.ch
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
gateway_probe_git_ref:
|
||||
type: string
|
||||
default: nym-vpn-core-v1.4.0
|
||||
required: true
|
||||
description: Which gateway probe git ref to build the image with
|
||||
release_image:
|
||||
description: 'Tag image as a release'
|
||||
required: true
|
||||
@@ -43,16 +38,6 @@ jobs:
|
||||
VERSION=$(yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml)
|
||||
echo "result=$VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: cleanup-gateway-probe-ref
|
||||
id: cleanup_gateway_probe_ref
|
||||
run: |
|
||||
GATEWAY_PROBE_GIT_REF=${{ github.event.inputs.gateway_probe_git_ref }}
|
||||
GIT_REF_SLUG="${GATEWAY_PROBE_GIT_REF//\//-}"
|
||||
echo "git_ref=${GIT_REF_SLUG}" >> $GITHUB_OUTPUT
|
||||
|
||||
- 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
|
||||
|
||||
- name: Initialize RELEASE_TAG
|
||||
run: echo "RELEASE_TAG=" >> $GITHUB_ENV
|
||||
|
||||
@@ -61,24 +46,12 @@ jobs:
|
||||
run: echo "RELEASE_TAG=golden-" >> $GITHUB_ENV
|
||||
|
||||
- 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 }}" >> $GITHUB_ENV
|
||||
|
||||
- name: New env vars
|
||||
run: echo "RELEASE_TAG='$RELEASE_TAG' GIT_TAG='$GIT_TAG' IMAGE_NAME_AND_TAGS='$IMAGE_NAME_AND_TAGS'"
|
||||
|
||||
# - name: Remove existing tag if exists
|
||||
# run: |
|
||||
# if git rev-parse $${{ env.GIT_TAG }} >/dev/null 2>&1; then
|
||||
# git push --delete origin $${{ env.GIT_TAG }}
|
||||
# git tag -d $${{ env.GIT_TAG }}
|
||||
# fi
|
||||
|
||||
# - name: Create tag
|
||||
# run: |
|
||||
# git tag -a $${{ env.GIT_TAG }} -m "Version ${{ steps.get_version.outputs.result }}-${{ steps.cleanup_gateway_probe_ref.outputs.git_ref }}"
|
||||
# git push origin $${{ env.GIT_TAG }}
|
||||
run: echo "RELEASE_TAG='$RELEASE_TAG' IMAGE_NAME_AND_TAGS='$IMAGE_NAME_AND_TAGS'"
|
||||
|
||||
- name: BuildAndPushImageOnHarbor
|
||||
run: |
|
||||
docker build --build-arg GIT_REF=${{ github.event.inputs.gateway_probe_git_ref }} -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.IMAGE_NAME_AND_TAGS }}
|
||||
docker build -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.IMAGE_NAME_AND_TAGS }}
|
||||
docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags
|
||||
|
||||
@@ -4,6 +4,78 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [2025.20-leerdammer] (2025-11-12)
|
||||
|
||||
- Max/tweak ts sdk actions ([#6185])
|
||||
- chore: resolve clippy 1.91 warnings ([#6168])
|
||||
- [chore] Remove unused dependencies ([#6151])
|
||||
- Use typed-builder for registration client builder config ([#6150])
|
||||
- tommy is too quick ([#6149])
|
||||
- configurable mixnet client startup timeout ([#6148])
|
||||
- [Feature/operators]: QUIC bridge deployment script v2 ([#6145])
|
||||
- Bugfix: Add circuit breaker ([#6143])
|
||||
- bugfix: update internal owner address in transferred share ([#6139])
|
||||
- Update quic_bridge_deployment.sh for IPv4 and .deb package ([#6138])
|
||||
- feat: expose more explicit new_with_fronted_urls builder for http API client ([#6136])
|
||||
- bugfix: update stored epoch share when changing ownership ([#6135])
|
||||
- Domain fronting ([#6134])
|
||||
- bugfix: update stored epoch share when changing announce address ([#6131])
|
||||
|
||||
[#6185]: https://github.com/nymtech/nym/pull/6185
|
||||
[#6168]: https://github.com/nymtech/nym/pull/6168
|
||||
[#6151]: https://github.com/nymtech/nym/pull/6151
|
||||
[#6150]: https://github.com/nymtech/nym/pull/6150
|
||||
[#6149]: https://github.com/nymtech/nym/pull/6149
|
||||
[#6148]: https://github.com/nymtech/nym/pull/6148
|
||||
[#6145]: https://github.com/nymtech/nym/pull/6145
|
||||
[#6143]: https://github.com/nymtech/nym/pull/6143
|
||||
[#6139]: https://github.com/nymtech/nym/pull/6139
|
||||
[#6138]: https://github.com/nymtech/nym/pull/6138
|
||||
[#6136]: https://github.com/nymtech/nym/pull/6136
|
||||
[#6135]: https://github.com/nymtech/nym/pull/6135
|
||||
[#6134]: https://github.com/nymtech/nym/pull/6134
|
||||
[#6131]: https://github.com/nymtech/nym/pull/6131
|
||||
|
||||
## [2025.19-kase] (2025-10-30)
|
||||
|
||||
- update ns agent workflow ([#6154])
|
||||
- Cherry pick - request #6143 from nymtech/bugfix/mix-tx-closed-v2 ([#6153])
|
||||
- bugfix: nym-credential-proxy query params parsing regression ([#6121])
|
||||
- bugfix: revert some dep updates introduced in #6043 ([#6120])
|
||||
- Skip ipv6 metadata endpoint request ([#6118])
|
||||
- update to no longer use 1mb files ([#6117])
|
||||
- chore: restore pending dkg contract state migration ([#6116])
|
||||
- Revert "Propagate cancel token to mixnet client" ([#6115])
|
||||
- Update dirs to 6.0 ([#6109])
|
||||
- Propagate cancel token to mixnet client ([#6105])
|
||||
- bugfix: retrieve and update ticketbook in the same query ([#6101])
|
||||
- bugfix: include network name in the default gateway probe config path ([#6100])
|
||||
- Bugfix/incompatibility fixes ([#6099])
|
||||
- [DOCs/operators] QUIC deployment script & docs ([#6098])
|
||||
- bugfix: testnet manager 02sql migration ([#6096])
|
||||
- feat: move gateway probe to monorepo (and update to rust edition 2024) ([#6094])
|
||||
- bugfix: use custom topology provider for list of init gateways ([#6092])
|
||||
- Max/fix wasm client + build commands ([#6043])
|
||||
|
||||
[#6154]: https://github.com/nymtech/nym/pull/6154
|
||||
[#6153]: https://github.com/nymtech/nym/pull/6153
|
||||
[#6121]: https://github.com/nymtech/nym/pull/6121
|
||||
[#6120]: https://github.com/nymtech/nym/pull/6120
|
||||
[#6118]: https://github.com/nymtech/nym/pull/6118
|
||||
[#6117]: https://github.com/nymtech/nym/pull/6117
|
||||
[#6116]: https://github.com/nymtech/nym/pull/6116
|
||||
[#6115]: https://github.com/nymtech/nym/pull/6115
|
||||
[#6109]: https://github.com/nymtech/nym/pull/6109
|
||||
[#6105]: https://github.com/nymtech/nym/pull/6105
|
||||
[#6101]: https://github.com/nymtech/nym/pull/6101
|
||||
[#6100]: https://github.com/nymtech/nym/pull/6100
|
||||
[#6099]: https://github.com/nymtech/nym/pull/6099
|
||||
[#6098]: https://github.com/nymtech/nym/pull/6098
|
||||
[#6096]: https://github.com/nymtech/nym/pull/6096
|
||||
[#6094]: https://github.com/nymtech/nym/pull/6094
|
||||
[#6092]: https://github.com/nymtech/nym/pull/6092
|
||||
[#6043]: https://github.com/nymtech/nym/pull/6043
|
||||
|
||||
## [2025.18-jarlsberg] (2025-10-14)
|
||||
|
||||
- ns-api: add descriptions to dVPN gateway responses ([#6102])
|
||||
|
||||
Generated
+337
-54
@@ -1,6 +1,6 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "accessory"
|
||||
@@ -133,9 +133,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
|
||||
[[package]]
|
||||
name = "ammonia"
|
||||
version = "4.1.1"
|
||||
version = "4.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6b346764dd0814805de8abf899fe03065bcee69bb1a4771c785817e39f3978f"
|
||||
checksum = "17e913097e1a2124b46746c980134e8c954bc17a6a59bb3fde96f088d126dde6"
|
||||
dependencies = [
|
||||
"cssparser",
|
||||
"html5ever",
|
||||
@@ -929,7 +929,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "bls12_381"
|
||||
version = "0.8.0"
|
||||
source = "git+https://github.com/jstuczyn/bls12_381?branch=temp/experimental-serdect-updated#9bf520059cb28323fc51469cae86868ef4fa6fbd"
|
||||
source = "git+https://github.com/jstuczyn/bls12_381?branch=temp%2Fexperimental-serdect-updated#9bf520059cb28323fc51469cae86868ef4fa6fbd"
|
||||
dependencies = [
|
||||
"digest 0.10.7",
|
||||
"ff",
|
||||
@@ -2135,6 +2135,37 @@ dependencies = [
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947"
|
||||
dependencies = [
|
||||
"derive_builder_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder_core"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder_macro"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
|
||||
dependencies = [
|
||||
"derive_builder_core",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "1.0.0"
|
||||
@@ -2215,23 +2246,23 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "5.0.1"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
|
||||
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
|
||||
dependencies = [
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.4.1"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
|
||||
checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"option-ext",
|
||||
"redox_users",
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2548,7 +2579,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "extension-storage"
|
||||
version = "1.4.0-rc.0"
|
||||
version = "1.4.1"
|
||||
dependencies = [
|
||||
"bip39",
|
||||
"console_error_panic_hook",
|
||||
@@ -4447,7 +4478,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mix-fetch-wasm"
|
||||
version = "1.4.0-rc.0"
|
||||
version = "1.4.1"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"futures",
|
||||
@@ -4457,6 +4488,7 @@ dependencies = [
|
||||
"nym-ordered-buffer",
|
||||
"nym-service-providers-common",
|
||||
"nym-socks5-requests",
|
||||
"nym-validator-client",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"serde-wasm-bindgen 0.6.5",
|
||||
@@ -4650,6 +4682,12 @@ version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c"
|
||||
|
||||
[[package]]
|
||||
name = "no-std-net"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
@@ -4787,7 +4825,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-api"
|
||||
version = "1.1.67"
|
||||
version = "1.1.69"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -4950,6 +4988,7 @@ dependencies = [
|
||||
"nym-network-defaults",
|
||||
"nym-service-provider-requests-common",
|
||||
"nym-sphinx",
|
||||
"nym-test-utils",
|
||||
"nym-wireguard-types",
|
||||
"rand 0.8.5",
|
||||
"semver 1.0.26",
|
||||
@@ -4957,6 +4996,7 @@ dependencies = [
|
||||
"sha2 0.10.9",
|
||||
"strum_macros",
|
||||
"thiserror 2.0.12",
|
||||
"tracing",
|
||||
"x25519-dalek",
|
||||
]
|
||||
|
||||
@@ -4965,21 +5005,16 @@ name = "nym-bandwidth-controller"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bip39",
|
||||
"log",
|
||||
"nym-credential-storage",
|
||||
"nym-credentials",
|
||||
"nym-credentials-interface",
|
||||
"nym-crypto",
|
||||
"nym-ecash-contract-common",
|
||||
"nym-ecash-time",
|
||||
"nym-network-defaults",
|
||||
"nym-task",
|
||||
"nym-validator-client",
|
||||
"rand 0.8.5",
|
||||
"thiserror 2.0.12",
|
||||
"url",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5001,7 +5036,7 @@ dependencies = [
|
||||
"tracing-subscriber",
|
||||
"tracing-tree",
|
||||
"utoipa",
|
||||
"vergen",
|
||||
"vergen 8.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5013,7 +5048,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-cli"
|
||||
version = "1.1.64"
|
||||
version = "1.1.66"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
@@ -5096,7 +5131,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-client"
|
||||
version = "1.1.64"
|
||||
version = "1.1.66"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"clap",
|
||||
@@ -5169,6 +5204,7 @@ dependencies = [
|
||||
"nym-task",
|
||||
"nym-topology",
|
||||
"nym-validator-client",
|
||||
"once_cell",
|
||||
"rand 0.8.5",
|
||||
"rand_chacha 0.3.1",
|
||||
"serde",
|
||||
@@ -5181,6 +5217,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tokio-tungstenite",
|
||||
"tokio_with_wasm",
|
||||
"tracing",
|
||||
"tungstenite 0.20.1",
|
||||
"url",
|
||||
@@ -5243,27 +5280,48 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-client-rtt-tester"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"nym-client-core",
|
||||
"nym-config",
|
||||
"nym-crypto",
|
||||
"nym-network-defaults",
|
||||
"nym-sdk",
|
||||
"nym-sphinx",
|
||||
"nym-task",
|
||||
"nym-topology",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-client-wasm"
|
||||
version = "1.4.0-rc.0"
|
||||
version = "1.4.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures",
|
||||
"gloo-timers",
|
||||
"js-sys",
|
||||
"nym-bin-common",
|
||||
"nym-gateway-requests",
|
||||
"nym-node-tester-utils",
|
||||
"nym-node-tester-wasm",
|
||||
"once_cell",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"serde-wasm-bindgen 0.6.5",
|
||||
"serde_json",
|
||||
"thiserror 2.0.12",
|
||||
"tokio_with_wasm",
|
||||
"tsify",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"wasm-bindgen-test",
|
||||
"wasm-client-core",
|
||||
"wasm-utils",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5288,6 +5346,14 @@ dependencies = [
|
||||
"nym-multisig-contract-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-common"
|
||||
version = "1.18.0"
|
||||
dependencies = [
|
||||
"tracing",
|
||||
"tracing-test",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-compact-ecash"
|
||||
version = "0.1.0"
|
||||
@@ -5326,6 +5392,24 @@ dependencies = [
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-connection-monitor"
|
||||
version = "1.18.0"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"bytes",
|
||||
"futures",
|
||||
"nym-common",
|
||||
"nym-config",
|
||||
"nym-ip-packet-requests",
|
||||
"nym-sdk",
|
||||
"pnet_packet",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-contracts-common"
|
||||
version = "0.5.0"
|
||||
@@ -5340,7 +5424,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"thiserror 2.0.12",
|
||||
"utoipa",
|
||||
"vergen",
|
||||
"vergen 8.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5393,8 +5477,11 @@ dependencies = [
|
||||
"nym-crypto",
|
||||
"nym-ecash-contract-common",
|
||||
"nym-ecash-signer-check",
|
||||
"nym-http-api-client",
|
||||
"nym-http-api-common",
|
||||
"nym-network-defaults",
|
||||
"nym-pemstore",
|
||||
"nym-upgrade-mode-check",
|
||||
"nym-validator-client",
|
||||
"rand 0.8.5",
|
||||
"reqwest 0.12.22",
|
||||
@@ -5465,10 +5552,12 @@ dependencies = [
|
||||
"nym-http-api-client",
|
||||
"nym-http-api-common",
|
||||
"nym-serde-helpers",
|
||||
"nym-upgrade-mode-check",
|
||||
"reqwest 0.12.22",
|
||||
"schemars 0.8.22",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
"time",
|
||||
"tsify",
|
||||
"utoipa",
|
||||
@@ -5495,6 +5584,7 @@ dependencies = [
|
||||
"sqlx",
|
||||
"sqlx-pool-guard",
|
||||
"thiserror 2.0.12",
|
||||
"time",
|
||||
"tokio",
|
||||
"zeroize",
|
||||
]
|
||||
@@ -5530,12 +5620,13 @@ dependencies = [
|
||||
"nym-api-requests",
|
||||
"nym-credentials",
|
||||
"nym-credentials-interface",
|
||||
"nym-crypto",
|
||||
"nym-ecash-contract-common",
|
||||
"nym-gateway-requests",
|
||||
"nym-gateway-storage",
|
||||
"nym-task",
|
||||
"nym-upgrade-mode-check",
|
||||
"nym-validator-client",
|
||||
"rand 0.8.5",
|
||||
"si-scale",
|
||||
"thiserror 2.0.12",
|
||||
"time",
|
||||
@@ -5575,6 +5666,7 @@ dependencies = [
|
||||
"nym-compact-ecash",
|
||||
"nym-ecash-time",
|
||||
"nym-network-defaults",
|
||||
"nym-upgrade-mode-check",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"strum",
|
||||
@@ -5591,6 +5683,7 @@ dependencies = [
|
||||
"aead",
|
||||
"aes",
|
||||
"aes-gcm-siv",
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
"blake3",
|
||||
"bs58",
|
||||
@@ -5604,10 +5697,12 @@ dependencies = [
|
||||
"jwt-simple",
|
||||
"nym-pemstore",
|
||||
"nym-sphinx-types",
|
||||
"nym-test-utils",
|
||||
"rand 0.8.5",
|
||||
"rand_chacha 0.3.1",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
"serde_json",
|
||||
"sha2 0.10.9",
|
||||
"subtle-encoding",
|
||||
"thiserror 2.0.12",
|
||||
@@ -5625,7 +5720,6 @@ dependencies = [
|
||||
"criterion",
|
||||
"ff",
|
||||
"group",
|
||||
"lazy_static",
|
||||
"nym-contracts-common",
|
||||
"nym-pemstore",
|
||||
"rand 0.8.5",
|
||||
@@ -5723,7 +5817,6 @@ dependencies = [
|
||||
name = "nym-gateway"
|
||||
version = "1.1.36"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"bincode",
|
||||
"bip39",
|
||||
@@ -5734,7 +5827,6 @@ dependencies = [
|
||||
"futures",
|
||||
"ipnetwork",
|
||||
"mock_instant",
|
||||
"nym-api-requests",
|
||||
"nym-authenticator-requests",
|
||||
"nym-client-core",
|
||||
"nym-credential-verification",
|
||||
@@ -5747,7 +5839,6 @@ dependencies = [
|
||||
"nym-id",
|
||||
"nym-ip-packet-router",
|
||||
"nym-mixnet-client",
|
||||
"nym-mixnode-common",
|
||||
"nym-network-defaults",
|
||||
"nym-network-requester",
|
||||
"nym-node-metrics",
|
||||
@@ -5757,20 +5848,18 @@ dependencies = [
|
||||
"nym-statistics-common",
|
||||
"nym-task",
|
||||
"nym-topology",
|
||||
"nym-types",
|
||||
"nym-upgrade-mode-check",
|
||||
"nym-validator-client",
|
||||
"nym-wireguard",
|
||||
"nym-wireguard-private-metadata-server",
|
||||
"nym-wireguard-types",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"sha2 0.10.9",
|
||||
"thiserror 2.0.12",
|
||||
"time",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tokio-tungstenite",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
"url",
|
||||
"zeroize",
|
||||
@@ -5814,6 +5903,51 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-gateway-probe"
|
||||
version = "1.18.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
"bincode",
|
||||
"bs58",
|
||||
"bytes",
|
||||
"clap",
|
||||
"futures",
|
||||
"hex",
|
||||
"nym-authenticator-client",
|
||||
"nym-authenticator-requests",
|
||||
"nym-bandwidth-controller",
|
||||
"nym-bin-common",
|
||||
"nym-client-core",
|
||||
"nym-config",
|
||||
"nym-connection-monitor",
|
||||
"nym-credential-utils",
|
||||
"nym-credentials",
|
||||
"nym-credentials-interface",
|
||||
"nym-crypto",
|
||||
"nym-http-api-client",
|
||||
"nym-http-api-client-macro",
|
||||
"nym-ip-packet-client",
|
||||
"nym-ip-packet-requests",
|
||||
"nym-node-status-client",
|
||||
"nym-sdk",
|
||||
"nym-topology",
|
||||
"nym-validator-client",
|
||||
"pnet_packet",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"url",
|
||||
"vergen-gitcl",
|
||||
"x25519-dalek",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-gateway-requests"
|
||||
version = "0.1.0"
|
||||
@@ -5936,6 +6070,7 @@ dependencies = [
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"url",
|
||||
"wasmtimer",
|
||||
]
|
||||
@@ -6240,7 +6375,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-requester"
|
||||
version = "1.1.65"
|
||||
version = "1.1.67"
|
||||
dependencies = [
|
||||
"addr",
|
||||
"anyhow",
|
||||
@@ -6290,7 +6425,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-node"
|
||||
version = "1.19.0"
|
||||
version = "1.21.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
@@ -6321,6 +6456,7 @@ dependencies = [
|
||||
"nym-bin-common",
|
||||
"nym-client-core-config-types",
|
||||
"nym-config",
|
||||
"nym-credential-verification",
|
||||
"nym-crypto",
|
||||
"nym-gateway",
|
||||
"nym-gateway-stats-storage",
|
||||
@@ -6393,13 +6529,13 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"celes",
|
||||
"humantime",
|
||||
"humantime-serde",
|
||||
"nym-bin-common",
|
||||
"nym-crypto",
|
||||
"nym-exit-policy",
|
||||
"nym-http-api-client",
|
||||
"nym-noise-keys",
|
||||
"nym-upgrade-mode-check",
|
||||
"nym-wireguard-types",
|
||||
"rand_chacha 0.3.1",
|
||||
"schemars 0.8.22",
|
||||
@@ -6410,6 +6546,7 @@ dependencies = [
|
||||
"thiserror 2.0.12",
|
||||
"time",
|
||||
"tokio",
|
||||
"url",
|
||||
"utoipa",
|
||||
]
|
||||
|
||||
@@ -6432,7 +6569,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-node-status-api"
|
||||
version = "4.0.5"
|
||||
version = "4.0.11-rc1"
|
||||
dependencies = [
|
||||
"ammonia",
|
||||
"anyhow",
|
||||
@@ -6528,7 +6665,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-node-tester-wasm"
|
||||
version = "1.3.0-rc.0"
|
||||
version = "1.3.1"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"js-sys",
|
||||
@@ -6671,6 +6808,7 @@ dependencies = [
|
||||
name = "nym-registration-client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"nym-authenticator-client",
|
||||
"nym-bandwidth-controller",
|
||||
"nym-credential-storage",
|
||||
@@ -6683,6 +6821,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
"typed-builder",
|
||||
"url",
|
||||
]
|
||||
|
||||
@@ -6815,7 +6954,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.64"
|
||||
version = "1.1.66"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"clap",
|
||||
@@ -6998,6 +7137,7 @@ dependencies = [
|
||||
"serde",
|
||||
"thiserror 2.0.12",
|
||||
"utoipa",
|
||||
"wasmtimer",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7261,12 +7401,14 @@ dependencies = [
|
||||
"jwt-simple",
|
||||
"nym-crypto",
|
||||
"nym-http-api-client",
|
||||
"nym-test-utils",
|
||||
"reqwest 0.12.22",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.12",
|
||||
"time",
|
||||
"tracing",
|
||||
"utoipa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7446,17 +7588,10 @@ dependencies = [
|
||||
name = "nym-wireguard"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.22.1",
|
||||
"bincode",
|
||||
"chrono",
|
||||
"dashmap",
|
||||
"defguard_wireguard_rs",
|
||||
"dyn-clone",
|
||||
"futures",
|
||||
"ip_network",
|
||||
"log",
|
||||
"nym-authenticator-requests",
|
||||
"nym-credential-verification",
|
||||
"nym-credentials-interface",
|
||||
"nym-crypto",
|
||||
@@ -7467,11 +7602,9 @@ dependencies = [
|
||||
"nym-task",
|
||||
"nym-wireguard-types",
|
||||
"thiserror 2.0.12",
|
||||
"time",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tracing",
|
||||
"x25519-dalek",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7523,15 +7656,20 @@ version = "1.0.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum",
|
||||
"futures",
|
||||
"nym-credential-verification",
|
||||
"nym-credentials-interface",
|
||||
"nym-crypto",
|
||||
"nym-http-api-client",
|
||||
"nym-http-api-common",
|
||||
"nym-upgrade-mode-check",
|
||||
"nym-wireguard",
|
||||
"nym-wireguard-private-metadata-client",
|
||||
"nym-wireguard-private-metadata-server",
|
||||
"nym-wireguard-private-metadata-shared",
|
||||
"time",
|
||||
"tokio",
|
||||
"tower 0.5.2",
|
||||
"tower-http 0.5.2",
|
||||
"utoipa",
|
||||
]
|
||||
@@ -7541,10 +7679,7 @@ name = "nym-wireguard-types"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"log",
|
||||
"nym-config",
|
||||
"nym-crypto",
|
||||
"nym-network-defaults",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"thiserror 2.0.12",
|
||||
@@ -7553,7 +7688,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nymvisor"
|
||||
version = "0.1.29"
|
||||
version = "0.1.31"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -7707,9 +7842,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.109"
|
||||
version = "0.9.110"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571"
|
||||
checksum = "0a9f0075ba3c21b09f8e8b2026584b1d18d49388648f2fbbf3c97ea8deced8e2"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -8159,6 +8294,48 @@ dependencies = [
|
||||
"plotters-backend",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pnet_base"
|
||||
version = "0.35.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffc190d4067df16af3aba49b3b74c469e611cad6314676eaf1157f31aa0fb2f7"
|
||||
dependencies = [
|
||||
"no-std-net",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pnet_macros"
|
||||
version = "0.35.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13325ac86ee1a80a480b0bc8e3d30c25d133616112bb16e86f712dcf8a71c863"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pnet_macros_support"
|
||||
version = "0.35.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eed67a952585d509dd0003049b1fc56b982ac665c8299b124b90ea2bdb3134ab"
|
||||
dependencies = [
|
||||
"pnet_base",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pnet_packet"
|
||||
version = "0.35.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c96ebadfab635fcc23036ba30a7d33a80c39e8461b8bd7dc7bb186acb96560f"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"pnet_base",
|
||||
"pnet_macros",
|
||||
"pnet_macros_support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "polling"
|
||||
version = "2.8.0"
|
||||
@@ -8614,13 +8791,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.4.6"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
|
||||
checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
|
||||
dependencies = [
|
||||
"getrandom 0.2.16",
|
||||
"libredox",
|
||||
"thiserror 1.0.69",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10753,6 +10930,30 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio_with_wasm"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4dfba9b946459940fb564dcf576631074cdfb0bfe4c962acd4c31f0dca7897e6"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"tokio",
|
||||
"tokio_with_wasm_proc",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio_with_wasm_proc"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37e04c1865c281139e5ccf633cb9f76ffdaabeebfe53b703984cf82878e2aabb"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.11"
|
||||
@@ -11035,6 +11236,27 @@ dependencies = [
|
||||
"tracing-log 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-test"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "557b891436fe0d5e0e363427fc7f217abf9ccd510d5136549847bdcbcd011d68"
|
||||
dependencies = [
|
||||
"tracing-core",
|
||||
"tracing-subscriber",
|
||||
"tracing-test-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-test-macro"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04659ddb06c87d233c566112c1c9c5b9e98256d9af50ec3bc9c8327f873a7568"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-tree"
|
||||
version = "0.2.5"
|
||||
@@ -11188,6 +11410,26 @@ dependencies = [
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typed-builder"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d0dd654273fc253fde1df4172c31fb6615cf8b041d3a4008a028ef8b1119e66"
|
||||
dependencies = [
|
||||
"typed-builder-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typed-builder-macro"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "016c26257f448222014296978b2c8456e2cad4de308c35bdb1e383acd569ef5b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.18.0"
|
||||
@@ -11587,6 +11829,47 @@ dependencies = [
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vergen"
|
||||
version = "9.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b2bf58be11fc9414104c6d3a2e464163db5ef74b12296bda593cac37b6e4777"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cargo_metadata 0.19.2",
|
||||
"derive_builder",
|
||||
"regex",
|
||||
"rustc_version 0.4.1",
|
||||
"rustversion",
|
||||
"time",
|
||||
"vergen-lib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vergen-gitcl"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9dfc1de6eb2e08a4ddf152f1b179529638bedc0ea95e6d667c014506377aefe"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"derive_builder",
|
||||
"rustversion",
|
||||
"time",
|
||||
"vergen 9.0.6",
|
||||
"vergen-lib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vergen-lib"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b07e6010c0f3e59fcb164e0163834597da68d1f864e2b8ca49f74de01e9c166"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"derive_builder",
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
|
||||
+28
-22
@@ -18,6 +18,7 @@ resolver = "2"
|
||||
members = [
|
||||
"clients/native",
|
||||
"clients/native/websocket-requests",
|
||||
"clients/rtt-tester",
|
||||
"clients/socks5",
|
||||
"common/async-file-watcher",
|
||||
"common/authenticator-requests",
|
||||
@@ -31,6 +32,7 @@ members = [
|
||||
"common/client-libs/mixnet-client",
|
||||
"common/client-libs/validator-client",
|
||||
"common/commands",
|
||||
"common/nym-common",
|
||||
"common/config",
|
||||
"common/cosmwasm-smart-contracts/coconut-dkg",
|
||||
"common/cosmwasm-smart-contracts/contracts-common",
|
||||
@@ -58,7 +60,8 @@ members = [
|
||||
"common/gateway-requests",
|
||||
"common/gateway-stats-storage",
|
||||
"common/gateway-storage",
|
||||
"common/http-api-client", "common/http-api-client-macro",
|
||||
"common/http-api-client",
|
||||
"common/http-api-client-macro",
|
||||
"common/http-api-common",
|
||||
"common/inclusion-probability",
|
||||
"common/ip-packet-requests",
|
||||
@@ -66,7 +69,9 @@ members = [
|
||||
"common/mixnode-common",
|
||||
"common/network-defaults",
|
||||
"common/node-tester-utils",
|
||||
"common/nonexhaustive-delayqueue", "common/nym-cache",
|
||||
"common/nonexhaustive-delayqueue",
|
||||
"common/nym-cache",
|
||||
"common/nym-connection-monitor",
|
||||
"common/nym-id",
|
||||
"common/nym-metrics",
|
||||
"common/nym_offline_compact_ecash",
|
||||
@@ -98,7 +103,8 @@ members = [
|
||||
"common/ticketbooks-merkle",
|
||||
"common/topology",
|
||||
"common/tun",
|
||||
"common/types", "common/upgrade-mode-check",
|
||||
"common/types",
|
||||
"common/upgrade-mode-check",
|
||||
"common/verloc",
|
||||
"common/wasm/client-core",
|
||||
"common/wasm/storage",
|
||||
@@ -127,7 +133,7 @@ members = [
|
||||
"nym-node-status-api/nym-node-status-client",
|
||||
"nym-node/nym-node-metrics",
|
||||
"nym-node/nym-node-requests",
|
||||
"nym-outfox",
|
||||
"nym-outfox",
|
||||
"nym-registration-client",
|
||||
"nym-signers-monitor",
|
||||
"nym-statistics-api",
|
||||
@@ -145,7 +151,7 @@ members = [
|
||||
"tools/internal/contract-state-importer/importer-cli",
|
||||
"tools/internal/contract-state-importer/importer-contract",
|
||||
"tools/internal/mixnet-connectivity-check",
|
||||
# "tools/internal/sdk-version-bump",
|
||||
# "tools/internal/sdk-version-bump",
|
||||
"tools/internal/ssl-inject",
|
||||
"tools/internal/testnet-manager",
|
||||
"tools/internal/testnet-manager/dkg-bypass-contract",
|
||||
@@ -160,11 +166,13 @@ members = [
|
||||
"wasm/mix-fetch",
|
||||
"wasm/node-tester",
|
||||
"wasm/zknym-lib",
|
||||
"nym-gateway-probe"
|
||||
]
|
||||
|
||||
default-members = [
|
||||
"clients/native",
|
||||
"clients/socks5",
|
||||
"nym-authenticator-client",
|
||||
"nym-api",
|
||||
"nym-credential-proxy/nym-credential-proxy",
|
||||
"nym-node",
|
||||
@@ -178,16 +186,16 @@ default-members = [
|
||||
"tools/nymvisor",
|
||||
]
|
||||
|
||||
exclude = ["explorer", "contracts", "nym-wallet", "cpu-cycles"]
|
||||
exclude = ["contracts", "nym-wallet", "cpu-cycles"]
|
||||
|
||||
[workspace.package]
|
||||
authors = ["Nym Technologies SA"]
|
||||
repository = "https://github.com/nymtech/nym"
|
||||
homepage = "https://nymtech.net"
|
||||
documentation = "https://nymtech.net"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
license = "Apache-2.0"
|
||||
rust-version = "1.81"
|
||||
rust-version = "1.85"
|
||||
readme = "README.md"
|
||||
|
||||
[workspace.dependencies]
|
||||
@@ -209,7 +217,6 @@ base64 = "0.22.1"
|
||||
base85rs = "0.1.3"
|
||||
bincode = "1.3.3"
|
||||
bip39 = { version = "2.0.0", features = ["zeroize"] }
|
||||
bit-vec = "0.7.0" # can we unify those?
|
||||
bitvec = "1.0.0"
|
||||
blake3 = "1.7.0"
|
||||
bloomfilter = "3.0.1"
|
||||
@@ -237,13 +244,11 @@ criterion = "0.5"
|
||||
csv = "1.3.1"
|
||||
ctr = "0.9.1"
|
||||
cupid = "0.6.1"
|
||||
curve25519-dalek = "4.1"
|
||||
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
|
||||
defguard_wireguard_rs = { git = "https://github.com/DefGuard/wireguard-rs.git", rev = "v0.4.7" }
|
||||
digest = "0.10.7"
|
||||
dirs = "5.0"
|
||||
doc-comment = "0.3"
|
||||
dirs = "6.0"
|
||||
dotenvy = "0.15.6"
|
||||
dyn-clone = "1.0.19"
|
||||
ecdsa = "0.16"
|
||||
@@ -259,11 +264,8 @@ futures = "0.3.31"
|
||||
futures-util = "0.3"
|
||||
generic-array = "0.14.7"
|
||||
getrandom = "0.2.10"
|
||||
getset = "0.1.5"
|
||||
handlebars = "3.5.5"
|
||||
headers = "0.4.0"
|
||||
hex = "0.4.3"
|
||||
hex-literal = "0.3.3"
|
||||
hickory-resolver = "0.25"
|
||||
hkdf = "0.12.3"
|
||||
hmac = "0.12.1"
|
||||
@@ -287,12 +289,10 @@ lazy_static = "1.5.0"
|
||||
ledger-transport = "0.10.0"
|
||||
ledger-transport-hid = "0.10.0"
|
||||
log = "0.4"
|
||||
maxminddb = "0.23.0"
|
||||
mime = "0.3.17"
|
||||
moka = { version = "0.12", features = ["future"] }
|
||||
nix = "0.27.1"
|
||||
notify = "5.1.0"
|
||||
okapi = "0.7.0"
|
||||
once_cell = "1.21.3"
|
||||
opentelemetry = "0.19.0"
|
||||
opentelemetry-jaeger = "0.18.0"
|
||||
@@ -300,7 +300,7 @@ parking_lot = "0.12.3"
|
||||
pem = "0.8"
|
||||
petgraph = "0.6.5"
|
||||
pin-project = "1.1"
|
||||
pin-project-lite = "0.2.16"
|
||||
pnet_packet = "0.35.0"
|
||||
publicsuffix = "2.3.0"
|
||||
proc_pidinfo = "0.1.3"
|
||||
quote = "1"
|
||||
@@ -308,13 +308,10 @@ rand = "0.8.5"
|
||||
rand_chacha = "0.3"
|
||||
rand_core = "0.6.3"
|
||||
rand_distr = "0.4"
|
||||
rand_pcg = "0.3.1"
|
||||
rand_seeder = "0.2.3"
|
||||
rayon = "1.5.1"
|
||||
regex = "1.10.6"
|
||||
reqwest = { version = "0.12.15", default-features = false }
|
||||
rs_merkle = "1.5.0"
|
||||
safer-ffi = "0.1.13"
|
||||
schemars = "0.8.22"
|
||||
semver = "1.0.26"
|
||||
serde = "1.0.219"
|
||||
@@ -358,8 +355,10 @@ tracing-opentelemetry = "0.19.0"
|
||||
tracing-subscriber = "0.3.19"
|
||||
tracing-tree = "0.2.2"
|
||||
tracing-indicatif = "0.3.9"
|
||||
tracing-test = "0.2.5"
|
||||
ts-rs = "10.1.0"
|
||||
tungstenite = { version = "0.20.1", default-features = false }
|
||||
typed-builder = "0.23.0"
|
||||
uniffi = "0.29.2"
|
||||
uniffi_build = "0.29.0"
|
||||
url = "2.5"
|
||||
@@ -368,6 +367,7 @@ utoipa-swagger-ui = "8.1"
|
||||
utoipauto = "0.2"
|
||||
uuid = "*"
|
||||
vergen = { version = "=8.3.1", default-features = false }
|
||||
vergen-gitcl = { version = "1.0.8", default-features = false }
|
||||
walkdir = "2"
|
||||
x25519-dalek = "2.0.0"
|
||||
zeroize = "1.7.0"
|
||||
@@ -407,18 +407,19 @@ prost = { version = "0.13", default-features = false }
|
||||
# wasm-related dependencies
|
||||
gloo-utils = "0.2.0"
|
||||
gloo-net = "0.6.0"
|
||||
gloo-timers = "0.3.0"
|
||||
|
||||
indexed_db_futures = "0.6.4"
|
||||
js-sys = "0.3.76"
|
||||
serde-wasm-bindgen = "0.6.5"
|
||||
tsify = "0.4.5"
|
||||
tokio_with_wasm = { version = "0.8.7" }
|
||||
wasm-bindgen = "0.2.99"
|
||||
wasm-bindgen-futures = "0.4.49"
|
||||
wasm-bindgen-test = "0.3.49"
|
||||
wasmtimer = "0.4.1"
|
||||
web-sys = "0.3.76"
|
||||
|
||||
|
||||
# for local development:
|
||||
#[patch.crates-io]
|
||||
#sphinx-packet = { path = "../sphinx" }
|
||||
@@ -454,6 +455,11 @@ opt-level = 'z'
|
||||
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(tokio_unstable)'] }
|
||||
|
||||
[workspace.lints.clippy]
|
||||
suspicious = "deny"
|
||||
complexity = "deny"
|
||||
perf = "deny"
|
||||
style = "deny"
|
||||
|
||||
unwrap_used = "deny"
|
||||
expect_used = "deny"
|
||||
todo = "deny"
|
||||
|
||||
@@ -109,7 +109,7 @@ sdk-wasm-build:
|
||||
$(MAKE) -C wasm/node-tester
|
||||
$(MAKE) -C wasm/mix-fetch
|
||||
$(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
|
||||
sdk-typescript-build:
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.1.64"
|
||||
version = "1.1.66"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Client"
|
||||
edition = "2021"
|
||||
rust-version = "1.70"
|
||||
rust-version = "1.85"
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -60,6 +60,7 @@ impl SocketClient {
|
||||
let ClientInput {
|
||||
connection_command_sender,
|
||||
input_sender,
|
||||
..
|
||||
} = client_input;
|
||||
|
||||
let ClientOutput {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "nym-client-rtt-tester"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = "RTT testing client built using nym-client-core"
|
||||
license.workspace = true
|
||||
|
||||
[[bin]]
|
||||
name = "nym-client-rtt-tester"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
tokio = { workspace = true, features = ["full"] }
|
||||
tracing = { workspace = true }
|
||||
nym-client-core = { path = "../../common/client-core" }
|
||||
nym-network-defaults = { path = "../../common/network-defaults" }
|
||||
nym-sphinx = { path = "../../common/nymsphinx" }
|
||||
nym-topology = { path = "../../common/topology" }
|
||||
nym-config = { path = "../../common/config" }
|
||||
nym-task = { path = "../../common/task" }
|
||||
nym-crypto = { path = "../../common/crypto" }
|
||||
nym-sdk = { path = "../../sdk/rust/nym-sdk" }
|
||||
@@ -0,0 +1,466 @@
|
||||
use nym_sdk::mixnet;
|
||||
use nym_sdk::mixnet::MixnetMessageSender;
|
||||
|
||||
use nym_client_core::client::rtt_analyzer::{RttAnalyzer, RttConfig, RttEvent, RttPattern};
|
||||
use nym_sdk::DebugConfig;
|
||||
use tokio::io::{self, AsyncBufReadExt};
|
||||
use tokio::time::{sleep, Duration};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
// ============================================================
|
||||
// 1. Start RTT Analyzer + background worker
|
||||
// ============================================================
|
||||
let _analyzer = RttAnalyzer::new();
|
||||
|
||||
let tx = RttAnalyzer::producer().expect("Analyzer was not initialized!");
|
||||
|
||||
// ============================================================
|
||||
// 2. Build mixnet client
|
||||
// ============================================================
|
||||
let mut debug = DebugConfig::default();
|
||||
|
||||
// Disable ALL Poisson & cover streams
|
||||
debug.traffic.disable_main_poisson_packet_distribution = true;
|
||||
debug.cover_traffic.disable_loop_cover_traffic_stream = false;
|
||||
|
||||
let client = mixnet::MixnetClientBuilder::new_ephemeral()
|
||||
.debug_config(debug)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let client = client.connect_to_mixnet().await.unwrap();
|
||||
|
||||
let our_address = client.nym_address();
|
||||
println!("Our client nym address is: {our_address}");
|
||||
|
||||
// ============================================================
|
||||
// 3. Ask the user for RTT TEST configuration
|
||||
// ============================================================
|
||||
let config = ask_user_for_rtt_config().await;
|
||||
|
||||
println!("\nStarting RTT test with:");
|
||||
println!(" packets_per_route = {}", config.packets_per_route);
|
||||
println!(" pattern = {:?}", config.pattern);
|
||||
println!(" delay (ms) = {}", config.inter_route_delay_ms);
|
||||
|
||||
// START THE TEST
|
||||
let _ = client
|
||||
.send_rtt_test(*our_address, None, tx.clone(), config)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// ============================================================
|
||||
// 4. Background listener for incoming messages
|
||||
// ============================================================
|
||||
tokio::spawn({
|
||||
let mut client = client;
|
||||
async move {
|
||||
loop {
|
||||
if client.wait_for_messages().await.is_some() {
|
||||
//I should do something here to shutdown
|
||||
}
|
||||
sleep(Duration::from_millis(10)).await;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// 5. Main input loop
|
||||
// ============================================================
|
||||
let stdin = io::BufReader::new(io::stdin());
|
||||
let mut lines = stdin.lines();
|
||||
|
||||
println!("Type 'menu' to show RTT commands.");
|
||||
|
||||
loop {
|
||||
if let Ok(Some(input)) = lines.next_line().await {
|
||||
let input = input.trim().to_lowercase();
|
||||
|
||||
if input == "menu" {
|
||||
show_menu_and_handle_choice(&tx).await;
|
||||
}
|
||||
}
|
||||
|
||||
sleep(Duration::from_millis(50)).await;
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// ASK USER FOR RTT TEST SETTINGS AT PROGRAM START
|
||||
// =====================================================================
|
||||
async fn ask_user_for_rtt_config() -> RttConfig {
|
||||
let stdin = io::BufReader::new(io::stdin());
|
||||
let mut lines = stdin.lines();
|
||||
|
||||
println!("\n========== RTT TEST CONFIGURATION ==========");
|
||||
|
||||
// -----------------------------
|
||||
// Ask for packets per route
|
||||
// -----------------------------
|
||||
println!("Enter number of packets per route: ");
|
||||
let packets = read_u32_from_stdin(&mut lines).await;
|
||||
|
||||
// -----------------------------
|
||||
// Ask for pattern: Burst / RR
|
||||
// -----------------------------
|
||||
println!("Choose pattern:");
|
||||
println!(" 1) Burst");
|
||||
println!(" 2) Round Robin");
|
||||
let pattern = loop {
|
||||
let input = read_string(&mut lines).await;
|
||||
|
||||
match input.as_str() {
|
||||
"1" => break RttPattern::Burst,
|
||||
"2" => break RttPattern::RoundRobin,
|
||||
_ => println!("Invalid choice! Please type 1 or 2:"),
|
||||
}
|
||||
};
|
||||
|
||||
// -----------------------------
|
||||
// Ask for delay between packets
|
||||
// -----------------------------
|
||||
println!("Enter delay between packets (ms): ");
|
||||
let delay = read_u64_from_stdin(&mut lines).await;
|
||||
|
||||
// Build Config
|
||||
RttConfig {
|
||||
packets_per_route: packets,
|
||||
pattern,
|
||||
inter_route_delay_ms: delay,
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// Util functions for reading typed input
|
||||
// =====================================================================
|
||||
async fn read_string(lines: &mut tokio::io::Lines<io::BufReader<io::Stdin>>) -> String {
|
||||
loop {
|
||||
if let Ok(Some(line)) = lines.next_line().await {
|
||||
let trimmed = line.trim().to_string();
|
||||
if !trimmed.is_empty() {
|
||||
return trimmed;
|
||||
}
|
||||
}
|
||||
println!("Please type a value:");
|
||||
}
|
||||
}
|
||||
|
||||
async fn read_u32_from_stdin(lines: &mut tokio::io::Lines<io::BufReader<io::Stdin>>) -> u32 {
|
||||
loop {
|
||||
if let Ok(Some(line)) = lines.next_line().await {
|
||||
if let Ok(num) = line.trim().parse::<u32>() {
|
||||
return num;
|
||||
}
|
||||
}
|
||||
println!("Invalid number, try again:");
|
||||
}
|
||||
}
|
||||
|
||||
async fn read_u64_from_stdin(lines: &mut tokio::io::Lines<io::BufReader<io::Stdin>>) -> u64 {
|
||||
loop {
|
||||
if let Ok(Some(line)) = lines.next_line().await {
|
||||
if let Ok(num) = line.trim().parse::<u64>() {
|
||||
return num;
|
||||
}
|
||||
}
|
||||
println!("Invalid number, try again:");
|
||||
}
|
||||
}
|
||||
// =====================================================================
|
||||
// MENU HANDLER (FULL VERSION WITH HELP / DOCS)
|
||||
// =====================================================================
|
||||
async fn show_menu_and_handle_choice(tx: &tokio::sync::mpsc::Sender<RttEvent>) {
|
||||
println!("\n======================== RTT MENU ========================");
|
||||
println!("1) Print global RTT statistics");
|
||||
println!("2) Write statistics to CSV file");
|
||||
println!("3) Print route details by ROUTE INDEX");
|
||||
println!("4) Print route details by ROUTE NODES STRING");
|
||||
println!("5) Print routes with AVG RTT above threshold");
|
||||
println!("6) Print routes with ANY RTT above threshold");
|
||||
println!("7) Help (Show all commands & how to use them)");
|
||||
println!("8) Write CSV and generate RTT histogram(s) with Python");
|
||||
println!("9) Show overall experiment completion percentage");
|
||||
println!("===========================================================");
|
||||
print!("Select option: ");
|
||||
|
||||
use std::io::Write;
|
||||
std::io::stdout().flush().unwrap();
|
||||
|
||||
let mut input = String::new();
|
||||
let _ = std::io::stdin().read_line(&mut input);
|
||||
let choice = input.trim();
|
||||
|
||||
match choice {
|
||||
// -------------------- 1. PRINT GLOBAL STATS --------------------
|
||||
"1" => {
|
||||
let _ = tx.send(RttEvent::PrintStats).await;
|
||||
}
|
||||
|
||||
// -------------------- 2. WRITE STATS ---------------------------
|
||||
"2" => {
|
||||
print!("Enter file path: ");
|
||||
std::io::stdout().flush().unwrap();
|
||||
|
||||
let mut path = String::new();
|
||||
let _ = std::io::stdin().read_line(&mut path);
|
||||
|
||||
let path = path.trim().to_string();
|
||||
let _ = tx.send(RttEvent::WriteStats { path }).await;
|
||||
}
|
||||
|
||||
// -------------------- 3. PRINT ROUTE DETAILS -------------------
|
||||
"3" => {
|
||||
print!("Enter route index (0-based): ");
|
||||
std::io::stdout().flush().unwrap();
|
||||
|
||||
let mut s = String::new();
|
||||
let _ = std::io::stdin().read_line(&mut s);
|
||||
|
||||
if let Ok(index) = s.trim().parse::<usize>() {
|
||||
let _ = tx
|
||||
.send(RttEvent::PrintRouteDetail { route_index: index })
|
||||
.await;
|
||||
} else {
|
||||
println!("Invalid index.");
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------- 4. PRINT STATS BY NODE STRING -----------
|
||||
"4" => {
|
||||
println!("Enter Node String EXACTLY as stored.");
|
||||
println!("Example format:");
|
||||
println!(" <base58_node1> > <base58_node2> > <base58_node3>");
|
||||
print!("Nodes: ");
|
||||
std::io::stdout().flush().unwrap();
|
||||
|
||||
let mut nodes = String::new();
|
||||
let _ = std::io::stdin().read_line(&mut nodes);
|
||||
|
||||
let nodes = nodes.trim().to_string();
|
||||
let _ = tx.send(RttEvent::PrintRouteStatsByNodes { nodes }).await;
|
||||
}
|
||||
|
||||
// -------------------- 5. AVG ABOVE THRESHOLD ------------------
|
||||
"5" => {
|
||||
print!("Enter threshold in ms: ");
|
||||
std::io::stdout().flush().unwrap();
|
||||
|
||||
let mut s = String::new();
|
||||
let _ = std::io::stdin().read_line(&mut s);
|
||||
|
||||
if let Ok(th) = s.trim().parse::<u128>() {
|
||||
let _ = tx
|
||||
.send(RttEvent::PrintRoutesWithAvgAbove { threshold_ms: th })
|
||||
.await;
|
||||
} else {
|
||||
println!("Invalid number.");
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------- 6. ANY ABOVE THRESHOLD ------------------
|
||||
"6" => {
|
||||
print!("Enter threshold in ms: ");
|
||||
std::io::stdout().flush().unwrap();
|
||||
|
||||
let mut s = String::new();
|
||||
let _ = std::io::stdin().read_line(&mut s);
|
||||
|
||||
if let Ok(th) = s.trim().parse::<u128>() {
|
||||
let _ = tx
|
||||
.send(RttEvent::PrintRoutesWithAnyAbove { threshold_ms: th })
|
||||
.await;
|
||||
} else {
|
||||
println!("Invalid number.");
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------- 7. HELP --------------------------------
|
||||
"7" => {
|
||||
print_help();
|
||||
}
|
||||
"8" => {
|
||||
// Ask for CSV path
|
||||
print!("Enter CSV output path (e.g. rtt_stats.csv): ");
|
||||
std::io::stdout().flush().unwrap();
|
||||
|
||||
let mut path = String::new();
|
||||
let _ = std::io::stdin().read_line(&mut path);
|
||||
let path = path.trim().to_string();
|
||||
|
||||
// Sub-menu for histogram mode
|
||||
println!("\nHistogram mode:");
|
||||
println!(" 1) One plot with ALL RTT samples (including outliers)");
|
||||
println!(" 2) One plot with INLIERS only (RTT <= cutoff)");
|
||||
println!(" 3) TWO plots: one for INLIERS and one for OUTLIERS");
|
||||
print!("Select mode: ");
|
||||
std::io::stdout().flush().unwrap();
|
||||
|
||||
let mut mode_input = String::new();
|
||||
let _ = std::io::stdin().read_line(&mut mode_input);
|
||||
let mode_choice = mode_input.trim();
|
||||
|
||||
let outlier_mode = match mode_choice {
|
||||
// 1) All RTTs
|
||||
"1" => "all".to_string(),
|
||||
|
||||
// 2) Only inliers, ask for cutoff in seconds
|
||||
"2" => {
|
||||
print!("Enter cutoff in seconds (e.g. 1.0 for 1 second): ");
|
||||
std::io::stdout().flush().unwrap();
|
||||
|
||||
let mut c = String::new();
|
||||
let _ = std::io::stdin().read_line(&mut c);
|
||||
let cutoff = c.trim();
|
||||
cutoff.to_string() // e.g. "1.0"
|
||||
}
|
||||
|
||||
// 3) Two plots: inliers + outliers (both)
|
||||
"3" => {
|
||||
print!("Enter cutoff in seconds (e.g. 1.0 for 1 second): ");
|
||||
std::io::stdout().flush().unwrap();
|
||||
|
||||
let mut c = String::new();
|
||||
let _ = std::io::stdin().read_line(&mut c);
|
||||
let cutoff = c.trim();
|
||||
// Encode as 'both:<cutoff>' so Python can understand it
|
||||
format!("both:{cutoff}")
|
||||
}
|
||||
|
||||
_ => {
|
||||
println!("Invalid mode, aborting histogram generation.");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let _ = tx
|
||||
.send(RttEvent::WriteStatsAndPlot { path, outlier_mode })
|
||||
.await;
|
||||
}
|
||||
|
||||
"9" => {
|
||||
// Send an event to the RTT analyzer to compute and print progress
|
||||
let _ = tx.send(RttEvent::PrintExperimentProgress).await;
|
||||
}
|
||||
|
||||
_ => println!("Invalid selection."),
|
||||
}
|
||||
}
|
||||
|
||||
fn print_help() {
|
||||
println!("\n======================== RTT HELP ========================\n");
|
||||
|
||||
println!("This tool allows you to perform detailed RTT analysis over all mixnet routes.");
|
||||
println!("The client sends RTT probe traffic through every candidate route,");
|
||||
println!("and the RTT analyzer collects per-route statistics in the background.\n");
|
||||
|
||||
println!("Main commands (from the RTT menu):\n");
|
||||
|
||||
println!(" 1) Print global RTT statistics");
|
||||
println!(" Prints one summary line per route:");
|
||||
println!(" - route index");
|
||||
println!(" - packets sent (including retransmissions)");
|
||||
println!(" - number of ACKs");
|
||||
println!(" - number of timeouts");
|
||||
println!(" - average RTT (computed over all stored RTT samples, in ms)");
|
||||
println!();
|
||||
|
||||
println!(" 2) Write stats to CSV file");
|
||||
println!(" Writes one line per route to a CSV file on disk.");
|
||||
println!(" Current CSV columns:");
|
||||
println!(" route,sent,acks,timeouts,avg_rtt");
|
||||
println!(" route : numeric route index");
|
||||
println!(" sent : how many FragmentSent events were recorded");
|
||||
println!(" acks : how many FragmentAckReceived events were recorded");
|
||||
println!(" timeouts : how many FragmentAckExpired events were recorded");
|
||||
println!(" avg_rtt : average RTT (in milliseconds) from all RTT samples");
|
||||
println!();
|
||||
|
||||
println!(" 3) Print route details BY ROUTE INDEX");
|
||||
println!(" Input: a 0-based route index.");
|
||||
println!(" Output for that route:");
|
||||
println!(" - node list (base58 identities) in order: Node1 > Node2 > Node3");
|
||||
println!(" - ALL RTT samples recorded for that route (each sample shown in ms)");
|
||||
println!(" This is useful when you already know the route index and");
|
||||
println!(" want to inspect exactly how it behaves packet by packet.");
|
||||
println!();
|
||||
|
||||
println!(" 4) Print route details BY NODE STRING");
|
||||
println!(" Input format must match exactly what the analyzer stored, for example:");
|
||||
println!(" <node1_base58> > <node2_base58> > <node3_base58>");
|
||||
println!(" If a route with that node sequence exists, the tool will:");
|
||||
println!(" - print the matching route index");
|
||||
println!(" - print the full per-route detail (same as option 3).");
|
||||
println!(" This is useful when you have a specific mixnode combination");
|
||||
println!(" (e.g. a slow or suspicious path) and want its statistics.");
|
||||
println!();
|
||||
|
||||
println!(" 5) Print routes with AVERAGE RTT ABOVE a threshold");
|
||||
println!(" You provide a threshold in milliseconds (e.g. 150).");
|
||||
println!(" The tool will:");
|
||||
println!(" - compute avg RTT for each route");
|
||||
println!(" - select only routes where avg RTT > threshold");
|
||||
println!(" - print detailed info for each matching route (nodes + RTT samples).");
|
||||
println!(" Use this to quickly find generally slow routes.");
|
||||
println!();
|
||||
|
||||
println!(" 6) Print routes with ANY RTT ABOVE a threshold");
|
||||
println!(" You provide a threshold in milliseconds (e.g. 500).");
|
||||
println!(" For each route, if at least one RTT sample exceeds the threshold,");
|
||||
println!(" that route is printed with full details.");
|
||||
println!(" Use this to find routes that occasionally spike very high,");
|
||||
println!(" even if their average RTT is still acceptable.");
|
||||
println!();
|
||||
|
||||
println!(" 7) Show experiment progress (percentage completed)");
|
||||
println!(" Uses the stored experiment configuration (total_routes, packets_per_route)");
|
||||
println!(" plus the number of RTT samples recorded so far to estimate:");
|
||||
println!(" completion = received_samples / (total_routes * packets_per_route)");
|
||||
println!(" The result is printed as a percentage (0%–100%).");
|
||||
println!(" This tells you roughly how far the RTT experiment has progressed.");
|
||||
println!();
|
||||
|
||||
println!(" 8) Write stats AND generate histogram(s) via Python");
|
||||
println!(" This command will:");
|
||||
println!(" 1) Write the current route statistics to a CSV file (same as option 2).");
|
||||
println!(" 2) Call the Python script 'rtt_histogram.py' to visualize RTTs.");
|
||||
println!();
|
||||
println!(" When prompted, you will provide two things:");
|
||||
println!(" - CSV file path (where to save the stats)");
|
||||
println!(" - outlier_mode string, which controls which histograms are generated:");
|
||||
println!();
|
||||
println!(" • \"all\"");
|
||||
println!(" Use ALL avg_rtt values from the CSV.");
|
||||
println!(
|
||||
" Result: a single histogram containing every route's avg RTT (in seconds)."
|
||||
);
|
||||
println!();
|
||||
println!(" • \"<cutoff>\" (numeric, in seconds, e.g. \"1.0\")");
|
||||
println!(" Only keep avg_rtt <= cutoff.");
|
||||
println!(" Result: a single histogram with INLIERS only (values <= cutoff).");
|
||||
println!(
|
||||
" Example: \"1.0\" keeps everything at or below 1.0s and drops slower routes."
|
||||
);
|
||||
println!();
|
||||
println!(" • \"both:<cutoff>\" (e.g. \"both:1.0\")");
|
||||
println!(" Split the data into two sets:");
|
||||
println!(" - inliers : avg_rtt <= cutoff");
|
||||
println!(" - outliers : avg_rtt > cutoff");
|
||||
println!(" Result: TWO histograms are generated:");
|
||||
println!(" 1) Distribution of inliers");
|
||||
println!(" 2) Distribution of outliers");
|
||||
println!(
|
||||
" This helps visually compare the \"normal\" routes and the very slow ones."
|
||||
);
|
||||
println!();
|
||||
|
||||
println!("Helpful notes:");
|
||||
println!(" • RTT samples are computed when a FragmentReceived event arrives.");
|
||||
println!(" For each fragment that may be retransmitted, the analyzer stores");
|
||||
println!(" multiple send times and receive times, and pairs them in order");
|
||||
println!(" to compute multiple RTT values for that fragment if needed.");
|
||||
println!(" • \"sent\" in the stats includes retransmissions as well, so it may be");
|
||||
println!(" higher than packets_per_route for unstable routes.");
|
||||
|
||||
println!("===========================================================\n");
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
[package]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.64"
|
||||
version = "1.1.66"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
|
||||
edition = "2021"
|
||||
rust-version = "1.70"
|
||||
rust-version = "1.85"
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use futures::channel::mpsc;
|
||||
use futures::StreamExt;
|
||||
use futures::channel::mpsc;
|
||||
use notify::event::{DataChange, MetadataKind, ModifyKind};
|
||||
use notify::{Config, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
|
||||
use std::collections::HashMap;
|
||||
@@ -96,10 +96,10 @@ impl AsyncFileWatcher {
|
||||
// when testing I was consistently getting two `Modify(Data(Any))` events in quick succession
|
||||
// (probably to modify content and metadata).
|
||||
// we really only want to propagate one of them
|
||||
if let Some(previous) = self.last_received.get(&event.kind) {
|
||||
if now.duration_since(*previous) < self.tick_duration {
|
||||
return false;
|
||||
}
|
||||
if let Some(previous) = self.last_received.get(&event.kind)
|
||||
&& now.duration_since(*previous) < self.tick_duration
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
let Some(filters) = &self.filters else {
|
||||
|
||||
@@ -16,6 +16,7 @@ serde = { workspace = true, features = ["derive"] }
|
||||
semver = { workspace = true }
|
||||
strum_macros = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
|
||||
nym-credentials-interface = { path = "../credentials-interface" }
|
||||
nym-crypto = { path = "../crypto", features = ["asymmetric"] }
|
||||
@@ -29,7 +30,13 @@ hmac = { workspace = true, optional = true }
|
||||
sha2 = { workspace = true, optional = true }
|
||||
x25519-dalek = { workspace = true, features = ["static_secrets"] }
|
||||
|
||||
[dev-dependencies]
|
||||
nym-test-utils = { path = "../test-utils" }
|
||||
|
||||
[features]
|
||||
default = ["verify"]
|
||||
# this is moved to a separate feature as we really need clients to import it (especially, *cough*, wasm)
|
||||
verify = ["hmac", "sha2"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -5,9 +5,12 @@ use nym_sphinx::addressing::Recipient;
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
|
||||
use crate::{
|
||||
latest::registration::IpPair,
|
||||
traits::{FinalMessage, InitMessage, QueryBandwidthMessage, TopUpMessage, Versionable},
|
||||
v2, v3, v4, v5, AuthenticatorVersion, Error,
|
||||
AuthenticatorVersion, Error,
|
||||
traits::{
|
||||
FinalMessage, InitMessage, QueryBandwidthMessage, TopUpMessage, UpgradeModeMessage,
|
||||
Versionable,
|
||||
},
|
||||
v2, v3, v4, v5, v6,
|
||||
};
|
||||
|
||||
// This is very redundant with AuthenticatorRequest and I reckon they could be smooshed.
|
||||
@@ -18,6 +21,293 @@ pub enum ClientMessage {
|
||||
Final(Box<dyn FinalMessage + Send + Sync + 'static>),
|
||||
Query(Box<dyn QueryBandwidthMessage + Send + Sync + 'static>),
|
||||
TopUp(Box<dyn TopUpMessage + Send + Sync + 'static>),
|
||||
UpgradeModeCheck(Box<dyn UpgradeModeMessage + Send + Sync + 'static>),
|
||||
}
|
||||
|
||||
pub struct SerialisedRequest {
|
||||
pub bytes: Vec<u8>,
|
||||
pub request_id: u64,
|
||||
}
|
||||
|
||||
impl SerialisedRequest {
|
||||
pub fn new(bytes: Vec<u8>, request_id: u64) -> Self {
|
||||
Self { bytes, request_id }
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientMessage {
|
||||
fn serialise_v1(&self) -> Result<SerialisedRequest, Error> {
|
||||
Err(Error::UnsupportedVersion)
|
||||
}
|
||||
|
||||
fn serialise_v2(&self, reply_to: Recipient) -> Result<SerialisedRequest, Error> {
|
||||
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(SerialisedRequest::new(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()
|
||||
.and_then(|c| c.credential.into_zk_nym())
|
||||
.map(|c| *c),
|
||||
},
|
||||
reply_to,
|
||||
);
|
||||
Ok(SerialisedRequest::new(req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::Query(query_message) => {
|
||||
let (req, id) =
|
||||
AuthenticatorRequest::new_query_request(query_message.pub_key(), reply_to);
|
||||
Ok(SerialisedRequest::new(req.to_bytes()?, id))
|
||||
}
|
||||
_ => Err(Error::UnsupportedMessage),
|
||||
}
|
||||
}
|
||||
|
||||
fn serialise_v3(&self, reply_to: Recipient) -> Result<SerialisedRequest, Error> {
|
||||
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(SerialisedRequest::new(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()
|
||||
.and_then(|c| c.credential.into_zk_nym())
|
||||
.map(|c| *c),
|
||||
},
|
||||
reply_to,
|
||||
);
|
||||
Ok(SerialisedRequest::new(req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::Query(query_message) => {
|
||||
let (req, id) =
|
||||
AuthenticatorRequest::new_query_request(query_message.pub_key(), reply_to);
|
||||
Ok(SerialisedRequest::new(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(SerialisedRequest::new(req.to_bytes()?, id))
|
||||
}
|
||||
_ => Err(Error::UnsupportedMessage),
|
||||
}
|
||||
}
|
||||
|
||||
fn serialise_v4(&self, reply_to: Recipient) -> Result<SerialisedRequest, Error> {
|
||||
use v4::{
|
||||
registration::{ClientMac, FinalMessage, GatewayClient, InitMessage, IpPair},
|
||||
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(SerialisedRequest::new(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()
|
||||
.and_then(|c| c.credential.into_zk_nym())
|
||||
.map(|c| *c),
|
||||
},
|
||||
reply_to,
|
||||
);
|
||||
Ok(SerialisedRequest::new(req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::Query(query_message) => {
|
||||
let (req, id) =
|
||||
AuthenticatorRequest::new_query_request(query_message.pub_key(), reply_to);
|
||||
Ok(SerialisedRequest::new(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(SerialisedRequest::new(req.to_bytes()?, id))
|
||||
}
|
||||
_ => Err(Error::UnsupportedMessage),
|
||||
}
|
||||
}
|
||||
|
||||
fn serialise_v5(&self) -> Result<SerialisedRequest, Error> {
|
||||
use v5::{
|
||||
registration::{ClientMac, FinalMessage, GatewayClient, InitMessage, IpPair},
|
||||
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(SerialisedRequest::new(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()
|
||||
.and_then(|c| c.credential.into_zk_nym())
|
||||
.map(|c| *c),
|
||||
});
|
||||
Ok(SerialisedRequest::new(req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::Query(query_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_query_request(query_message.pub_key());
|
||||
Ok(SerialisedRequest::new(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(SerialisedRequest::new(req.to_bytes()?, id))
|
||||
}
|
||||
_ => Err(Error::UnsupportedMessage),
|
||||
}
|
||||
}
|
||||
|
||||
fn serialise_v6(&self) -> Result<SerialisedRequest, Error> {
|
||||
use v6::{
|
||||
registration::{ClientMac, FinalMessage, GatewayClient, InitMessage, IpPair},
|
||||
request::AuthenticatorRequest,
|
||||
topup::TopUpMessage,
|
||||
upgrade_mode_check::UpgradeModeCheckRequest,
|
||||
};
|
||||
match self {
|
||||
ClientMessage::Initial(init_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_initial_request(InitMessage {
|
||||
pub_key: init_message.pub_key(),
|
||||
});
|
||||
Ok(SerialisedRequest::new(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(SerialisedRequest::new(req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::Query(query_message) => {
|
||||
let (req, id) = AuthenticatorRequest::new_query_request(query_message.pub_key());
|
||||
Ok(SerialisedRequest::new(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(SerialisedRequest::new(req.to_bytes()?, id))
|
||||
}
|
||||
ClientMessage::UpgradeModeCheck(upgrade_mode_check) => {
|
||||
// currently JWT is the only emergency credential option
|
||||
let Some(upgrade_mode_jwt) =
|
||||
upgrade_mode_check.upgrade_mode_global_attestation_jwt()
|
||||
else {
|
||||
return Err(Error::conversion(
|
||||
"no valid known upgrade mode check variants",
|
||||
));
|
||||
};
|
||||
let msg = UpgradeModeCheckRequest::UpgradeModeJwt {
|
||||
token: upgrade_mode_jwt,
|
||||
};
|
||||
|
||||
let (req, id) = AuthenticatorRequest::new_upgrade_mode_check_request(msg);
|
||||
Ok(SerialisedRequest::new(req.to_bytes()?, id))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientMessage {
|
||||
@@ -26,7 +316,7 @@ impl ClientMessage {
|
||||
match self {
|
||||
Self::Final(msg) => msg.credential().is_some(),
|
||||
Self::TopUp(_) => true,
|
||||
Self::Initial(_) | Self::Query(_) => false,
|
||||
Self::Initial(_) | Self::Query(_) | Self::UpgradeModeCheck(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,208 +326,18 @@ impl ClientMessage {
|
||||
ClientMessage::Final(msg) => msg.version(),
|
||||
ClientMessage::Query(msg) => msg.version(),
|
||||
ClientMessage::TopUp(msg) => msg.version(),
|
||||
ClientMessage::UpgradeModeCheck(msg) => msg.version(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bytes(&self, reply_to: Recipient) -> Result<(Vec<u8>, u64), Error> {
|
||||
pub fn bytes(&self, reply_to: Recipient) -> Result<SerialisedRequest, 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::V1 => self.serialise_v1(),
|
||||
AuthenticatorVersion::V2 => self.serialise_v2(reply_to),
|
||||
AuthenticatorVersion::V3 => self.serialise_v3(reply_to),
|
||||
AuthenticatorVersion::V4 => self.serialise_v4(reply_to),
|
||||
AuthenticatorVersion::V5 => self.serialise_v5(),
|
||||
AuthenticatorVersion::V6 => self.serialise_v6(),
|
||||
AuthenticatorVersion::UNKNOWN => Err(Error::UnknownVersion),
|
||||
}
|
||||
}
|
||||
@@ -246,7 +346,7 @@ impl ClientMessage {
|
||||
use AuthenticatorVersion::*;
|
||||
match self.version() {
|
||||
V1 | V2 | V3 | V4 => false,
|
||||
V5 => true,
|
||||
V5 | V6 => true,
|
||||
UNKNOWN => true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::fmt::Display;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
@@ -37,3 +38,13 @@ pub enum Error {
|
||||
#[error(transparent)]
|
||||
Bincode(#[from] bincode::Error),
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub fn conversion(msg: impl Into<String>) -> Self {
|
||||
Error::Conversion(msg.into())
|
||||
}
|
||||
|
||||
pub fn conversion_display(msg: impl Display) -> Self {
|
||||
Error::Conversion(msg.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod client_message;
|
||||
pub mod models;
|
||||
pub mod request;
|
||||
pub mod response;
|
||||
pub mod traits;
|
||||
@@ -10,13 +11,14 @@ pub mod v2;
|
||||
pub mod v3;
|
||||
pub mod v4;
|
||||
pub mod v5;
|
||||
pub mod v6;
|
||||
|
||||
mod error;
|
||||
mod util;
|
||||
mod version;
|
||||
|
||||
pub use error::Error;
|
||||
pub use v5 as latest;
|
||||
pub use v6 as latest;
|
||||
pub use version::AuthenticatorVersion;
|
||||
|
||||
pub const CURRENT_VERSION: u8 = latest::VERSION;
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_credentials_interface::{
|
||||
BandwidthCredential, CredentialSpendingData, TicketType, UnknownTicketType,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
|
||||
pub enum CurrentUpgradeModeStatus {
|
||||
Enabled,
|
||||
Disabled,
|
||||
// everything pre-v6
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl CurrentUpgradeModeStatus {
|
||||
pub fn is_enabled(&self) -> bool {
|
||||
matches!(self, CurrentUpgradeModeStatus::Enabled)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for CurrentUpgradeModeStatus {
|
||||
fn from(value: bool) -> Self {
|
||||
if value {
|
||||
CurrentUpgradeModeStatus::Enabled
|
||||
} else {
|
||||
CurrentUpgradeModeStatus::Disabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CurrentUpgradeModeStatus> for Option<bool> {
|
||||
fn from(value: CurrentUpgradeModeStatus) -> Self {
|
||||
match value {
|
||||
CurrentUpgradeModeStatus::Enabled => Some(true),
|
||||
CurrentUpgradeModeStatus::Disabled => Some(false),
|
||||
CurrentUpgradeModeStatus::Unknown => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct BandwidthClaim {
|
||||
pub credential: BandwidthCredential,
|
||||
pub kind: TicketType,
|
||||
}
|
||||
|
||||
impl TryFrom<CredentialSpendingData> for BandwidthClaim {
|
||||
type Error = UnknownTicketType;
|
||||
|
||||
fn try_from(credential: CredentialSpendingData) -> Result<Self, Self::Error> {
|
||||
Ok(BandwidthClaim {
|
||||
kind: TicketType::try_from_encoded(credential.payment.t_type)?,
|
||||
credential: BandwidthCredential::from(credential),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,10 @@
|
||||
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};
|
||||
use crate::traits::{
|
||||
FinalMessage, InitMessage, QueryBandwidthMessage, TopUpMessage, UpgradeModeMessage,
|
||||
};
|
||||
use crate::{v1, v2, v3, v4, v5, v6};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AuthenticatorRequest {
|
||||
@@ -33,6 +35,11 @@ pub enum AuthenticatorRequest {
|
||||
reply_to: Option<Recipient>,
|
||||
request_id: u64,
|
||||
},
|
||||
CheckUpgradeMode {
|
||||
msg: Box<dyn UpgradeModeMessage + Send + Sync + 'static>,
|
||||
protocol: Protocol,
|
||||
request_id: u64,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<v1::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
@@ -202,3 +209,45 @@ impl From<v5::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v6::request::AuthenticatorRequest> for AuthenticatorRequest {
|
||||
fn from(value: v6::request::AuthenticatorRequest) -> Self {
|
||||
match value.data {
|
||||
v6::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
|
||||
msg: Box::new(init_message),
|
||||
protocol: value.protocol,
|
||||
reply_to: None,
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v6::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
|
||||
msg: final_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: None,
|
||||
request_id: value.request_id,
|
||||
},
|
||||
v6::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,
|
||||
}
|
||||
}
|
||||
v6::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => {
|
||||
Self::TopUpBandwidth {
|
||||
msg: top_up_message,
|
||||
protocol: value.protocol,
|
||||
reply_to: None,
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
v6::request::AuthenticatorRequestData::CheckUpgradeMode(upgrade_mode_check_msg) => {
|
||||
Self::CheckUpgradeMode {
|
||||
msg: Box::new(upgrade_mode_check_msg),
|
||||
protocol: value.protocol,
|
||||
request_id: value.request_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::models::CurrentUpgradeModeStatus;
|
||||
use crate::traits::{
|
||||
Id, PendingRegistrationResponse, RegisteredResponse, RemainingBandwidthResponse,
|
||||
TopUpBandwidthResponse,
|
||||
TopUpBandwidthResponse, UpgradeModeStatus,
|
||||
};
|
||||
use crate::{v2, v3, v4, v5};
|
||||
use crate::{v2, v3, v4, v5, v6};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AuthenticatorResponse {
|
||||
@@ -13,6 +14,29 @@ pub enum AuthenticatorResponse {
|
||||
Registered(Box<dyn RegisteredResponse + Send + Sync + 'static>),
|
||||
RemainingBandwidth(Box<dyn RemainingBandwidthResponse + Send + Sync + 'static>),
|
||||
TopUpBandwidth(Box<dyn TopUpBandwidthResponse + Send + Sync + 'static>),
|
||||
UpgradeMode(Box<dyn UpgradeModeStatus + Send + Sync + 'static>),
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for AuthenticatorResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
match self {
|
||||
AuthenticatorResponse::PendingRegistration(pending_registration_response) => {
|
||||
pending_registration_response.upgrade_mode_status()
|
||||
}
|
||||
AuthenticatorResponse::Registered(registered_response) => {
|
||||
registered_response.upgrade_mode_status()
|
||||
}
|
||||
AuthenticatorResponse::RemainingBandwidth(remaining_bandwidth_response) => {
|
||||
remaining_bandwidth_response.upgrade_mode_status()
|
||||
}
|
||||
AuthenticatorResponse::TopUpBandwidth(top_up_bandwidth_response) => {
|
||||
top_up_bandwidth_response.upgrade_mode_status()
|
||||
}
|
||||
AuthenticatorResponse::UpgradeMode(upgrade_mode_response) => {
|
||||
upgrade_mode_response.upgrade_mode_status()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for AuthenticatorResponse {
|
||||
@@ -28,6 +52,7 @@ impl Id for AuthenticatorResponse {
|
||||
AuthenticatorResponse::TopUpBandwidth(top_up_bandwidth_response) => {
|
||||
top_up_bandwidth_response.id()
|
||||
}
|
||||
AuthenticatorResponse::UpgradeMode(upgrade_mode_response) => upgrade_mode_response.id(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,3 +129,25 @@ impl From<v5::response::AuthenticatorResponse> for AuthenticatorResponse {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v6::response::AuthenticatorResponse> for AuthenticatorResponse {
|
||||
fn from(value: v6::response::AuthenticatorResponse) -> Self {
|
||||
match value.data {
|
||||
v6::response::AuthenticatorResponseData::PendingRegistration(
|
||||
pending_registration_response,
|
||||
) => Self::PendingRegistration(Box::new(pending_registration_response)),
|
||||
v6::response::AuthenticatorResponseData::Registered(registered_response) => {
|
||||
Self::Registered(Box::new(registered_response))
|
||||
}
|
||||
v6::response::AuthenticatorResponseData::RemainingBandwidth(
|
||||
remaining_bandwidth_response,
|
||||
) => Self::RemainingBandwidth(Box::new(remaining_bandwidth_response)),
|
||||
v6::response::AuthenticatorResponseData::TopUpBandwidth(top_up_bandwidth_response) => {
|
||||
Self::TopUpBandwidth(Box::new(top_up_bandwidth_response))
|
||||
}
|
||||
v6::response::AuthenticatorResponseData::UpgradeMode(upgrade_mode_check_response) => {
|
||||
Self::UpgradeMode(Box::new(upgrade_mode_check_response))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::latest::registration::IpPair;
|
||||
use crate::models::{BandwidthClaim, CurrentUpgradeModeStatus};
|
||||
use crate::{AuthenticatorVersion, Error, v1, v2, v3, v4, v5, v6};
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
use nym_crypto::asymmetric::x25519;
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
use std::fmt;
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
use nym_crypto::asymmetric::x25519::PrivateKey;
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
|
||||
use crate::latest::registration::IpPair;
|
||||
use crate::{v1, v2, v3, v4, v5, AuthenticatorVersion, Error};
|
||||
use tracing::error;
|
||||
|
||||
pub trait Versionable {
|
||||
fn version(&self) -> AuthenticatorVersion;
|
||||
@@ -51,6 +51,12 @@ impl Versionable for v5::registration::InitMessage {
|
||||
}
|
||||
}
|
||||
|
||||
impl Versionable for v6::registration::InitMessage {
|
||||
fn version(&self) -> AuthenticatorVersion {
|
||||
AuthenticatorVersion::V6
|
||||
}
|
||||
}
|
||||
|
||||
impl Versionable for v2::registration::FinalMessage {
|
||||
fn version(&self) -> AuthenticatorVersion {
|
||||
AuthenticatorVersion::V2
|
||||
@@ -75,6 +81,12 @@ impl Versionable for v5::registration::FinalMessage {
|
||||
}
|
||||
}
|
||||
|
||||
impl Versionable for v6::registration::FinalMessage {
|
||||
fn version(&self) -> AuthenticatorVersion {
|
||||
AuthenticatorVersion::V6
|
||||
}
|
||||
}
|
||||
|
||||
impl Versionable for PeerPublicKey {
|
||||
fn version(&self) -> AuthenticatorVersion {
|
||||
AuthenticatorVersion::V3
|
||||
@@ -98,6 +110,158 @@ impl Versionable for v5::topup::TopUpMessage {
|
||||
AuthenticatorVersion::V5
|
||||
}
|
||||
}
|
||||
impl Versionable for v6::topup::TopUpMessage {
|
||||
fn version(&self) -> AuthenticatorVersion {
|
||||
AuthenticatorVersion::V6
|
||||
}
|
||||
}
|
||||
|
||||
impl Versionable for v6::upgrade_mode_check::UpgradeModeCheckRequest {
|
||||
fn version(&self) -> AuthenticatorVersion {
|
||||
AuthenticatorVersion::V6
|
||||
}
|
||||
}
|
||||
|
||||
pub trait UpgradeModeStatus: Id + fmt::Debug {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus;
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v1::response::PendingRegistrationResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v1::response::RegisteredResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v1::response::RemainingBandwidthResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v2::response::PendingRegistrationResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v2::response::RegisteredResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v2::response::RemainingBandwidthResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
impl UpgradeModeStatus for v3::response::PendingRegistrationResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v3::response::RegisteredResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v3::response::RemainingBandwidthResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v3::response::TopUpBandwidthResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v4::response::PendingRegistrationResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v4::response::RegisteredResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v4::response::RemainingBandwidthResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v4::response::TopUpBandwidthResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v5::response::PendingRegistrationResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v5::response::RegisteredResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v5::response::RemainingBandwidthResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v5::response::TopUpBandwidthResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
CurrentUpgradeModeStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v6::response::PendingRegistrationResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
self.upgrade_mode_enabled.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v6::response::RegisteredResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
self.upgrade_mode_enabled.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v6::response::RemainingBandwidthResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
self.upgrade_mode_enabled.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v6::response::TopUpBandwidthResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
self.upgrade_mode_enabled.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl UpgradeModeStatus for v6::response::UpgradeModeResponse {
|
||||
fn upgrade_mode_status(&self) -> CurrentUpgradeModeStatus {
|
||||
self.upgrade_mode_enabled.into()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait InitMessage: Versionable + fmt::Debug {
|
||||
fn pub_key(&self) -> PeerPublicKey;
|
||||
@@ -133,14 +297,20 @@ impl InitMessage for v5::registration::InitMessage {
|
||||
}
|
||||
}
|
||||
|
||||
impl InitMessage for v6::registration::InitMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.pub_key
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FinalMessage: Versionable + fmt::Debug {
|
||||
fn gateway_client_pub_key(&self) -> PeerPublicKey;
|
||||
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error>;
|
||||
fn verify(&self, private_key: &x25519::PrivateKey, nonce: u64) -> Result<(), Error>;
|
||||
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<BandwidthClaim>;
|
||||
}
|
||||
|
||||
impl FinalMessage for v1::GatewayClient {
|
||||
@@ -148,7 +318,7 @@ impl FinalMessage for v1::GatewayClient {
|
||||
self.pub_key
|
||||
}
|
||||
|
||||
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
fn verify(&self, private_key: &x25519::PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
self.verify(private_key, nonce)
|
||||
}
|
||||
|
||||
@@ -171,7 +341,7 @@ impl FinalMessage for v1::GatewayClient {
|
||||
self.mac.to_vec()
|
||||
}
|
||||
|
||||
fn credential(&self) -> Option<CredentialSpendingData> {
|
||||
fn credential(&self) -> Option<BandwidthClaim> {
|
||||
None
|
||||
}
|
||||
}
|
||||
@@ -181,7 +351,7 @@ impl FinalMessage for v2::registration::FinalMessage {
|
||||
self.gateway_client.pub_key
|
||||
}
|
||||
|
||||
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
fn verify(&self, private_key: &x25519::PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
self.gateway_client.verify(private_key, nonce)
|
||||
}
|
||||
|
||||
@@ -204,8 +374,12 @@ impl FinalMessage for v2::registration::FinalMessage {
|
||||
self.gateway_client.mac.to_vec()
|
||||
}
|
||||
|
||||
fn credential(&self) -> Option<CredentialSpendingData> {
|
||||
self.credential.clone()
|
||||
fn credential(&self) -> Option<BandwidthClaim> {
|
||||
self.credential.clone().and_then(|c| {
|
||||
c.try_into()
|
||||
.inspect_err(|err| error!("credential conversion error: {err}"))
|
||||
.ok()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,7 +388,7 @@ impl FinalMessage for v3::registration::FinalMessage {
|
||||
self.gateway_client.pub_key
|
||||
}
|
||||
|
||||
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
fn verify(&self, private_key: &x25519::PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
self.gateway_client.verify(private_key, nonce)
|
||||
}
|
||||
|
||||
@@ -237,8 +411,12 @@ impl FinalMessage for v3::registration::FinalMessage {
|
||||
self.gateway_client.mac.to_vec()
|
||||
}
|
||||
|
||||
fn credential(&self) -> Option<CredentialSpendingData> {
|
||||
self.credential.clone()
|
||||
fn credential(&self) -> Option<BandwidthClaim> {
|
||||
self.credential.clone().and_then(|c| {
|
||||
c.try_into()
|
||||
.inspect_err(|err| error!("credential conversion error: {err}"))
|
||||
.ok()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,7 +425,42 @@ impl FinalMessage for v4::registration::FinalMessage {
|
||||
self.gateway_client.pub_key
|
||||
}
|
||||
|
||||
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
fn verify(&self, private_key: &x25519::PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
self.gateway_client.verify(private_key, nonce)
|
||||
}
|
||||
|
||||
fn private_ips(&self) -> IpPair {
|
||||
// v4 -> v5 -> v6
|
||||
v5::registration::IpPair::from(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<BandwidthClaim> {
|
||||
self.credential.clone().and_then(|c| {
|
||||
c.try_into()
|
||||
.inspect_err(|err| error!("credential conversion error: {err}"))
|
||||
.ok()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl FinalMessage for v5::registration::FinalMessage {
|
||||
fn gateway_client_pub_key(&self) -> PeerPublicKey {
|
||||
self.gateway_client.pub_key
|
||||
}
|
||||
|
||||
fn verify(&self, private_key: &x25519::PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
self.gateway_client.verify(private_key, nonce)
|
||||
}
|
||||
|
||||
@@ -267,17 +480,21 @@ impl FinalMessage for v4::registration::FinalMessage {
|
||||
self.gateway_client.mac.to_vec()
|
||||
}
|
||||
|
||||
fn credential(&self) -> Option<CredentialSpendingData> {
|
||||
self.credential.clone()
|
||||
fn credential(&self) -> Option<BandwidthClaim> {
|
||||
self.credential.clone().and_then(|c| {
|
||||
c.try_into()
|
||||
.inspect_err(|err| error!("credential conversion error: {err}"))
|
||||
.ok()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl FinalMessage for v5::registration::FinalMessage {
|
||||
impl FinalMessage for v6::registration::FinalMessage {
|
||||
fn gateway_client_pub_key(&self) -> PeerPublicKey {
|
||||
self.gateway_client.pub_key
|
||||
}
|
||||
|
||||
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
fn verify(&self, private_key: &x25519::PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
self.gateway_client.verify(private_key, nonce)
|
||||
}
|
||||
|
||||
@@ -297,7 +514,7 @@ impl FinalMessage for v5::registration::FinalMessage {
|
||||
self.gateway_client.mac.to_vec()
|
||||
}
|
||||
|
||||
fn credential(&self) -> Option<CredentialSpendingData> {
|
||||
fn credential(&self) -> Option<BandwidthClaim> {
|
||||
self.credential.clone()
|
||||
}
|
||||
}
|
||||
@@ -347,10 +564,42 @@ impl TopUpMessage for v5::topup::TopUpMessage {
|
||||
}
|
||||
}
|
||||
|
||||
impl TopUpMessage for v6::topup::TopUpMessage {
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
self.pub_key
|
||||
}
|
||||
|
||||
fn credential(&self) -> CredentialSpendingData {
|
||||
self.credential.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait UpgradeModeMessage: Versionable + fmt::Debug {
|
||||
// the idea is to expose different types of emergency credentials here,
|
||||
// like upgrade mode JWT, emergency threshold credential issued by signers, etc.
|
||||
fn upgrade_mode_global_attestation_jwt(&self) -> Option<String>;
|
||||
}
|
||||
|
||||
impl UpgradeModeMessage for v6::upgrade_mode_check::UpgradeModeCheckRequest {
|
||||
fn upgrade_mode_global_attestation_jwt(&self) -> Option<String> {
|
||||
use v6::upgrade_mode_check::UpgradeModeCheckRequest;
|
||||
|
||||
match self {
|
||||
UpgradeModeCheckRequest::UpgradeModeJwt { token } => Some(token.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Id {
|
||||
fn id(&self) -> u64;
|
||||
}
|
||||
|
||||
impl Id for v1::response::PendingRegistrationResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for v2::response::PendingRegistrationResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
@@ -375,6 +624,18 @@ impl Id for v5::response::PendingRegistrationResponse {
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for v6::response::PendingRegistrationResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for v1::response::RegisteredResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for v2::response::RegisteredResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
@@ -399,6 +660,18 @@ impl Id for v5::response::RegisteredResponse {
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for v6::response::RegisteredResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for v1::response::RemainingBandwidthResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for v2::response::RemainingBandwidthResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
@@ -423,6 +696,12 @@ impl Id for v5::response::RemainingBandwidthResponse {
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for v6::response::RemainingBandwidthResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for v3::response::TopUpBandwidthResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
@@ -441,11 +720,28 @@ impl Id for v5::response::TopUpBandwidthResponse {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PendingRegistrationResponse: Id + fmt::Debug {
|
||||
impl Id for v6::response::TopUpBandwidthResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for v6::response::UpgradeModeResponse {
|
||||
fn id(&self) -> u64 {
|
||||
self.request_id
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PendingRegistrationResponse: Id + UpgradeModeStatus + fmt::Debug {
|
||||
fn nonce(&self) -> u64;
|
||||
fn verify(&self, gateway_key: &PrivateKey) -> std::result::Result<(), Error>;
|
||||
fn verify(&self, gateway_key: &x25519::PrivateKey) -> Result<(), Error>;
|
||||
fn pub_key(&self) -> PeerPublicKey;
|
||||
fn private_ips(&self) -> IpPair;
|
||||
fn finalise_registration(
|
||||
&self,
|
||||
private_key: &x25519::PrivateKey,
|
||||
credential: Option<BandwidthClaim>,
|
||||
) -> Box<dyn FinalMessage + Send + Sync>;
|
||||
}
|
||||
|
||||
impl PendingRegistrationResponse for v2::response::PendingRegistrationResponse {
|
||||
@@ -453,7 +749,7 @@ impl PendingRegistrationResponse for v2::response::PendingRegistrationResponse {
|
||||
self.reply.nonce
|
||||
}
|
||||
|
||||
fn verify(&self, gateway_key: &PrivateKey) -> std::result::Result<(), Error> {
|
||||
fn verify(&self, gateway_key: &x25519::PrivateKey) -> Result<(), Error> {
|
||||
self.reply.gateway_data.verify(gateway_key, self.nonce())
|
||||
}
|
||||
|
||||
@@ -464,6 +760,22 @@ impl PendingRegistrationResponse for v2::response::PendingRegistrationResponse {
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.reply.gateway_data.private_ip.into()
|
||||
}
|
||||
|
||||
fn finalise_registration(
|
||||
&self,
|
||||
private_key: &x25519::PrivateKey,
|
||||
credential: Option<BandwidthClaim>,
|
||||
) -> Box<dyn FinalMessage + Send + Sync> {
|
||||
Box::new(v2::registration::FinalMessage {
|
||||
gateway_client: v2::registration::GatewayClient::new(
|
||||
private_key,
|
||||
self.pub_key().inner(),
|
||||
self.private_ips().ipv4.into(),
|
||||
self.nonce(),
|
||||
),
|
||||
credential: credential.and_then(|b| b.credential.into_zk_nym().map(|c| *c)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl PendingRegistrationResponse for v3::response::PendingRegistrationResponse {
|
||||
@@ -471,7 +783,7 @@ impl PendingRegistrationResponse for v3::response::PendingRegistrationResponse {
|
||||
self.reply.nonce
|
||||
}
|
||||
|
||||
fn verify(&self, gateway_key: &PrivateKey) -> std::result::Result<(), Error> {
|
||||
fn verify(&self, gateway_key: &x25519::PrivateKey) -> Result<(), Error> {
|
||||
self.reply.gateway_data.verify(gateway_key, self.nonce())
|
||||
}
|
||||
|
||||
@@ -482,6 +794,22 @@ impl PendingRegistrationResponse for v3::response::PendingRegistrationResponse {
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.reply.gateway_data.private_ip.into()
|
||||
}
|
||||
|
||||
fn finalise_registration(
|
||||
&self,
|
||||
private_key: &x25519::PrivateKey,
|
||||
credential: Option<BandwidthClaim>,
|
||||
) -> Box<dyn FinalMessage + Send + Sync> {
|
||||
Box::new(v3::registration::FinalMessage {
|
||||
gateway_client: v3::registration::GatewayClient::new(
|
||||
private_key,
|
||||
self.pub_key().inner(),
|
||||
self.private_ips().ipv4.into(),
|
||||
self.nonce(),
|
||||
),
|
||||
credential: credential.and_then(|b| b.credential.into_zk_nym().map(|c| *c)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl PendingRegistrationResponse for v4::response::PendingRegistrationResponse {
|
||||
@@ -489,7 +817,42 @@ impl PendingRegistrationResponse for v4::response::PendingRegistrationResponse {
|
||||
self.reply.nonce
|
||||
}
|
||||
|
||||
fn verify(&self, gateway_key: &PrivateKey) -> std::result::Result<(), Error> {
|
||||
fn verify(&self, gateway_key: &x25519::PrivateKey) -> 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 {
|
||||
// v4 -> v5 -> v6
|
||||
v5::registration::IpPair::from(self.reply.gateway_data.private_ips).into()
|
||||
}
|
||||
|
||||
fn finalise_registration(
|
||||
&self,
|
||||
private_key: &x25519::PrivateKey,
|
||||
credential: Option<BandwidthClaim>,
|
||||
) -> Box<dyn FinalMessage + Send + Sync> {
|
||||
Box::new(v4::registration::FinalMessage {
|
||||
gateway_client: v4::registration::GatewayClient::new(
|
||||
private_key,
|
||||
self.pub_key().inner(),
|
||||
self.reply.gateway_data.private_ips,
|
||||
self.nonce(),
|
||||
),
|
||||
credential: credential.and_then(|b| b.credential.into_zk_nym().map(|c| *c)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl PendingRegistrationResponse for v5::response::PendingRegistrationResponse {
|
||||
fn nonce(&self) -> u64 {
|
||||
self.reply.nonce
|
||||
}
|
||||
|
||||
fn verify(&self, gateway_key: &x25519::PrivateKey) -> Result<(), Error> {
|
||||
self.reply.gateway_data.verify(gateway_key, self.nonce())
|
||||
}
|
||||
|
||||
@@ -500,14 +863,30 @@ impl PendingRegistrationResponse for v4::response::PendingRegistrationResponse {
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.reply.gateway_data.private_ips.into()
|
||||
}
|
||||
|
||||
fn finalise_registration(
|
||||
&self,
|
||||
private_key: &x25519::PrivateKey,
|
||||
credential: Option<BandwidthClaim>,
|
||||
) -> Box<dyn FinalMessage + Send + Sync> {
|
||||
Box::new(v5::registration::FinalMessage {
|
||||
gateway_client: v5::registration::GatewayClient::new(
|
||||
private_key,
|
||||
self.pub_key().inner(),
|
||||
self.reply.gateway_data.private_ips,
|
||||
self.nonce(),
|
||||
),
|
||||
credential: credential.and_then(|b| b.credential.into_zk_nym().map(|c| *c)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl PendingRegistrationResponse for v5::response::PendingRegistrationResponse {
|
||||
impl PendingRegistrationResponse for v6::response::PendingRegistrationResponse {
|
||||
fn nonce(&self) -> u64 {
|
||||
self.reply.nonce
|
||||
}
|
||||
|
||||
fn verify(&self, gateway_key: &PrivateKey) -> std::result::Result<(), Error> {
|
||||
fn verify(&self, gateway_key: &x25519::PrivateKey) -> Result<(), Error> {
|
||||
self.reply.gateway_data.verify(gateway_key, self.nonce())
|
||||
}
|
||||
|
||||
@@ -518,9 +897,25 @@ impl PendingRegistrationResponse for v5::response::PendingRegistrationResponse {
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.reply.gateway_data.private_ips
|
||||
}
|
||||
|
||||
fn finalise_registration(
|
||||
&self,
|
||||
private_key: &x25519::PrivateKey,
|
||||
credential: Option<BandwidthClaim>,
|
||||
) -> Box<dyn FinalMessage + Send + Sync> {
|
||||
Box::new(v6::registration::FinalMessage {
|
||||
gateway_client: v6::registration::GatewayClient::new(
|
||||
private_key,
|
||||
self.pub_key().inner(),
|
||||
self.reply.gateway_data.private_ips,
|
||||
self.nonce(),
|
||||
),
|
||||
credential,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RegisteredResponse: Id + fmt::Debug {
|
||||
pub trait RegisteredResponse: Id + UpgradeModeStatus + fmt::Debug {
|
||||
fn private_ips(&self) -> IpPair;
|
||||
fn pub_key(&self) -> PeerPublicKey;
|
||||
fn wg_port(&self) -> u16;
|
||||
@@ -555,7 +950,8 @@ impl RegisteredResponse for v3::response::RegisteredResponse {
|
||||
}
|
||||
impl RegisteredResponse for v4::response::RegisteredResponse {
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.reply.private_ips.into()
|
||||
// v4 -> v5 -> v6
|
||||
v5::registration::IpPair::from(self.reply.private_ips).into()
|
||||
}
|
||||
|
||||
fn pub_key(&self) -> PeerPublicKey {
|
||||
@@ -568,6 +964,20 @@ impl RegisteredResponse for v4::response::RegisteredResponse {
|
||||
}
|
||||
|
||||
impl RegisteredResponse for v5::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 v6::response::RegisteredResponse {
|
||||
fn private_ips(&self) -> IpPair {
|
||||
self.reply.private_ips
|
||||
}
|
||||
@@ -581,7 +991,7 @@ impl RegisteredResponse for v5::response::RegisteredResponse {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RemainingBandwidthResponse: Id + fmt::Debug {
|
||||
pub trait RemainingBandwidthResponse: Id + UpgradeModeStatus + fmt::Debug {
|
||||
fn available_bandwidth(&self) -> Option<i64>;
|
||||
}
|
||||
|
||||
@@ -609,7 +1019,13 @@ impl RemainingBandwidthResponse for v5::response::RemainingBandwidthResponse {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TopUpBandwidthResponse: Id + fmt::Debug {
|
||||
impl RemainingBandwidthResponse for v6::response::RemainingBandwidthResponse {
|
||||
fn available_bandwidth(&self) -> Option<i64> {
|
||||
self.reply.as_ref().map(|r| r.available_bandwidth)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TopUpBandwidthResponse: Id + UpgradeModeStatus + fmt::Debug {
|
||||
fn available_bandwidth(&self) -> i64;
|
||||
}
|
||||
|
||||
@@ -630,3 +1046,9 @@ impl TopUpBandwidthResponse for v5::response::TopUpBandwidthResponse {
|
||||
self.reply.available_bandwidth
|
||||
}
|
||||
}
|
||||
|
||||
impl TopUpBandwidthResponse for v6::response::TopUpBandwidthResponse {
|
||||
fn available_bandwidth(&self) -> i64 {
|
||||
self.reply.available_bandwidth
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::Error;
|
||||
use base64::{engine::general_purpose, Engine};
|
||||
use base64::{Engine, engine::general_purpose};
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
@@ -48,7 +48,7 @@ pub struct RegistrationData {
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct RegistredData {
|
||||
pub struct RegisteredData {
|
||||
pub pub_key: PeerPublicKey,
|
||||
pub private_ip: IpAddr,
|
||||
pub wg_port: u16,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::registration::{RegistrationData, RegistredData, RemainingBandwidthData};
|
||||
use super::registration::{RegisteredData, RegistrationData, RemainingBandwidthData};
|
||||
use nym_sphinx::addressing::Recipient;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -34,7 +34,7 @@ impl AuthenticatorResponse {
|
||||
}
|
||||
|
||||
pub fn new_registered(
|
||||
registred_data: RegistredData,
|
||||
registred_data: RegisteredData,
|
||||
reply_to: Recipient,
|
||||
request_id: u64,
|
||||
) -> Self {
|
||||
@@ -108,7 +108,7 @@ pub struct PendingRegistrationResponse {
|
||||
pub struct RegisteredResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
pub reply: RegistredData,
|
||||
pub reply: RegisteredData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
|
||||
@@ -154,8 +154,8 @@ impl From<v2::registration::RegistrationData> for v1::registration::Registration
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v2::registration::RegistredData> for v1::registration::RegistredData {
|
||||
fn from(value: v2::registration::RegistredData) -> Self {
|
||||
impl From<v2::registration::RegisteredData> for v1::registration::RegisteredData {
|
||||
fn from(value: v2::registration::RegisteredData) -> Self {
|
||||
Self {
|
||||
pub_key: value.pub_key,
|
||||
private_ip: value.private_ip,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::Error;
|
||||
use base64::{engine::general_purpose, Engine};
|
||||
use base64::{Engine, engine::general_purpose};
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -58,7 +58,7 @@ pub struct RegistrationData {
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct RegistredData {
|
||||
pub struct RegisteredData {
|
||||
pub pub_key: PeerPublicKey,
|
||||
pub private_ip: IpAddr,
|
||||
pub wg_port: u16,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::registration::{RegistrationData, RegistredData, RemainingBandwidthData};
|
||||
use super::registration::{RegisteredData, RegistrationData, RemainingBandwidthData};
|
||||
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
|
||||
use nym_sphinx::addressing::Recipient;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -38,7 +38,7 @@ impl AuthenticatorResponse {
|
||||
}
|
||||
|
||||
pub fn new_registered(
|
||||
registred_data: RegistredData,
|
||||
registred_data: RegisteredData,
|
||||
reply_to: Recipient,
|
||||
request_id: u64,
|
||||
) -> Self {
|
||||
@@ -118,7 +118,7 @@ pub struct PendingRegistrationResponse {
|
||||
pub struct RegisteredResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
pub reply: RegistredData,
|
||||
pub reply: RegisteredData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
|
||||
@@ -299,8 +299,8 @@ impl From<v2::registration::RegistrationData> for v3::registration::Registration
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v3::registration::RegistredData> for v2::registration::RegistredData {
|
||||
fn from(value: v3::registration::RegistredData) -> Self {
|
||||
impl From<v3::registration::RegisteredData> for v2::registration::RegisteredData {
|
||||
fn from(value: v3::registration::RegisteredData) -> Self {
|
||||
Self {
|
||||
pub_key: value.pub_key,
|
||||
private_ip: value.private_ip,
|
||||
@@ -309,8 +309,8 @@ impl From<v3::registration::RegistredData> for v2::registration::RegistredData {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v2::registration::RegistredData> for v3::registration::RegistredData {
|
||||
fn from(value: v2::registration::RegistredData) -> Self {
|
||||
impl From<v2::registration::RegisteredData> for v3::registration::RegisteredData {
|
||||
fn from(value: v2::registration::RegisteredData) -> Self {
|
||||
Self {
|
||||
pub_key: value.pub_key,
|
||||
private_ip: value.private_ip,
|
||||
@@ -674,7 +674,7 @@ mod tests {
|
||||
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
|
||||
let private_ip = IpAddr::from_str("10.10.10.10").unwrap();
|
||||
let wg_port = 51822;
|
||||
let registred_data = v2::registration::RegistredData {
|
||||
let registred_data = v2::registration::RegisteredData {
|
||||
pub_key,
|
||||
private_ip,
|
||||
wg_port,
|
||||
@@ -701,7 +701,7 @@ mod tests {
|
||||
v3::response::AuthenticatorResponseData::Registered(v3::response::RegisteredResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: v3::registration::RegistredData {
|
||||
reply: v3::registration::RegisteredData {
|
||||
wg_port,
|
||||
pub_key,
|
||||
private_ip
|
||||
@@ -715,7 +715,7 @@ mod tests {
|
||||
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
|
||||
let private_ip = IpAddr::from_str("10.10.10.10").unwrap();
|
||||
let wg_port = 51822;
|
||||
let registred_data = v3::registration::RegistredData {
|
||||
let registred_data = v3::registration::RegisteredData {
|
||||
pub_key,
|
||||
private_ip,
|
||||
wg_port,
|
||||
@@ -742,7 +742,7 @@ mod tests {
|
||||
v2::response::AuthenticatorResponseData::Registered(v2::response::RegisteredResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: v2::registration::RegistredData {
|
||||
reply: v2::registration::RegisteredData {
|
||||
wg_port,
|
||||
pub_key,
|
||||
private_ip
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::Error;
|
||||
use base64::{engine::general_purpose, Engine};
|
||||
use base64::{Engine, engine::general_purpose};
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -58,7 +58,7 @@ pub struct RegistrationData {
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct RegistredData {
|
||||
pub struct RegisteredData {
|
||||
pub pub_key: PeerPublicKey,
|
||||
pub private_ip: IpAddr,
|
||||
pub wg_port: u16,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::registration::{RegistrationData, RegistredData, RemainingBandwidthData};
|
||||
use super::registration::{RegisteredData, RegistrationData, RemainingBandwidthData};
|
||||
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
|
||||
use nym_sphinx::addressing::Recipient;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -38,7 +38,7 @@ impl AuthenticatorResponse {
|
||||
}
|
||||
|
||||
pub fn new_registered(
|
||||
registred_data: RegistredData,
|
||||
registred_data: RegisteredData,
|
||||
reply_to: Recipient,
|
||||
request_id: u64,
|
||||
) -> Self {
|
||||
@@ -139,7 +139,7 @@ pub struct PendingRegistrationResponse {
|
||||
pub struct RegisteredResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
pub reply: RegistredData,
|
||||
pub reply: RegisteredData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
|
||||
@@ -262,8 +262,8 @@ impl From<v4::response::TopUpBandwidthResponse> for v3::response::TopUpBandwidth
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v3::registration::RegistredData> for v4::registration::RegistredData {
|
||||
fn from(value: v3::registration::RegistredData) -> Self {
|
||||
impl From<v3::registration::RegisteredData> for v4::registration::RegisteredData {
|
||||
fn from(value: v3::registration::RegisteredData) -> Self {
|
||||
Self {
|
||||
pub_key: value.pub_key,
|
||||
private_ips: value.private_ip.into(),
|
||||
@@ -272,8 +272,8 @@ impl From<v3::registration::RegistredData> for v4::registration::RegistredData {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::registration::RegistredData> for v3::registration::RegistredData {
|
||||
fn from(value: v4::registration::RegistredData) -> Self {
|
||||
impl From<v4::registration::RegisteredData> for v3::registration::RegisteredData {
|
||||
fn from(value: v4::registration::RegisteredData) -> Self {
|
||||
Self {
|
||||
pub_key: value.pub_key,
|
||||
private_ip: value.private_ips.ipv4.into(),
|
||||
@@ -565,7 +565,7 @@ mod tests {
|
||||
let private_ips =
|
||||
v4::registration::IpPair::new(ipv4, Ipv6Addr::from_str("fc01::a0a").unwrap());
|
||||
let wg_port = 51822;
|
||||
let registred_data = v3::registration::RegistredData {
|
||||
let registred_data = v3::registration::RegisteredData {
|
||||
pub_key,
|
||||
private_ip: ipv4.into(),
|
||||
wg_port,
|
||||
@@ -592,7 +592,7 @@ mod tests {
|
||||
v4::response::AuthenticatorResponseData::Registered(v4::response::RegisteredResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: v4::registration::RegistredData {
|
||||
reply: v4::registration::RegisteredData {
|
||||
wg_port,
|
||||
pub_key,
|
||||
private_ips
|
||||
@@ -608,7 +608,7 @@ mod tests {
|
||||
let private_ips =
|
||||
v4::registration::IpPair::new(ipv4, Ipv6Addr::from_str("fc01::10").unwrap());
|
||||
let wg_port = 51822;
|
||||
let registred_data = v4::registration::RegistredData {
|
||||
let registred_data = v4::registration::RegisteredData {
|
||||
pub_key,
|
||||
private_ips,
|
||||
wg_port,
|
||||
@@ -635,7 +635,7 @@ mod tests {
|
||||
v3::response::AuthenticatorResponseData::Registered(v3::response::RegisteredResponse {
|
||||
request_id,
|
||||
reply_to,
|
||||
reply: v3::registration::RegistredData {
|
||||
reply: v3::registration::RegisteredData {
|
||||
wg_port,
|
||||
pub_key,
|
||||
private_ip: ipv4.into()
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::Error;
|
||||
use base64::{engine::general_purpose, Engine};
|
||||
use base64::{Engine, engine::general_purpose};
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
use nym_network_defaults::constants::{WG_TUN_DEVICE_IP_ADDRESS_V4, WG_TUN_DEVICE_IP_ADDRESS_V6};
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
@@ -110,7 +110,7 @@ pub struct RegistrationData {
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct RegistredData {
|
||||
pub struct RegisteredData {
|
||||
pub pub_key: PeerPublicKey,
|
||||
pub private_ips: IpPair,
|
||||
pub wg_port: u16,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::registration::{RegistrationData, RegistredData, RemainingBandwidthData};
|
||||
use super::registration::{RegisteredData, RegistrationData, RemainingBandwidthData};
|
||||
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
|
||||
use nym_sphinx::addressing::Recipient;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -38,7 +38,7 @@ impl AuthenticatorResponse {
|
||||
}
|
||||
|
||||
pub fn new_registered(
|
||||
registred_data: RegistredData,
|
||||
registred_data: RegisteredData,
|
||||
reply_to: Recipient,
|
||||
request_id: u64,
|
||||
) -> Self {
|
||||
@@ -139,7 +139,7 @@ pub struct PendingRegistrationResponse {
|
||||
pub struct RegisteredResponse {
|
||||
pub request_id: u64,
|
||||
pub reply_to: Recipient,
|
||||
pub reply: RegistredData,
|
||||
pub reply: RegisteredData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
|
||||
@@ -186,8 +186,8 @@ impl From<v4::response::TopUpBandwidthResponse> for v5::response::TopUpBandwidth
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v4::registration::RegistredData> for v5::registration::RegistredData {
|
||||
fn from(value: v4::registration::RegistredData) -> Self {
|
||||
impl From<v4::registration::RegisteredData> for v5::registration::RegisteredData {
|
||||
fn from(value: v4::registration::RegisteredData) -> Self {
|
||||
Self {
|
||||
pub_key: value.pub_key,
|
||||
private_ips: value.private_ips.into(),
|
||||
@@ -405,7 +405,7 @@ mod tests {
|
||||
let ipv6 = Ipv6Addr::from_str("fc01::a0a").unwrap();
|
||||
let private_ips = v4::registration::IpPair::new(ipv4, ipv6);
|
||||
let wg_port = 51822;
|
||||
let registred_data = v4::registration::RegistredData {
|
||||
let registred_data = v4::registration::RegisteredData {
|
||||
pub_key,
|
||||
private_ips,
|
||||
wg_port,
|
||||
@@ -431,7 +431,7 @@ mod tests {
|
||||
upgraded_msg.data,
|
||||
v5::response::AuthenticatorResponseData::Registered(v5::response::RegisteredResponse {
|
||||
request_id,
|
||||
reply: v5::registration::RegistredData {
|
||||
reply: v5::registration::RegisteredData {
|
||||
wg_port,
|
||||
pub_key,
|
||||
private_ips: v5::registration::IpPair::new(ipv4, ipv6)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::Error;
|
||||
use base64::{engine::general_purpose, Engine};
|
||||
use base64::{Engine, engine::general_purpose};
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
use nym_network_defaults::constants::{WG_TUN_DEVICE_IP_ADDRESS_V4, WG_TUN_DEVICE_IP_ADDRESS_V6};
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
@@ -108,7 +108,7 @@ pub struct RegistrationData {
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct RegistredData {
|
||||
pub struct RegisteredData {
|
||||
pub pub_key: PeerPublicKey,
|
||||
pub private_ips: IpPair,
|
||||
pub wg_port: u16,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::registration::{RegistrationData, RegistredData, RemainingBandwidthData};
|
||||
use super::registration::{RegisteredData, RegistrationData, RemainingBandwidthData};
|
||||
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -32,7 +32,7 @@ impl AuthenticatorResponse {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_registered(registred_data: RegistredData, request_id: u64) -> Self {
|
||||
pub fn new_registered(registred_data: RegisteredData, request_id: u64) -> Self {
|
||||
Self {
|
||||
protocol: Protocol {
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
@@ -116,7 +116,7 @@ pub struct PendingRegistrationResponse {
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct RegisteredResponse {
|
||||
pub request_id: u64,
|
||||
pub reply: RegistredData,
|
||||
pub reply: RegisteredData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
|
||||
@@ -0,0 +1,441 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{v5, v6};
|
||||
|
||||
impl TryFrom<v5::request::AuthenticatorRequest> for v6::request::AuthenticatorRequest {
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(
|
||||
authenticator_request: v5::request::AuthenticatorRequest,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
protocol: v6::PROTOCOL,
|
||||
data: authenticator_request.data.try_into()?,
|
||||
request_id: authenticator_request.request_id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<v5::request::AuthenticatorRequestData> for v6::request::AuthenticatorRequestData {
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(
|
||||
authenticator_request_data: v5::request::AuthenticatorRequestData,
|
||||
) -> Result<Self, Self::Error> {
|
||||
match authenticator_request_data {
|
||||
v5::request::AuthenticatorRequestData::Initial(init_msg) => Ok(
|
||||
v6::request::AuthenticatorRequestData::Initial(init_msg.into()),
|
||||
),
|
||||
v5::request::AuthenticatorRequestData::Final(final_msg) => Ok(
|
||||
v6::request::AuthenticatorRequestData::Final(Box::new((*final_msg).try_into()?)),
|
||||
),
|
||||
v5::request::AuthenticatorRequestData::QueryBandwidth(pub_key) => Ok(
|
||||
v6::request::AuthenticatorRequestData::QueryBandwidth(pub_key),
|
||||
),
|
||||
v5::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => Ok(
|
||||
v6::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message.into()),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::registration::InitMessage> for v6::registration::InitMessage {
|
||||
fn from(init_msg: v5::registration::InitMessage) -> Self {
|
||||
Self {
|
||||
pub_key: init_msg.pub_key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<v5::registration::FinalMessage> for v6::registration::FinalMessage {
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(final_msg: v5::registration::FinalMessage) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
gateway_client: final_msg.gateway_client.into(),
|
||||
credential: final_msg
|
||||
.credential
|
||||
.map(TryInto::try_into)
|
||||
.transpose()
|
||||
.map_err(Self::Error::conversion_display)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::registration::GatewayClient> for v6::registration::GatewayClient {
|
||||
fn from(gateway_client: v5::registration::GatewayClient) -> Self {
|
||||
Self {
|
||||
pub_key: gateway_client.pub_key,
|
||||
private_ips: gateway_client.private_ips.into(),
|
||||
mac: gateway_client.mac.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v6::registration::GatewayClient> for v5::registration::GatewayClient {
|
||||
fn from(gateway_client: v6::registration::GatewayClient) -> Self {
|
||||
Self {
|
||||
pub_key: gateway_client.pub_key,
|
||||
private_ips: gateway_client.private_ips.into(),
|
||||
mac: gateway_client.mac.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::registration::ClientMac> for v6::registration::ClientMac {
|
||||
fn from(client_mac: v5::registration::ClientMac) -> Self {
|
||||
Self::new((*client_mac).clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v6::registration::ClientMac> for v5::registration::ClientMac {
|
||||
fn from(client_mac: v6::registration::ClientMac) -> Self {
|
||||
Self::new((*client_mac).clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Box<v5::topup::TopUpMessage>> for Box<v6::topup::TopUpMessage> {
|
||||
fn from(top_up_message: Box<v5::topup::TopUpMessage>) -> Self {
|
||||
Box::new(v6::topup::TopUpMessage {
|
||||
pub_key: top_up_message.pub_key,
|
||||
credential: top_up_message.credential,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::response::AuthenticatorResponse> for v6::response::AuthenticatorResponse {
|
||||
fn from(value: v5::response::AuthenticatorResponse) -> Self {
|
||||
Self {
|
||||
protocol: v6::PROTOCOL,
|
||||
data: value.data.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::response::AuthenticatorResponseData> for v6::response::AuthenticatorResponseData {
|
||||
fn from(authenticator_response_data: v5::response::AuthenticatorResponseData) -> Self {
|
||||
match authenticator_response_data {
|
||||
v5::response::AuthenticatorResponseData::PendingRegistration(pending_response) => {
|
||||
v6::response::AuthenticatorResponseData::PendingRegistration(
|
||||
pending_response.into(),
|
||||
)
|
||||
}
|
||||
v5::response::AuthenticatorResponseData::Registered(registered_response) => {
|
||||
v6::response::AuthenticatorResponseData::Registered(registered_response.into())
|
||||
}
|
||||
v5::response::AuthenticatorResponseData::RemainingBandwidth(
|
||||
remaining_bandwidth_response,
|
||||
) => v6::response::AuthenticatorResponseData::RemainingBandwidth(
|
||||
remaining_bandwidth_response.into(),
|
||||
),
|
||||
v5::response::AuthenticatorResponseData::TopUpBandwidth(top_up_response) => {
|
||||
v6::response::AuthenticatorResponseData::TopUpBandwidth(top_up_response.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::response::RegisteredResponse> for v6::response::RegisteredResponse {
|
||||
fn from(value: v5::response::RegisteredResponse) -> Self {
|
||||
Self {
|
||||
request_id: value.request_id,
|
||||
reply: value.reply.into(),
|
||||
upgrade_mode_enabled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::response::PendingRegistrationResponse> for v6::response::PendingRegistrationResponse {
|
||||
fn from(value: v5::response::PendingRegistrationResponse) -> Self {
|
||||
Self {
|
||||
request_id: value.request_id,
|
||||
reply: value.reply.into(),
|
||||
upgrade_mode_enabled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::registration::RegistrationData> for v6::registration::RegistrationData {
|
||||
fn from(value: v5::registration::RegistrationData) -> Self {
|
||||
Self {
|
||||
nonce: value.nonce,
|
||||
gateway_data: value.gateway_data.into(),
|
||||
wg_port: value.wg_port,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v6::registration::RegistrationData> for v5::registration::RegistrationData {
|
||||
fn from(value: v6::registration::RegistrationData) -> Self {
|
||||
Self {
|
||||
nonce: value.nonce,
|
||||
gateway_data: value.gateway_data.into(),
|
||||
wg_port: value.wg_port,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::response::RemainingBandwidthResponse> for v6::response::RemainingBandwidthResponse {
|
||||
fn from(value: v5::response::RemainingBandwidthResponse) -> Self {
|
||||
Self {
|
||||
request_id: value.request_id,
|
||||
reply: value.reply.map(Into::into),
|
||||
upgrade_mode_enabled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::response::TopUpBandwidthResponse> for v6::response::TopUpBandwidthResponse {
|
||||
fn from(value: v5::response::TopUpBandwidthResponse) -> Self {
|
||||
Self {
|
||||
request_id: value.request_id,
|
||||
reply: value.reply.into(),
|
||||
upgrade_mode_enabled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::registration::RegisteredData> for v6::registration::RegisteredData {
|
||||
fn from(value: v5::registration::RegisteredData) -> Self {
|
||||
Self {
|
||||
pub_key: value.pub_key,
|
||||
private_ips: value.private_ips.into(),
|
||||
wg_port: value.wg_port,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::registration::RemainingBandwidthData> for v6::registration::RemainingBandwidthData {
|
||||
fn from(value: v5::registration::RemainingBandwidthData) -> Self {
|
||||
Self {
|
||||
available_bandwidth: value.available_bandwidth,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v5::registration::IpPair> for v6::registration::IpPair {
|
||||
fn from(value: v5::registration::IpPair) -> Self {
|
||||
Self {
|
||||
ipv4: value.ipv4,
|
||||
ipv6: value.ipv6,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<v6::registration::IpPair> for v5::registration::IpPair {
|
||||
fn from(value: v6::registration::IpPair) -> Self {
|
||||
Self {
|
||||
ipv4: value.ipv4,
|
||||
ipv6: value.ipv6,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{
|
||||
net::{Ipv4Addr, Ipv6Addr},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use nym_credentials_interface::{BandwidthCredential, CredentialSpendingData, TicketType};
|
||||
use nym_crypto::asymmetric::x25519::PrivateKey;
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
use x25519_dalek::PublicKey;
|
||||
|
||||
use super::*;
|
||||
use crate::models::BandwidthClaim;
|
||||
use crate::{util::tests::CREDENTIAL_BYTES, v5};
|
||||
|
||||
#[test]
|
||||
fn upgrade_initial_req() {
|
||||
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
|
||||
|
||||
let (msg, _) = v5::request::AuthenticatorRequest::new_initial_request(
|
||||
v5::registration::InitMessage::new(pub_key),
|
||||
);
|
||||
let upgraded_msg = v6::request::AuthenticatorRequest::try_from(msg).unwrap();
|
||||
|
||||
assert_eq!(upgraded_msg.protocol, v6::PROTOCOL);
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v6::request::AuthenticatorRequestData::Initial(v6::registration::InitMessage {
|
||||
pub_key
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_final_req() {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let local_secret = PrivateKey::new(&mut rng);
|
||||
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
|
||||
let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap();
|
||||
let ipv6 = Ipv6Addr::from_str("fc01::a0a").unwrap();
|
||||
let ips = v5::registration::IpPair::new(ipv4, ipv6);
|
||||
let nonce = 42;
|
||||
let gateway_client = v5::registration::GatewayClient::new(
|
||||
&local_secret,
|
||||
(&remote_secret).into(),
|
||||
ips,
|
||||
nonce,
|
||||
);
|
||||
let credential = CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap();
|
||||
let final_message = v5::registration::FinalMessage {
|
||||
gateway_client: gateway_client.clone(),
|
||||
credential: Some(credential.clone()),
|
||||
};
|
||||
|
||||
let (msg, _) = v5::request::AuthenticatorRequest::new_final_request(final_message);
|
||||
let upgraded_msg = v6::request::AuthenticatorRequest::try_from(msg).unwrap();
|
||||
|
||||
assert_eq!(upgraded_msg.protocol, v6::PROTOCOL);
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v6::request::AuthenticatorRequestData::Final(Box::new(
|
||||
v6::registration::FinalMessage {
|
||||
gateway_client: v6::registration::GatewayClient::new(
|
||||
&local_secret,
|
||||
(&remote_secret).into(),
|
||||
v6::registration::IpPair::new(ipv4, ipv6),
|
||||
nonce
|
||||
),
|
||||
credential: Some(BandwidthClaim {
|
||||
credential: BandwidthCredential::ZkNym(Box::new(credential)),
|
||||
kind: TicketType::V1MixnetEntry,
|
||||
})
|
||||
}
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_query_req() {
|
||||
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
|
||||
|
||||
let (msg, _) = v5::request::AuthenticatorRequest::new_query_request(pub_key);
|
||||
let upgraded_msg = v6::request::AuthenticatorRequest::try_from(msg).unwrap();
|
||||
|
||||
assert_eq!(upgraded_msg.protocol, v6::PROTOCOL);
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v6::request::AuthenticatorRequestData::QueryBandwidth(pub_key)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_pending_reg_resp() {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let local_secret = PrivateKey::new(&mut rng);
|
||||
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
|
||||
let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap();
|
||||
let ipv6 = Ipv6Addr::from_str("fc01::a0a").unwrap();
|
||||
let ips = v5::registration::IpPair::new(ipv4, ipv6);
|
||||
let nonce = 42;
|
||||
let wg_port = 51822;
|
||||
let gateway_data = v5::registration::GatewayClient::new(
|
||||
&local_secret,
|
||||
(&remote_secret).into(),
|
||||
ips,
|
||||
nonce,
|
||||
);
|
||||
let registration_data = v5::registration::RegistrationData {
|
||||
nonce,
|
||||
gateway_data,
|
||||
wg_port,
|
||||
};
|
||||
let request_id = 123;
|
||||
|
||||
let msg = v5::response::AuthenticatorResponse::new_pending_registration_success(
|
||||
registration_data,
|
||||
request_id,
|
||||
);
|
||||
let upgraded_msg = v6::response::AuthenticatorResponse::from(msg);
|
||||
|
||||
assert_eq!(upgraded_msg.protocol, v6::PROTOCOL);
|
||||
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v6::response::AuthenticatorResponseData::PendingRegistration(
|
||||
v6::response::PendingRegistrationResponse {
|
||||
request_id,
|
||||
reply: v6::registration::RegistrationData {
|
||||
nonce,
|
||||
gateway_data: v6::registration::GatewayClient::new(
|
||||
&local_secret,
|
||||
(&remote_secret).into(),
|
||||
v6::registration::IpPair::new(ipv4, ipv6),
|
||||
nonce
|
||||
),
|
||||
wg_port
|
||||
},
|
||||
upgrade_mode_enabled: false,
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_registered_resp() {
|
||||
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
|
||||
let ipv4 = Ipv4Addr::from_str("10.1.10.10").unwrap();
|
||||
let ipv6 = Ipv6Addr::from_str("fc01::a0a").unwrap();
|
||||
let private_ips = v5::registration::IpPair::new(ipv4, ipv6);
|
||||
let wg_port = 51822;
|
||||
let registered_data = v5::registration::RegisteredData {
|
||||
pub_key,
|
||||
private_ips,
|
||||
wg_port,
|
||||
};
|
||||
let request_id = 123;
|
||||
|
||||
let msg = v5::response::AuthenticatorResponse::new_registered(registered_data, request_id);
|
||||
let upgraded_msg = v6::response::AuthenticatorResponse::from(msg);
|
||||
|
||||
assert_eq!(upgraded_msg.protocol, v6::PROTOCOL);
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v6::response::AuthenticatorResponseData::Registered(v6::response::RegisteredResponse {
|
||||
request_id,
|
||||
reply: v6::registration::RegisteredData {
|
||||
wg_port,
|
||||
pub_key,
|
||||
private_ips: v6::registration::IpPair::new(ipv4, ipv6)
|
||||
},
|
||||
upgrade_mode_enabled: false,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upgrade_remaining_bandwidth_resp() {
|
||||
let available_bandwidth = 42;
|
||||
let remaining_bandwidth_data = Some(v5::registration::RemainingBandwidthData {
|
||||
available_bandwidth,
|
||||
});
|
||||
let request_id = 123;
|
||||
|
||||
let msg = v5::response::AuthenticatorResponse::new_remaining_bandwidth(
|
||||
remaining_bandwidth_data,
|
||||
request_id,
|
||||
);
|
||||
let upgraded_msg = v6::response::AuthenticatorResponse::from(msg);
|
||||
|
||||
assert_eq!(upgraded_msg.protocol, v6::PROTOCOL);
|
||||
assert_eq!(
|
||||
upgraded_msg.data,
|
||||
v6::response::AuthenticatorResponseData::RemainingBandwidth(
|
||||
v6::response::RemainingBandwidthResponse {
|
||||
request_id,
|
||||
reply: Some(v6::registration::RemainingBandwidthData {
|
||||
available_bandwidth,
|
||||
}),
|
||||
upgrade_mode_enabled: false,
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
|
||||
|
||||
pub mod conversion;
|
||||
pub mod registration;
|
||||
pub mod request;
|
||||
pub mod response;
|
||||
pub mod topup;
|
||||
pub mod upgrade_mode_check;
|
||||
|
||||
pub const VERSION: u8 = 6;
|
||||
|
||||
pub const PROTOCOL: Protocol = Protocol::new(VERSION, ServiceProviderType::Authenticator);
|
||||
@@ -0,0 +1,287 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::models::BandwidthClaim;
|
||||
use base64::{Engine, engine::general_purpose};
|
||||
use nym_network_defaults::constants::{WG_TUN_DEVICE_IP_ADDRESS_V4, WG_TUN_DEVICE_IP_ADDRESS_V6};
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
use std::time::SystemTime;
|
||||
use std::{fmt, ops::Deref, str::FromStr};
|
||||
|
||||
#[cfg(feature = "verify")]
|
||||
use hmac::{Hmac, Mac};
|
||||
#[cfg(feature = "verify")]
|
||||
use nym_crypto::asymmetric::x25519::{PrivateKey, PublicKey};
|
||||
#[cfg(feature = "verify")]
|
||||
use sha2::Sha256;
|
||||
|
||||
pub type PendingRegistrations = HashMap<PeerPublicKey, RegistrationData>;
|
||||
pub type PrivateIPs = HashMap<IpPair, Taken>;
|
||||
|
||||
#[cfg(feature = "verify")]
|
||||
pub type HmacSha256 = Hmac<Sha256>;
|
||||
|
||||
pub type Nonce = u64;
|
||||
pub type Taken = Option<SystemTime>;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct IpPair {
|
||||
pub ipv4: Ipv4Addr,
|
||||
pub ipv6: Ipv6Addr,
|
||||
}
|
||||
|
||||
impl IpPair {
|
||||
pub fn new(ipv4: Ipv4Addr, ipv6: Ipv6Addr) -> Self {
|
||||
IpPair { ipv4, ipv6 }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(Ipv4Addr, Ipv6Addr)> for IpPair {
|
||||
fn from((ipv4, ipv6): (Ipv4Addr, Ipv6Addr)) -> Self {
|
||||
IpPair { ipv4, ipv6 }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for IpPair {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "({}, {})", self.ipv4, self.ipv6)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IpAddr> for IpPair {
|
||||
fn from(value: IpAddr) -> Self {
|
||||
let (before_last_byte, last_byte) = match value {
|
||||
IpAddr::V4(ipv4_addr) => (ipv4_addr.octets()[2], ipv4_addr.octets()[3]),
|
||||
IpAddr::V6(ipv6_addr) => (ipv6_addr.octets()[14], ipv6_addr.octets()[15]),
|
||||
};
|
||||
let last_bytes = ((before_last_byte as u16) << 8) | last_byte as u16;
|
||||
let ipv4 = Ipv4Addr::new(
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V4.octets()[0],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V4.octets()[1],
|
||||
before_last_byte,
|
||||
last_byte,
|
||||
);
|
||||
let ipv6 = Ipv6Addr::new(
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[0],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[1],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[2],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[3],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[4],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[5],
|
||||
WG_TUN_DEVICE_IP_ADDRESS_V6.segments()[6],
|
||||
last_bytes,
|
||||
);
|
||||
IpPair::new(ipv4, ipv6)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct InitMessage {
|
||||
/// Base64 encoded x25519 public key
|
||||
pub pub_key: PeerPublicKey,
|
||||
}
|
||||
|
||||
impl InitMessage {
|
||||
pub fn new(pub_key: PeerPublicKey) -> Self {
|
||||
InitMessage { pub_key }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct FinalMessage {
|
||||
/// Gateway client data
|
||||
pub gateway_client: GatewayClient,
|
||||
|
||||
/// Ecash credential
|
||||
pub credential: Option<BandwidthClaim>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct RegistrationData {
|
||||
pub nonce: u64,
|
||||
pub gateway_data: GatewayClient,
|
||||
pub wg_port: u16,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct RegisteredData {
|
||||
pub pub_key: PeerPublicKey,
|
||||
pub private_ips: IpPair,
|
||||
pub wg_port: u16,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct RemainingBandwidthData {
|
||||
pub available_bandwidth: i64,
|
||||
}
|
||||
|
||||
/// Client that wants to register sends its PublicKey bytes mac digest encrypted with a DH shared secret.
|
||||
/// Gateway/Nym node can then verify pub_key payload using the same process
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct GatewayClient {
|
||||
/// Base64 encoded x25519 public key
|
||||
pub pub_key: PeerPublicKey,
|
||||
|
||||
/// Assigned private IPs (v4 and v6)
|
||||
pub private_ips: IpPair,
|
||||
|
||||
/// Sha256 hmac on the data (alongside the prior nonce)
|
||||
pub mac: ClientMac,
|
||||
}
|
||||
|
||||
impl GatewayClient {
|
||||
#[cfg(feature = "verify")]
|
||||
pub fn new(
|
||||
local_secret: &PrivateKey,
|
||||
remote_public: x25519_dalek::PublicKey,
|
||||
private_ips: IpPair,
|
||||
nonce: u64,
|
||||
) -> Self {
|
||||
let local_public = PublicKey::from(local_secret);
|
||||
let remote_public = PublicKey::from(remote_public);
|
||||
|
||||
let dh = local_secret.diffie_hellman(&remote_public);
|
||||
|
||||
// TODO: change that to use our nym_crypto::hmac module instead
|
||||
#[allow(clippy::expect_used)]
|
||||
let mut mac = HmacSha256::new_from_slice(&dh[..])
|
||||
.expect("x25519 shared secret is always 32 bytes long");
|
||||
|
||||
mac.update(local_public.as_bytes());
|
||||
mac.update(private_ips.to_string().as_bytes());
|
||||
mac.update(&nonce.to_le_bytes());
|
||||
|
||||
GatewayClient {
|
||||
pub_key: PeerPublicKey::new(local_public.into()),
|
||||
private_ips,
|
||||
mac: ClientMac(mac.finalize().into_bytes().to_vec()),
|
||||
}
|
||||
}
|
||||
|
||||
// Reusable secret should be gateways Wireguard PK
|
||||
// Client should perform this step when generating its payload, using its own WG PK
|
||||
#[cfg(feature = "verify")]
|
||||
pub fn verify(&self, gateway_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
|
||||
// use gateways key as a ref to an x25519_dalek key
|
||||
let dh = gateway_key.inner().diffie_hellman(&self.pub_key);
|
||||
|
||||
// TODO: change that to use our nym_crypto::hmac module instead
|
||||
#[allow(clippy::expect_used)]
|
||||
let mut mac = HmacSha256::new_from_slice(dh.as_bytes())
|
||||
.expect("x25519 shared secret is always 32 bytes long");
|
||||
|
||||
mac.update(self.pub_key.as_bytes());
|
||||
mac.update(self.private_ips.to_string().as_bytes());
|
||||
mac.update(&nonce.to_le_bytes());
|
||||
|
||||
mac.verify_slice(&self.mac)
|
||||
.map_err(|source| Error::FailedClientMacVerification {
|
||||
client: self.pub_key.to_string(),
|
||||
source,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn pub_key(&self) -> PeerPublicKey {
|
||||
self.pub_key
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: change the inner type into generic array of size HmacSha256::OutputSize
|
||||
// TODO2: rely on our internal crypto/hmac
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ClientMac(Vec<u8>);
|
||||
|
||||
impl fmt::Display for ClientMac {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", general_purpose::STANDARD.encode(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for ClientMac {
|
||||
fn from(v: Vec<u8>) -> Self {
|
||||
ClientMac(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientMac {
|
||||
#[allow(dead_code)]
|
||||
pub fn new(mac: Vec<u8>) -> Self {
|
||||
ClientMac(mac)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ClientMac {
|
||||
type Target = Vec<u8>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for ClientMac {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mac_bytes: Vec<u8> =
|
||||
general_purpose::STANDARD
|
||||
.decode(s)
|
||||
.map_err(|source| Error::MalformedClientMac {
|
||||
mac: s.to_string(),
|
||||
source,
|
||||
})?;
|
||||
|
||||
Ok(ClientMac(mac_bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for ClientMac {
|
||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let encoded_key = general_purpose::STANDARD.encode(self.0.clone());
|
||||
serializer.serialize_str(&encoded_key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for ClientMac {
|
||||
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
let encoded_key = String::deserialize(deserializer)?;
|
||||
ClientMac::from_str(&encoded_key).map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use nym_crypto::asymmetric::x25519;
|
||||
use nym_test_utils::helpers::deterministic_rng;
|
||||
|
||||
#[test]
|
||||
fn create_ip_pair() {
|
||||
let ipv4: IpAddr = Ipv4Addr::from_str("10.1.10.50").unwrap().into();
|
||||
let ipv6: IpAddr = Ipv6Addr::from_str("fc01::0a32").unwrap().into();
|
||||
|
||||
assert_eq!(IpPair::from(ipv4), IpPair::from(ipv6));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "verify")]
|
||||
fn client_request_roundtrip() {
|
||||
let mut rng = deterministic_rng();
|
||||
|
||||
let gateway_key_pair = x25519::KeyPair::new(&mut rng);
|
||||
let client_key_pair = x25519::KeyPair::new(&mut rng);
|
||||
|
||||
let nonce = 1234567890;
|
||||
|
||||
let client = GatewayClient::new(
|
||||
client_key_pair.private_key(),
|
||||
x25519_dalek::PublicKey::from(gateway_key_pair.public_key().to_bytes()),
|
||||
IpPair::new("10.0.0.42".parse().unwrap(), "fc00::42".parse().unwrap()),
|
||||
nonce,
|
||||
);
|
||||
assert!(client.verify(gateway_key_pair.private_key(), nonce).is_ok())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::{
|
||||
PROTOCOL,
|
||||
registration::{FinalMessage, InitMessage},
|
||||
topup::TopUpMessage,
|
||||
upgrade_mode_check::UpgradeModeCheckRequest,
|
||||
};
|
||||
use nym_service_provider_requests_common::Protocol;
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::make_bincode_serializer;
|
||||
|
||||
fn generate_random() -> u64 {
|
||||
use rand::RngCore;
|
||||
let mut rng = rand::rngs::OsRng;
|
||||
rng.next_u64()
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct AuthenticatorRequest {
|
||||
pub protocol: Protocol,
|
||||
pub data: AuthenticatorRequestData,
|
||||
pub request_id: u64,
|
||||
}
|
||||
|
||||
impl AuthenticatorRequest {
|
||||
pub fn from_reconstructed_message(
|
||||
message: &nym_sphinx::receiver::ReconstructedMessage,
|
||||
) -> Result<Self, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().deserialize(&message.message)
|
||||
}
|
||||
|
||||
pub fn new_initial_request(init_message: InitMessage) -> (Self, u64) {
|
||||
let request_id = generate_random();
|
||||
(
|
||||
Self {
|
||||
protocol: PROTOCOL,
|
||||
data: AuthenticatorRequestData::Initial(init_message),
|
||||
request_id,
|
||||
},
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_final_request(final_message: FinalMessage) -> (Self, u64) {
|
||||
let request_id = generate_random();
|
||||
(
|
||||
Self {
|
||||
protocol: PROTOCOL,
|
||||
data: AuthenticatorRequestData::Final(Box::new(final_message)),
|
||||
request_id,
|
||||
},
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_query_request(peer_public_key: PeerPublicKey) -> (Self, u64) {
|
||||
let request_id = generate_random();
|
||||
(
|
||||
Self {
|
||||
protocol: PROTOCOL,
|
||||
data: AuthenticatorRequestData::QueryBandwidth(peer_public_key),
|
||||
request_id,
|
||||
},
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_topup_request(top_up_message: TopUpMessage) -> (Self, u64) {
|
||||
let request_id = generate_random();
|
||||
(
|
||||
Self {
|
||||
protocol: PROTOCOL,
|
||||
data: AuthenticatorRequestData::TopUpBandwidth(Box::new(top_up_message)),
|
||||
request_id,
|
||||
},
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_upgrade_mode_check_request(message: UpgradeModeCheckRequest) -> (Self, u64) {
|
||||
let request_id = generate_random();
|
||||
(
|
||||
Self {
|
||||
protocol: PROTOCOL,
|
||||
data: AuthenticatorRequestData::CheckUpgradeMode(message),
|
||||
request_id,
|
||||
},
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Result<Vec<u8>, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().serialize(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub enum AuthenticatorRequestData {
|
||||
Initial(InitMessage),
|
||||
Final(Box<FinalMessage>),
|
||||
QueryBandwidth(PeerPublicKey),
|
||||
TopUpBandwidth(Box<TopUpMessage>),
|
||||
CheckUpgradeMode(UpgradeModeCheckRequest),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::VERSION;
|
||||
use super::*;
|
||||
use nym_service_provider_requests_common::ServiceProviderType;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[test]
|
||||
fn check_first_bytes_protocol() {
|
||||
let version = VERSION;
|
||||
let data = AuthenticatorRequest {
|
||||
protocol: Protocol {
|
||||
version,
|
||||
service_provider_type: ServiceProviderType::Authenticator,
|
||||
},
|
||||
data: AuthenticatorRequestData::Initial(InitMessage::new(
|
||||
PeerPublicKey::from_str("yvNUDpT5l7W/xDhiu6HkqTHDQwbs/B3J5UrLmORl1EQ=").unwrap(),
|
||||
)),
|
||||
request_id: 1,
|
||||
};
|
||||
let bytes = *data.to_bytes().unwrap().first_chunk::<2>().unwrap();
|
||||
assert_eq!(bytes, [version, ServiceProviderType::Authenticator as u8]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::registration::{RegisteredData, RegistrationData, RemainingBandwidthData};
|
||||
use nym_service_provider_requests_common::Protocol;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::make_bincode_serializer;
|
||||
|
||||
use super::PROTOCOL;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct AuthenticatorResponse {
|
||||
pub protocol: Protocol,
|
||||
pub data: AuthenticatorResponseData,
|
||||
}
|
||||
|
||||
impl AuthenticatorResponse {
|
||||
pub fn new_pending_registration_success(
|
||||
registration_data: RegistrationData,
|
||||
request_id: u64,
|
||||
upgrade_mode_enabled: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
protocol: PROTOCOL,
|
||||
data: AuthenticatorResponseData::PendingRegistration(PendingRegistrationResponse {
|
||||
reply: registration_data,
|
||||
request_id,
|
||||
upgrade_mode_enabled,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_registered(
|
||||
registered_data: RegisteredData,
|
||||
request_id: u64,
|
||||
upgrade_mode_enabled: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
protocol: PROTOCOL,
|
||||
data: AuthenticatorResponseData::Registered(RegisteredResponse {
|
||||
reply: registered_data,
|
||||
request_id,
|
||||
upgrade_mode_enabled,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_remaining_bandwidth(
|
||||
remaining_bandwidth_data: Option<RemainingBandwidthData>,
|
||||
request_id: u64,
|
||||
upgrade_mode_enabled: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
protocol: PROTOCOL,
|
||||
data: AuthenticatorResponseData::RemainingBandwidth(RemainingBandwidthResponse {
|
||||
reply: remaining_bandwidth_data,
|
||||
request_id,
|
||||
upgrade_mode_enabled,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_topup_bandwidth(
|
||||
remaining_bandwidth_data: RemainingBandwidthData,
|
||||
request_id: u64,
|
||||
upgrade_mode_enabled: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
protocol: PROTOCOL,
|
||||
data: AuthenticatorResponseData::TopUpBandwidth(TopUpBandwidthResponse {
|
||||
reply: remaining_bandwidth_data,
|
||||
request_id,
|
||||
upgrade_mode_enabled,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_upgrade_mode_check(request_id: u64, upgrade_mode_enabled: bool) -> Self {
|
||||
Self {
|
||||
protocol: PROTOCOL,
|
||||
data: AuthenticatorResponseData::UpgradeMode(UpgradeModeResponse {
|
||||
request_id,
|
||||
upgrade_mode_enabled,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Result<Vec<u8>, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().serialize(self)
|
||||
}
|
||||
|
||||
pub fn from_reconstructed_message(
|
||||
message: &nym_sphinx::receiver::ReconstructedMessage,
|
||||
) -> Result<Self, bincode::Error> {
|
||||
use bincode::Options;
|
||||
make_bincode_serializer().deserialize(&message.message)
|
||||
}
|
||||
|
||||
pub fn id(&self) -> Option<u64> {
|
||||
match &self.data {
|
||||
AuthenticatorResponseData::PendingRegistration(response) => Some(response.request_id),
|
||||
AuthenticatorResponseData::Registered(response) => Some(response.request_id),
|
||||
AuthenticatorResponseData::RemainingBandwidth(response) => Some(response.request_id),
|
||||
AuthenticatorResponseData::TopUpBandwidth(response) => Some(response.request_id),
|
||||
AuthenticatorResponseData::UpgradeMode(response) => Some(response.request_id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub enum AuthenticatorResponseData {
|
||||
PendingRegistration(PendingRegistrationResponse),
|
||||
Registered(RegisteredResponse),
|
||||
RemainingBandwidth(RemainingBandwidthResponse),
|
||||
TopUpBandwidth(TopUpBandwidthResponse),
|
||||
UpgradeMode(UpgradeModeResponse),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct PendingRegistrationResponse {
|
||||
pub request_id: u64,
|
||||
pub reply: RegistrationData,
|
||||
pub upgrade_mode_enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct RegisteredResponse {
|
||||
pub request_id: u64,
|
||||
pub reply: RegisteredData,
|
||||
pub upgrade_mode_enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct RemainingBandwidthResponse {
|
||||
pub request_id: u64,
|
||||
pub reply: Option<RemainingBandwidthData>,
|
||||
pub upgrade_mode_enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct TopUpBandwidthResponse {
|
||||
pub request_id: u64,
|
||||
pub reply: RemainingBandwidthData,
|
||||
pub upgrade_mode_enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct UpgradeModeResponse {
|
||||
pub request_id: u64,
|
||||
pub upgrade_mode_enabled: bool,
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_credentials_interface::CredentialSpendingData;
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct TopUpMessage {
|
||||
/// Base64 encoded x25519 public key
|
||||
pub pub_key: PeerPublicKey,
|
||||
|
||||
/// Ecash credential
|
||||
pub credential: CredentialSpendingData,
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub enum UpgradeModeCheckRequest {
|
||||
/// Attempt to request upgrade mode recheck via the JWT issued as the result of
|
||||
/// global attestation.json being published
|
||||
UpgradeModeJwt { token: String },
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::{v1, v2, v3, v4, v5};
|
||||
use super::{v1, v2, v3, v4, v5, v6};
|
||||
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, strum_macros::Display)]
|
||||
@@ -22,11 +22,15 @@ pub enum AuthenticatorVersion {
|
||||
/// introduced in dorina-patched release (1.6.1)
|
||||
V5,
|
||||
|
||||
/// introduced in niolo release (1.23.0)
|
||||
V6,
|
||||
|
||||
/// an unknown, future, variant that can be present if running outdated software
|
||||
UNKNOWN,
|
||||
}
|
||||
|
||||
impl AuthenticatorVersion {
|
||||
pub const LATEST: Self = Self::V5;
|
||||
pub const LATEST: Self = Self::V6;
|
||||
|
||||
pub const fn release_version(&self) -> semver::Version {
|
||||
match self {
|
||||
@@ -35,6 +39,7 @@ impl AuthenticatorVersion {
|
||||
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::V6 => semver::Version::new(1, 23, 0),
|
||||
AuthenticatorVersion::UNKNOWN => semver::Version::new(0, 0, 0),
|
||||
}
|
||||
}
|
||||
@@ -54,6 +59,8 @@ impl From<Protocol> for AuthenticatorVersion {
|
||||
AuthenticatorVersion::V4
|
||||
} else if value.version == v5::VERSION {
|
||||
AuthenticatorVersion::V5
|
||||
} else if value.version == v6::VERSION {
|
||||
AuthenticatorVersion::V6
|
||||
} else {
|
||||
AuthenticatorVersion::UNKNOWN
|
||||
}
|
||||
@@ -72,6 +79,8 @@ impl From<u8> for AuthenticatorVersion {
|
||||
AuthenticatorVersion::V4
|
||||
} else if value == v5::VERSION {
|
||||
AuthenticatorVersion::V5
|
||||
} else if value == v6::VERSION {
|
||||
AuthenticatorVersion::V6
|
||||
} else {
|
||||
AuthenticatorVersion::UNKNOWN
|
||||
}
|
||||
@@ -126,10 +135,17 @@ impl From<semver::Version> for AuthenticatorVersion {
|
||||
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)
|
||||
if semver < AuthenticatorVersion::V6.release_version() {
|
||||
return Self::V5;
|
||||
}
|
||||
// if provided version is higher (or equal) to release version of V6,
|
||||
// we return the latest (i.e. v6)
|
||||
|
||||
debug_assert_eq!(Self::V5, Self::LATEST, "a new AuthenticatorVersion variant has been introduced without adjusting the `From<semver::Version>` trait");
|
||||
debug_assert_eq!(
|
||||
Self::V6,
|
||||
Self::LATEST,
|
||||
"a new AuthenticatorVersion variant has been introduced without adjusting the `From<semver::Version>` trait"
|
||||
);
|
||||
Self::LATEST
|
||||
}
|
||||
}
|
||||
@@ -187,5 +203,9 @@ mod tests {
|
||||
assert_eq!(AuthenticatorVersion::V5, "1.7.0".into());
|
||||
assert_eq!(AuthenticatorVersion::V5, "1.16.11".into());
|
||||
assert_eq!(AuthenticatorVersion::V5, "1.17.0".into());
|
||||
assert_eq!(AuthenticatorVersion::V5, "1.22.0".into());
|
||||
assert_eq!(AuthenticatorVersion::V6, "1.23.0".into());
|
||||
assert_eq!(AuthenticatorVersion::V6, "1.23.1".into());
|
||||
assert_eq!(AuthenticatorVersion::V6, "1.24.0".into());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,21 +7,16 @@ license.workspace = true
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
async-trait = { workspace = true }
|
||||
bip39 = { workspace = true }
|
||||
async-trait = { workspace = true }
|
||||
log = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
url = { workspace = true }
|
||||
zeroize = { workspace = true }
|
||||
|
||||
nym-credential-storage = { path = "../credential-storage" }
|
||||
nym-credentials = { path = "../credentials" }
|
||||
nym-credentials-interface = { path = "../credentials-interface" }
|
||||
nym-crypto = { path = "../crypto", features = ["rand", "asymmetric", "stream_cipher", "aes", "hashing"] }
|
||||
nym-ecash-contract-common = { path = "../cosmwasm-smart-contracts/ecash-contract" }
|
||||
nym-ecash-time = { path = "../ecash-time" }
|
||||
nym-network-defaults = { path = "../network-defaults" }
|
||||
nym-task = { path = "../task" }
|
||||
nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
|
||||
|
||||
|
||||
@@ -21,6 +21,9 @@ pub enum BandwidthControllerError {
|
||||
#[error("There was a credential storage error - {0}")]
|
||||
CredentialStorageError(Box<dyn std::error::Error + Send + Sync>),
|
||||
|
||||
#[error("retrieved upgrade mode token is not a valid String")]
|
||||
MalformedUpgradeModeToken,
|
||||
|
||||
#[error("the credential storage does not contain any usable credentials")]
|
||||
NoCredentialsAvailable,
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::utils::{
|
||||
ApiClientsWrapper,
|
||||
};
|
||||
use log::error;
|
||||
use nym_credential_storage::models::RetrievedTicketbook;
|
||||
use nym_credential_storage::models::{EmergencyCredential, RetrievedTicketbook};
|
||||
use nym_credential_storage::storage::Storage;
|
||||
use nym_credentials::ecash::bandwidth::CredentialSpendingData;
|
||||
use nym_credentials_interface::{
|
||||
@@ -220,6 +220,19 @@ impl<C, St: Storage> BandwidthController<C, St> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_emergency_credential(
|
||||
&self,
|
||||
typ: &str,
|
||||
) -> Result<Option<EmergencyCredential>, BandwidthControllerError>
|
||||
where
|
||||
<St as Storage>::StorageError: Send + Sync + 'static,
|
||||
{
|
||||
self.storage
|
||||
.get_emergency_credential(typ)
|
||||
.await
|
||||
.map_err(BandwidthControllerError::credential_storage_error)
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, St> Clone for BandwidthController<C, St>
|
||||
|
||||
@@ -11,6 +11,9 @@ use crate::{error::BandwidthControllerError, BandwidthController, PreparedCreden
|
||||
|
||||
pub const DEFAULT_TICKETS_TO_SPEND: u32 = 1;
|
||||
|
||||
// TODO: this does not really belong here
|
||||
pub const UPGRADE_MODE_JWT_TYPE: &str = "UPGRADE_MODE_JWT";
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
pub trait BandwidthTicketProvider: Send + Sync {
|
||||
@@ -20,6 +23,8 @@ pub trait BandwidthTicketProvider: Send + Sync {
|
||||
gateway_id: ed25519::PublicKey,
|
||||
tickets_to_spend: u32,
|
||||
) -> Result<PreparedCredential, BandwidthControllerError>;
|
||||
|
||||
async fn get_upgrade_mode_token(&self) -> Result<Option<String>, BandwidthControllerError>;
|
||||
}
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
@@ -39,4 +44,16 @@ where
|
||||
self.prepare_ecash_ticket(ticket_type, gateway_id.to_bytes(), tickets_to_spend)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_upgrade_mode_token(&self) -> Result<Option<String>, BandwidthControllerError> {
|
||||
let Some(emergency_credential) =
|
||||
self.get_emergency_credential(UPGRADE_MODE_JWT_TYPE).await?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
// upgrade mode credential is just a simple stringified JWT
|
||||
let token = String::from_utf8(emergency_credential.data.content)
|
||||
.map_err(|_| BandwidthControllerError::MalformedUpgradeModeToken)?;
|
||||
Ok(Some(token))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use clap::Args;
|
||||
use clap::builder::Command;
|
||||
use clap::clap_derive::ValueEnum;
|
||||
use clap::Args;
|
||||
use clap_complete::generator::generate;
|
||||
use clap_complete::Shell as ClapShell;
|
||||
use clap_complete::generator::generate;
|
||||
use std::io;
|
||||
|
||||
pub fn fig_generate(command: &mut Command, name: &str) {
|
||||
|
||||
@@ -3,7 +3,7 @@ name = "nym-client-core"
|
||||
version = "1.1.15"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.76"
|
||||
rust-version = "1.85"
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
@@ -29,6 +29,8 @@ time = { workspace = true }
|
||||
tokio = { workspace = true, features = ["sync", "macros"] }
|
||||
tracing = { workspace = true }
|
||||
zeroize = { workspace = true }
|
||||
once_cell = "1.19"
|
||||
|
||||
|
||||
# internal
|
||||
nym-id = { path = "../nym-id" }
|
||||
@@ -36,7 +38,7 @@ nym-bandwidth-controller = { path = "../bandwidth-controller" }
|
||||
nym-crypto = { path = "../crypto" }
|
||||
nym-gateway-client = { path = "../client-libs/gateway-client" }
|
||||
nym-gateway-requests = { path = "../gateway-requests" }
|
||||
nym-http-api-client = { path = "../http-api-client" }
|
||||
nym-http-api-client = { path = "../http-api-client", features = ["network-defaults"] }
|
||||
nym-nonexhaustive-delayqueue = { path = "../nonexhaustive-delayqueue" }
|
||||
nym-sphinx = { path = "../nymsphinx" }
|
||||
nym-statistics-common = { path = "../statistics" }
|
||||
@@ -69,7 +71,6 @@ workspace = true
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.hyper-util]
|
||||
workspace = true
|
||||
features = ["tokio"]
|
||||
###
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-stream]
|
||||
workspace = true
|
||||
@@ -103,7 +104,7 @@ workspace = true
|
||||
features = ["tokio"]
|
||||
|
||||
[target."cfg(target_arch = \"wasm32\")".dependencies.gloo-timers]
|
||||
version = "0.3.0"
|
||||
workspace = true
|
||||
features = ["futures"]
|
||||
|
||||
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-utils]
|
||||
@@ -114,6 +115,10 @@ features = ["websocket"]
|
||||
workspace = true
|
||||
features = ["wasm-bindgen"]
|
||||
|
||||
[target."cfg(target_arch = \"wasm32\")".dependencies.tokio_with_wasm]
|
||||
workspace = true
|
||||
features = ["full"]
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = { workspace = true }
|
||||
|
||||
|
||||
@@ -707,13 +707,10 @@ pub struct DebugConfig {
|
||||
|
||||
/// Defines all configuration options related to reply SURBs.
|
||||
pub reply_surbs: ReplySurbs,
|
||||
|
||||
/// Defines all configuration options related to stats reporting.
|
||||
pub stats_reporting: StatsReporting,
|
||||
|
||||
/// Defines all configuration options related to the forget me flag.
|
||||
pub forget_me: ForgetMe,
|
||||
|
||||
/// Defines all configuration options related to the remember me flag.
|
||||
pub remember_me: RememberMe,
|
||||
}
|
||||
|
||||
@@ -543,10 +543,8 @@ pub struct DebugConfigV6 {
|
||||
|
||||
/// Defines all configuration options related to reply SURBs.
|
||||
pub reply_surbs: ReplySurbsV6,
|
||||
|
||||
/// Defines all configuration options related to stats reporting.
|
||||
pub stats_reporting: StatsReportingV6,
|
||||
|
||||
/// Defines all configuration options related to the forget me flag.
|
||||
pub forget_me: ForgetMeV6,
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ impl StorageManager {
|
||||
}
|
||||
})?;
|
||||
}
|
||||
|
||||
let opts = sqlx::sqlite::SqliteConnectOptions::new()
|
||||
.journal_mode(sqlx::sqlite::SqliteJournalMode::Wal)
|
||||
.synchronous(SqliteSynchronous::Normal)
|
||||
|
||||
@@ -114,13 +114,12 @@ where
|
||||
})?;
|
||||
hardcoded_topology.entry_capable_nodes().cloned().collect()
|
||||
} else {
|
||||
let mut rng = rand::thread_rng();
|
||||
crate::init::helpers::gateways_for_init(
|
||||
&mut rng,
|
||||
&core.client.nym_api_urls,
|
||||
user_agent,
|
||||
core.debug.topology.minimum_gateway_performance,
|
||||
core.debug.topology.ignore_ingress_epoch_role,
|
||||
None,
|
||||
)
|
||||
.await?
|
||||
};
|
||||
|
||||
@@ -81,6 +81,10 @@ pub struct CommonClientInitArgs {
|
||||
#[cfg_attr(feature = "cli", clap(long, hide = true))]
|
||||
pub enabled_credentials_mode: Option<bool>,
|
||||
|
||||
/// Change the default minimum node performance used during initial node selection filtering.
|
||||
#[cfg_attr(feature = "cli", clap(long, hide = true))]
|
||||
pub minimum_gateway_performance: Option<u8>,
|
||||
|
||||
/// Mostly debug-related option to increase default traffic rate so that you would not need to
|
||||
/// modify config post init
|
||||
#[cfg_attr(feature = "cli", clap(long, hide = true))]
|
||||
@@ -173,13 +177,16 @@ where
|
||||
})?;
|
||||
hardcoded_topology.entry_capable_nodes().cloned().collect()
|
||||
} else {
|
||||
let mut rng = rand::thread_rng();
|
||||
let minimum_performance = common_args
|
||||
.minimum_gateway_performance
|
||||
.unwrap_or(core.debug.topology.minimum_gateway_performance);
|
||||
|
||||
crate::init::helpers::gateways_for_init(
|
||||
&mut rng,
|
||||
&core.client.nym_api_urls,
|
||||
user_agent,
|
||||
core.debug.topology.minimum_gateway_performance,
|
||||
minimum_performance,
|
||||
core.debug.topology.ignore_ingress_epoch_role,
|
||||
None,
|
||||
)
|
||||
.await?
|
||||
};
|
||||
|
||||
@@ -7,11 +7,12 @@ use super::statistics_control::StatisticsControl;
|
||||
use crate::client::base_client::storage::helpers::store_client_keys;
|
||||
use crate::client::base_client::storage::MixnetClientStorage;
|
||||
use crate::client::cover_traffic_stream::LoopCoverTrafficStream;
|
||||
use crate::client::event_control::EventControl;
|
||||
use crate::client::inbound_messages::{InputMessage, InputMessageReceiver, InputMessageSender};
|
||||
use crate::client::key_manager::persistence::KeyStore;
|
||||
use crate::client::key_manager::ClientKeys;
|
||||
use crate::client::mix_traffic::transceiver::{GatewayReceiver, GatewayTransceiver, RemoteGateway};
|
||||
use crate::client::mix_traffic::{BatchMixMessageSender, MixTrafficController};
|
||||
use crate::client::mix_traffic::{BatchMixMessageSender, MixTrafficController, MixTrafficEvent};
|
||||
use crate::client::real_messages_control;
|
||||
use crate::client::real_messages_control::RealMessagesController;
|
||||
use crate::client::received_buffer::{
|
||||
@@ -66,9 +67,16 @@ use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use time::OffsetDateTime;
|
||||
use tokio::sync::mpsc::Sender;
|
||||
use tracing::*;
|
||||
use url::Url;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(debug_assertions)]
|
||||
use wasm_utils::console_log;
|
||||
|
||||
/// Default number of retries for Nym API requests when using network details with domain fronting.
|
||||
/// This allows the client to try alternative URLs if the primary endpoint is unavailable.
|
||||
const DEFAULT_NYM_API_RETRIES: usize = 3;
|
||||
|
||||
#[cfg(all(
|
||||
not(target_arch = "wasm32"),
|
||||
feature = "fs-surb-storage",
|
||||
@@ -79,10 +87,28 @@ pub mod non_wasm_helpers;
|
||||
pub mod helpers;
|
||||
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)]
|
||||
pub struct ClientInput {
|
||||
pub connection_command_sender: ConnectionCommandSender,
|
||||
pub input_sender: InputMessageSender,
|
||||
pub client_request_sender: ClientRequestSender,
|
||||
}
|
||||
|
||||
impl ClientInput {
|
||||
@@ -190,10 +216,14 @@ pub struct BaseClientBuilder<C, S: MixnetClientStorage> {
|
||||
client_store: S,
|
||||
dkg_query_client: Option<C>,
|
||||
|
||||
// Optional API URLs for domain fronting support
|
||||
nym_api_urls: Option<Vec<nym_network_defaults::ApiUrl>>,
|
||||
|
||||
wait_for_gateway: bool,
|
||||
custom_topology_provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
|
||||
custom_gateway_transceiver: Option<Box<dyn GatewayTransceiver + Send>>,
|
||||
shutdown: Option<ShutdownTracker>,
|
||||
event_tx: Option<EventSender>,
|
||||
user_agent: Option<UserAgent>,
|
||||
|
||||
setup_method: GatewaySetup,
|
||||
@@ -218,10 +248,12 @@ where
|
||||
config: base_config,
|
||||
client_store,
|
||||
dkg_query_client,
|
||||
nym_api_urls: None,
|
||||
wait_for_gateway: false,
|
||||
custom_topology_provider: None,
|
||||
custom_gateway_transceiver: None,
|
||||
shutdown: None,
|
||||
event_tx: None,
|
||||
user_agent: None,
|
||||
setup_method: GatewaySetup::MustLoad { gateway_id: None },
|
||||
#[cfg(unix)]
|
||||
@@ -239,6 +271,16 @@ where
|
||||
self
|
||||
}
|
||||
|
||||
/// Set Nym API URLs for domain fronting support.
|
||||
///
|
||||
/// When provided, the client will use these API URLs (which include front_hosts)
|
||||
/// to construct HTTP clients with domain fronting enabled.
|
||||
#[must_use]
|
||||
pub fn with_nym_api_urls(mut self, nym_api_urls: Vec<nym_network_defaults::ApiUrl>) -> Self {
|
||||
self.nym_api_urls = Some(nym_api_urls);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_forget_me(mut self, forget_me: &ForgetMe) -> Self {
|
||||
self.config.debug.forget_me = *forget_me;
|
||||
@@ -284,6 +326,12 @@ where
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_event_tx(mut self, event_tx: EventSender) -> Self {
|
||||
self.event_tx = Some(event_tx);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_user_agent(mut self, user_agent: UserAgent) -> Self {
|
||||
self.user_agent = Some(user_agent);
|
||||
@@ -314,6 +362,18 @@ where
|
||||
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
|
||||
// the pumped traffic goes to the MixTrafficController
|
||||
fn start_cover_traffic_stream(
|
||||
@@ -325,7 +385,7 @@ where
|
||||
stats_tx: ClientStatsSender,
|
||||
shutdown_tracker: &ShutdownTracker,
|
||||
) {
|
||||
info!("Starting loop cover traffic stream...");
|
||||
tracing::info!("Starting loop cover traffic stream...");
|
||||
|
||||
let mut stream = LoopCoverTrafficStream::new(
|
||||
ack_key,
|
||||
@@ -357,7 +417,7 @@ where
|
||||
stats_tx: ClientStatsSender,
|
||||
shutdown_tracker: &ShutdownTracker,
|
||||
) {
|
||||
info!("Starting real traffic stream...");
|
||||
tracing::info!("Starting real traffic stream...");
|
||||
|
||||
let real_messages_controller = RealMessagesController::new(
|
||||
controller_config,
|
||||
@@ -442,7 +502,7 @@ where
|
||||
metrics_reporter: ClientStatsSender,
|
||||
shutdown_tracker: &ShutdownTracker,
|
||||
) {
|
||||
info!("Starting received messages buffer controller...");
|
||||
tracing::info!("Starting received messages buffer controller...");
|
||||
let controller = ReceivedMessagesBufferController::<SphinxMessageReceiver>::new(
|
||||
local_encryption_keypair,
|
||||
query_receiver,
|
||||
@@ -553,7 +613,7 @@ where
|
||||
details_store
|
||||
.upgrade_stored_remote_gateway_key(gateway_client.gateway_identity(), &updated_key)
|
||||
.await.map_err(|err| {
|
||||
error!("failed to store upgraded gateway key! this connection might be forever broken now: {err}");
|
||||
tracing::error!("failed to store upgraded gateway key! this connection might be forever broken now: {err}");
|
||||
ClientCoreError::GatewaysDetailsStoreError { source: Box::new(err) }
|
||||
})?
|
||||
}
|
||||
@@ -650,7 +710,7 @@ where
|
||||
|
||||
if topology_config.disable_refreshing {
|
||||
// if we're not spawning the refresher, don't cause shutdown immediately
|
||||
info!("The background topology refresher is not going to be started");
|
||||
tracing::info!("The background topology refresher is not going to be started");
|
||||
}
|
||||
|
||||
let mut topology_refresher = TopologyRefresher::new(
|
||||
@@ -660,7 +720,7 @@ where
|
||||
);
|
||||
// before returning, block entire runtime to refresh the current network view so that any
|
||||
// components depending on topology would see a non-empty view
|
||||
info!("Obtaining initial network topology");
|
||||
tracing::info!("Obtaining initial network topology");
|
||||
topology_refresher.try_refresh().await;
|
||||
|
||||
if let Err(err) = topology_refresher.ensure_topology_is_routable().await {
|
||||
@@ -686,13 +746,13 @@ where
|
||||
.wait_for_gateway(local_gateway, waiting_timeout)
|
||||
.await
|
||||
{
|
||||
error!(
|
||||
tracing::error!(
|
||||
"the gateway did not come back online within the specified timeout: {err}"
|
||||
);
|
||||
return Err(err.into());
|
||||
}
|
||||
} else {
|
||||
error!("the gateway we're supposedly connected to does not exist. We'll not be able to send any packets to ourselves: {err}");
|
||||
tracing::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());
|
||||
}
|
||||
}
|
||||
@@ -700,7 +760,7 @@ where
|
||||
if !topology_config.disable_refreshing {
|
||||
// don't spawn the refresher if we don't want to be refreshing the topology.
|
||||
// only use the initial values obtained
|
||||
info!("Starting topology refresher...");
|
||||
tracing::info!("Starting topology refresher...");
|
||||
shutdown_tracker.try_spawn_named_with_shutdown(
|
||||
async move { topology_refresher.run().await },
|
||||
"TopologyRefresher",
|
||||
@@ -717,7 +777,7 @@ where
|
||||
input_sender: Sender<InputMessage>,
|
||||
shutdown_tracker: &ShutdownTracker,
|
||||
) -> ClientStatsSender {
|
||||
info!("Starting statistics control...");
|
||||
tracing::info!("Starting statistics control...");
|
||||
StatisticsControl::create_and_start(
|
||||
config.debug.stats_reporting,
|
||||
user_agent
|
||||
@@ -732,10 +792,17 @@ where
|
||||
fn start_mix_traffic_controller(
|
||||
gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
|
||||
shutdown_tracker: &ShutdownTracker,
|
||||
event_tx: EventSender,
|
||||
) -> (BatchMixMessageSender, ClientRequestSender) {
|
||||
info!("Starting mix traffic controller...");
|
||||
let (mut mix_traffic_controller, mix_tx, client_tx) =
|
||||
MixTrafficController::new(gateway_transceiver, shutdown_tracker.clone_shutdown_token());
|
||||
tracing::info!("Starting mix traffic controller...");
|
||||
let mut mix_traffic_controller = MixTrafficController::new(
|
||||
gateway_transceiver,
|
||||
shutdown_tracker.clone_shutdown_token(),
|
||||
event_tx,
|
||||
);
|
||||
|
||||
let mix_tx = mix_traffic_controller.mix_tx();
|
||||
let client_tx = mix_traffic_controller.client_tx();
|
||||
|
||||
shutdown_tracker.try_spawn_named(
|
||||
async move { mix_traffic_controller.run().await },
|
||||
@@ -799,7 +866,7 @@ where
|
||||
{
|
||||
// if client keys do not exist already, create and persist them
|
||||
if key_store.load_keys().await.is_err() {
|
||||
info!("could not find valid client keys - a new set will be generated");
|
||||
tracing::info!("could not find valid client keys - a new set will be generated");
|
||||
let mut rng = OsRng;
|
||||
let keys = if let Some(derivation_material) = derivation_material {
|
||||
ClientKeys::from_master_key(&mut rng, &derivation_material)
|
||||
@@ -814,21 +881,67 @@ where
|
||||
}
|
||||
|
||||
fn construct_nym_api_client(
|
||||
nym_api_urls: Option<&Vec<nym_network_defaults::ApiUrl>>,
|
||||
config: &Config,
|
||||
user_agent: Option<UserAgent>,
|
||||
) -> Result<nym_http_api_client::Client, ClientCoreError> {
|
||||
tracing::debug!(
|
||||
"construct_nym_api_client called with nym_api_urls: {}",
|
||||
nym_api_urls.is_some()
|
||||
);
|
||||
|
||||
// If API URLs are provided, use new_with_fronted_urls() which handles domain fronting
|
||||
if let Some(nym_api_urls) = nym_api_urls {
|
||||
if nym_api_urls.is_empty() {
|
||||
tracing::warn!("Provided nym_api_urls is empty, falling back to config endpoints");
|
||||
} else {
|
||||
tracing::info!(
|
||||
"Building nym-api client from provided URLs (with domain fronting support): {} URLs",
|
||||
nym_api_urls.len()
|
||||
);
|
||||
|
||||
let mut builder =
|
||||
nym_http_api_client::ClientBuilder::new_with_fronted_urls(nym_api_urls.clone())
|
||||
.map_err(ClientCoreError::from)?
|
||||
.with_retries(DEFAULT_NYM_API_RETRIES);
|
||||
|
||||
if let Some(user_agent) = user_agent {
|
||||
builder = builder.with_user_agent(user_agent);
|
||||
}
|
||||
|
||||
return builder.build().map_err(ClientCoreError::from);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to basic client for backwards compatibility
|
||||
tracing::debug!("Building basic nym-api HTTP client from config endpoints");
|
||||
|
||||
let mut nym_api_urls = config.get_nym_api_endpoints();
|
||||
if nym_api_urls.is_empty() {
|
||||
tracing::warn!("No API endpoints configured in config, this may cause issues");
|
||||
}
|
||||
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)?;
|
||||
// Convert config URLs to ApiUrl format for consistency
|
||||
let api_urls: Vec<nym_network_defaults::ApiUrl> = nym_api_urls
|
||||
.into_iter()
|
||||
.map(|url| nym_network_defaults::ApiUrl {
|
||||
url: url.to_string(),
|
||||
front_hosts: None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
tracing::debug!("Using {} config API endpoints", api_urls.len());
|
||||
|
||||
let mut builder = nym_http_api_client::ClientBuilder::new_with_fronted_urls(api_urls)
|
||||
.map_err(ClientCoreError::from)?
|
||||
.with_retries(DEFAULT_NYM_API_RETRIES)
|
||||
.with_bincode();
|
||||
|
||||
if let Some(user_agent) = user_agent {
|
||||
builder = builder.with_user_agent(user_agent);
|
||||
}
|
||||
|
||||
builder = builder.with_bincode();
|
||||
|
||||
builder.build().map_err(ClientCoreError::from)
|
||||
}
|
||||
|
||||
@@ -846,7 +959,12 @@ where
|
||||
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static,
|
||||
<S::GatewaysDetailsStore as GatewaysDetailsStore>::StorageError: Sync + Send,
|
||||
{
|
||||
info!("Starting nym client");
|
||||
tracing::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
|
||||
let init_res = Self::initialise_keys_and_gateway(
|
||||
@@ -875,6 +993,9 @@ where
|
||||
// channels responsible for controlling real messages
|
||||
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
|
||||
let (ack_sender, ack_receiver) = mpsc::unbounded();
|
||||
let shared_topology_accessor =
|
||||
@@ -883,10 +1004,12 @@ where
|
||||
// Create a shutdown tracker for this client - either as a child of provided tracker
|
||||
// or get one from the registry
|
||||
let shutdown_tracker = match self.shutdown {
|
||||
Some(parent_tracker) => parent_tracker.child_tracker(),
|
||||
None => nym_task::get_sdk_shutdown_tracker()?,
|
||||
Some(parent_tracker) => parent_tracker.clone(),
|
||||
None => nym_task::create_sdk_shutdown_tracker()?,
|
||||
};
|
||||
|
||||
Self::start_event_control(self.event_tx, event_receiver, &shutdown_tracker);
|
||||
|
||||
// channels responsible for dealing with reply-related fun
|
||||
let (reply_controller_sender, reply_controller_receiver) =
|
||||
reply_controller::requests::new_control_channels();
|
||||
@@ -902,7 +1025,11 @@ where
|
||||
.dkg_query_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.nym_api_urls.as_ref(),
|
||||
&self.config,
|
||||
self.user_agent.clone(),
|
||||
)?;
|
||||
let key_rotation_config = Self::determine_key_rotation_state(&nym_api_client).await?;
|
||||
|
||||
let topology_provider = Self::setup_topology_provider(
|
||||
@@ -917,7 +1044,7 @@ where
|
||||
self.user_agent.clone(),
|
||||
generate_client_stats_id(*self_address.identity()),
|
||||
input_sender.clone(),
|
||||
&shutdown_tracker.child_tracker(),
|
||||
&shutdown_tracker.clone(),
|
||||
);
|
||||
|
||||
// needs to be started as the first thing to block if required waiting for the gateway
|
||||
@@ -927,7 +1054,7 @@ where
|
||||
shared_topology_accessor.clone(),
|
||||
self_address.gateway(),
|
||||
self.wait_for_gateway,
|
||||
&shutdown_tracker.child_tracker(),
|
||||
&shutdown_tracker.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -947,7 +1074,7 @@ where
|
||||
stats_reporter.clone(),
|
||||
#[cfg(unix)]
|
||||
self.connection_fd_callback,
|
||||
&shutdown_tracker.child_tracker(),
|
||||
&shutdown_tracker.clone(),
|
||||
)
|
||||
.await?;
|
||||
let gateway_ws_fd = gateway_transceiver.ws_fd();
|
||||
@@ -955,7 +1082,7 @@ where
|
||||
let reply_storage = Self::setup_persistent_reply_storage(
|
||||
reply_storage_backend,
|
||||
key_rotation_config,
|
||||
&shutdown_tracker.child_tracker(),
|
||||
&shutdown_tracker.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -966,7 +1093,7 @@ where
|
||||
reply_storage.key_storage(),
|
||||
reply_controller_sender.clone(),
|
||||
stats_reporter.clone(),
|
||||
&shutdown_tracker.child_tracker(),
|
||||
&shutdown_tracker.clone(),
|
||||
);
|
||||
|
||||
// The message_sender is the transmitter for any component generating sphinx packets
|
||||
@@ -976,7 +1103,8 @@ where
|
||||
|
||||
let (message_sender, client_request_sender) = Self::start_mix_traffic_controller(
|
||||
gateway_transceiver,
|
||||
&shutdown_tracker.child_tracker(),
|
||||
&shutdown_tracker.clone(),
|
||||
EventSender(event_sender),
|
||||
);
|
||||
|
||||
// Channels that the websocket listener can use to signal downstream to the real traffic
|
||||
@@ -1006,7 +1134,7 @@ where
|
||||
shared_lane_queue_lengths.clone(),
|
||||
client_connection_rx,
|
||||
stats_reporter.clone(),
|
||||
&shutdown_tracker.child_tracker(),
|
||||
&shutdown_tracker.clone(),
|
||||
);
|
||||
|
||||
if !self
|
||||
@@ -1022,12 +1150,19 @@ where
|
||||
shared_topology_accessor.clone(),
|
||||
message_sender,
|
||||
stats_reporter.clone(),
|
||||
&shutdown_tracker.child_tracker(),
|
||||
&shutdown_tracker.clone(),
|
||||
);
|
||||
}
|
||||
|
||||
debug!("Core client startup finished!");
|
||||
debug!("The address of this client is: {self_address}");
|
||||
tracing::debug!("Core client startup finished!");
|
||||
tracing::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 {
|
||||
address: self_address,
|
||||
@@ -1036,6 +1171,7 @@ where
|
||||
client_input: ClientInput {
|
||||
connection_command_sender: client_connection_tx,
|
||||
input_sender,
|
||||
client_request_sender,
|
||||
},
|
||||
},
|
||||
client_output: ClientOutputStatus::AwaitingConsumer {
|
||||
@@ -1051,7 +1187,6 @@ where
|
||||
},
|
||||
stats_reporter,
|
||||
shutdown_handle: shutdown_tracker, // The primary tracker for this client
|
||||
client_request_sender,
|
||||
forget_me: self.config.debug.forget_me,
|
||||
remember_me: self.config.debug.remember_me,
|
||||
})
|
||||
@@ -1065,8 +1200,57 @@ pub struct BaseClient {
|
||||
pub client_output: ClientOutputStatus,
|
||||
pub client_state: ClientState,
|
||||
pub stats_reporter: ClientStatsSender,
|
||||
pub client_request_sender: ClientRequestSender,
|
||||
pub shutdown_handle: ShutdownTracker,
|
||||
pub forget_me: ForgetMe,
|
||||
pub remember_me: RememberMe,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use nym_network_defaults::{ApiUrl, NymNetworkDetails};
|
||||
|
||||
#[test]
|
||||
fn test_network_details_with_multiple_urls() {
|
||||
// Verify that network details can be configured with multiple API URLs
|
||||
let mut network_details = NymNetworkDetails::new_empty();
|
||||
network_details.nym_api_urls = Some(vec![
|
||||
ApiUrl {
|
||||
url: "https://validator.nymtech.net/api/".to_string(),
|
||||
front_hosts: None,
|
||||
},
|
||||
ApiUrl {
|
||||
url: "https://nym-frontdoor.vercel.app/api/".to_string(),
|
||||
front_hosts: Some(vec!["vercel.app".to_string(), "vercel.com".to_string()]),
|
||||
},
|
||||
]);
|
||||
|
||||
assert_eq!(network_details.nym_api_urls.as_ref().unwrap().len(), 2);
|
||||
assert!(network_details.nym_api_urls.as_ref().unwrap()[1]
|
||||
.front_hosts
|
||||
.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_network_details_with_front_hosts() {
|
||||
// Verify that ApiUrl can store domain fronting configuration
|
||||
let api_url = ApiUrl {
|
||||
url: "https://nym-frontdoor.vercel.app/api/".to_string(),
|
||||
front_hosts: Some(vec!["vercel.app".to_string(), "vercel.com".to_string()]),
|
||||
};
|
||||
|
||||
assert_eq!(api_url.url, "https://nym-frontdoor.vercel.app/api/");
|
||||
assert_eq!(api_url.front_hosts.as_ref().unwrap().len(), 2);
|
||||
assert!(api_url
|
||||
.front_hosts
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.contains(&"vercel.app".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_nym_api_retries_constant() {
|
||||
// Verify the retry constant is set correctly
|
||||
assert_eq!(DEFAULT_NYM_API_RETRIES, 3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,7 +205,7 @@ impl LoopCoverTrafficStream<OsRng> {
|
||||
TrySendError::Full(_) => {
|
||||
// This isn't a problem, if the channel is full means we're already sending the
|
||||
// max amount of messages downstream can handle.
|
||||
tracing::debug!("Failed to send cover message - channel full");
|
||||
tracing::trace!("Failed to send cover message - channel full");
|
||||
}
|
||||
TrySendError::Closed(_) => {
|
||||
tracing::warn!("Failed to send cover message - channel closed");
|
||||
@@ -225,9 +225,15 @@ impl LoopCoverTrafficStream<OsRng> {
|
||||
// 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.
|
||||
|
||||
// TODO: temporary and BAD workaround for wasm (we should find a way to yield here in wasm)
|
||||
#[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
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
// 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
|
||||
|
||||
#![allow(unused_imports)]
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub use wasmtimer::{std::Instant, tokio::*};
|
||||
pub type IntervalStream = gloo_timers::future::IntervalStream;
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::rtt_analyzer::{RttConfig, RttEvent};
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
|
||||
use nym_sphinx::forwarding::packet::MixPacket;
|
||||
use nym_sphinx::params::PacketType;
|
||||
use nym_task::connections::TransmissionLane;
|
||||
use tokio::sync::mpsc::Sender;
|
||||
|
||||
pub type InputMessageSender = tokio::sync::mpsc::Sender<InputMessage>;
|
||||
pub type InputMessageReceiver = tokio::sync::mpsc::Receiver<InputMessage>;
|
||||
@@ -46,6 +48,13 @@ pub enum InputMessage {
|
||||
lane: TransmissionLane,
|
||||
max_retransmissions: Option<u32>,
|
||||
},
|
||||
RunRTTTest {
|
||||
recipient: Recipient,
|
||||
lane: TransmissionLane,
|
||||
max_retransmissions: Option<u32>,
|
||||
sender: Sender<RttEvent>,
|
||||
config: RttConfig,
|
||||
},
|
||||
|
||||
/// Attempt to use our internally received and stored `ReplySurb` to send the message back
|
||||
/// to specified recipient whilst not knowing its full identity (or even gateway).
|
||||
@@ -150,6 +159,7 @@ impl InputMessage {
|
||||
match self {
|
||||
InputMessage::Regular { lane, .. }
|
||||
| InputMessage::Anonymous { lane, .. }
|
||||
| InputMessage::RunRTTTest { lane, .. }
|
||||
| InputMessage::Reply { lane, .. }
|
||||
| InputMessage::Premade { lane, .. } => lane,
|
||||
InputMessage::MessageWrapper { message, .. } => message.lane(),
|
||||
@@ -166,6 +176,10 @@ impl InputMessage {
|
||||
max_retransmissions: m,
|
||||
..
|
||||
}
|
||||
| InputMessage::RunRTTTest {
|
||||
max_retransmissions: m,
|
||||
..
|
||||
}
|
||||
| InputMessage::Reply {
|
||||
max_retransmissions: m,
|
||||
..
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::mix_traffic::transceiver::GatewayTransceiver;
|
||||
use crate::client::{
|
||||
base_client::{EventSender, MixnetClientEvent},
|
||||
mix_traffic::transceiver::GatewayTransceiver,
|
||||
};
|
||||
use nym_gateway_requests::ClientRequest;
|
||||
use nym_sphinx::forwarding::packet::MixPacket;
|
||||
use nym_task::ShutdownToken;
|
||||
@@ -17,33 +20,41 @@ pub mod transceiver;
|
||||
|
||||
// We remind ourselves that 32 x 32kb = 1024kb, a reasonable size for a network buffer.
|
||||
pub const MIX_MESSAGE_RECEIVER_BUFFER_SIZE: usize = 32;
|
||||
const MAX_FAILURE_COUNT: usize = 100;
|
||||
|
||||
/// Reduced from 100 to 20 to fail fast (~1-2 seconds instead of ~6 seconds).
|
||||
/// If we can't send 20 packets in a row, the gateway is unreachable.
|
||||
const MAX_FAILURE_COUNT: usize = 20;
|
||||
|
||||
// that's also disgusting.
|
||||
pub struct Empty;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum MixTrafficEvent {
|
||||
FailedSendingSphinx,
|
||||
}
|
||||
|
||||
pub struct MixTrafficController {
|
||||
gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
|
||||
|
||||
mix_tx: BatchMixMessageSender,
|
||||
mix_rx: BatchMixMessageReceiver,
|
||||
client_rx: ClientRequestReceiver,
|
||||
client_tx: ClientRequestSender,
|
||||
|
||||
// TODO: this is temporary work-around.
|
||||
// in long run `gateway_client` will be moved away from `MixTrafficController` anyway.
|
||||
consecutive_gateway_failure_count: usize,
|
||||
|
||||
shutdown_token: ShutdownToken,
|
||||
event_tx: EventSender,
|
||||
}
|
||||
|
||||
impl MixTrafficController {
|
||||
pub fn new<T>(
|
||||
gateway_transceiver: T,
|
||||
shutdown_token: ShutdownToken,
|
||||
) -> (
|
||||
MixTrafficController,
|
||||
BatchMixMessageSender,
|
||||
ClientRequestSender,
|
||||
)
|
||||
event_tx: EventSender,
|
||||
) -> MixTrafficController
|
||||
where
|
||||
T: GatewayTransceiver + Send + 'static,
|
||||
{
|
||||
@@ -52,41 +63,32 @@ impl MixTrafficController {
|
||||
|
||||
let (client_sender, client_receiver) = tokio::sync::mpsc::channel(8);
|
||||
|
||||
(
|
||||
MixTrafficController {
|
||||
gateway_transceiver: Box::new(gateway_transceiver),
|
||||
mix_rx: message_receiver,
|
||||
client_rx: client_receiver,
|
||||
consecutive_gateway_failure_count: 0,
|
||||
shutdown_token,
|
||||
},
|
||||
message_sender,
|
||||
client_sender,
|
||||
)
|
||||
MixTrafficController {
|
||||
gateway_transceiver: Box::new(gateway_transceiver),
|
||||
mix_tx: message_sender,
|
||||
mix_rx: message_receiver,
|
||||
client_rx: client_receiver,
|
||||
client_tx: client_sender,
|
||||
consecutive_gateway_failure_count: 0,
|
||||
shutdown_token,
|
||||
event_tx,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_dynamic(
|
||||
gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
|
||||
shutdown_token: ShutdownToken,
|
||||
) -> (
|
||||
MixTrafficController,
|
||||
BatchMixMessageSender,
|
||||
ClientRequestSender,
|
||||
) {
|
||||
let (message_sender, message_receiver) =
|
||||
tokio::sync::mpsc::channel(MIX_MESSAGE_RECEIVER_BUFFER_SIZE);
|
||||
let (client_sender, client_receiver) = tokio::sync::mpsc::channel(8);
|
||||
(
|
||||
MixTrafficController {
|
||||
gateway_transceiver,
|
||||
mix_rx: message_receiver,
|
||||
client_rx: client_receiver,
|
||||
consecutive_gateway_failure_count: 0,
|
||||
shutdown_token,
|
||||
},
|
||||
message_sender,
|
||||
client_sender,
|
||||
)
|
||||
event_tx: EventSender,
|
||||
) -> MixTrafficController {
|
||||
Self::new(gateway_transceiver, shutdown_token, event_tx)
|
||||
}
|
||||
|
||||
pub fn client_tx(&self) -> ClientRequestSender {
|
||||
self.client_tx.clone()
|
||||
}
|
||||
|
||||
pub fn mix_tx(&self) -> BatchMixMessageSender {
|
||||
self.mix_tx.clone()
|
||||
}
|
||||
|
||||
async fn on_messages(
|
||||
@@ -145,34 +147,31 @@ impl MixTrafficController {
|
||||
trace!("MixTrafficController: Received shutdown");
|
||||
break;
|
||||
}
|
||||
mix_packets = self.mix_rx.recv() => match mix_packets {
|
||||
Some(mix_packets) => {
|
||||
if let Err(err) = self.on_messages(mix_packets).await {
|
||||
error!("Failed to send sphinx packet(s) to the gateway: {err}");
|
||||
if self.consecutive_gateway_failure_count == MAX_FAILURE_COUNT {
|
||||
// Disconnect from the gateway. If we should try to re-connect
|
||||
// is handled at a higher layer.
|
||||
error!("Failed to send sphinx packet to the gateway {MAX_FAILURE_COUNT} times in a row - assuming the gateway is dead");
|
||||
// Do we need to handle the embedded mixnet client case
|
||||
// separately?
|
||||
break;
|
||||
}
|
||||
// mix_rx should never error out as we're holding one instance of the sender
|
||||
|
||||
Some(mix_packets) = self.mix_rx.recv() => {
|
||||
if let Err(err) = self.on_messages(mix_packets).await {
|
||||
error!("Failed to send sphinx packet(s) to the gateway: {err}");
|
||||
if self.consecutive_gateway_failure_count == MAX_FAILURE_COUNT {
|
||||
// Disconnect from the gateway. If we should try to re-connect
|
||||
// is handled at a higher layer.
|
||||
error!("Failed to send sphinx packet to the gateway {MAX_FAILURE_COUNT} times in a row - assuming the gateway is dead");
|
||||
// Do we need to handle the embedded mixnet client case
|
||||
// separately?
|
||||
self.event_tx.send(MixnetClientEvent::Traffic(MixTrafficEvent::FailedSendingSphinx));
|
||||
// IMO it shouldn't be signalled from there but it is how it is
|
||||
// TODO : report the failure upwards and shutdown from upwards
|
||||
// Gateway is dead, we have to shut down currently
|
||||
error!("Signalling shutdown from the MixTrafficController");
|
||||
self.shutdown_token.cancel();
|
||||
break;
|
||||
}
|
||||
},
|
||||
None => {
|
||||
trace!("MixTrafficController: Stopping since channel closed");
|
||||
break;
|
||||
}
|
||||
},
|
||||
client_request = self.client_rx.recv() => match client_request {
|
||||
Some(client_request) => {
|
||||
self.on_client_request(client_request).await;
|
||||
},
|
||||
None => {
|
||||
trace!("MixTrafficController, client request channel closed");
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
debug!("MixTrafficController: Exiting");
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
pub mod base_client;
|
||||
pub mod cover_traffic_stream;
|
||||
pub(crate) mod event_control;
|
||||
pub(crate) mod helpers;
|
||||
pub mod inbound_messages;
|
||||
pub mod key_manager;
|
||||
@@ -10,6 +11,7 @@ pub mod mix_traffic;
|
||||
pub mod real_messages_control;
|
||||
pub mod received_buffer;
|
||||
pub mod replies;
|
||||
pub mod rtt_analyzer;
|
||||
pub mod statistics_control;
|
||||
pub mod topology_control;
|
||||
pub(crate) mod transmission_buffer;
|
||||
|
||||
+25
-4
@@ -4,6 +4,7 @@
|
||||
use super::action_controller::{AckActionSender, Action};
|
||||
use nym_statistics_common::clients::{packet_statistics::PacketStatisticsEvent, ClientStatsSender};
|
||||
|
||||
use crate::client::rtt_analyzer::{RttAnalyzer, RttEvent};
|
||||
use futures::StreamExt;
|
||||
use nym_gateway_client::AcknowledgementReceiver;
|
||||
use nym_sphinx::{
|
||||
@@ -12,6 +13,7 @@ use nym_sphinx::{
|
||||
};
|
||||
use nym_task::ShutdownToken;
|
||||
use std::sync::Arc;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use tracing::*;
|
||||
|
||||
/// Module responsible for listening for any data resembling acknowledgements from the network
|
||||
@@ -38,7 +40,11 @@ impl AcknowledgementListener {
|
||||
}
|
||||
}
|
||||
|
||||
async fn on_ack(&mut self, ack_content: Vec<u8>) {
|
||||
async fn on_ack(
|
||||
&mut self,
|
||||
ack_content: Vec<u8>,
|
||||
rtt_producer: Option<tokio::sync::mpsc::Sender<RttEvent>>,
|
||||
) {
|
||||
trace!("Received an ack");
|
||||
self.stats_tx
|
||||
.report(PacketStatisticsEvent::AckReceived(ack_content.len()).into());
|
||||
@@ -62,6 +68,16 @@ impl AcknowledgementListener {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(ref producer) = rtt_producer {
|
||||
if let Ok(duration) = SystemTime::now().duration_since(UNIX_EPOCH) {
|
||||
let now = duration.as_millis();
|
||||
let _ = producer.try_send(RttEvent::FragmentAckReceived {
|
||||
fragment_id: frag_id.set_id().to_string(),
|
||||
timestamp: now,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
trace!("Received {frag_id} from the mix network");
|
||||
self.stats_tx
|
||||
.report(PacketStatisticsEvent::RealAckReceived(ack_content.len()).into());
|
||||
@@ -70,15 +86,20 @@ impl AcknowledgementListener {
|
||||
.unbounded_send(Action::new_remove(frag_id));
|
||||
}
|
||||
|
||||
async fn handle_ack_receiver_item(&mut self, item: Vec<Vec<u8>>) {
|
||||
async fn handle_ack_receiver_item(
|
||||
&mut self,
|
||||
item: Vec<Vec<u8>>,
|
||||
rtt_producer: Option<tokio::sync::mpsc::Sender<RttEvent>>,
|
||||
) {
|
||||
// realistically we would only be getting one ack at the time
|
||||
for ack in item {
|
||||
self.on_ack(ack).await;
|
||||
self.on_ack(ack, rtt_producer.clone()).await;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn run(&mut self, shutdown_token: ShutdownToken) {
|
||||
debug!("Started AcknowledgementListener with graceful shutdown support");
|
||||
let rtt_producer = RttAnalyzer::producer();
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
@@ -88,7 +109,7 @@ impl AcknowledgementListener {
|
||||
break;
|
||||
}
|
||||
acks = self.ack_receiver.next() => match acks {
|
||||
Some(acks) => self.handle_ack_receiver_item(acks).await,
|
||||
Some(acks) => self.handle_ack_receiver_item(acks,rtt_producer.clone()).await,
|
||||
None => {
|
||||
tracing::trace!("AcknowledgementListener: Stopping since channel closed");
|
||||
break;
|
||||
|
||||
+19
-3
@@ -3,6 +3,7 @@
|
||||
|
||||
use super::PendingAcknowledgement;
|
||||
use crate::client::real_messages_control::acknowledgement_control::RetransmissionRequestSender;
|
||||
use crate::client::rtt_analyzer::{RttAnalyzer, RttEvent};
|
||||
use futures::channel::mpsc;
|
||||
use futures::StreamExt;
|
||||
use nym_nonexhaustive_delayqueue::{Expired, NonExhaustiveDelayQueue, QueueKey};
|
||||
@@ -16,6 +17,7 @@ use tracing::*;
|
||||
|
||||
pub(crate) type AckActionSender = mpsc::UnboundedSender<Action>;
|
||||
pub(crate) type AckActionReceiver = mpsc::UnboundedReceiver<Action>;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
// The actual data being sent off as well as potential key to the delay queue
|
||||
type PendingAckEntry = (Arc<PendingAcknowledgement>, Option<QueueKey>);
|
||||
@@ -207,8 +209,22 @@ impl ActionController {
|
||||
|
||||
// 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>,
|
||||
rtt_producer: Option<tokio::sync::mpsc::Sender<RttEvent>>,
|
||||
) {
|
||||
let frag_id = expired_ack.into_inner();
|
||||
if let Some(ref producer) = rtt_producer {
|
||||
if let Ok(duration) = SystemTime::now().duration_since(UNIX_EPOCH) {
|
||||
let now: u128 = duration.as_millis();
|
||||
|
||||
let _ = producer.try_send(RttEvent::FragmentAckExpired {
|
||||
fragment_id: frag_id.set_id().to_string(),
|
||||
timestamp: now,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
trace!("{frag_id} has expired");
|
||||
|
||||
@@ -244,7 +260,7 @@ impl ActionController {
|
||||
|
||||
pub(crate) async fn run(&mut self, shutdown_token: ShutdownToken) {
|
||||
debug!("Started ActionController with graceful shutdown support");
|
||||
|
||||
let rtt_producer = RttAnalyzer::producer();
|
||||
loop {
|
||||
tokio::select! {
|
||||
biased;
|
||||
@@ -262,7 +278,7 @@ impl ActionController {
|
||||
}
|
||||
},
|
||||
expired_ack = self.pending_acks_timers.next() => match expired_ack {
|
||||
Some(expired_ack) => self.handle_expired_ack_timer(expired_ack),
|
||||
Some(expired_ack) => self.handle_expired_ack_timer(expired_ack,rtt_producer.clone()),
|
||||
None => {
|
||||
tracing::trace!("ActionController: Stopping since ack channel closed");
|
||||
break;
|
||||
|
||||
+60
-1
@@ -5,6 +5,7 @@ use crate::client::inbound_messages::{InputMessage, InputMessageReceiver};
|
||||
use crate::client::real_messages_control::message_handler::MessageHandler;
|
||||
use crate::client::real_messages_control::real_traffic_stream::RealMessage;
|
||||
use crate::client::replies::reply_controller::ReplyControllerSender;
|
||||
use crate::client::rtt_analyzer::{RttConfig, RttEvent};
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
|
||||
use nym_sphinx::forwarding::packet::MixPacket;
|
||||
@@ -12,6 +13,7 @@ use nym_sphinx::params::PacketType;
|
||||
use nym_task::connections::TransmissionLane;
|
||||
use nym_task::ShutdownToken;
|
||||
use rand::{CryptoRng, Rng};
|
||||
use tokio::sync::mpsc::Sender;
|
||||
use tracing::*;
|
||||
|
||||
/// Module responsible for dealing with the received messages: splitting them, creating acknowledgements,
|
||||
@@ -111,7 +113,30 @@ where
|
||||
warn!("failed to send a repliable message - {err}")
|
||||
}
|
||||
}
|
||||
|
||||
async fn run_rtt_test(
|
||||
&mut self,
|
||||
recipient: Recipient,
|
||||
lane: TransmissionLane,
|
||||
packet_type: PacketType,
|
||||
max_retransmissions: Option<u32>,
|
||||
sender: Sender<RttEvent>,
|
||||
config: RttConfig,
|
||||
) {
|
||||
if let Err(err) = self
|
||||
.message_handler
|
||||
.try_run_rtt_test(
|
||||
recipient,
|
||||
lane,
|
||||
packet_type,
|
||||
max_retransmissions,
|
||||
sender,
|
||||
config,
|
||||
)
|
||||
.await
|
||||
{
|
||||
warn!("failed to send a repliable message - {err}")
|
||||
}
|
||||
}
|
||||
#[allow(clippy::panic)]
|
||||
async fn on_input_message(&mut self, msg: InputMessage) {
|
||||
match msg {
|
||||
@@ -147,6 +172,23 @@ where
|
||||
)
|
||||
.await
|
||||
}
|
||||
InputMessage::RunRTTTest {
|
||||
recipient,
|
||||
lane,
|
||||
max_retransmissions,
|
||||
sender,
|
||||
config,
|
||||
} => {
|
||||
self.run_rtt_test(
|
||||
recipient,
|
||||
lane,
|
||||
PacketType::Mix,
|
||||
max_retransmissions,
|
||||
sender,
|
||||
config,
|
||||
)
|
||||
.await
|
||||
}
|
||||
InputMessage::Reply {
|
||||
recipient_tag,
|
||||
data,
|
||||
@@ -176,6 +218,23 @@ where
|
||||
)
|
||||
.await
|
||||
}
|
||||
InputMessage::RunRTTTest {
|
||||
recipient,
|
||||
lane,
|
||||
max_retransmissions,
|
||||
sender,
|
||||
config,
|
||||
} => {
|
||||
self.run_rtt_test(
|
||||
recipient,
|
||||
lane,
|
||||
PacketType::Mix,
|
||||
max_retransmissions,
|
||||
sender,
|
||||
config,
|
||||
)
|
||||
.await
|
||||
}
|
||||
InputMessage::Anonymous {
|
||||
recipient,
|
||||
data,
|
||||
|
||||
@@ -8,6 +8,7 @@ use crate::client::real_messages_control::real_traffic_stream::{
|
||||
use crate::client::real_messages_control::{AckActionSender, Action};
|
||||
use crate::client::replies::reply_controller::MaxRetransmissions;
|
||||
use crate::client::replies::reply_storage::{ReceivedReplySurbsMap, SentReplyKeys, UsedSenderTags};
|
||||
use crate::client::rtt_analyzer::{RttConfig, RttEvent, RttPattern};
|
||||
use crate::client::topology_control::{TopologyAccessor, TopologyReadPermit};
|
||||
use nym_client_core_surb_storage::RetrievedReplySurb;
|
||||
use nym_sphinx::acknowledgements::AckKey;
|
||||
@@ -27,6 +28,8 @@ use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use thiserror::Error;
|
||||
use tokio::sync::mpsc::Sender;
|
||||
use tokio::time::sleep;
|
||||
use tracing::{debug, error, info, trace, warn};
|
||||
|
||||
// TODO: move that error elsewhere since it seems to be contaminating different files
|
||||
@@ -555,6 +558,97 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn try_split_and_send_non_reply_rtt_message(
|
||||
&mut self,
|
||||
sender_tag: AnonymousSenderTag,
|
||||
recipient: Recipient,
|
||||
lane: TransmissionLane,
|
||||
packet_type: PacketType,
|
||||
topology: &NymRouteProvider,
|
||||
max_retransmissions: Option<u32>,
|
||||
route_index: usize,
|
||||
sender: Sender<RttEvent>,
|
||||
) -> Result<(), PreparationError> {
|
||||
debug!("Sending RTT test message on route index {route_index} with packet type {packet_type:?}");
|
||||
|
||||
// Construct the base message
|
||||
let message = NymMessage::new_repliable(RepliableMessage::new_data(
|
||||
self.config.use_legacy_sphinx_format,
|
||||
Vec::new(),
|
||||
sender_tag,
|
||||
Vec::new(),
|
||||
));
|
||||
|
||||
debug_assert!(!matches!(message, NymMessage::Reply(_)));
|
||||
|
||||
let packet_size = if packet_type == PacketType::Outfox {
|
||||
PacketSize::OutfoxRegularPacket
|
||||
} else {
|
||||
self.optimal_packet_size(&message)
|
||||
};
|
||||
|
||||
trace!("Using packet size {packet_size:?}");
|
||||
|
||||
// ✅ Drop the read lock before mutably borrowing self
|
||||
|
||||
// Prepare fragments from message
|
||||
let fragments = self
|
||||
.message_preparer
|
||||
.pad_and_split_message(message, packet_size);
|
||||
|
||||
if fragments.len() > 1 {
|
||||
println!(
|
||||
"[RTT TEST] Warning: message was split into {} fragments",
|
||||
fragments.len()
|
||||
);
|
||||
}
|
||||
|
||||
let mut pending_acks = Vec::with_capacity(fragments.len());
|
||||
let mut real_messages = Vec::with_capacity(fragments.len());
|
||||
|
||||
for fragment in &fragments {
|
||||
let prepared_fragment = self
|
||||
.message_preparer
|
||||
.prepare_chunk_for_sending_with_deterministic_route(
|
||||
fragment.clone(),
|
||||
&topology,
|
||||
&self.config.ack_key,
|
||||
&recipient,
|
||||
packet_type,
|
||||
route_index,
|
||||
)?;
|
||||
|
||||
let _ = sender.try_send(RttEvent::RouteUsed {
|
||||
route_index,
|
||||
fragment_id: (fragment.fragment_identifier().set_id().to_string()),
|
||||
});
|
||||
|
||||
let real_message = RealMessage::new(
|
||||
prepared_fragment.mix_packet,
|
||||
Some(fragment.fragment_identifier().clone()),
|
||||
);
|
||||
|
||||
let pending_ack = PendingAcknowledgement::new_known(
|
||||
fragment.clone(),
|
||||
prepared_fragment.total_delay,
|
||||
recipient,
|
||||
max_retransmissions,
|
||||
);
|
||||
|
||||
real_messages.push(real_message);
|
||||
pending_acks.push(pending_ack);
|
||||
}
|
||||
|
||||
// Record ACKs and forward messages for *this route only*
|
||||
self.insert_pending_acks(pending_acks);
|
||||
self.forward_messages(real_messages, lane).await;
|
||||
|
||||
// // Optional: small delay to avoid flooding
|
||||
// sleep(Duration::from_millis(200)).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn try_send_additional_reply_surbs(
|
||||
&mut self,
|
||||
recipient: Recipient,
|
||||
@@ -633,6 +727,142 @@ where
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// Helper: sends ONE RTT packet on ONE specific route.
|
||||
// Rust requires this to be a standalone async function (not an async closure),
|
||||
// because async closures cannot borrow local variables safely.
|
||||
async fn send_packet_on_route(
|
||||
&mut self,
|
||||
recipient: &Recipient,
|
||||
num_reply_surbs: u32,
|
||||
lane: TransmissionLane,
|
||||
packet_type: PacketType,
|
||||
topology: &NymRouteProvider,
|
||||
max_retransmissions: Option<u32>,
|
||||
route_index: usize,
|
||||
sender: &Sender<RttEvent>,
|
||||
) -> Result<(), SurbWrappedPreparationError> {
|
||||
let sender_tag = self.get_or_create_sender_tag(recipient);
|
||||
|
||||
// Prepare reply SURBs
|
||||
let reply_surbs = self.generate_reply_surbs(num_reply_surbs as usize).await?;
|
||||
let reply_keys = reply_surbs
|
||||
.iter()
|
||||
.map(|s| *s.encryption_key())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Send message on the given route
|
||||
self.try_split_and_send_non_reply_rtt_message(
|
||||
sender_tag,
|
||||
recipient.clone(),
|
||||
lane,
|
||||
packet_type,
|
||||
topology,
|
||||
max_retransmissions,
|
||||
route_index,
|
||||
sender.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Store reply keys after sending
|
||||
self.reply_key_storage.insert_multiple(reply_keys);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
pub(crate) async fn try_run_rtt_test(
|
||||
&mut self,
|
||||
recipient: Recipient,
|
||||
lane: TransmissionLane,
|
||||
packet_type: PacketType,
|
||||
max_retransmissions: Option<u32>,
|
||||
sender: Sender<RttEvent>,
|
||||
config: RttConfig,
|
||||
) -> Result<(), SurbWrappedPreparationError> {
|
||||
debug!("Starting RTT test using pattern {:?}", config.pattern);
|
||||
|
||||
// Load topology
|
||||
let topology_permit = self.topology_access.get_read_permit().await;
|
||||
let mut topology = self.get_topology(&topology_permit)?.clone();
|
||||
let route_strings = topology
|
||||
.topology
|
||||
.initialize_static_mixnodes_for_rtt_testing()?;
|
||||
let total_routes = topology.topology.all_mix_routes.len();
|
||||
drop(topology_permit);
|
||||
// =====================================================
|
||||
// SEND ROUTE STRINGS TO RTT ANALYZER USING try_send()
|
||||
// =====================================================
|
||||
for (route_index, nodes_string) in route_strings {
|
||||
let _ = sender.try_send(RttEvent::RouteNodes {
|
||||
route_index,
|
||||
nodes: nodes_string,
|
||||
});
|
||||
}
|
||||
sender
|
||||
.send(RttEvent::ExperimentConfiguration {
|
||||
total_routes: total_routes as usize,
|
||||
per_route_sent: config.packets_per_route as usize,
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// ==============================================================
|
||||
// PATTERN: BURST
|
||||
// Send packets_per_route packets on each route sequentially
|
||||
// ==============================================================
|
||||
if let RttPattern::Burst = config.pattern {
|
||||
for route in 0..total_routes {
|
||||
for _ in 0..config.packets_per_route {
|
||||
self.send_packet_on_route(
|
||||
&recipient,
|
||||
0,
|
||||
lane,
|
||||
packet_type,
|
||||
&topology,
|
||||
max_retransmissions,
|
||||
route,
|
||||
&sender,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Optional delay between packets on the same route
|
||||
if config.inter_route_delay_ms > 0 {
|
||||
tokio::time::sleep(Duration::from_millis(config.inter_route_delay_ms))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// ==============================================================
|
||||
// PATTERN: ROUND ROBIN
|
||||
// Send packets in cycles: 1 on route 0, 1 on route 1, ..., repeat
|
||||
// ==============================================================
|
||||
if let RttPattern::RoundRobin = config.pattern {
|
||||
for _cycle in 0..config.packets_per_route {
|
||||
for route in 0..total_routes {
|
||||
self.send_packet_on_route(
|
||||
&recipient,
|
||||
0,
|
||||
lane,
|
||||
packet_type,
|
||||
&topology,
|
||||
max_retransmissions,
|
||||
route,
|
||||
&sender,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if config.inter_route_delay_ms > 0 {
|
||||
tokio::time::sleep(Duration::from_millis(config.inter_route_delay_ms))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn try_prepare_single_chunk_for_sending(
|
||||
&mut self,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
use self::sending_delay_controller::SendingDelayController;
|
||||
use crate::client::mix_traffic::BatchMixMessageSender;
|
||||
use crate::client::real_messages_control::acknowledgement_control::SentPacketNotificationSender;
|
||||
use crate::client::rtt_analyzer::{RttAnalyzer, RttEvent};
|
||||
use crate::client::topology_control::TopologyAccessor;
|
||||
use crate::client::transmission_buffer::TransmissionBuffer;
|
||||
use crate::config;
|
||||
@@ -21,6 +22,7 @@ use nym_statistics_common::clients::{packet_statistics::PacketStatisticsEvent, C
|
||||
use nym_task::connections::{
|
||||
ConnectionCommand, ConnectionCommandReceiver, ConnectionId, LaneQueueLengths, TransmissionLane,
|
||||
};
|
||||
|
||||
use nym_task::ShutdownToken;
|
||||
use rand::{CryptoRng, Rng};
|
||||
use std::pin::Pin;
|
||||
@@ -31,9 +33,9 @@ use tracing::*;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio::time::{sleep, Sleep};
|
||||
|
||||
// use wasm_utils::console_log;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasmtimer::tokio::{sleep, Sleep};
|
||||
|
||||
mod sending_delay_controller;
|
||||
|
||||
/// Configurable parameters of the `OutQueueControl`
|
||||
@@ -224,7 +226,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
async fn on_message(&mut self, next_message: StreamMessage) {
|
||||
async fn on_message(
|
||||
&mut self,
|
||||
next_message: StreamMessage,
|
||||
rtt_producer: Option<tokio::sync::mpsc::Sender<RttEvent>>,
|
||||
) {
|
||||
trace!("created new message");
|
||||
|
||||
let (next_message, fragment_id, packet_size) = match next_message {
|
||||
@@ -271,6 +277,21 @@ where
|
||||
)
|
||||
}
|
||||
StreamMessage::Real(real_message) => {
|
||||
if let Some(ref producer) = rtt_producer {
|
||||
if let Some(fragment_id_local) = real_message.fragment_id {
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
let now = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_millis();
|
||||
let _ = producer.try_send(RttEvent::FragmentSent {
|
||||
fragment_id: (fragment_id_local.set_id().to_string()),
|
||||
timestamp: (now),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let packet_size = real_message.packet_size();
|
||||
(
|
||||
real_message.mix_packet,
|
||||
@@ -298,6 +319,8 @@ where
|
||||
"failed to send mixnet packet due to closed channel (outside of shutdown!)"
|
||||
);
|
||||
}
|
||||
// Early return to avoid further processing when channel is closed
|
||||
return;
|
||||
}
|
||||
Ok(_) => {
|
||||
let event = if fragment_id.is_some() {
|
||||
@@ -325,9 +348,15 @@ where
|
||||
// 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
|
||||
|
||||
// TODO: temporary and BAD workaround for wasm (we should find a way to yield here in wasm)
|
||||
#[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) {
|
||||
@@ -576,7 +605,7 @@ where
|
||||
|
||||
pub(crate) async fn run(&mut self) {
|
||||
debug!("Started OutQueueControl with graceful shutdown support");
|
||||
|
||||
let rtt_producer = RttAnalyzer::producer();
|
||||
// avoid borrow on self
|
||||
let shutdown_token = self.shutdown_token.clone();
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
@@ -594,7 +623,7 @@ where
|
||||
self.log_status();
|
||||
}
|
||||
next_message = self.next() => if let Some(next_message) = next_message {
|
||||
self.on_message(next_message).await;
|
||||
self.on_message(next_message,rtt_producer.clone()).await;
|
||||
} else {
|
||||
tracing::trace!("OutQueueControl: Stopping since channel closed");
|
||||
break;
|
||||
|
||||
@@ -5,6 +5,7 @@ use crate::client::helpers::get_time_now;
|
||||
use crate::client::replies::{
|
||||
reply_controller::ReplyControllerSender, reply_storage::SentReplyKeys,
|
||||
};
|
||||
use crate::client::rtt_analyzer::{RttAnalyzer, RttEvent};
|
||||
use futures::channel::mpsc;
|
||||
use futures::lock::Mutex;
|
||||
use futures::StreamExt;
|
||||
@@ -55,6 +56,7 @@ struct ReceivedMessagesBufferInner<R: MessageReceiver> {
|
||||
|
||||
// Periodically check for stale buffers to clean up
|
||||
last_stale_check: crate::client::helpers::Instant,
|
||||
rtt_producer: Option<tokio::sync::mpsc::Sender<RttEvent>>,
|
||||
}
|
||||
|
||||
impl<R: MessageReceiver> ReceivedMessagesBufferInner<R> {
|
||||
@@ -81,7 +83,20 @@ impl<R: MessageReceiver> ReceivedMessagesBufferInner<R> {
|
||||
warn!("failed to recover fragment from raw data: {err}. The whole underlying message might be corrupted and unrecoverable!");
|
||||
return None;
|
||||
}
|
||||
Ok(frag) => frag,
|
||||
Ok(frag) => {
|
||||
if let Some(ref producer) = self.rtt_producer {
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
let now = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_millis();
|
||||
let _ = producer.try_send(RttEvent::FragmentReceived {
|
||||
fragment_id: frag.id().to_string(),
|
||||
timestamp: now,
|
||||
});
|
||||
}
|
||||
frag
|
||||
}
|
||||
};
|
||||
|
||||
if self.recently_reconstructed.contains(&fragment.id()) {
|
||||
@@ -181,6 +196,7 @@ impl<R: MessageReceiver> ReceivedMessagesBuffer<R> {
|
||||
reply_controller_sender: ReplyControllerSender,
|
||||
stats_tx: ClientStatsSender,
|
||||
shutdown_token: ShutdownToken,
|
||||
rtt_producer: Option<tokio::sync::mpsc::Sender<RttEvent>>,
|
||||
) -> Self {
|
||||
ReceivedMessagesBuffer {
|
||||
inner: Arc::new(Mutex::new(ReceivedMessagesBufferInner {
|
||||
@@ -191,6 +207,7 @@ impl<R: MessageReceiver> ReceivedMessagesBuffer<R> {
|
||||
recently_reconstructed: HashSet::new(),
|
||||
stats_tx,
|
||||
last_stale_check: get_time_now(),
|
||||
rtt_producer: rtt_producer.clone(),
|
||||
})),
|
||||
reply_key_storage,
|
||||
reply_controller_sender,
|
||||
@@ -585,6 +602,7 @@ impl<R: MessageReceiver + Clone + Send + 'static> ReceivedMessagesBufferControll
|
||||
reply_controller_sender,
|
||||
metrics_reporter,
|
||||
shutdown_token.clone(),
|
||||
RttAnalyzer::producer(),
|
||||
);
|
||||
|
||||
ReceivedMessagesBufferController {
|
||||
|
||||
@@ -0,0 +1,514 @@
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io::{BufWriter, Write};
|
||||
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::process::Command;
|
||||
use std::sync::Mutex;
|
||||
use tokio::sync::mpsc::{self, Sender};
|
||||
use tokio::task::JoinHandle;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum RttPattern {
|
||||
Burst,
|
||||
RoundRobin,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RttConfig {
|
||||
pub packets_per_route: u32,
|
||||
pub pattern: RttPattern,
|
||||
pub inter_route_delay_ms: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum RttEvent {
|
||||
RouteUsed {
|
||||
route_index: usize,
|
||||
fragment_id: String,
|
||||
},
|
||||
FragmentSent {
|
||||
fragment_id: String,
|
||||
timestamp: u128,
|
||||
},
|
||||
FragmentAckReceived {
|
||||
fragment_id: String,
|
||||
timestamp: u128,
|
||||
},
|
||||
FragmentAckExpired {
|
||||
fragment_id: String,
|
||||
timestamp: u128,
|
||||
},
|
||||
FragmentReceived {
|
||||
fragment_id: String,
|
||||
timestamp: u128,
|
||||
},
|
||||
RouteNodes {
|
||||
route_index: usize,
|
||||
nodes: String,
|
||||
},
|
||||
ExperimentConfiguration {
|
||||
total_routes: usize,
|
||||
per_route_sent: usize,
|
||||
},
|
||||
PrintRouteDetail {
|
||||
route_index: usize,
|
||||
},
|
||||
PrintRouteStatsByNodes {
|
||||
nodes: String,
|
||||
},
|
||||
PrintRoutesWithAvgAbove {
|
||||
threshold_ms: u128,
|
||||
},
|
||||
PrintRoutesWithAnyAbove {
|
||||
threshold_ms: u128,
|
||||
},
|
||||
PrintStats,
|
||||
WriteStats {
|
||||
path: String,
|
||||
},
|
||||
WriteStatsAndPlot {
|
||||
path: String,
|
||||
outlier_mode: String, // "all" or cutoff() seconds (e.g. "1.0"))
|
||||
},
|
||||
PrintExperimentProgress,
|
||||
}
|
||||
|
||||
pub struct StoredRouteSummary {
|
||||
pub total_routes: usize,
|
||||
pub per_route_sent: usize,
|
||||
}
|
||||
|
||||
static PRODUCER: OnceCell<Mutex<Option<Sender<RttEvent>>>> = OnceCell::new();
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct RouteStats {
|
||||
pub sent: u32,
|
||||
pub acks: u32,
|
||||
pub timeouts: u32,
|
||||
pub rtts: Vec<u128>,
|
||||
}
|
||||
|
||||
pub struct RttAnalyzer {
|
||||
/// fragment_id → (route, Vec<sent_times>)
|
||||
fragments: HashMap<String, (usize, Vec<u128>)>,
|
||||
|
||||
/// fragment_id → Vec<recv_times>
|
||||
receive_times: HashMap<String, Vec<u128>>,
|
||||
|
||||
/// fragment_id → last ack
|
||||
ack_times: HashMap<String, u128>,
|
||||
|
||||
route_stats: HashMap<usize, RouteStats>,
|
||||
route_summary: Option<StoredRouteSummary>,
|
||||
route_nodes: HashMap<usize, String>,
|
||||
consumer_handle: JoinHandle<()>,
|
||||
}
|
||||
|
||||
impl RttAnalyzer {
|
||||
pub fn consumer_handle(&self) -> &JoinHandle<()> {
|
||||
&self.consumer_handle
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
let (tx, mut rx) = mpsc::channel(80000);
|
||||
|
||||
PRODUCER
|
||||
.set(Mutex::new(Some(tx.clone())))
|
||||
.expect("PRODUCER already initialized");
|
||||
|
||||
let handle = tokio::spawn(async move {
|
||||
let mut analyzer = RttAnalyzer {
|
||||
fragments: HashMap::new(),
|
||||
receive_times: HashMap::new(),
|
||||
ack_times: HashMap::new(),
|
||||
route_stats: HashMap::new(),
|
||||
route_summary: None,
|
||||
route_nodes: HashMap::new(),
|
||||
consumer_handle: tokio::spawn(async {}),
|
||||
};
|
||||
|
||||
while let Some(event) = rx.recv().await {
|
||||
analyzer.process(event);
|
||||
}
|
||||
|
||||
println!("RTT Analyzer consumer exited");
|
||||
});
|
||||
|
||||
Self {
|
||||
fragments: HashMap::new(),
|
||||
receive_times: HashMap::new(),
|
||||
ack_times: HashMap::new(),
|
||||
route_stats: HashMap::new(),
|
||||
route_summary: None,
|
||||
route_nodes: HashMap::new(),
|
||||
consumer_handle: handle,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn producer() -> Option<Sender<RttEvent>> {
|
||||
let lock = PRODUCER.get()?.lock().unwrap();
|
||||
lock.clone()
|
||||
}
|
||||
|
||||
pub fn process(&mut self, event: RttEvent) {
|
||||
match event {
|
||||
// -------------------------
|
||||
// FIRST USE OF A FRAGMENT
|
||||
// -------------------------
|
||||
RttEvent::RouteUsed {
|
||||
route_index,
|
||||
fragment_id,
|
||||
} => {
|
||||
self.fragments
|
||||
.insert(fragment_id.clone(), (route_index, Vec::new()));
|
||||
}
|
||||
|
||||
// -------------------------
|
||||
// RETRANSMISSION → append new sent time
|
||||
// -------------------------
|
||||
RttEvent::FragmentSent {
|
||||
fragment_id,
|
||||
timestamp,
|
||||
} => {
|
||||
if let Some((_route, sent_list)) = self.fragments.get_mut(&fragment_id) {
|
||||
sent_list.push(timestamp);
|
||||
self.route_stats.entry(*_route).or_default().sent += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------
|
||||
// ACK RECEIVED
|
||||
// -------------------------
|
||||
RttEvent::FragmentAckReceived {
|
||||
fragment_id,
|
||||
timestamp,
|
||||
} => {
|
||||
if let Some((route, _)) = self.fragments.get(&fragment_id) {
|
||||
let stats = self.route_stats.entry(*route).or_default();
|
||||
stats.acks += 1;
|
||||
}
|
||||
self.ack_times.insert(fragment_id, timestamp);
|
||||
}
|
||||
|
||||
// -------------------------
|
||||
// ACK TIMEOUT
|
||||
// -------------------------
|
||||
RttEvent::FragmentAckExpired { fragment_id, .. } => {
|
||||
if let Some((route, _)) = self.fragments.get(&fragment_id) {
|
||||
self.route_stats.entry(*route).or_default().timeouts += 1;
|
||||
}
|
||||
}
|
||||
RttEvent::WriteStatsAndPlot { path, outlier_mode } => {
|
||||
// 1) write the csv
|
||||
if let Err(e) = self.write_csv(&path) {
|
||||
eprintln!("Failed to write CSV: {}", e);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2) Call the Python script
|
||||
if let Err(e) = Self::run_histogram_script(&path, &outlier_mode) {
|
||||
eprintln!("Failed to run histogram script: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------
|
||||
// PACKET RECEIVED → compute RTTs
|
||||
// -------------------------
|
||||
RttEvent::FragmentReceived {
|
||||
fragment_id,
|
||||
timestamp,
|
||||
} => {
|
||||
// Append receive time
|
||||
let recv_list = self.receive_times.entry(fragment_id.clone()).or_default();
|
||||
recv_list.push(timestamp);
|
||||
|
||||
// Lookup route + sent times
|
||||
if let Some((route, sent_list)) = self.fragments.get(&fragment_id) {
|
||||
let recv_list = self.receive_times.get(&fragment_id).unwrap();
|
||||
|
||||
// Index of the *newly added* receive time
|
||||
let idx = recv_list.len() - 1;
|
||||
/*
|
||||
Maybe we can put a retransmission flag and not counting the new RTTs and only the basic N?
|
||||
*/
|
||||
//println!("Fragment id: {} Sent list length: {} Recv list length:{}",fragment_id,sent_list.len(),recv_list.len());
|
||||
|
||||
// Check if we have a matching sent timestamp
|
||||
if idx < sent_list.len() {
|
||||
let sent_ts = sent_list[idx];
|
||||
let rtt = recv_list[idx] - sent_ts;
|
||||
|
||||
self.route_stats.entry(*route).or_default().rtts.push(rtt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RttEvent::RouteNodes { route_index, nodes } => {
|
||||
self.route_nodes.insert(route_index, nodes);
|
||||
}
|
||||
|
||||
RttEvent::PrintStats => self.print_stats(),
|
||||
|
||||
RttEvent::WriteStats { path } => {
|
||||
if let Err(e) = self.write_csv(&path) {
|
||||
eprintln!("Failed to write CSV: {}", e)
|
||||
}
|
||||
}
|
||||
|
||||
RttEvent::ExperimentConfiguration {
|
||||
total_routes,
|
||||
per_route_sent,
|
||||
} => {
|
||||
self.route_summary = Some(StoredRouteSummary {
|
||||
total_routes,
|
||||
per_route_sent,
|
||||
});
|
||||
}
|
||||
RttEvent::PrintExperimentProgress => {
|
||||
self.print_experiment_progress();
|
||||
}
|
||||
|
||||
RttEvent::PrintRouteDetail { route_index } => {
|
||||
self.print_route_detail(route_index);
|
||||
}
|
||||
|
||||
RttEvent::PrintRoutesWithAvgAbove { threshold_ms } => {
|
||||
self.print_routes_with_avg_above(threshold_ms);
|
||||
}
|
||||
|
||||
RttEvent::PrintRoutesWithAnyAbove { threshold_ms } => {
|
||||
self.print_routes_with_any_above(threshold_ms);
|
||||
}
|
||||
|
||||
RttEvent::PrintRouteStatsByNodes { nodes } => {
|
||||
self.print_route_by_nodes(nodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------- PRINT FUNCTIONS (unchanged) ----------------------
|
||||
pub fn print_stats(&self) {
|
||||
println!("\n================ Route RTT Statistics ================");
|
||||
for (route, stats) in self.route_stats.iter() {
|
||||
let avg_rtt = if !stats.rtts.is_empty() {
|
||||
stats.rtts.iter().sum::<u128>() as f64 / stats.rtts.len() as f64
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
println!(
|
||||
"Route {:5} | Sent {:4} | ACKs {:4} | Timeouts {:4} | Avg RTT {:8.2}",
|
||||
route, stats.sent, stats.acks, stats.timeouts, avg_rtt
|
||||
);
|
||||
}
|
||||
println!("======================================================\n");
|
||||
}
|
||||
|
||||
pub fn write_csv(&self, path: &str) -> std::io::Result<()> {
|
||||
let mut writer = BufWriter::new(File::create(path)?);
|
||||
|
||||
writer.write_all(b"route,sent,acks,timeouts,avg_rtt\n")?;
|
||||
|
||||
for (route, stats) in &self.route_stats {
|
||||
let avg_rtt = if !stats.rtts.is_empty() {
|
||||
stats.rtts.iter().sum::<u128>() as f64 / stats.rtts.len() as f64
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
writer.write_all(
|
||||
format!(
|
||||
"{},{},{},{},{:.2}\n",
|
||||
route, stats.sent, stats.acks, stats.timeouts, avg_rtt
|
||||
)
|
||||
.as_bytes(),
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn print_route_detail(&self, route_index: usize) {
|
||||
println!(
|
||||
"\n================ Route #{} Details ================\n",
|
||||
route_index
|
||||
);
|
||||
|
||||
if let Some(nodes) = self.route_nodes.get(&route_index) {
|
||||
println!(" Route Nodes:");
|
||||
for (i, node) in nodes.split(" > ").enumerate() {
|
||||
println!(" • Node {}: {}", i + 1, node);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(stats) = self.route_stats.get(&route_index) {
|
||||
println!("\n RTT Values:");
|
||||
for (i, rtt) in stats.rtts.iter().enumerate() {
|
||||
println!(" [{:3}] {} ms", i, rtt);
|
||||
}
|
||||
}
|
||||
|
||||
println!("======================================================\n");
|
||||
}
|
||||
fn run_histogram_script(csv_path: &str, outlier_mode: &str) -> std::io::Result<()> {
|
||||
let status = Command::new("python")
|
||||
.arg("rtt_histogram.py") // path του script
|
||||
.arg(csv_path)
|
||||
.arg(outlier_mode)
|
||||
.status()?;
|
||||
|
||||
if !status.success() {
|
||||
eprintln!("Python histogram script exited with status: {}", status);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn print_routes_with_avg_above(&self, threshold_ms: u128) {
|
||||
println!(
|
||||
"\n======= Routes with AVG RTT > {} ms =======\n",
|
||||
threshold_ms
|
||||
);
|
||||
|
||||
let mut matches: Vec<usize> = self
|
||||
.route_stats
|
||||
.iter()
|
||||
.filter_map(|(route, stats)| {
|
||||
if stats.rtts.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let avg = stats.rtts.iter().sum::<u128>() as f64 / stats.rtts.len() as f64;
|
||||
if (avg as u128) > threshold_ms {
|
||||
Some(*route)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
matches.sort();
|
||||
|
||||
for route in matches {
|
||||
self.print_route_detail(route);
|
||||
}
|
||||
|
||||
println!("====================================================\n");
|
||||
}
|
||||
/// Prints overall experiment completion percentage.
|
||||
///
|
||||
/// It uses:
|
||||
/// - self.route_summary.total_routes
|
||||
/// - self.route_summary.per_route_sent
|
||||
/// to compute how many packets were planned in total.
|
||||
///
|
||||
/// Then it sums, over all routes:
|
||||
/// - how many packets were actually sent (RouteStats.sent)
|
||||
/// - how many packets have a completed RTT sample (RouteStats.rtts.len())
|
||||
///
|
||||
/// Finally it prints:
|
||||
/// - total expected packets
|
||||
/// - total sent packets and percentage
|
||||
/// - total completed RTT packets and percentage
|
||||
pub fn print_experiment_progress(&self) {
|
||||
println!("\n=========== RTT Experiment Progress ===========");
|
||||
|
||||
// Check if experiment configuration is available
|
||||
let summary = match &self.route_summary {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
println!("No experiment configuration stored (route_summary is None).");
|
||||
println!("You must send an ExperimentConfiguration event first.");
|
||||
println!("==============================================\n");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let total_routes = summary.total_routes;
|
||||
let per_route_sent = summary.per_route_sent;
|
||||
|
||||
// Total number of packets that were planned for the whole experiment
|
||||
let expected_total: usize = total_routes.saturating_mul(per_route_sent);
|
||||
|
||||
if expected_total == 0 {
|
||||
println!("Experiment configuration has zero expected packets.");
|
||||
println!("==============================================\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Sum how many packets were actually sent and how many have a measured RTT
|
||||
let mut sent_total: usize = 0;
|
||||
let mut received_total: usize = 0;
|
||||
|
||||
for (_route_idx, stats) in &self.route_stats {
|
||||
// 'sent' counts how many times we called FragmentSent for this route
|
||||
sent_total += std::cmp::min(stats.sent, per_route_sent as u32) as usize;
|
||||
|
||||
// Each RTT entry corresponds to one packet for which we have both send and receive time
|
||||
let route_recv = std::cmp::min(stats.rtts.len(), per_route_sent);
|
||||
received_total += route_recv;
|
||||
}
|
||||
|
||||
let sent_pct = (sent_total as f64 / expected_total as f64) * 100.0;
|
||||
let recv_pct = (received_total as f64 / expected_total as f64) * 100.0;
|
||||
|
||||
println!("Total routes configured : {}", total_routes);
|
||||
println!("Packets per route (planned) : {}", per_route_sent);
|
||||
println!("Total expected packets : {}", expected_total);
|
||||
println!("---------------------------------------------");
|
||||
println!(
|
||||
"Total sent packets : {} ({:.2}%)",
|
||||
sent_total, sent_pct
|
||||
);
|
||||
println!(
|
||||
"Total completed RTT packets : {} ({:.2}%)",
|
||||
received_total, recv_pct
|
||||
);
|
||||
println!("==============================================\n");
|
||||
}
|
||||
|
||||
pub fn print_routes_with_any_above(&self, threshold_ms: u128) {
|
||||
println!(
|
||||
"\n======= Routes with ANY RTT > {} ms =======\n",
|
||||
threshold_ms
|
||||
);
|
||||
|
||||
let mut matches: Vec<usize> = self
|
||||
.route_stats
|
||||
.iter()
|
||||
.filter_map(|(route, stats)| {
|
||||
if stats.rtts.iter().any(|&x| x > threshold_ms) {
|
||||
Some(*route)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
matches.sort();
|
||||
|
||||
for route in matches {
|
||||
self.print_route_detail(route);
|
||||
}
|
||||
|
||||
println!("====================================================\n");
|
||||
}
|
||||
|
||||
pub fn print_route_by_nodes(&self, nodes: String) {
|
||||
println!("\n========== Searching route by nodes ==========\n");
|
||||
|
||||
let mut routes: Vec<(usize, &String)> =
|
||||
self.route_nodes.iter().map(|(k, v)| (*k, v)).collect();
|
||||
routes.sort_by_key(|(idx, _)| *idx);
|
||||
|
||||
for (route, stored) in routes {
|
||||
if *stored == nodes {
|
||||
println!("Found route {}!", route);
|
||||
self.print_route_detail(route);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
println!("No route found with nodes: {}", nodes);
|
||||
println!("=============================================\n");
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,7 @@ pub enum ClientCoreError {
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[error("resolution failed: {0}")]
|
||||
ResolutionFailed(#[from] nym_http_api_client::HickoryDnsError),
|
||||
ResolutionFailed(#[from] nym_http_api_client::ResolveError),
|
||||
|
||||
#[error("no gateways on network")]
|
||||
NoGatewaysOnNetwork,
|
||||
|
||||
@@ -45,6 +45,7 @@ type WsConn = JSWebsocket;
|
||||
|
||||
const CONCURRENT_GATEWAYS_MEASURED: usize = 20;
|
||||
const MEASUREMENTS: usize = 3;
|
||||
const DEFAULT_NYM_API_RETRIES: usize = 3;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
const CONN_TIMEOUT: Duration = Duration::from_millis(1500);
|
||||
@@ -132,25 +133,27 @@ impl<'a, G: ConnectableGateway> GatewayWithLatency<'a, G> {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn gateways_for_init<R: Rng>(
|
||||
rng: &mut R,
|
||||
pub async fn gateways_for_init(
|
||||
nym_apis: &[Url],
|
||||
user_agent: Option<UserAgent>,
|
||||
minimum_performance: u8,
|
||||
ignore_epoch_roles: bool,
|
||||
retry_count: Option<usize>,
|
||||
) -> Result<Vec<RoutingNode>, ClientCoreError> {
|
||||
let nym_api = nym_apis
|
||||
.choose(rng)
|
||||
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
|
||||
// Build client with ALL URLs for fallback support
|
||||
let nym_api_urls: Vec<nym_http_api_client::Url> = nym_apis
|
||||
.iter()
|
||||
.map(|url| nym_http_api_client::Url::from(url.clone()))
|
||||
.collect();
|
||||
|
||||
// Use the unified HTTP client directly with optional user agent
|
||||
let mut builder = nym_http_api_client::Client::builder(nym_api.clone())
|
||||
.map_err(|e| {
|
||||
ClientCoreError::ValidatorClientError(nym_validator_client::ValidatorClientError::from(
|
||||
e,
|
||||
))
|
||||
})?
|
||||
.with_bincode(); // Use bincode for better performance
|
||||
if nym_api_urls.is_empty() {
|
||||
return Err(ClientCoreError::ListOfNymApisIsEmpty);
|
||||
}
|
||||
|
||||
let retry_count = retry_count.unwrap_or(DEFAULT_NYM_API_RETRIES);
|
||||
let mut builder = nym_http_api_client::ClientBuilder::new_with_urls(nym_api_urls.clone())?
|
||||
.with_retries(retry_count)
|
||||
.with_bincode();
|
||||
|
||||
if let Some(user_agent) = user_agent {
|
||||
builder = builder.with_user_agent(user_agent);
|
||||
@@ -160,7 +163,7 @@ pub async fn gateways_for_init<R: Rng>(
|
||||
ClientCoreError::ValidatorClientError(nym_validator_client::ValidatorClientError::from(e))
|
||||
})?;
|
||||
|
||||
tracing::debug!("Fetching list of gateways from: {nym_api}");
|
||||
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)
|
||||
@@ -172,17 +175,15 @@ pub async fn gateways_for_init<R: Rng>(
|
||||
|
||||
// filter out gateways below minimum performance and ones that could operate as a mixnode
|
||||
// (we don't want instability)
|
||||
let valid_gateways = gateways
|
||||
let valid_gateways: Vec<RoutingNode> = gateways
|
||||
.iter()
|
||||
.filter(|g| ignore_epoch_roles || !g.supported_roles.mixnode)
|
||||
.filter(|g| g.performance.round_to_integer() >= minimum_performance)
|
||||
.filter_map(|gateway| gateway.try_into().ok())
|
||||
.collect::<Vec<_>>();
|
||||
tracing::debug!("After checking validity: {}", valid_gateways.len());
|
||||
tracing::trace!("Valid gateways: {valid_gateways:#?}");
|
||||
.collect();
|
||||
|
||||
tracing::info!(
|
||||
"and {} after validity and performance filtering",
|
||||
"Found {} valid gateways after filtering",
|
||||
valid_gateways.len()
|
||||
);
|
||||
|
||||
@@ -345,13 +346,20 @@ pub(super) fn get_specified_gateway(
|
||||
must_use_tls: bool,
|
||||
) -> Result<RoutingNode, ClientCoreError> {
|
||||
tracing::debug!("Requesting specified gateway: {gateway_identity}");
|
||||
|
||||
let user_gateway = ed25519::PublicKey::from_base58_string(gateway_identity)
|
||||
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
|
||||
|
||||
let gateway = gateways
|
||||
.iter()
|
||||
.find(|gateway| gateway.identity_key == user_gateway)
|
||||
.ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_identity.to_string()))?;
|
||||
.ok_or_else(|| {
|
||||
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 {
|
||||
return Err(ClientCoreError::UnsupportedEntry {
|
||||
@@ -414,3 +422,52 @@ pub(super) async fn register_with_gateway(
|
||||
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 = [
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ pub(crate) async fn connect_async(
|
||||
resolver
|
||||
.resolve_str(domain)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|a| SocketAddr::new(a, port))
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -24,7 +24,9 @@ pub fn spawn_future<F>(future: F)
|
||||
where
|
||||
F: Future<Output = ()> + 'static,
|
||||
{
|
||||
wasm_bindgen_futures::spawn_local(future);
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
future.await;
|
||||
});
|
||||
}
|
||||
|
||||
#[deprecated(note = "use spawn_future from nym_task crate instead")]
|
||||
|
||||
@@ -88,3 +88,6 @@ features = ["js"]
|
||||
|
||||
[features]
|
||||
wasm = []
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use si_scale::helpers::bibytes2;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::sync::atomic::{AtomicBool, AtomicI64, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
@@ -26,6 +27,39 @@ pub struct ClientBandwidth {
|
||||
inner: Arc<ClientBandwidthInner>,
|
||||
}
|
||||
|
||||
// simple helper for logging purposes to accommodate 'unknown' case
|
||||
pub(crate) enum UpgradeModeEnabledWrapper {
|
||||
True,
|
||||
False,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl From<Option<bool>> for UpgradeModeEnabledWrapper {
|
||||
fn from(value: Option<bool>) -> Self {
|
||||
match value {
|
||||
Some(true) => UpgradeModeEnabledWrapper::True,
|
||||
Some(false) => UpgradeModeEnabledWrapper::False,
|
||||
None => UpgradeModeEnabledWrapper::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for UpgradeModeEnabledWrapper {
|
||||
fn from(value: bool) -> Self {
|
||||
Some(value).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for UpgradeModeEnabledWrapper {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
UpgradeModeEnabledWrapper::True => write!(f, "true"),
|
||||
UpgradeModeEnabledWrapper::False => write!(f, "false"),
|
||||
UpgradeModeEnabledWrapper::Unknown => write!(f, "unknown"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ClientBandwidthInner {
|
||||
/// the actual bandwidth amount (in bytes) available
|
||||
available: AtomicI64,
|
||||
@@ -71,26 +105,41 @@ impl ClientBandwidth {
|
||||
self.inner.available.load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
pub(crate) fn maybe_log_bandwidth(&self, now: Option<OffsetDateTime>) {
|
||||
pub(crate) fn maybe_log_bandwidth(
|
||||
&self,
|
||||
now: Option<OffsetDateTime>,
|
||||
upgrade_mode: impl Into<UpgradeModeEnabledWrapper>,
|
||||
) {
|
||||
let last = self.last_logged();
|
||||
let now = now.unwrap_or_else(OffsetDateTime::now_utc);
|
||||
if last + Duration::from_secs(10) < now {
|
||||
self.log_bandwidth(Some(now))
|
||||
self.log_bandwidth(Some(now), upgrade_mode)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn log_bandwidth(&self, now: Option<OffsetDateTime>) {
|
||||
pub(crate) fn log_bandwidth(
|
||||
&self,
|
||||
now: Option<OffsetDateTime>,
|
||||
upgrade_mode: impl Into<UpgradeModeEnabledWrapper>,
|
||||
) {
|
||||
let now = now.unwrap_or_else(OffsetDateTime::now_utc);
|
||||
let upgrade_mode = upgrade_mode.into();
|
||||
|
||||
let remaining = self.remaining();
|
||||
let remaining_bi2 = bibytes2(remaining as f64);
|
||||
|
||||
if remaining < 0 {
|
||||
tracing::warn!("OUT OF BANDWIDTH. remaining: {remaining_bi2}");
|
||||
tracing::warn!(
|
||||
"OUT OF BANDWIDTH. remaining: {remaining_bi2}. in 'upgrade mode': {upgrade_mode}"
|
||||
);
|
||||
} else if remaining < 1_000_000 {
|
||||
tracing::info!("remaining bandwidth: {remaining_bi2}");
|
||||
tracing::info!(
|
||||
"remaining bandwidth: {remaining_bi2}. in 'upgrade mode': {upgrade_mode}"
|
||||
);
|
||||
} else {
|
||||
tracing::debug!("remaining bandwidth: {remaining_bi2}");
|
||||
tracing::trace!(
|
||||
"remaining bandwidth: {remaining_bi2}. in 'upgrade mode': {upgrade_mode}"
|
||||
);
|
||||
}
|
||||
|
||||
self.inner
|
||||
@@ -98,26 +147,35 @@ impl ClientBandwidth {
|
||||
.store(now.unix_timestamp(), Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub(crate) fn update_and_maybe_log(&self, remaining: i64) {
|
||||
pub(crate) fn update_and_maybe_log(
|
||||
&self,
|
||||
remaining: i64,
|
||||
upgrade_mode: impl Into<UpgradeModeEnabledWrapper>,
|
||||
) {
|
||||
let now = OffsetDateTime::now_utc();
|
||||
self.inner.available.store(remaining, Ordering::Release);
|
||||
self.inner
|
||||
.last_updated_ts
|
||||
.store(now.unix_timestamp(), Ordering::Relaxed);
|
||||
self.maybe_log_bandwidth(Some(now))
|
||||
self.maybe_log_bandwidth(Some(now), upgrade_mode)
|
||||
}
|
||||
|
||||
pub(crate) fn update_and_log(&self, remaining: i64) {
|
||||
pub(crate) fn update_and_log(
|
||||
&self,
|
||||
remaining: i64,
|
||||
upgrade_mode: impl Into<UpgradeModeEnabledWrapper>,
|
||||
) {
|
||||
let now = OffsetDateTime::now_utc();
|
||||
self.inner.available.store(remaining, Ordering::Release);
|
||||
self.inner
|
||||
.last_updated_ts
|
||||
.store(now.unix_timestamp(), Ordering::Relaxed);
|
||||
self.log_bandwidth(Some(now))
|
||||
self.log_bandwidth(Some(now), upgrade_mode)
|
||||
}
|
||||
|
||||
fn last_logged(&self) -> OffsetDateTime {
|
||||
// SAFETY: this value is always populated with valid timestamps
|
||||
#[allow(clippy::unwrap_used)]
|
||||
OffsetDateTime::from_unix_timestamp(self.inner.last_logged_ts.load(Ordering::Relaxed))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::GatewayClientError;
|
||||
use nym_network_defaults::TicketTypeRepr::V1MixnetEntry;
|
||||
use nym_credentials_interface::DEFAULT_MIXNET_REQUEST_BANDWIDTH_THRESHOLD;
|
||||
use si_scale::helpers::bibytes2;
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -103,7 +103,7 @@ impl BandwidthTickets {
|
||||
|
||||
// 20% of entry ticket value
|
||||
pub const DEFAULT_REMAINING_BANDWIDTH_THRESHOLD: i64 =
|
||||
(V1MixnetEntry.bandwidth_value() / 5) as i64;
|
||||
DEFAULT_MIXNET_REQUEST_BANDWIDTH_THRESHOLD;
|
||||
|
||||
pub const DEFAULT_CUTOFF_REMAINING_BANDWIDTH_THRESHOLD: Option<i64> = None;
|
||||
|
||||
|
||||
@@ -20,9 +20,9 @@ use nym_credentials_interface::TicketType;
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_gateway_requests::registration::handshake::client_handshake;
|
||||
use nym_gateway_requests::{
|
||||
BinaryRequest, ClientControlRequest, ClientRequest, GatewayProtocolVersionExt,
|
||||
GatewayRequestsError, SensitiveServerResponse, ServerResponse, SharedGatewayKey,
|
||||
SharedSymmetricKey, CREDENTIAL_UPDATE_V2_PROTOCOL_VERSION, CURRENT_PROTOCOL_VERSION,
|
||||
BandwidthResponse, BinaryRequest, ClientControlRequest, ClientRequest, GatewayProtocolVersion,
|
||||
GatewayProtocolVersionExt, GatewayRequestsError, SensitiveServerResponse, ServerResponse,
|
||||
SharedGatewayKey, SharedSymmetricKey, CREDENTIAL_UPDATE_V2_PROTOCOL_VERSION,
|
||||
};
|
||||
use nym_sphinx::forwarding::packet::MixPacket;
|
||||
use nym_statistics_common::clients::connection::ConnectionStatsEvent;
|
||||
@@ -101,8 +101,7 @@ pub struct GatewayClient<C, St = EphemeralCredentialStorage> {
|
||||
bandwidth_controller: Option<BandwidthController<C, St>>,
|
||||
stats_reporter: ClientStatsSender,
|
||||
|
||||
// currently unused (but populated)
|
||||
negotiated_protocol: Option<u8>,
|
||||
negotiated_protocol: Option<GatewayProtocolVersion>,
|
||||
|
||||
// Callback on the fd as soon as the connection has been established
|
||||
#[cfg(unix)]
|
||||
@@ -166,10 +165,12 @@ impl<C, St> GatewayClient<C, St> {
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[allow(clippy::unreachable)]
|
||||
async fn _close_connection(&mut self) -> Result<(), GatewayClientError> {
|
||||
match std::mem::replace(&mut self.connection, SocketState::NotConnected) {
|
||||
SocketState::Available(mut socket) => Ok((*socket).close(None).await?),
|
||||
SocketState::PartiallyDelegated(_) => {
|
||||
// SAFETY: this is only called after the caller has already recovered the connection
|
||||
unreachable!("this branch should have never been reached!")
|
||||
}
|
||||
_ => Ok(()), // no need to do anything in those cases
|
||||
@@ -177,6 +178,7 @@ impl<C, St> GatewayClient<C, St> {
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[allow(clippy::unreachable)]
|
||||
async fn _close_connection(&mut self) -> Result<(), GatewayClientError> {
|
||||
match std::mem::replace(&mut self.connection, SocketState::NotConnected) {
|
||||
SocketState::Available(socket) => {
|
||||
@@ -184,6 +186,7 @@ impl<C, St> GatewayClient<C, St> {
|
||||
Ok(())
|
||||
}
|
||||
SocketState::PartiallyDelegated(_) => {
|
||||
// SAFETY: this is only called after the caller has already recovered the connection
|
||||
unreachable!("this branch should have never been reached!")
|
||||
}
|
||||
_ => Ok(()), // no need to do anything in those cases
|
||||
@@ -458,43 +461,16 @@ impl<C, St> GatewayClient<C, St> {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_gateway_protocol(
|
||||
&self,
|
||||
gateway_protocol: Option<u8>,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
debug!("gateway protocol: {gateway_protocol:?}, ours: {CURRENT_PROTOCOL_VERSION}");
|
||||
|
||||
// right now there are no failure cases here, but this might change in the future
|
||||
match gateway_protocol {
|
||||
None => {
|
||||
warn!("the gateway we're connected to has not specified its protocol version. It's probably running version < 1.1.X, but that's still fine for now. It will become a hard error in 1.2.0");
|
||||
// note: in +1.2.0 we will have to return a hard error here
|
||||
Ok(())
|
||||
}
|
||||
Some(v) if v > CURRENT_PROTOCOL_VERSION => {
|
||||
let err = GatewayClientError::IncompatibleProtocol {
|
||||
gateway: Some(v),
|
||||
current: CURRENT_PROTOCOL_VERSION,
|
||||
};
|
||||
error!("{err}");
|
||||
Err(err)
|
||||
}
|
||||
|
||||
Some(_) => {
|
||||
debug!("the gateway is using exactly the same (or older) protocol version as we are. We're good to continue!");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn register(
|
||||
&mut self,
|
||||
derive_aes256_gcm_siv_key: bool,
|
||||
supported_gateway_protocol: Option<GatewayProtocolVersion>,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
if !self.connection.is_established() {
|
||||
return Err(GatewayClientError::ConnectionNotEstablished);
|
||||
}
|
||||
|
||||
let derive_aes256_gcm_siv_key = supported_gateway_protocol.supports_aes256_gcm_siv();
|
||||
|
||||
debug_assert!(self.connection.is_available());
|
||||
log::debug!(
|
||||
"registering with gateway. using legacy key derivation: {}",
|
||||
@@ -505,14 +481,13 @@ impl<C, St> GatewayClient<C, St> {
|
||||
// and putting it into the GatewayClient struct would be a hassle
|
||||
let mut rng = OsRng;
|
||||
|
||||
let shared_key = match &mut self.connection {
|
||||
let handshake_result = match &mut self.connection {
|
||||
SocketState::Available(ws_stream) => client_handshake(
|
||||
&mut rng,
|
||||
ws_stream,
|
||||
self.local_identity.as_ref(),
|
||||
self.gateway_identity,
|
||||
self.cfg.bandwidth.require_tickets,
|
||||
derive_aes256_gcm_siv_key,
|
||||
supported_gateway_protocol,
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
self.shutdown_token.clone(),
|
||||
)
|
||||
@@ -521,26 +496,31 @@ impl<C, St> GatewayClient<C, St> {
|
||||
_ => return Err(GatewayClientError::ConnectionInInvalidState),
|
||||
}?;
|
||||
|
||||
let (authentication_status, gateway_protocol) = match self.read_control_response().await? {
|
||||
let authentication_status = match self.read_control_response().await? {
|
||||
ServerResponse::Register {
|
||||
protocol_version,
|
||||
status,
|
||||
} => (status, protocol_version),
|
||||
upgrade_mode,
|
||||
..
|
||||
} => {
|
||||
if upgrade_mode {
|
||||
warn!("the system is currently undergoing an upgrade. some of its functionalities might be unstable")
|
||||
}
|
||||
status
|
||||
}
|
||||
ServerResponse::Error { message } => {
|
||||
return Err(GatewayClientError::GatewayError(message))
|
||||
}
|
||||
other => return Err(GatewayClientError::UnexpectedResponse { name: other.name() }),
|
||||
};
|
||||
|
||||
self.check_gateway_protocol(gateway_protocol)?;
|
||||
self.authenticated = authentication_status;
|
||||
|
||||
if self.authenticated {
|
||||
self.shared_key = Some(Arc::new(shared_key));
|
||||
self.shared_key = Some(Arc::new(handshake_result.derived_key));
|
||||
}
|
||||
|
||||
// populate the negotiated protocol for future uses
|
||||
self.negotiated_protocol = gateway_protocol;
|
||||
self.negotiated_protocol = Some(handshake_result.negotiated_protocol);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -623,13 +603,24 @@ impl<C, St> GatewayClient<C, St> {
|
||||
protocol_version,
|
||||
status,
|
||||
bandwidth_remaining,
|
||||
upgrade_mode,
|
||||
} => {
|
||||
self.check_gateway_protocol(protocol_version)?;
|
||||
if protocol_version.is_future_version() {
|
||||
// SAFETY: future version is always defined
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let version = protocol_version.unwrap();
|
||||
error!("the gateway insists on using v{version} protocol which is not supported by this client");
|
||||
return Err(GatewayClientError::AuthenticationFailure);
|
||||
}
|
||||
self.authenticated = status;
|
||||
self.bandwidth.update_and_maybe_log(bandwidth_remaining);
|
||||
self.bandwidth
|
||||
.update_and_maybe_log(bandwidth_remaining, upgrade_mode);
|
||||
|
||||
self.negotiated_protocol = protocol_version;
|
||||
log::debug!("authenticated: {status}, bandwidth remaining: {bandwidth_remaining}");
|
||||
if upgrade_mode {
|
||||
warn!("the system is currently undergoing an upgrade. some of its functionalities might be unstable")
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -650,7 +641,7 @@ impl<C, St> GatewayClient<C, St> {
|
||||
.public_key()
|
||||
.derive_destination_address();
|
||||
|
||||
let msg = ClientControlRequest::new_authenticate(
|
||||
let msg = ClientControlRequest::new_legacy_authenticate(
|
||||
self_address,
|
||||
shared_key,
|
||||
self.cfg.bandwidth.require_tickets,
|
||||
@@ -659,25 +650,40 @@ impl<C, St> GatewayClient<C, St> {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn authenticate_v2(&mut self) -> Result<(), GatewayClientError> {
|
||||
async fn authenticate_v2(
|
||||
&mut self,
|
||||
requested_protocol_version: GatewayProtocolVersion,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
debug!("using v2 authentication");
|
||||
let Some(shared_key) = self.shared_key.as_ref() else {
|
||||
return Err(GatewayClientError::NoSharedKeyAvailable);
|
||||
};
|
||||
|
||||
let msg = ClientControlRequest::new_authenticate_v2(shared_key, &self.local_identity)?;
|
||||
let msg = ClientControlRequest::new_authenticate_v2(
|
||||
shared_key,
|
||||
&self.local_identity,
|
||||
requested_protocol_version,
|
||||
)?;
|
||||
self.send_authenticate_request_and_handle_response(msg)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn authenticate(&mut self, use_v2: bool) -> Result<(), GatewayClientError> {
|
||||
async fn authenticate(
|
||||
&mut self,
|
||||
supported_gateway_protocol: Option<GatewayProtocolVersion>,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
if !self.connection.is_established() {
|
||||
return Err(GatewayClientError::ConnectionNotEstablished);
|
||||
}
|
||||
debug!("authenticating with gateway");
|
||||
|
||||
if use_v2 {
|
||||
self.authenticate_v2().await
|
||||
if supported_gateway_protocol.supports_authenticate_v2() {
|
||||
// use the highest possible protocol version the gateway has announced support for
|
||||
|
||||
// SAFETY: if announced protocol supports auth v2, it means it's properly set
|
||||
#[allow(clippy::unwrap_used)]
|
||||
self.authenticate_v2(supported_gateway_protocol.unwrap())
|
||||
.await
|
||||
} else {
|
||||
self.authenticate_v1().await
|
||||
}
|
||||
@@ -708,9 +714,12 @@ impl<C, St> GatewayClient<C, St> {
|
||||
}
|
||||
};
|
||||
|
||||
debug!("supported gateway protocol: {gw_protocol:?}");
|
||||
|
||||
let supports_aes_gcm_siv = gw_protocol.supports_aes256_gcm_siv();
|
||||
let supports_auth_v2 = gw_protocol.supports_authenticate_v2();
|
||||
let supports_key_rotation_info = gw_protocol.supports_key_rotation_packet();
|
||||
let supports_upgrade_mode = gw_protocol.supports_upgrade_mode();
|
||||
|
||||
if !supports_aes_gcm_siv {
|
||||
warn!("this gateway is on an old version that doesn't support AES256-GCM-SIV");
|
||||
@@ -721,6 +730,16 @@ impl<C, St> GatewayClient<C, St> {
|
||||
if !supports_key_rotation_info {
|
||||
warn!("this gateway is on an old version that doesn't support key rotation packets")
|
||||
}
|
||||
if !supports_upgrade_mode {
|
||||
warn!("this gateway is on an old version that doesn't support upgrade mode")
|
||||
}
|
||||
|
||||
let gw_protocol = if gw_protocol.is_future_version() {
|
||||
warn!("we're running outdated software as gateway is announcing protocol {gw_protocol:?} whilst we're using {}. we're going to attempt to downgrade", GatewayProtocolVersion::CURRENT);
|
||||
Some(GatewayProtocolVersion::CURRENT)
|
||||
} else {
|
||||
gw_protocol
|
||||
};
|
||||
|
||||
if self.authenticated {
|
||||
debug!("Already authenticated");
|
||||
@@ -735,10 +754,11 @@ impl<C, St> GatewayClient<C, St> {
|
||||
}
|
||||
|
||||
if self.shared_key.is_some() {
|
||||
self.authenticate(supports_auth_v2).await?;
|
||||
self.authenticate(gw_protocol).await?;
|
||||
|
||||
if self.authenticated {
|
||||
// if we are authenticated it means we MUST have an associated shared_key
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let shared_key = self.shared_key.as_ref().unwrap();
|
||||
|
||||
let requires_key_upgrade = shared_key.is_legacy() && supports_aes_gcm_siv;
|
||||
@@ -751,9 +771,10 @@ impl<C, St> GatewayClient<C, St> {
|
||||
Err(GatewayClientError::AuthenticationFailure)
|
||||
}
|
||||
} else {
|
||||
self.register(supports_aes_gcm_siv).await?;
|
||||
self.register(gw_protocol).await?;
|
||||
|
||||
// if registration didn't return an error, we MUST have an associated shared key
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let shared_key = self.shared_key.as_ref().unwrap();
|
||||
|
||||
// we're always registering with the highest supported protocol,
|
||||
@@ -783,51 +804,81 @@ impl<C, St> GatewayClient<C, St> {
|
||||
}
|
||||
}
|
||||
|
||||
async fn claim_ecash_bandwidth(
|
||||
async fn wait_for_bandwidth_response(
|
||||
&mut self,
|
||||
credential: CredentialSpendingData,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
let msg = ClientControlRequest::new_enc_ecash_credential(
|
||||
credential,
|
||||
self.shared_key.as_ref().unwrap(),
|
||||
)?;
|
||||
let bandwidth_remaining = match self
|
||||
msg: ClientControlRequest,
|
||||
) -> Result<BandwidthResponse, GatewayClientError> {
|
||||
let response = match self
|
||||
.send_websocket_message_with_non_send_response(msg)
|
||||
.await?
|
||||
{
|
||||
ServerResponse::Bandwidth { available_total } => Ok(available_total),
|
||||
ServerResponse::Bandwidth(response) => {
|
||||
if response.upgrade_mode {
|
||||
info!("the system is currently undergoing an upgrade. our bandwidth shouldn't have been metered")
|
||||
}
|
||||
Ok(response)
|
||||
}
|
||||
ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)),
|
||||
ServerResponse::TypedError { error } => {
|
||||
Err(GatewayClientError::TypedGatewayError(error))
|
||||
}
|
||||
other => Err(GatewayClientError::UnexpectedResponse { name: other.name() }),
|
||||
}?;
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
async fn claim_ecash_bandwidth(
|
||||
&mut self,
|
||||
credential: CredentialSpendingData,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
// SAFETY: claiming ecash bandwidth is called as part of `claim_bandwidth` which
|
||||
// ensures the shared key is defined
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let msg = ClientControlRequest::new_enc_ecash_credential(
|
||||
credential,
|
||||
self.shared_key.as_ref().unwrap(),
|
||||
)?;
|
||||
let response = self.wait_for_bandwidth_response(msg).await?;
|
||||
|
||||
// TODO: create tracing span
|
||||
info!("managed to claim ecash bandwidth");
|
||||
self.bandwidth.update_and_log(bandwidth_remaining);
|
||||
self.bandwidth
|
||||
.update_and_log(response.available_total, response.upgrade_mode);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn send_upgrade_mode_jwt(&mut self, token: String) -> Result<(), GatewayClientError> {
|
||||
let msg = ClientControlRequest::new_upgrade_mode_jwt(token);
|
||||
let response = self.wait_for_bandwidth_response(msg).await?;
|
||||
|
||||
// if gateway rejected our jwt, we would have returned an error
|
||||
info!("gateway has accepted our jwt");
|
||||
if !response.upgrade_mode {
|
||||
error!("but we're not in upgrade mode - something is wrong!");
|
||||
return Err(GatewayClientError::UnexpectedUpgradeModeState);
|
||||
}
|
||||
|
||||
self.bandwidth
|
||||
.update_and_log(response.available_total, response.upgrade_mode);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn try_claim_testnet_bandwidth(&mut self) -> Result<(), GatewayClientError> {
|
||||
let msg = ClientControlRequest::ClaimFreeTestnetBandwidth;
|
||||
let bandwidth_remaining = match self
|
||||
.send_websocket_message_with_non_send_response(msg)
|
||||
.await?
|
||||
{
|
||||
ServerResponse::Bandwidth { available_total } => Ok(available_total),
|
||||
ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)),
|
||||
other => Err(GatewayClientError::UnexpectedResponse { name: other.name() }),
|
||||
}?;
|
||||
let response = self.wait_for_bandwidth_response(msg).await?;
|
||||
|
||||
info!("managed to claim testnet bandwidth");
|
||||
self.bandwidth.update_and_log(bandwidth_remaining);
|
||||
self.bandwidth
|
||||
.update_and_log(response.available_total, response.upgrade_mode);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unchecked_bandwidth_controller(&self) -> &BandwidthController<C, St> {
|
||||
// this is an unchecked method
|
||||
#[allow(clippy::unwrap_used)]
|
||||
self.bandwidth_controller.as_ref().unwrap()
|
||||
}
|
||||
|
||||
@@ -919,6 +970,7 @@ impl<C, St> GatewayClient<C, St> {
|
||||
BinaryRequest::ForwardSphinx { packet }
|
||||
};
|
||||
|
||||
#[allow(clippy::expect_used)]
|
||||
req.into_ws_message(
|
||||
self.shared_key
|
||||
.as_ref()
|
||||
@@ -1025,6 +1077,8 @@ impl<C, St> GatewayClient<C, St> {
|
||||
self.send_with_reconnection_on_failure(msg).await
|
||||
}
|
||||
|
||||
// SAFETY: this method is only called when the connection is in `PartiallyDelegated` state
|
||||
#[allow(clippy::unreachable)]
|
||||
async fn recover_socket_connection(&mut self) -> Result<(), GatewayClientError> {
|
||||
if self.connection.is_available() {
|
||||
return Ok(());
|
||||
@@ -1054,6 +1108,7 @@ impl<C, St> GatewayClient<C, St> {
|
||||
return Err(GatewayClientError::ConnectionInInvalidState);
|
||||
}
|
||||
|
||||
#[allow(clippy::expect_used)]
|
||||
let partially_delegated =
|
||||
match std::mem::replace(&mut self.connection, SocketState::Invalid) {
|
||||
SocketState::Available(conn) => {
|
||||
@@ -1069,7 +1124,13 @@ impl<C, St> GatewayClient<C, St> {
|
||||
self.shutdown_token.clone(),
|
||||
)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
other => {
|
||||
error!(
|
||||
"attempted to start mixnet listener whilst the connection is in {} state!",
|
||||
other.name()
|
||||
);
|
||||
return Err(GatewayClientError::ConnectionInInvalidState);
|
||||
}
|
||||
};
|
||||
|
||||
self.connection = SocketState::PartiallyDelegated(partially_delegated);
|
||||
@@ -1082,8 +1143,7 @@ impl<C, St> GatewayClient<C, St> {
|
||||
}
|
||||
|
||||
// if we're reconnecting, because we lost connection, we need to re-authenticate the connection
|
||||
self.authenticate(self.negotiated_protocol.supports_authenticate_v2())
|
||||
.await?;
|
||||
self.authenticate(self.negotiated_protocol).await?;
|
||||
|
||||
// this call is NON-blocking
|
||||
self.start_listening_for_mixnet_messages()?;
|
||||
|
||||
@@ -39,7 +39,6 @@ pub(crate) async fn connect_async(
|
||||
resolver
|
||||
.resolve_str(domain)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|a| SocketAddr::new(a, port))
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ pub enum GatewayClientError {
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[error("resolution failed: {0}")]
|
||||
ResolutionFailed(#[from] nym_http_api_client::HickoryDnsError),
|
||||
ResolutionFailed(#[from] nym_http_api_client::ResolveError),
|
||||
|
||||
#[error("No shared key was provided or obtained")]
|
||||
NoSharedKeyAvailable,
|
||||
@@ -128,6 +128,9 @@ pub enum GatewayClientError {
|
||||
"this operation couldn't be completed as the program is in the process of shutting down"
|
||||
)]
|
||||
ShutdownInProgress,
|
||||
|
||||
#[error("the system is an unexpected upgrade mode state")]
|
||||
UnexpectedUpgradeModeState,
|
||||
}
|
||||
|
||||
impl From<WsError> for GatewayClientError {
|
||||
|
||||
@@ -35,6 +35,7 @@ impl PacketRouter {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::panic)]
|
||||
pub fn route_mixnet_messages(
|
||||
&self,
|
||||
received_messages: Vec<Vec<u8>>,
|
||||
@@ -54,6 +55,7 @@ impl PacketRouter {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::panic)]
|
||||
pub fn route_acks(&self, received_acks: Vec<Vec<u8>>) -> Result<(), GatewayClientError> {
|
||||
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
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::bandwidth::ClientBandwidth;
|
||||
use crate::client::config::BandwidthTickets;
|
||||
use crate::error::GatewayClientError;
|
||||
use crate::packet_router::PacketRouter;
|
||||
use crate::traits::GatewayPacketRouter;
|
||||
@@ -10,7 +11,9 @@ use futures::channel::oneshot;
|
||||
use futures::stream::{SplitSink, SplitStream};
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use nym_gateway_requests::shared_key::SharedGatewayKey;
|
||||
use nym_gateway_requests::{SensitiveServerResponse, ServerResponse, SimpleGatewayRequestsError};
|
||||
use nym_gateway_requests::{
|
||||
SendResponse, SensitiveServerResponse, ServerResponse, SimpleGatewayRequestsError,
|
||||
};
|
||||
use nym_task::ShutdownToken;
|
||||
use si_scale::helpers::bibytes2;
|
||||
use std::os::raw::c_int as RawFd;
|
||||
@@ -154,11 +157,12 @@ impl PartiallyDelegatedRouter {
|
||||
fn handle_text_message(&self, text: String) -> Result<(), GatewayClientError> {
|
||||
// if we fail to deserialise the response, return a hard error. we can't handle garbage
|
||||
match ServerResponse::try_from(text).map_err(|_| GatewayClientError::MalformedResponse)? {
|
||||
ServerResponse::Send {
|
||||
ServerResponse::Send(SendResponse {
|
||||
remaining_bandwidth,
|
||||
} => {
|
||||
upgrade_mode,
|
||||
}) => {
|
||||
self.client_bandwidth
|
||||
.update_and_maybe_log(remaining_bandwidth);
|
||||
.update_and_maybe_log(remaining_bandwidth, upgrade_mode);
|
||||
Ok(())
|
||||
}
|
||||
ServerResponse::Error { message } => {
|
||||
@@ -174,7 +178,20 @@ impl PartiallyDelegatedRouter {
|
||||
let available_bi2 = bibytes2(available as f64);
|
||||
let required_bi2 = bibytes2(required as f64);
|
||||
warn!("run out of bandwidth when attempting to send the message! we got {available_bi2} available, but needed at least {required_bi2} to send the previous message");
|
||||
self.client_bandwidth.update_and_log(available);
|
||||
// if we run out of bandwidth (and tried to send reasonable amount of data),
|
||||
// the upgrade mode is implicitly disabled, as otherwise we would have been
|
||||
// to proceed
|
||||
let upgrade_mode = if available
|
||||
< BandwidthTickets::DEFAULT_REMAINING_BANDWIDTH_THRESHOLD
|
||||
{
|
||||
Some(false)
|
||||
} else {
|
||||
// we were attempting to send a lot of data at once
|
||||
// - we have no certainty about upgrade mode at this point
|
||||
None
|
||||
};
|
||||
self.client_bandwidth
|
||||
.update_and_log(available, upgrade_mode);
|
||||
// UNIMPLEMENTED: we should stop sending messages until we recover bandwidth
|
||||
Ok(())
|
||||
}
|
||||
@@ -327,6 +344,7 @@ impl PartiallyDelegatedHandle {
|
||||
Ok(self.sink_half.send_all(&mut send_stream).await?)
|
||||
}
|
||||
|
||||
#[allow(clippy::panic)]
|
||||
pub(crate) async fn merge(self) -> Result<WsConn, GatewayClientError> {
|
||||
let (mut stream_receiver, notify) = self.delegated_stream;
|
||||
|
||||
@@ -355,8 +373,10 @@ impl PartiallyDelegatedHandle {
|
||||
// in receive_res
|
||||
.map_err(|_| GatewayClientError::ConnectionAbruptlyClosed)?;
|
||||
let stream = stream_results?;
|
||||
|
||||
// the error is thrown when trying to reunite sink and stream that did not originate
|
||||
// from the same split which is impossible to happen here
|
||||
#[allow(clippy::unwrap_used)]
|
||||
Ok(self.sink_half.reunite(stream).unwrap())
|
||||
}
|
||||
}
|
||||
@@ -387,4 +407,13 @@ impl SocketState {
|
||||
SocketState::Available(_) | SocketState::PartiallyDelegated(_)
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn name(&self) -> &'static str {
|
||||
match self {
|
||||
SocketState::Available(_) => "available",
|
||||
SocketState::PartiallyDelegated(_) => "partially delegated",
|
||||
SocketState::NotConnected => "not connected",
|
||||
SocketState::Invalid => "invalid",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ name = "nym-validator-client"
|
||||
version = "0.1.0"
|
||||
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.56"
|
||||
rust-version = "1.85"
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -10,7 +10,7 @@ use cosmrs::tx;
|
||||
use cosmrs::tx::SignDoc;
|
||||
use nym_config::defaults;
|
||||
use thiserror::Error;
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
|
||||
|
||||
type Secp256k1Keypair = (SigningKey, PublicKey);
|
||||
|
||||
@@ -128,9 +128,20 @@ impl DirectSecp256k1HdWallet {
|
||||
Ok(accounts)
|
||||
}
|
||||
|
||||
pub fn secret(&self) -> &bip39::Mnemonic {
|
||||
&self.secret
|
||||
}
|
||||
|
||||
#[deprecated(
|
||||
note = "use either .secret() for obtaining &bip39::Mnemonic or .mnemonic_string() for Zeroizing wrapper around the String"
|
||||
)]
|
||||
pub fn mnemonic(&self) -> String {
|
||||
self.secret.to_string()
|
||||
}
|
||||
|
||||
pub fn mnemonic_string(&self) -> Zeroizing<String> {
|
||||
Zeroizing::new(self.secret.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
||||
@@ -18,6 +18,6 @@ pub fn create_account(args: Args, prefix: &str) {
|
||||
let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic);
|
||||
|
||||
// Output address and mnemonics into separate lines for easier parsing
|
||||
println!("{}", wallet.mnemonic());
|
||||
println!("{}", wallet.mnemonic_string().as_str());
|
||||
println!("{}", wallet.try_derive_accounts().unwrap()[0].address());
|
||||
}
|
||||
|
||||
@@ -241,23 +241,28 @@ impl Epoch {
|
||||
//
|
||||
// Note: It's important that the variant ordering is not changed otherwise it would mess up the derived `PartialOrd`
|
||||
#[cw_serde]
|
||||
#[derive(Copy)]
|
||||
#[derive(Copy, Default)]
|
||||
pub enum EpochState {
|
||||
#[default]
|
||||
WaitingInitialisation,
|
||||
PublicKeySubmission { resharing: bool },
|
||||
DealingExchange { resharing: bool },
|
||||
VerificationKeySubmission { resharing: bool },
|
||||
VerificationKeyValidation { resharing: bool },
|
||||
VerificationKeyFinalization { resharing: bool },
|
||||
PublicKeySubmission {
|
||||
resharing: bool,
|
||||
},
|
||||
DealingExchange {
|
||||
resharing: bool,
|
||||
},
|
||||
VerificationKeySubmission {
|
||||
resharing: bool,
|
||||
},
|
||||
VerificationKeyValidation {
|
||||
resharing: bool,
|
||||
},
|
||||
VerificationKeyFinalization {
|
||||
resharing: bool,
|
||||
},
|
||||
InProgress,
|
||||
}
|
||||
|
||||
impl Default for EpochState {
|
||||
fn default() -> Self {
|
||||
Self::WaitingInitialisation
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for EpochState {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_std::testing::{message_info, MockApi, MockQuerier, MockStorage};
|
||||
use cosmwasm_std::testing::{MockApi, MockQuerier, MockStorage, message_info};
|
||||
use cosmwasm_std::{
|
||||
coins, Addr, BankMsg, CosmosMsg, Decimal, Empty, Env, MemoryStorage, MessageInfo, Order,
|
||||
OwnedDeps, Response, StdResult, Storage,
|
||||
Addr, BankMsg, CosmosMsg, Decimal, Empty, Env, MemoryStorage, MessageInfo, Order, OwnedDeps,
|
||||
Response, StdResult, Storage, coins,
|
||||
};
|
||||
use cw_storage_plus::{KeyDeserialize, Map, Prefix, PrimaryKey};
|
||||
use nym_contracts_common::events::may_find_attribute;
|
||||
use rand::{RngCore, SeedableRng};
|
||||
use rand_chacha::ChaCha20Rng;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
use serde::de::DeserializeOwned;
|
||||
use std::fmt::Debug;
|
||||
use std::str::FromStr;
|
||||
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
use crate::{ContractTester, TestableNymContract};
|
||||
use cosmwasm_std::testing::{message_info, mock_env};
|
||||
use cosmwasm_std::{
|
||||
from_json, Addr, BlockInfo, Coin, ContractInfo, Deps, DepsMut, Env, MessageInfo, Response,
|
||||
StdResult, Storage, Timestamp,
|
||||
Addr, BlockInfo, Coin, ContractInfo, Deps, DepsMut, Env, MessageInfo, Response, StdResult,
|
||||
Storage, Timestamp, from_json,
|
||||
};
|
||||
use cw_multi_test::{next_block, AppResponse, Executor};
|
||||
use serde::de::DeserializeOwned;
|
||||
use cw_multi_test::{AppResponse, Executor, next_block};
|
||||
use serde::Serialize;
|
||||
use serde::de::DeserializeOwned;
|
||||
use std::any::type_name;
|
||||
use std::fmt::Debug;
|
||||
|
||||
|
||||
@@ -2,18 +2,18 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{
|
||||
CommonStorageKeys, ContractOpts, ContractTester, StorageWrapper, TestableNymContract,
|
||||
TEST_DENOM,
|
||||
CommonStorageKeys, ContractOpts, ContractTester, StorageWrapper, TEST_DENOM,
|
||||
TestableNymContract,
|
||||
};
|
||||
use cosmwasm_std::testing::message_info;
|
||||
use cosmwasm_std::{
|
||||
coin, coins, from_json, to_json_vec, Addr, Coin, MessageInfo, StdError, StdResult, Storage,
|
||||
Addr, Coin, MessageInfo, StdError, StdResult, Storage, coin, coins, from_json, to_json_vec,
|
||||
};
|
||||
use cw_multi_test::Executor;
|
||||
use cw_storage_plus::{Key, Path, PrimaryKey};
|
||||
use rand_chacha::ChaCha20Rng;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
use serde::de::DeserializeOwned;
|
||||
use std::any::type_name;
|
||||
use std::ops::Deref;
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{mock_api, test_rng, TEST_DENOM};
|
||||
use crate::{TEST_DENOM, mock_api, test_rng};
|
||||
use cosmwasm_std::testing::MockApi;
|
||||
use cosmwasm_std::{
|
||||
coin, coins, Addr, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Order, QuerierWrapper,
|
||||
Record, Response, Storage,
|
||||
Addr, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Order, QuerierWrapper, Record, Response,
|
||||
Storage, coin, coins,
|
||||
};
|
||||
use cw_multi_test::{App, AppBuilder, BankKeeper, Contract, ContractWrapper, Executor};
|
||||
use rand_chacha::ChaCha20Rng;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
use serde::de::DeserializeOwned;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_std::{from_json, Binary, CustomQuery, QuerierWrapper, StdResult};
|
||||
use serde::de::DeserializeOwned;
|
||||
use cosmwasm_std::{Binary, CustomQuery, QuerierWrapper, StdResult, from_json};
|
||||
use serde::Serialize;
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
// re-expose methods from QuerierWrapper as traits so that we could more easily define extension traits
|
||||
pub trait ContractQuerier {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_std::{from_json, to_json_vec, Addr, Coin, MessageInfo, StdResult};
|
||||
use cosmwasm_std::{Addr, Coin, MessageInfo, StdResult, from_json, to_json_vec};
|
||||
use schemars::JsonSchema;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::str::FromStr;
|
||||
pub use verifier::Verifier;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name = "nym-mixnet-contract-common"
|
||||
version = "0.6.0"
|
||||
description = "Common library for the Nym mixnet contract"
|
||||
rust-version = "1.62"
|
||||
rust-version = "1.85"
|
||||
edition = { workspace = true }
|
||||
authors = { workspace = true }
|
||||
license = { workspace = true }
|
||||
|
||||
@@ -5,8 +5,8 @@ use crate::nym_node::Role;
|
||||
use crate::{
|
||||
EpochEventId, EpochState, IntervalEventId, NodeId, OperatingCostRange, ProfitMarginRange,
|
||||
};
|
||||
use contracts_common::signing::verifier::ApiVerifierError;
|
||||
use contracts_common::Percent;
|
||||
use contracts_common::signing::verifier::ApiVerifierError;
|
||||
use cosmwasm_std::{Addr, Coin, Decimal, Uint128};
|
||||
use cw_controllers::AdminError;
|
||||
use thiserror::Error;
|
||||
@@ -47,7 +47,9 @@ pub enum MixnetContractError {
|
||||
)]
|
||||
InvalidPubKey,
|
||||
|
||||
#[error("Attempted to reduce node pledge ({current}{denom} - {decrease_by}{denom}) below the minimum amount: {minimum}{denom}")]
|
||||
#[error(
|
||||
"Attempted to reduce node pledge ({current}{denom} - {decrease_by}{denom}) below the minimum amount: {minimum}{denom}"
|
||||
)]
|
||||
InvalidPledgeReduction {
|
||||
current: Uint128,
|
||||
decrease_by: Uint128,
|
||||
@@ -123,7 +125,9 @@ pub enum MixnetContractError {
|
||||
#[error("Provided ed25519 signature did not verify correctly")]
|
||||
InvalidEd25519Signature,
|
||||
|
||||
#[error("Can't perform the specified action as the current epoch is still progress. It started at {epoch_start} and finishes at {epoch_end}, while the current block time is {current_block_time}")]
|
||||
#[error(
|
||||
"Can't perform the specified action as the current epoch is still progress. It started at {epoch_start} and finishes at {epoch_end}, while the current block time is {current_block_time}"
|
||||
)]
|
||||
EpochInProgress {
|
||||
current_block_time: u64,
|
||||
epoch_start: i64,
|
||||
@@ -133,7 +137,9 @@ pub enum MixnetContractError {
|
||||
#[error("attempted to reward a gateway node - this has not been fully integrated yet")]
|
||||
GatewayRewarding,
|
||||
|
||||
#[error("node {node_id} has already been rewarded during the current rewarding epoch ({absolute_epoch_id})")]
|
||||
#[error(
|
||||
"node {node_id} has already been rewarded during the current rewarding epoch ({absolute_epoch_id})"
|
||||
)]
|
||||
NodeAlreadyRewarded {
|
||||
node_id: NodeId,
|
||||
absolute_epoch_id: u32,
|
||||
@@ -172,7 +178,9 @@ pub enum MixnetContractError {
|
||||
#[error("one of the roles in the new active set is empty")]
|
||||
EmptyRoleAssignment,
|
||||
|
||||
#[error("the number of mixnodes in the rewarded set is not divisible by the number of mix-layers (3)")]
|
||||
#[error(
|
||||
"the number of mixnodes in the rewarded set is not divisible by the number of mix-layers (3)"
|
||||
)]
|
||||
UnevenLayerAssignment,
|
||||
|
||||
#[error("provided active set is bigger than the rewarded set")]
|
||||
@@ -196,25 +204,35 @@ pub enum MixnetContractError {
|
||||
#[error("key rotation validity below minimum value")]
|
||||
TooShortRotationInterval,
|
||||
|
||||
#[error("this validator ({current_validator}) is not the one responsible for advancing this epoch. It's responsibility of {chosen_validator}.")]
|
||||
#[error(
|
||||
"this validator ({current_validator}) is not the one responsible for advancing this epoch. It's responsibility of {chosen_validator}."
|
||||
)]
|
||||
RewardingValidatorMismatch {
|
||||
current_validator: Addr,
|
||||
chosen_validator: Addr,
|
||||
},
|
||||
|
||||
#[error("the epoch is currently in the process of being advanced. (the state is {current_state}) Please try sending your transaction again once this has finished")]
|
||||
#[error(
|
||||
"the epoch is currently in the process of being advanced. (the state is {current_state}) Please try sending your transaction again once this has finished"
|
||||
)]
|
||||
EpochAdvancementInProgress { current_state: EpochState },
|
||||
|
||||
#[error("the epoch is in an unexpected state. expected 'mix rewarding' state, but we're in {current_state} instead.")]
|
||||
#[error(
|
||||
"the epoch is in an unexpected state. expected 'mix rewarding' state, but we're in {current_state} instead."
|
||||
)]
|
||||
UnexpectedNonRewardingEpochState { current_state: EpochState },
|
||||
|
||||
#[error("attempted to reward mixnode out of order. Attempted to reward {attempted_to_reward} while last rewarded was {last_rewarded}.")]
|
||||
#[error(
|
||||
"attempted to reward mixnode out of order. Attempted to reward {attempted_to_reward} while last rewarded was {last_rewarded}."
|
||||
)]
|
||||
RewardingOutOfOrder {
|
||||
last_rewarded: NodeId,
|
||||
attempted_to_reward: NodeId,
|
||||
},
|
||||
|
||||
#[error("the epoch is currently not in the 'event reconciliation' state. (the state is {current_state})")]
|
||||
#[error(
|
||||
"the epoch is currently not in the 'event reconciliation' state. (the state is {current_state})"
|
||||
)]
|
||||
EpochNotInEventReconciliationState { current_state: EpochState },
|
||||
|
||||
#[error(
|
||||
@@ -225,14 +243,18 @@ pub enum MixnetContractError {
|
||||
#[error("unexpected role assignment. got: {got} while expected: {expected}")]
|
||||
UnexpectedRoleAssignment { expected: Role, got: Role },
|
||||
|
||||
#[error("attempted to assign an invalid number of nodes for a role of {role}. got {assigned}, but the maximum allowed is {allowed}")]
|
||||
#[error(
|
||||
"attempted to assign an invalid number of nodes for a role of {role}. got {assigned}, but the maximum allowed is {allowed}"
|
||||
)]
|
||||
IllegalRoleCount {
|
||||
role: Role,
|
||||
assigned: u32,
|
||||
allowed: u32,
|
||||
},
|
||||
|
||||
#[error("the epoch is currently not in the 'epoch advancement' state. (the state is {current_state})")]
|
||||
#[error(
|
||||
"the epoch is currently not in the 'epoch advancement' state. (the state is {current_state})"
|
||||
)]
|
||||
EpochNotInAdvancementState { current_state: EpochState },
|
||||
|
||||
#[error("failed to verify message signature: {source}")]
|
||||
@@ -241,7 +263,9 @@ pub enum MixnetContractError {
|
||||
source: ApiVerifierError,
|
||||
},
|
||||
|
||||
#[error("this operation is no longer allowed to be performed with vesting tokens. please move them to your liquid balance and try again")]
|
||||
#[error(
|
||||
"this operation is no longer allowed to be performed with vesting tokens. please move them to your liquid balance and try again"
|
||||
)]
|
||||
DisabledVestingOperation,
|
||||
|
||||
#[error(
|
||||
@@ -249,7 +273,9 @@ pub enum MixnetContractError {
|
||||
)]
|
||||
NotAVestingMixnode,
|
||||
|
||||
#[error("this delegation has not been performed with the vesting tokens or has already been migrated")]
|
||||
#[error(
|
||||
"this delegation has not been performed with the vesting tokens or has already been migrated"
|
||||
)]
|
||||
NotAVestingDelegation,
|
||||
|
||||
#[error("the provided profit margin ({provided}) is outside the allowed range: {range}")]
|
||||
@@ -258,7 +284,9 @@ pub enum MixnetContractError {
|
||||
range: ProfitMarginRange,
|
||||
},
|
||||
|
||||
#[error("the provided interval operating cost ({provided}{denom}) is outside the allowed range: {range}")]
|
||||
#[error(
|
||||
"the provided interval operating cost ({provided}{denom}) is outside the allowed range: {range}"
|
||||
)]
|
||||
OperatingCostOutsideRange {
|
||||
denom: String,
|
||||
provided: Uint128,
|
||||
@@ -279,7 +307,9 @@ pub enum MixnetContractError {
|
||||
#[error("the provided nym-node version is not a valid semver. got: {provided}")]
|
||||
InvalidNymNodeSemver { provided: String },
|
||||
|
||||
#[error("the provided nym-node version is not greater than the current one. got: {provided}. current: {current}")]
|
||||
#[error(
|
||||
"the provided nym-node version is not greater than the current one. got: {provided}. current: {current}"
|
||||
)]
|
||||
NonIncreasingSemver { provided: String, current: String },
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::reward_params::{ActiveSetUpdate, IntervalRewardParams, IntervalReward
|
||||
use crate::rewarding::RewardDistribution;
|
||||
use crate::{BlockHeight, ContractStateParamsUpdate, EpochId, IdentityKeyRef, Interval, NodeId};
|
||||
pub use contracts_common::events::*;
|
||||
use cosmwasm_std::{attr, Addr, Coin, Decimal, Event};
|
||||
use cosmwasm_std::{Addr, Coin, Decimal, Event, attr};
|
||||
use std::fmt::Display;
|
||||
|
||||
pub const EVENT_VERSION_PREFIX: &str = "v2_";
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use crate::{IdentityKey, NodeId, SphinxKey};
|
||||
use cosmwasm_schema::cw_serde;
|
||||
use cosmwasm_std::{to_json_string, Addr, Coin};
|
||||
use cosmwasm_std::{Addr, Coin, to_json_string};
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt::Display;
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::NodeId;
|
||||
use crate::error::MixnetContractError;
|
||||
use crate::nym_node::Role;
|
||||
use crate::NodeId;
|
||||
use cosmwasm_schema::cw_serde;
|
||||
use cosmwasm_schema::schemars::gen::SchemaGenerator;
|
||||
use cosmwasm_schema::schemars::schema::{InstanceType, Schema, SchemaObject};
|
||||
use cosmwasm_schema::schemars::JsonSchema;
|
||||
use cosmwasm_schema::schemars::r#gen::SchemaGenerator;
|
||||
use cosmwasm_schema::schemars::schema::{InstanceType, Schema, SchemaObject};
|
||||
use cosmwasm_std::{Addr, Env};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::{Display, Formatter};
|
||||
@@ -27,8 +27,8 @@ pub(crate) mod string_rfc3339_offset_date_time {
|
||||
use serde::ser::Error;
|
||||
use serde::{Deserializer, Serialize, Serializer};
|
||||
use std::fmt::Formatter;
|
||||
use time::format_description::well_known::Rfc3339;
|
||||
use time::OffsetDateTime;
|
||||
use time::format_description::well_known::Rfc3339;
|
||||
|
||||
struct Rfc3339OffsetDateTimeVisitor;
|
||||
|
||||
@@ -91,7 +91,7 @@ impl EpochStatus {
|
||||
) -> Result<bool, MixnetContractError> {
|
||||
match &mut self.state {
|
||||
EpochState::Rewarding {
|
||||
ref mut last_rewarded,
|
||||
last_rewarded,
|
||||
final_node_id,
|
||||
} => {
|
||||
if new_last_rewarded <= *last_rewarded {
|
||||
@@ -254,7 +254,7 @@ impl JsonSchema for Interval {
|
||||
"Interval".to_owned()
|
||||
}
|
||||
|
||||
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||
fn json_schema(r#gen: &mut SchemaGenerator) -> Schema {
|
||||
let mut schema_object = SchemaObject {
|
||||
instance_type: Some(InstanceType::Object.into()),
|
||||
..SchemaObject::default()
|
||||
@@ -263,12 +263,13 @@ impl JsonSchema for Interval {
|
||||
let object_validation = schema_object.object();
|
||||
object_validation
|
||||
.properties
|
||||
.insert("id".to_owned(), gen.subschema_for::<IntervalId>());
|
||||
.insert("id".to_owned(), r#gen.subschema_for::<IntervalId>());
|
||||
object_validation.required.insert("id".to_owned());
|
||||
|
||||
object_validation
|
||||
.properties
|
||||
.insert("epochs_in_interval".to_owned(), gen.subschema_for::<u32>());
|
||||
object_validation.properties.insert(
|
||||
"epochs_in_interval".to_owned(),
|
||||
r#gen.subschema_for::<u32>(),
|
||||
);
|
||||
object_validation
|
||||
.required
|
||||
.insert("epochs_in_interval".to_owned());
|
||||
@@ -277,7 +278,7 @@ impl JsonSchema for Interval {
|
||||
// serialization to string, so we just specify the schema to be String.
|
||||
object_validation.properties.insert(
|
||||
"current_epoch_start".to_owned(),
|
||||
gen.subschema_for::<String>(),
|
||||
r#gen.subschema_for::<String>(),
|
||||
);
|
||||
object_validation
|
||||
.required
|
||||
@@ -285,7 +286,7 @@ impl JsonSchema for Interval {
|
||||
|
||||
object_validation.properties.insert(
|
||||
"current_epoch_id".to_owned(),
|
||||
gen.subschema_for::<EpochId>(),
|
||||
r#gen.subschema_for::<EpochId>(),
|
||||
);
|
||||
object_validation
|
||||
.required
|
||||
@@ -293,12 +294,12 @@ impl JsonSchema for Interval {
|
||||
|
||||
object_validation
|
||||
.properties
|
||||
.insert("epoch_length".to_owned(), gen.subschema_for::<Duration>());
|
||||
.insert("epoch_length".to_owned(), r#gen.subschema_for::<Duration>());
|
||||
object_validation.required.insert("epoch_length".to_owned());
|
||||
|
||||
object_validation.properties.insert(
|
||||
"total_elapsed_epochs".to_owned(),
|
||||
gen.subschema_for::<EpochId>(),
|
||||
r#gen.subschema_for::<EpochId>(),
|
||||
);
|
||||
object_validation
|
||||
.required
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user