Compare commits

..

1 Commits

Author SHA1 Message Date
Bogdan-Ștefan Neacşu 79b8b4d324 Add query message wrapper 2024-11-15 16:31:51 +02:00
1254 changed files with 24418 additions and 49545 deletions
+1
View File
@@ -4,3 +4,4 @@
**/node_modules **/node_modules
**/target **/target
dist dist
documentation
-1
View File
@@ -1 +0,0 @@
nym-validator-rewarder/.sqlx/** diff=nodiff
-2
View File
@@ -31,5 +31,3 @@ updates:
update-types: update-types:
- "patch" - "patch"
open-pull-requests-limit: 10 open-pull-requests-limit: 10
assignees:
- "octol"
+8 -10
View File
@@ -9,16 +9,15 @@ on:
- 'gateway/**' - 'gateway/**'
- 'integrations/**' - 'integrations/**'
- 'mixnode/**' - 'mixnode/**'
- 'nym-api/**'
- 'nym-data-observatory/**'
- 'nym-network-monitor/**'
- 'nym-node/**'
- 'nym-node-status-api/**'
- 'nym-outfox/**'
- 'nym-validator-rewarder/**'
- 'sdk/lib/**'
- 'sdk/rust/**' - 'sdk/rust/**'
- 'sdk/lib/**'
- 'service-providers/**' - 'service-providers/**'
- 'nym-network-monitor/**'
- 'nym-api/**'
- 'nym-node/**'
- 'nym-outfox/**'
- 'nym-data-observatory/**'
- 'nym-validator-rewarder/**'
- 'tools/**' - 'tools/**'
- 'wasm/**' - 'wasm/**'
- 'Cargo.toml' - 'Cargo.toml'
@@ -31,11 +30,10 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [ arc-ubuntu-20.04, custom-windows-11, custom-runner-mac-m1 ] os: [ arc-ubuntu-20.04, custom-runner-mac-m1 ]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
IPINFO_API_TOKEN: ${{ secrets.IPINFO_API_TOKEN }}
steps: steps:
- name: Install Dependencies (Linux) - name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler
+1 -1
View File
@@ -11,7 +11,7 @@ on:
jobs: jobs:
check-schema: check-schema:
name: Generate and check schema name: Generate and check schema
runs-on: ubuntu-20.04 runs-on: arc-ubuntu-20.04
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
steps: steps:
+1 -1
View File
@@ -13,7 +13,7 @@ on:
jobs: jobs:
matrix_prep: matrix_prep:
runs-on: ubuntu-20.04 runs-on: arc-ubuntu-20.04
outputs: outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }} matrix: ${{ steps.set-matrix.outputs.matrix }}
steps: steps:
-1
View File
@@ -15,7 +15,6 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
IPINFO_API_TOKEN: ${{ secrets.IPINFO_API_TOKEN }}
continue-on-error: true continue-on-error: true
steps: steps:
- name: Check out repository code - name: Check out repository code
@@ -55,7 +55,6 @@ jobs:
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
toolchain: stable toolchain: stable
override: true
- name: Build all binaries - name: Build all binaries
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
+3 -4
View File
@@ -14,14 +14,13 @@ jobs:
- name: Install Rust stable - name: Install Rust stable
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
toolchain: 1.77 toolchain: stable
target: wasm32-unknown-unknown target: wasm32-unknown-unknown
override: true override: true
components: rustfmt, clippy
- name: Install wasm-opt - name: Install wasm-opt
uses: ./.github/actions/install-wasm-opt run: cargo install --version 0.114.0 wasm-opt
with:
version: '114'
- name: Build release contracts - name: Build release contracts
run: make contracts run: make contracts
@@ -56,6 +56,14 @@ jobs:
rustup target add aarch64-linux-android \ rustup target add aarch64-linux-android \
x86_64-linux-android x86_64-linux-android
- name: Build lib nym-socks5-listener
working-directory: sdk/lib/socks5-listener/
env:
RELEASE: true
RUSTFLAGS: "-C link-args=-Wl,--hash-style=gnu"
# build for arm64 and x86_64
run: ./build-android.sh aarch64 x86_64
- name: Build APKs (unsigned) - name: Build APKs (unsigned)
working-directory: nym-connect/native/android working-directory: nym-connect/native/android
env: env:
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder" git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml - name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.1 uses: mikefarah/yq@v4.44.3
id: get_version id: get_version
with: with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-credential-proxy/Cargo.toml cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-credential-proxy/Cargo.toml
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder" git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml - name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.1 uses: mikefarah/yq@v4.44.3
id: get_version id: get_version
with: with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
+18 -14
View File
@@ -25,27 +25,31 @@ jobs:
git config --global user.email "lawrence@nymtech.net" git config --global user.email "lawrence@nymtech.net"
git config --global user.name "Lawrence Stalder" git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml - name: Get version from package.json
uses: mikefarah/yq@v4.45.1 uses: sergeysova/jq-action@v2
id: get_version id: get_version
with: with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-network-monitor/Cargo.toml cmd: jq -r '.version' ${{ env.WORKING_DIRECTORY }}/package.json
- name: Check if tag exists
run: |
if git rev-parse ${{ steps.get_version.outputs.value }} >/dev/null 2>&1; then
echo "Tag ${{ steps.get_version.outputs.value }} already exists"
fi
- name: Remove existing tag if exists - name: Remove existing tag if exists
run: | run: |
echo "Checking if tag ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }} exists..." if git rev-parse ${{ steps.get_version.outputs.value }} >/dev/null 2>&1; then
if git rev-parse ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }} >/dev/null 2>&1; then git push --delete origin ${{ steps.get_version.outputs.value }}
echo "Tag ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }} already exists" git tag -d ${{ steps.get_version.outputs.value }}
git push --delete origin ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }}
git tag -d ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }}
fi fi
- name: Create tag - name: Create tag
run: | run: |
git tag -a ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }} -m "Version ${{ steps.get_version.outputs.result }}" git tag -a ${{ steps.get_version.outputs.value }} -m "Version ${{ steps.get_version.outputs.value }}"
git push origin ${{ env.CONTAINER_NAME }}-${{ steps.get_version.outputs.result }} git push origin ${{ steps.get_version.outputs.value }}
- name: BuildAndPushImageOnHarbor - name: BuildAndPushImageOnHarbor
run: | run: |
docker build -f nym-network-monitor.dockerfile ${{ env.WORKING_DIRECTORY }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.result }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:latest docker build -f nym-network-monitor.dockerfile ${{ env.WORKING_DIRECTORY }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.value }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:latest
docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags
@@ -8,7 +8,7 @@ on:
description: Which gateway probe git ref to build the image with description: Which gateway probe git ref to build the image with
env: env:
WORKING_DIRECTORY: "nym-node-status-api/nym-node-status-agent" WORKING_DIRECTORY: "nym-node-status-agent"
CONTAINER_NAME: "node-status-agent" CONTAINER_NAME: "node-status-agent"
jobs: jobs:
@@ -31,7 +31,7 @@ jobs:
git config --global user.name "Lawrence Stalder" git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml - name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.1 uses: mikefarah/yq@v4.44.3
id: get_version id: get_version
with: with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
+2 -2
View File
@@ -3,7 +3,7 @@ on:
workflow_dispatch: workflow_dispatch:
env: env:
WORKING_DIRECTORY: "nym-node-status-api/nym-node-status-api" WORKING_DIRECTORY: "nym-node-status-api"
CONTAINER_NAME: "node-status-api" CONTAINER_NAME: "node-status-api"
jobs: jobs:
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder" git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml - name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.1 uses: mikefarah/yq@v4.44.3
id: get_version id: get_version
with: with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
git config --global user.name "Lawrence Stalder" git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml - name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.1 uses: mikefarah/yq@v4.44.3
id: get_version id: get_version
with: with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
@@ -1,55 +0,0 @@
name: Build and upload Nyx Chain Watcher container to harbor.nymte.ch
on:
workflow_dispatch:
env:
WORKING_DIRECTORY: "nyx-chain-watcher"
CONTAINER_NAME: "nyx-chain-watcher"
jobs:
build-container:
runs-on: arc-ubuntu-22.04-dind
steps:
- name: Login to Harbor
uses: docker/login-action@v3
with:
registry: harbor.nymte.ch
username: ${{ secrets.HARBOR_ROBOT_USERNAME }}
password: ${{ secrets.HARBOR_ROBOT_SECRET }}
- name: Checkout repo
uses: actions/checkout@v4
- name: Configure git identity
run: |
git config --global user.email "lawrence@nymtech.net"
git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.1
id: get_version
with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml
- name: Check if tag exists
run: |
if git rev-parse ${{ steps.get_version.outputs.value }} >/dev/null 2>&1; then
echo "Tag ${{ steps.get_version.outputs.value }} already exists"
fi
- name: Remove existing tag if exists
run: |
if git rev-parse ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} >/dev/null 2>&1; then
git push --delete origin ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}
git tag -d ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}
fi
- name: Create tag
run: |
git tag -a ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }} -m "Version ${{ steps.get_version.outputs.result }}"
git push origin ${{ env.WORKING_DIRECTORY }}-${{ steps.get_version.outputs.result }}
- name: BuildAndPushImageOnHarbor
run: |
docker build -f ${{ env.WORKING_DIRECTORY }}/Dockerfile . -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:${{ steps.get_version.outputs.result }} -t harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }}:latest
docker push harbor.nymte.ch/nym/${{ env.CONTAINER_NAME }} --all-tags
@@ -26,10 +26,10 @@ jobs:
git config --global user.name "Lawrence Stalder" git config --global user.name "Lawrence Stalder"
- name: Get version from cargo.toml - name: Get version from cargo.toml
uses: mikefarah/yq@v4.45.1 uses: mikefarah/yq@v4.44.3
id: get_version id: get_version
with: with:
cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/Cargo.toml cmd: yq -oy '.package.version' ${{ env.WORKING_DIRECTORY }}/nym-credential-proxy/Cargo.toml
- name: Remove existing tag if exists - name: Remove existing tag if exists
run: | run: |
+1 -8
View File
@@ -51,11 +51,4 @@ ppa-private-key.b64
ppa-private-key.asc ppa-private-key.asc
nym-network-monitor/topology.json nym-network-monitor/topology.json
nym-network-monitor/__pycache__ nym-network-monitor/__pycache__
nym-network-monitor/*.key nym-network-monitor/*.key
nym-network-monitor/.envrc
nym-network-monitor/.envrc
nym-api/redocly/formatted-openapi.json
*.sqlite
.build
-429
View File
@@ -4,435 +4,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased] ## [Unreleased]
## [2025.1-reeses] (2025-01-15)
- Feture/legacy alert ([#5346])
- chore: readjusted --mode behaviour to fix the regression ([#5331])
- chore: apply 1.84 linter suggestions ([#5330])
- bugfix: make sure refresh data key matches bond info ([#5329])
- reduce log severity for number of packets being delayed ([#5321])
- feat: warn users if node is run in exit mode only ([#5320])
- Bugfix/contract version assignment ([#5318])
- fixed client session histogram buckets ([#5316])
- amend 250gb limit ([#5313])
- feature: expand nym-node prometheus metrics ([#5298])
- Cherry picked #5286 ([#5287])
- Add close to credential storage ([#5283])
- feature: wireguard metrics ([#5278])
- Add PATCH support to nym-http-api-client ([#5260])
- chore: removed legacy socks5 listener ([#5259])
- bugfix: make sure to apply gateway score filtering when choosing initial node ([#5256])
- Update TS bindings ([#5255])
- Add conversion unit tests for auth msg ([#5251])
- Add control messages to GatewayTransciver ([#5247])
- Remove unneeded async function annotation ([#5246])
- bugfix: make sure to update timestamp of last batch verification to prevent double redemption ([#5239])
- Add FromStr impl for UserAgent ([#5236])
- Extend swagger docs ([#5235])
- TicketType derive Hash and Eq ([#5233])
- Add fd callback to client core ([#5230])
- Extend raw ws fd for gateway client ([#5218])
- Shipping raw metrics to PG ([#5216])
- Change sqlite journal mode to WAL ([#5213])
- Derive serialize for UserAgent ([#5210])
- Restore Location fields ([#5208])
- better date serialization ([#5207])
- Fix overflow ([#5204])
- feature: hopefully final steps of the smoosh™️ ([#5201])
- Fix overflow ([#5184])
- NS API - Gateway stats scraping ([#5180])
- introduced initial internal commands for nym-cli: ecash key and request generation ([#5174])
- Move NS client to separate package under NS API ([#5171])
- build(deps): bump micromatch from 4.0.4 to 4.0.8 in /testnet-faucet ([#4813])
[#5346]: https://github.com/nymtech/nym/pull/5346
[#5331]: https://github.com/nymtech/nym/pull/5331
[#5330]: https://github.com/nymtech/nym/pull/5330
[#5329]: https://github.com/nymtech/nym/pull/5329
[#5321]: https://github.com/nymtech/nym/pull/5321
[#5320]: https://github.com/nymtech/nym/pull/5320
[#5318]: https://github.com/nymtech/nym/pull/5318
[#5316]: https://github.com/nymtech/nym/pull/5316
[#5313]: https://github.com/nymtech/nym/pull/5313
[#5298]: https://github.com/nymtech/nym/pull/5298
[#5287]: https://github.com/nymtech/nym/pull/5287
[#5283]: https://github.com/nymtech/nym/pull/5283
[#5278]: https://github.com/nymtech/nym/pull/5278
[#5260]: https://github.com/nymtech/nym/pull/5260
[#5259]: https://github.com/nymtech/nym/pull/5259
[#5256]: https://github.com/nymtech/nym/pull/5256
[#5255]: https://github.com/nymtech/nym/pull/5255
[#5251]: https://github.com/nymtech/nym/pull/5251
[#5247]: https://github.com/nymtech/nym/pull/5247
[#5246]: https://github.com/nymtech/nym/pull/5246
[#5239]: https://github.com/nymtech/nym/pull/5239
[#5236]: https://github.com/nymtech/nym/pull/5236
[#5235]: https://github.com/nymtech/nym/pull/5235
[#5233]: https://github.com/nymtech/nym/pull/5233
[#5230]: https://github.com/nymtech/nym/pull/5230
[#5218]: https://github.com/nymtech/nym/pull/5218
[#5216]: https://github.com/nymtech/nym/pull/5216
[#5213]: https://github.com/nymtech/nym/pull/5213
[#5210]: https://github.com/nymtech/nym/pull/5210
[#5208]: https://github.com/nymtech/nym/pull/5208
[#5207]: https://github.com/nymtech/nym/pull/5207
[#5204]: https://github.com/nymtech/nym/pull/5204
[#5201]: https://github.com/nymtech/nym/pull/5201
[#5184]: https://github.com/nymtech/nym/pull/5184
[#5180]: https://github.com/nymtech/nym/pull/5180
[#5174]: https://github.com/nymtech/nym/pull/5174
[#5171]: https://github.com/nymtech/nym/pull/5171
[#4813]: https://github.com/nymtech/nym/pull/4813
## [2024.14-crunch-patched] (2024-12-17)
- Fixes an issue to allow previously registred clients to connect to latest nym-nodes
- Fixes compatibility issues between nym-nodes and older clients
## [2024.14-crunch] (2024-12-11)
- Merge/release/2024.14-crunch ([#5242])
- bugfix: added explicit openapi servers to account for route prefixes ([#5237])
- Further config score adjustments ([#5225])
- feature: remve any filtering on node semver ([#5224])
- Backport #5218 ([#5220])
- Derive serialize for UserAgent (#5210) ([#5217])
- dont consider legacy nodes for rewarded set selection ([#5215])
- introduce UNSTABLE endpoints for returning network monitor run details ([#5214])
- Nmv2 add debug config ([#5212])
- nym-api NMv1 adjustments ([#5209])
- adjusted config score penalty calculation ([#5206])
- Fix backwards compat mac generation ([#5202])
- merge crunch into develop ([#5199])
- Update Security disclosure email, public key and policy ([#5195])
- Guard storage access with cache ([#5193])
- chore: apply 1.84 linter suggestions ([#5192])
- improvement: make internal gateway clients use the same topology cache ([#5191])
- Bugfix/credential proxy sequencing ([#5187])
- Add monitor_run and testing_route indexes ([#5182])
- Add indexes to monitor run and testing route ([#5181])
- bugfix: fixed nym-node config migrations (again) ([#5179])
- bugfix: use default value for verloc config when deserialising missing values ([#5177])
- Remove peers with no allowed ip from storage ([#5175])
- Move two minor jobs to free tier github hosted runners ([#5169])
- Add support for DELETE to nym-http-api-client ([#5166])
- Fix env var name ([#5165])
- Add strum::EnumIter for TicketType ([#5164])
- Add export_to_env to NymNetworkDetails ([#5162])
- bugfix: correctly expose ecash-related data on nym-api ([#5155])
- fix: validator-rewarder GH job ([#5151])
- build(deps): bump cross-spawn from 7.0.3 to 7.0.6 in /testnet-faucet ([#5150])
- build(deps): bump mikefarah/yq from 4.44.3 to 4.44.5 ([#5149])
- start session collection for exit gateways ([#5148])
- add version to clientStatsReport ([#5147])
- update serde_json_path due to compilation issue ([#5144])
- chore: remove standalone legacy mixnode/gateway binaries ([#5135])
- [Product Data] Set up country reporting from vpn-client ([#5134])
- removed ci-nym-api-tests.yml which was running outdated (and broken) tests ([#5133])
- CI: reduce jobs running on cluster ([#5132])
- [DOCS/operators]: Release changes v2024.13-magura & Tokenomics pages v1.0 ([#5128])
- NS Agent auth with NS API ([#5127])
- [Product Data] Config deserialization bug fix ([#5126])
- bugfix: don't send empty BankMsg in ecash contract ([#5121])
- [Product data] Data consumption with ecash ticket ([#5120])
- feat: add GH workflow for nym-validator-rewarder ([#5119])
- feat: add Dockerfile and add env vars for clap arguments ([#5118])
- feature: config score ([#5117])
- [Product Data] Add stats reporting configuration in client config ([#5115])
- Correct IPv6 address generation ([#5113])
- feature: rewarding for ticketbook issuance ([#5112])
- Add granular log on nym-node ([#5111])
- Send mixnet packet stats using task client ([#5109])
- Expose time range ([#5108])
- [Product Data] Client-side stats collection ([#5107])
- chore: ecash contract migration to remove unused 'redemption_gateway_share' ([#5104])
- [Product Data] Better unique user count on gateways ([#5084])
- feat: add nym node GH workflow ([#5080])
- IPv6 support for wireguard ([#5059])
- Node Status API ([#5050])
- Authenticator CLI client mode ([#5044])
- Integrate nym-credential-proxy into workspace ([#5027])
- [Product Data] Introduce data persistence on gateways ([#5022])
- Bump the patch-updates group across 1 directory with 10 updates ([#5011])
- build(deps): bump once_cell from 1.19.0 to 1.20.2 ([#4952])
- Create TaskStatusEvent trait instead of piggybacking on Error ([#4919])
- build(deps): bump lazy_static from 1.4.0 to 1.5.0 ([#4913])
- Sync code with .env in build.rs ([#4876])
- build(deps): bump axios from 1.6.0 to 1.7.5 in /nym-api/tests ([#4790])
- Bump elliptic from 6.5.4 to 6.5.7 in /testnet-faucet ([#4768])
[#5242]: https://github.com/nymtech/nym/pull/5242
[#5237]: https://github.com/nymtech/nym/pull/5237
[#5225]: https://github.com/nymtech/nym/pull/5225
[#5224]: https://github.com/nymtech/nym/pull/5224
[#5220]: https://github.com/nymtech/nym/pull/5220
[#5217]: https://github.com/nymtech/nym/pull/5217
[#5215]: https://github.com/nymtech/nym/pull/5215
[#5214]: https://github.com/nymtech/nym/pull/5214
[#5212]: https://github.com/nymtech/nym/pull/5212
[#5209]: https://github.com/nymtech/nym/pull/5209
[#5206]: https://github.com/nymtech/nym/pull/5206
[#5202]: https://github.com/nymtech/nym/pull/5202
[#5199]: https://github.com/nymtech/nym/pull/5199
[#5195]: https://github.com/nymtech/nym/pull/5195
[#5193]: https://github.com/nymtech/nym/pull/5193
[#5192]: https://github.com/nymtech/nym/pull/5192
[#5191]: https://github.com/nymtech/nym/pull/5191
[#5187]: https://github.com/nymtech/nym/pull/5187
[#5182]: https://github.com/nymtech/nym/pull/5182
[#5181]: https://github.com/nymtech/nym/pull/5181
[#5179]: https://github.com/nymtech/nym/pull/5179
[#5177]: https://github.com/nymtech/nym/pull/5177
[#5175]: https://github.com/nymtech/nym/pull/5175
[#5169]: https://github.com/nymtech/nym/pull/5169
[#5166]: https://github.com/nymtech/nym/pull/5166
[#5165]: https://github.com/nymtech/nym/pull/5165
[#5164]: https://github.com/nymtech/nym/pull/5164
[#5162]: https://github.com/nymtech/nym/pull/5162
[#5155]: https://github.com/nymtech/nym/pull/5155
[#5151]: https://github.com/nymtech/nym/pull/5151
[#5150]: https://github.com/nymtech/nym/pull/5150
[#5149]: https://github.com/nymtech/nym/pull/5149
[#5148]: https://github.com/nymtech/nym/pull/5148
[#5147]: https://github.com/nymtech/nym/pull/5147
[#5144]: https://github.com/nymtech/nym/pull/5144
[#5135]: https://github.com/nymtech/nym/pull/5135
[#5134]: https://github.com/nymtech/nym/pull/5134
[#5133]: https://github.com/nymtech/nym/pull/5133
[#5132]: https://github.com/nymtech/nym/pull/5132
[#5128]: https://github.com/nymtech/nym/pull/5128
[#5127]: https://github.com/nymtech/nym/pull/5127
[#5126]: https://github.com/nymtech/nym/pull/5126
[#5121]: https://github.com/nymtech/nym/pull/5121
[#5120]: https://github.com/nymtech/nym/pull/5120
[#5119]: https://github.com/nymtech/nym/pull/5119
[#5118]: https://github.com/nymtech/nym/pull/5118
[#5117]: https://github.com/nymtech/nym/pull/5117
[#5115]: https://github.com/nymtech/nym/pull/5115
[#5113]: https://github.com/nymtech/nym/pull/5113
[#5112]: https://github.com/nymtech/nym/pull/5112
[#5111]: https://github.com/nymtech/nym/pull/5111
[#5109]: https://github.com/nymtech/nym/pull/5109
[#5108]: https://github.com/nymtech/nym/pull/5108
[#5107]: https://github.com/nymtech/nym/pull/5107
[#5104]: https://github.com/nymtech/nym/pull/5104
[#5084]: https://github.com/nymtech/nym/pull/5084
[#5080]: https://github.com/nymtech/nym/pull/5080
[#5059]: https://github.com/nymtech/nym/pull/5059
[#5050]: https://github.com/nymtech/nym/pull/5050
[#5044]: https://github.com/nymtech/nym/pull/5044
[#5027]: https://github.com/nymtech/nym/pull/5027
[#5022]: https://github.com/nymtech/nym/pull/5022
[#5011]: https://github.com/nymtech/nym/pull/5011
[#4952]: https://github.com/nymtech/nym/pull/4952
[#4919]: https://github.com/nymtech/nym/pull/4919
[#4913]: https://github.com/nymtech/nym/pull/4913
[#4876]: https://github.com/nymtech/nym/pull/4876
[#4790]: https://github.com/nymtech/nym/pull/4790
[#4768]: https://github.com/nymtech/nym/pull/4768
## [2024.13-magura-drift] (2024-11-29)
- Optimised syncing bandwidth information to storage
## [2024.13-magura-patched] (2024-11-22)
- [experimental] allow clients to change between deterministic route selection based on packet headers and a pseudorandom distribution
- Introduced a configurable limit on retransmission frequency of packets if ACKs are not received
- Filtered out invalid IP addresses on nym-api
## [2024.13-magura] (2024-11-18)
- Limit race probability ([#5145])
- bugifx: assign 'node_id' when converting from 'GatewayDetails' to 'TestNode' ([#5143])
- bugfix: make sure to assign correct node_id and identity during 'gateway_details' table migration ([#5142])
- Respond to auth messages with same version ([#5140])
- Pain/polyfill deprecated endpoints ([#5131])
- change: dont select mixnodes bonded with vested tokens into the rewarded set ([#5129])
- nym-credential-proxy-requests: reqwest use rustls-tls ([#5116])
- bugfix: preserve as much as possible of the rewarded set during migration ([#5103])
- Feature/force refresh node ([#5101])
- Add NYM_VPN_API to env files ([#5099])
- bugfix: fixed historical uptimes for nodes ([#5097])
- Remove old use of 1GB constant ([#5096])
- Graceful agent 1.1.5 ([#5093])
- Add more translations from v2 to v3 authenticator ([#5091])
- Nym node - Fix claim delegator rewards ([#5090])
- Make 250 GB/30 days for free ride mode ([#5083])
- Don't increase bandwidth two times ([#5081])
- Fix expiration date as today + 7 days ([#5076])
- Fix gateway decreasing bandwidth ([#5075])
- Allow custom http port to be reset ([#5073])
- bugfix: additional checks inside credential proxy ([#5072])
- chore: deprecated old nym-api client methods and replaced them when possible ([#5069])
- NS API with directory v2 (#5058) ([#5068])
- bugfix: credential-proxy obtain-async ([#5067])
- Allow nym node config updates ([#5066])
- bugfix: use corrext axum extractors for ecash route arguments ([#5065])
- Merge2/release/2024.13 magura ([#5063])
- bugfix/feature: added NymApiClient method to get all skimmed nodes ([#5062])
- Merge1/release/2024.13 magura ([#5061])
- added hacky routes to return nymnodes alongside legacy nodes ([#5051])
- bugfix: mark migrated gateways as rewarded in the previous epoch in case theyre in the rewarded set ([#5049])
- bugfix: adjust runtime storage migration ([#5047])
- bugfix: supersede 'cb13be27f8f61d9ae74d924e85d2e6787895eb14' by using… ([#5046])
- bugfix: restore default http port for nym-api ([#5045])
- bugfix: fix ecash handlers routes ([#5043])
- bugfix: don't assign exit gateways to standby set ([#5041])
- bugfix: make sure nym-nodes are also tested by network monitor ([#5040])
- bugfix: use bonded nym-nodes for determining initial network monitor … ([#5039])
- bugfix: make gateways insert themselves into [local] topology ([#5038])
- Pass poisson flag ([#5037])
- bugfix: use human readable roles for annotations ([#5036])
- bugfix: use old name for 'epoch_role' in SkimmedNode ([#5034])
- bugfix: make sure to use correct highest node id when assigning role ([#5032])
- feature: use axum_client_ip for attempting to extract source ip ([#5031])
- bugfix: fixed backwards incompatibility for /gateways/described endpoint ([#5030])
- bugfix: verifying signed information of legacy nodes ([#5029])
- bugfix: introduce 'LegacyPendingMixNodeChanges' that does not contain 'cost_params_change' ([#5028])
- bugfix: missing #[serde(default)] for announce port ([#5024])
- bugfix: directory v2.1 `get_all_avg_gateway_reliability_in_interval` query ([#5023])
- added 'get_all_described_nodes' to NymApiClient and adjusted return t… ([#5016])
- Reapply fixes to new branch ([#5014])
- Consume only positive bandwidth ([#5013])
- feature: adjusted ticket sizes to the agreed amounts ([#5009])
- Push private ip before inserting ([#5008])
- chore: update itertools in compact ecash ([#4994])
- feature: make accepting t&c a hard requirement for rewarded set selection ([#4993])
- Fix rustfmt in nym-credential-proxy ([#4992])
- bugfix: client memory leak ([#4991])
- Eliminate 0 bandwidth race check ([#4988])
- [DOCs;/operators]: Release notes for v2024.12 aero ([#4984])
- Add topup req constructor ([#4983])
- Fix critical issues SI86 and SI87 from Cure53 ([#4982])
- Rename nym-vpn-api to nym-credential-proxy ([#4981])
- enable global ecash routes even if api is not a signer ([#4980])
- resolve beta clippy issues in contracts ([#4978])
- Re-enable vested delegation migration ([#4977])
- feature: require reporting using nym-node binary for rewarded set selection ([#4976])
- Top up bandwidth ([#4975])
- [Product Data] Add session type based on ecash ticket received ([#4974])
- Bugfix/additional directory fixes ([#4973])
- feat: add Dockerfile for nym node ([#4972])
- chore: remove unused rocket code ([#4968])
- Import nym-vpn-api crates ([#4967])
- feature: importer-cli to correctly handle mixnet/vesting import ([#4966])
- bugfix: fix expected return type on /v1/gateways endpoint ([#4965])
- [Product Data] First step in gateway usage data collection ([#4963])
- Bump sqlx to 0.7.4 ([#4959])
- Add env feature to clap and make clap parameters available as env variables ([#4957])
- Feature/contract state tools ([#4954])
- expose authenticator address along other address in node-details ([#4953])
- Extract packet processing from mixnode-common ([#4949])
- nym-api container ([#4948])
- Ticket type storage ([#4947])
- Add "utoipa" feature to nym-node ([#4945])
- build(deps): bump the patch-updates group across 1 directory with 9 updates ([#4944])
- V2 performance monitoring feature flag ([#4943])
- Bugfix/rewarder post pruning adjustments ([#4942])
- Switch over the last set of jobs to arc runners ([#4938])
- Fix broken build after merge ([#4937])
- bugfix: correctly paginate through 'search_tx' endpoint ([#4936])
- Add more conversions for responses of authenticator messages ([#4929])
- Directory Sevices v2.1 ([#4903])
- Migrate Legacy Node (Frontend) ([#4826])
- Fix critical issues SI84 and SI85 from Cure53 ([#4758])
[#5145]: https://github.com/nymtech/nym/pull/5145
[#5143]: https://github.com/nymtech/nym/pull/5143
[#5142]: https://github.com/nymtech/nym/pull/5142
[#5140]: https://github.com/nymtech/nym/pull/5140
[#5131]: https://github.com/nymtech/nym/pull/5131
[#5129]: https://github.com/nymtech/nym/pull/5129
[#5116]: https://github.com/nymtech/nym/pull/5116
[#5103]: https://github.com/nymtech/nym/pull/5103
[#5101]: https://github.com/nymtech/nym/pull/5101
[#5099]: https://github.com/nymtech/nym/pull/5099
[#5097]: https://github.com/nymtech/nym/pull/5097
[#5096]: https://github.com/nymtech/nym/pull/5096
[#5093]: https://github.com/nymtech/nym/pull/5093
[#5091]: https://github.com/nymtech/nym/pull/5091
[#5090]: https://github.com/nymtech/nym/pull/5090
[#5083]: https://github.com/nymtech/nym/pull/5083
[#5081]: https://github.com/nymtech/nym/pull/5081
[#5076]: https://github.com/nymtech/nym/pull/5076
[#5075]: https://github.com/nymtech/nym/pull/5075
[#5073]: https://github.com/nymtech/nym/pull/5073
[#5072]: https://github.com/nymtech/nym/pull/5072
[#5069]: https://github.com/nymtech/nym/pull/5069
[#5068]: https://github.com/nymtech/nym/pull/5068
[#5067]: https://github.com/nymtech/nym/pull/5067
[#5066]: https://github.com/nymtech/nym/pull/5066
[#5065]: https://github.com/nymtech/nym/pull/5065
[#5063]: https://github.com/nymtech/nym/pull/5063
[#5062]: https://github.com/nymtech/nym/pull/5062
[#5061]: https://github.com/nymtech/nym/pull/5061
[#5051]: https://github.com/nymtech/nym/pull/5051
[#5049]: https://github.com/nymtech/nym/pull/5049
[#5047]: https://github.com/nymtech/nym/pull/5047
[#5046]: https://github.com/nymtech/nym/pull/5046
[#5045]: https://github.com/nymtech/nym/pull/5045
[#5043]: https://github.com/nymtech/nym/pull/5043
[#5041]: https://github.com/nymtech/nym/pull/5041
[#5040]: https://github.com/nymtech/nym/pull/5040
[#5039]: https://github.com/nymtech/nym/pull/5039
[#5038]: https://github.com/nymtech/nym/pull/5038
[#5037]: https://github.com/nymtech/nym/pull/5037
[#5036]: https://github.com/nymtech/nym/pull/5036
[#5034]: https://github.com/nymtech/nym/pull/5034
[#5032]: https://github.com/nymtech/nym/pull/5032
[#5031]: https://github.com/nymtech/nym/pull/5031
[#5030]: https://github.com/nymtech/nym/pull/5030
[#5029]: https://github.com/nymtech/nym/pull/5029
[#5028]: https://github.com/nymtech/nym/pull/5028
[#5024]: https://github.com/nymtech/nym/pull/5024
[#5023]: https://github.com/nymtech/nym/pull/5023
[#5016]: https://github.com/nymtech/nym/pull/5016
[#5014]: https://github.com/nymtech/nym/pull/5014
[#5013]: https://github.com/nymtech/nym/pull/5013
[#5009]: https://github.com/nymtech/nym/pull/5009
[#5008]: https://github.com/nymtech/nym/pull/5008
[#4994]: https://github.com/nymtech/nym/pull/4994
[#4993]: https://github.com/nymtech/nym/pull/4993
[#4992]: https://github.com/nymtech/nym/pull/4992
[#4991]: https://github.com/nymtech/nym/pull/4991
[#4988]: https://github.com/nymtech/nym/pull/4988
[#4984]: https://github.com/nymtech/nym/pull/4984
[#4983]: https://github.com/nymtech/nym/pull/4983
[#4982]: https://github.com/nymtech/nym/pull/4982
[#4981]: https://github.com/nymtech/nym/pull/4981
[#4980]: https://github.com/nymtech/nym/pull/4980
[#4978]: https://github.com/nymtech/nym/pull/4978
[#4977]: https://github.com/nymtech/nym/pull/4977
[#4976]: https://github.com/nymtech/nym/pull/4976
[#4975]: https://github.com/nymtech/nym/pull/4975
[#4974]: https://github.com/nymtech/nym/pull/4974
[#4973]: https://github.com/nymtech/nym/pull/4973
[#4972]: https://github.com/nymtech/nym/pull/4972
[#4968]: https://github.com/nymtech/nym/pull/4968
[#4967]: https://github.com/nymtech/nym/pull/4967
[#4966]: https://github.com/nymtech/nym/pull/4966
[#4965]: https://github.com/nymtech/nym/pull/4965
[#4963]: https://github.com/nymtech/nym/pull/4963
[#4959]: https://github.com/nymtech/nym/pull/4959
[#4957]: https://github.com/nymtech/nym/pull/4957
[#4954]: https://github.com/nymtech/nym/pull/4954
[#4953]: https://github.com/nymtech/nym/pull/4953
[#4949]: https://github.com/nymtech/nym/pull/4949
[#4948]: https://github.com/nymtech/nym/pull/4948
[#4947]: https://github.com/nymtech/nym/pull/4947
[#4945]: https://github.com/nymtech/nym/pull/4945
[#4944]: https://github.com/nymtech/nym/pull/4944
[#4943]: https://github.com/nymtech/nym/pull/4943
[#4942]: https://github.com/nymtech/nym/pull/4942
[#4938]: https://github.com/nymtech/nym/pull/4938
[#4937]: https://github.com/nymtech/nym/pull/4937
[#4936]: https://github.com/nymtech/nym/pull/4936
[#4929]: https://github.com/nymtech/nym/pull/4929
[#4903]: https://github.com/nymtech/nym/pull/4903
[#4826]: https://github.com/nymtech/nym/pull/4826
[#4758]: https://github.com/nymtech/nym/pull/4758
## [2024.12-aero] (2024-10-17) ## [2024.12-aero] (2024-10-17)
- nym-node: don't use bloomfilters for double spending checks ([#4960]) - nym-node: don't use bloomfilters for double spending checks ([#4960])
Generated
+1024 -1342
View File
File diff suppressed because it is too large Load Diff
+59 -51
View File
@@ -61,6 +61,7 @@ members = [
"common/ip-packet-requests", "common/ip-packet-requests",
"common/ledger", "common/ledger",
"common/mixnode-common", "common/mixnode-common",
"common/models",
"common/network-defaults", "common/network-defaults",
"common/node-tester-utils", "common/node-tester-utils",
"common/nonexhaustive-delayqueue", "common/nonexhaustive-delayqueue",
@@ -98,15 +99,17 @@ members = [
"common/wasm/utils", "common/wasm/utils",
"common/wireguard", "common/wireguard",
"common/wireguard-types", "common/wireguard-types",
"documentation/autodoc", # "documentation/autodoc",
"explorer-api", "explorer-api",
"explorer-api/explorer-api-requests", "explorer-api/explorer-api-requests",
"explorer-api/explorer-client", "explorer-api/explorer-client",
"gateway", "gateway",
"integrations/bity", "integrations/bity",
"mixnode",
"sdk/ffi/cpp", "sdk/ffi/cpp",
"sdk/ffi/go", "sdk/ffi/go",
"sdk/ffi/shared", "sdk/ffi/shared",
"sdk/lib/socks5-listener",
"sdk/rust/nym-sdk", "sdk/rust/nym-sdk",
"service-providers/authenticator", "service-providers/authenticator",
"service-providers/common", "service-providers/common",
@@ -118,14 +121,13 @@ members = [
"nym-credential-proxy/nym-credential-proxy", "nym-credential-proxy/nym-credential-proxy",
"nym-credential-proxy/nym-credential-proxy-requests", "nym-credential-proxy/nym-credential-proxy-requests",
"nym-credential-proxy/vpn-api-lib-wasm", "nym-credential-proxy/vpn-api-lib-wasm",
"nym-data-observatory",
"nym-network-monitor", "nym-network-monitor",
"nyx-chain-watcher",
"nym-node", "nym-node",
"nym-node/nym-node-http-api",
"nym-node/nym-node-requests", "nym-node/nym-node-requests",
"nym-node/nym-node-metrics", "nym-node-status-api",
"nym-node-status-api/nym-node-status-agent", "nym-node-status-agent",
"nym-node-status-api/nym-node-status-api",
"nym-node-status-api/nym-node-status-client",
"nym-outfox", "nym-outfox",
"nym-validator-rewarder", "nym-validator-rewarder",
"tools/echo-server", "tools/echo-server",
@@ -148,21 +150,22 @@ members = [
"tools/internal/contract-state-importer/importer-contract", "tools/internal/contract-state-importer/importer-contract",
"tools/internal/testnet-manager", "tools/internal/testnet-manager",
"tools/internal/testnet-manager/dkg-bypass-contract", "tools/internal/testnet-manager/dkg-bypass-contract",
"common/verloc",
"tools/internal/mixnet-connectivity-check",
] ]
default-members = [ default-members = [
"clients/native", "clients/native",
"clients/socks5", "clients/socks5",
"common/models",
"explorer-api", "explorer-api",
"gateway",
"mixnode",
"nym-api", "nym-api",
"nym-credential-proxy/nym-credential-proxy", "nym-credential-proxy/nym-credential-proxy",
"nym-data-observatory",
"nym-node", "nym-node",
"nym-node-status-api/nym-node-status-agent", "nym-node-status-api",
"nym-node-status-api/nym-node-status-api",
"nym-validator-rewarder", "nym-validator-rewarder",
"nyx-chain-watcher", "nym-node-status-api",
"service-providers/authenticator", "service-providers/authenticator",
"service-providers/ip-packet-router", "service-providers/ip-packet-router",
"service-providers/network-requester", "service-providers/network-requester",
@@ -193,9 +196,9 @@ aes = "0.8.1"
aes-gcm = "0.10.1" aes-gcm = "0.10.1"
aes-gcm-siv = "0.11.1" aes-gcm-siv = "0.11.1"
aead = "0.5.2" aead = "0.5.2"
anyhow = "1.0.95" anyhow = "1.0.90"
argon2 = "0.5.0" argon2 = "0.5.0"
async-trait = "0.1.85" async-trait = "0.1.83"
axum-client-ip = "0.6.1" axum-client-ip = "0.6.1"
axum = "0.7.5" axum = "0.7.5"
axum-extra = "0.9.4" axum-extra = "0.9.4"
@@ -204,7 +207,7 @@ bincode = "1.3.3"
bip39 = { version = "2.0.0", features = ["zeroize"] } bip39 = { version = "2.0.0", features = ["zeroize"] }
bit-vec = "0.7.0" # can we unify those? bit-vec = "0.7.0" # can we unify those?
bitvec = "1.0.0" bitvec = "1.0.0"
blake3 = "1.5.5" blake3 = "1.5.4"
bloomfilter = "1.0.14" bloomfilter = "1.0.14"
bs58 = "0.5.1" bs58 = "0.5.1"
bytecodec = "0.4.15" bytecodec = "0.4.15"
@@ -214,20 +217,20 @@ celes = "2.4.0"
cfg-if = "1.0.0" cfg-if = "1.0.0"
chacha20 = "0.9.0" chacha20 = "0.9.0"
chacha20poly1305 = "0.10.1" chacha20poly1305 = "0.10.1"
chrono = "0.4.39" chrono = "0.4.31"
cipher = "0.4.3" cipher = "0.4.3"
clap = "4.5.26" clap = "4.5.20"
clap_complete = "4.5" clap_complete = "4.5"
clap_complete_fig = "4.5" clap_complete_fig = "4.5"
colored = "2.0" colored = "2.0"
comfy-table = "7.1.3" comfy-table = "7.1.1"
console = "0.15.10" console = "0.15.8"
console-subscriber = "0.1.1" console-subscriber = "0.1.1"
console_error_panic_hook = "0.1" console_error_panic_hook = "0.1"
const-str = "0.5.6" const-str = "0.5.6"
const_format = "0.2.34" const_format = "0.2.33"
criterion = "0.5" criterion = "0.4"
csv = "1.3.1" csv = "1.3.0"
ctr = "0.9.1" ctr = "0.9.1"
cupid = "0.6.1" cupid = "0.6.1"
curve25519-dalek = "4.1" curve25519-dalek = "4.1"
@@ -244,8 +247,8 @@ etherparse = "0.13.0"
envy = "0.4" envy = "0.4"
eyre = "0.6.9" eyre = "0.6.9"
fastrand = "2.1.1" fastrand = "2.1.1"
flate2 = "1.0.35" flate2 = "1.0.34"
futures = "0.3.31" futures = "0.3.28"
futures-util = "0.3" futures-util = "0.3"
generic-array = "0.14.7" generic-array = "0.14.7"
getrandom = "0.2.10" getrandom = "0.2.10"
@@ -261,10 +264,9 @@ http-body-util = "0.1"
httpcodec = "0.2.3" httpcodec = "0.2.3"
humantime = "2.1.0" humantime = "2.1.0"
humantime-serde = "1.1.1" humantime-serde = "1.1.1"
human-repr = "1.1.0"
hyper = "1.4.1" hyper = "1.4.1"
hyper-util = "0.1" hyper-util = "0.1"
indicatif = "0.17.9" indicatif = "0.17.8"
inquire = "0.6.2" inquire = "0.6.2"
ip_network = "0.4.1" ip_network = "0.4.1"
ipnetwork = "0.20" ipnetwork = "0.20"
@@ -289,7 +291,7 @@ parking_lot = "0.12.3"
pem = "0.8" pem = "0.8"
petgraph = "0.6.5" petgraph = "0.6.5"
pin-project = "1.1" pin-project = "1.1"
pin-project-lite = "0.2.16" pin-project-lite = "0.2.14"
pretty_env_logger = "0.4.0" pretty_env_logger = "0.4.0"
publicsuffix = "2.2.3" publicsuffix = "2.2.3"
quote = "1" quote = "1"
@@ -307,12 +309,12 @@ rocket_cors = "0.6.0"
rocket_okapi = "0.8.0" rocket_okapi = "0.8.0"
safer-ffi = "0.1.13" safer-ffi = "0.1.13"
schemars = "0.8.21" schemars = "0.8.21"
semver = "1.0.24" semver = "1.0.23"
serde = "1.0.217" serde = "1.0.211"
serde_bytes = "0.11.15" serde_bytes = "0.11.15"
serde_derive = "1.0" serde_derive = "1.0"
serde_json = "1.0.135" serde_json = "1.0.132"
serde_json_path = "0.7.1" serde_json_path = "0.6.7"
serde_repr = "0.1" serde_repr = "0.1"
serde_with = "3.9.0" serde_with = "3.9.0"
serde_yaml = "0.9.25" serde_yaml = "0.9.25"
@@ -326,34 +328,34 @@ subtle-encoding = "0.5"
syn = "1" syn = "1"
sysinfo = "0.30.13" sysinfo = "0.30.13"
tap = "1.0.1" tap = "1.0.1"
tar = "0.4.43" tar = "0.4.42"
tempfile = "3.15" tempfile = "3.5.0"
thiserror = "1.0.64" thiserror = "1.0.64"
time = "0.3.37" time = "0.3.30"
tokio = "1.39" tokio = "1.39"
tokio-stream = "0.1.17" tokio-stream = "0.1.16"
tokio-test = "0.4.4" tokio-test = "0.4.4"
tokio-tun = "0.11.5" tokio-tun = "0.11.5"
tokio-tungstenite = { version = "0.20.1" } tokio-tungstenite = { version = "0.20.1" }
tokio-util = "0.7.13" tokio-util = "0.7.12"
toml = "0.8.19" toml = "0.8.14"
tower = "0.4.13" tower = "0.4.13"
tower-http = "0.5.2" tower-http = "0.5.2"
tracing = "0.1.41" tracing = "0.1.37"
tracing-opentelemetry = "0.19.0" tracing-opentelemetry = "0.19.0"
tracing-subscriber = "0.3.19" tracing-subscriber = "0.3.16"
tracing-tree = "0.2.2" tracing-tree = "0.2.2"
tracing-log = "0.2" tracing-log = "0.2"
ts-rs = "10.1.0" ts-rs = "10.0.0"
tungstenite = { version = "0.20.1", default-features = false } tungstenite = { version = "0.20.1", default-features = false }
url = "2.5" url = "2.5"
utoipa = "5.2" utoipa = "4.2"
utoipa-swagger-ui = "8.0" utoipa-swagger-ui = "7.1"
utoipauto = "0.2" utoipauto = "0.1"
uuid = "*" uuid = "*"
vergen = { version = "=8.3.1", default-features = false } vergen = { version = "=8.3.1", default-features = false }
walkdir = "2" walkdir = "2"
wasm-bindgen-test = "0.3.49" wasm-bindgen-test = "0.3.43"
x25519-dalek = "2.0.0" x25519-dalek = "2.0.0"
zeroize = "1.6.0" zeroize = "1.6.0"
@@ -396,17 +398,19 @@ prost = { version = "0.12", default-features = false }
# wasm-related dependencies # wasm-related dependencies
gloo-utils = "0.2.0" gloo-utils = "0.2.0"
gloo-net = "0.6.0" gloo-net = "0.5.0"
# TODO: migrate to 0.6+ # use a separate branch due to feature unification failures
indexed_db_futures = "0.4.2" # this is blocked until the upstream removes outdates `wasm_bindgen` feature usage
js-sys = "0.3.76" # indexed_db_futures = "0.4.1"
indexed_db_futures = { git = "https://github.com/TiemenSch/rust-indexed-db", branch = "update-uuid" }
js-sys = "0.3.70"
serde-wasm-bindgen = "0.6.5" serde-wasm-bindgen = "0.6.5"
tsify = "0.4.5" tsify = "0.4.5"
wasm-bindgen = "0.2.99" wasm-bindgen = "0.2.95"
wasm-bindgen-futures = "0.4.49" wasm-bindgen-futures = "0.4.45"
wasmtimer = "0.4.1" wasmtimer = "0.2.0"
web-sys = "0.3.76" web-sys = "0.3.72"
# Profile settings for individual crates # Profile settings for individual crates
@@ -416,6 +420,10 @@ web-sys = "0.3.76"
[profile.dev.package.sqlx-macros] [profile.dev.package.sqlx-macros]
opt-level = 3 opt-level = 3
[profile.release.package.nym-socks5-listener]
strip = true
codegen-units = 1
[profile.release.package.nym-client-wasm] [profile.release.package.nym-client-wasm]
# lto = true # lto = true
opt-level = 'z' opt-level = 'z'
-23
View File
@@ -1,23 +0,0 @@
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
+11 -8
View File
@@ -14,7 +14,6 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
* `nym-socks5-client` - a Socks5 proxy you can run on your machine and use with existing applications. * `nym-socks5-client` - a Socks5 proxy you can run on your machine and use with existing applications.
* `nym-explorer` - a (projected) block explorer and (existing) mixnet viewer. * `nym-explorer` - a (projected) block explorer and (existing) mixnet viewer.
* `nym-wallet` - a desktop wallet implemented using the [Tauri](https://tauri.studio/en/docs/about/intro) framework. * `nym-wallet` - a desktop wallet implemented using the [Tauri](https://tauri.studio/en/docs/about/intro) framework.
* `nym-cli` - a tool for interacting with the network from the CLI.
<!-- coming soon <!-- coming soon
* `nym-network-monitor` - sends packets through the full system to check that they are working as expected, and stores node uptime histories as the basis of a rewards system ("mixmining" or "proof-of-mixing"). * `nym-network-monitor` - sends packets through the full system to check that they are working as expected, and stores node uptime histories as the basis of a rewards system ("mixmining" or "proof-of-mixing").
--> -->
@@ -36,20 +35,24 @@ client ───► Gateway ──┘ mix │ mix ┌─►mix ───►
### Building ### Building
* Platform build instructions are available on Nym [Operators Guide documentation](https://nymtech.net/operators/binaries/building-nym.html). * Platform build instructions are available on Nym [Operators Guide documentation](https://nymtech.net/operators/binaries/building-nym.html).
* Wallet build instructions are available [here](https://github.com/nymtech/nym/tree/master/nym-wallet#installation-prerequisites---linux--mac). * Wallet build instructions are available on Nym [Technical docs](https://nymtech.net/docs/wallet/desktop-wallet.html).
### Developing ### Developing
There's a [`sandbox.env`](https://github.com/nymtech/nym/envs/sandbox.env) file provided which you can rename to `.env` if you want convenient testing environment. Read more about sandbox environment in our [Operators Guide page](https://nymtech.net/operators/sandbox.html).
References for developers: References for developers:
* [Dev Docs](https://nym.com/docs/developers) * [Developers Portal](https://nymtech.net/developers)
* [SDKs](https://nym.com/docs/developers/rust) * [Typescript SDKs](https://sdk.nymtech.net/)
* [Network Docs](https://nym.com/docs/network) * [Technical Documentation - Nym network overview](https://nymtech.net/docs/)
* [Release Cycle - git flow](https://nym.com/docs/operators/release-cycle) * [Release Cycle - git flow](https://nymtech.net/operators/release-cycle.html)
### Developer chat ### Developer chat
You can chat to us in the #dev channel on [Matrix](https://matrix.to/#/#dev:nymtech.chat) or on the [Nym Forum](https://forum.nymtech.net). You can chat to us in two places:
* The #dev channel on [Matrix](https://matrix.to/#/#dev:nymtech.chat)
* The various developer channels on [Discord](https://nymtech.net/go/discord)
### Tokenomics & Rewards ### Tokenomics & Rewards
@@ -66,4 +69,4 @@ As a general approach, licensing is as follows this pattern:
- libraries and components are Apache 2.0 or MIT - libraries and components are Apache 2.0 or MIT
- documentation is Apache 2.0 or CC0-1.0 - documentation is Apache 2.0 or CC0-1.0
Nym Node Operators and Validators Temrs and Conditions can be found [here](https://nym.com/terms-and-conditions/operators/v1.0.0). Nym Node Operators and Validators Temrs and Conditions can be found [here](https://nymtech.net/terms-and-conditions/operators/v1.0.0).
+56 -68
View File
@@ -3,23 +3,37 @@ Critical bug or security issue 💥
If you're here because you're trying to figure out how to notify us of a security issue, send us a PGP encrypted email to: If you're here because you're trying to figure out how to notify us of a security issue, send us a PGP encrypted email to:
``` ```
security@nym.com security@nymte.ch
``` ```
Encrypted with our public key which is available below in plain text and also on keyservers: Encrypted with our public key which is available below in plain text and also on keyservers:
``` ```
sec rsa4096/7C3C727F05090550 2023-10-30 [SC] [expire : 2026-10-29] pub rsa4096 2023-10-30 [SC] [expire : 2026-10-29]
24B2592E801A5AAA8666C8BA7C3C727F05090550 24B2592E801A5AAA8666C8BA7C3C727F05090550
uid [ ultime ] Security Nym Technologies <security@nym.com> uid [ ultime ] Security Nym Technologies <security@nymte.ch>
ssb rsa4096/ACD0FBD79DC70ACC 2023-10-30 [E] [expire : 2026-10-29] sub rsa4096 2023-10-30 [E] [expire : 2026-10-29]
``` ```
The fingerprint of the key is on the second line above. The fingerprint of the key is on the second line above.
If you need to chat __urgently__ to our team for a __critical__ security issue:
go to Matrix, and alert the core engineers with a private direct message:
Jedrzej Stuczynski @jstuczyn:nymtech.chat
Mark Sinclair @mark:nymtech.chat
Raphaël Walther @raphael:nymtech.chat
Please avoid opening public issues on GitHub that contain information about a potential security vulnerability as this makes it difficult to reduce the impact and harm of valid security issues. Please avoid opening public issues on GitHub that contain information about a potential security vulnerability as this makes it difficult to reduce the impact and harm of valid security issues.
If you don't know what Matrix is, you can follow this documentation to create an account on this federation of instant messaging servers:
[Matrix for Instant Messaging](https://matrix.org/docs/chat_basics/matrix-for-im/)
``` ```
-----BEGIN PGP PUBLIC KEY BLOCK----- -----BEGIN PGP PUBLIC KEY BLOCK-----
@@ -34,69 +48,43 @@ vMFUIzBMHOPXH16036zGyFMC1esRd2qqil4b9KtLgCOkrD1VgpjcveoA0VyMJCN6
LmKTrVjwjjDMxby+d49BolRWGnCofXozXwvNQx+CYv8M2WPErTpyYoofYFtpqr7A LmKTrVjwjjDMxby+d49BolRWGnCofXozXwvNQx+CYv8M2WPErTpyYoofYFtpqr7A
fIufc/e0+um3zoGIbHejrhsbuH9Qf+MKsI+Ng93bdDtjeHz6MEgAlsTm0qeizYpj fIufc/e0+um3zoGIbHejrhsbuH9Qf+MKsI+Ng93bdDtjeHz6MEgAlsTm0qeizYpj
IyKZIObPmfvrAm08hFZ8JnGk+XuooF36XWbJYjCCy0bOyMw1r7ZG99TcSwARAQAB IyKZIObPmfvrAm08hFZ8JnGk+XuooF36XWbJYjCCy0bOyMw1r7ZG99TcSwARAQAB
tCxTZWN1cml0eSBOeW0gVGVjaG5vbG9naWVzIDxzZWN1cml0eUBueW0uY29tPokC tC1TZWN1cml0eSBOeW0gVGVjaG5vbG9naWVzIDxzZWN1cml0eUBueW10ZS5jaD6J
VAQTAQoAPhYhBCSyWS6AGlqqhmbIunw8cn8FCQVQBQJnSd5VAhsDBQkFo5qABQsJ AlQEEwEKAD4WIQQkslkugBpaqoZmyLp8PHJ/BQkFUAUCZT9elwIbAwUJBaOagAUL
CAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJEHw8cn8FCQVQPPIP/ipGz2zLAjE2dSE3 CQgHAwUVCgkICwUWAgMBAAIeAQIXgAAKCRB8PHJ/BQkFUL7dD/9zO73uI5VR+SWx
VcqOvras0DfqIL9HDm26Dg6QO2D/4YRntw0RqVyuy+zFnRUm+RZCKLPLUzbQ9Wjb PFmJW+9QsPiQbVRvGwNZurctmQ2s2Pe0vHRELFeqD5oYvSx2Lequ3Ir+zn/C3kDM
G/Og5ttQVYQMu5eKu7OMvXkrbRo3teZFU+8IL08zIW6pyf9haxO6YMhLRy6cLYwW kNs40obSL6jCBiLPkxEY0JqzPM9jZr7EjvlibWV3f6DxooRIqEyfN57I3OBGlqZE
0EYC6Qzn5gz3kI7VkI8fWfs2Dk4XEV3D+SVtBoF6KRxMXT6HZvpzoMSEJZBoNj8S 0Mx7sQuCcgau8C70DF952QhKUwXC2cmpmDKHVEEoio1xGSD4dQhGapCB32RQGtna
jw0TF8TFUQf49jUQbIHumukMswolrHi8a5ej8DSfNwSgz+Tt8oh5lu01kyUJiHn7 OGfAO9celNMvSq0Lp+aJxeACmWFY5T4/y79JPcT5vSs/yEIRmaH/fn2piwaFBsIq
nuHaY4Y9cHUVAOSwq/hovG52+ZE1r3aiswvle/B19o9pKeWWVvacSptGxDQagBtQ gHJJMxO3740P1hF8j7KWUoUofuFaEALHBpEpjWTOj8ej1wmFlu+5F+jSVoc781Wb
igoNLdRvY0XN2TEyX9pOHR0AoVOxtIW11CpkKuDbQG9vPwovqJ2L6+Fh3pzHYzcI ZZXu04cOBXnGTogzSxMpBe9TtLb28zd6WzFotC25KTI3pngMzXsQGLJLOwvoZKiS
2GIShNm/Z2SZBiUqbljJe9H4UAT/aHgMINkEG8qzUKwO42MA5HJT7YbHTR17/QSF LFjPRjg1rwobmB3Q3J2W5GYSveia0CDsZGP+g87GVVf/oD2Djpa68xyVYwIYeA6T
Il5dhneRzmSbNcW2rdRwx/BmzrcsFJfqCt4JG/WDF293xSOjhFqQYvU4gCO+OB7o 3DNdS77qHiRuGiS4kWXyVjDqOICboR4uCvt09zlkBuLDdTWqWYARUvZjtjs4w/Ol
KXjX907XXDjS2KEJ71OGqVfk/P7BqEfQNfrLtb02TyXJAPQXHhybv23c4E7zUs9V rdrBI3A88ti8fRldYaNpu17ME1ilpN44yKoJtqiWc3Tisk8eYLfx6c7FQF3PrRva
lMjNizzxYB96uwJb0LAB2ijzEwoP91uGT2tFjk6F08x2QiArmXUdgrv44b39Stia mr7FZvhFsYML5CeNFHTEzN6Y3jjKN/60DvCfodWnWFK47Txkl8UAXGY2W9B0fWqQ
gJS0GYKqSzyr10xHhUuDA+GKYtcitC1TZWN1cml0eSBOeW0gVGVjaG5vbG9naWVz wUVr8uLuMyyMiKbeoufi7rGOj6AMErkCDQRlP16XARAA8FGmD5J3tM1BOM1niJxZ
IDxzZWN1cml0eUBueW10ZS5jaD6JAjYEMAEKACAWIQQkslkugBpaqoZmyLp8PHJ/ JTdCauzEtxEoBL0RuqGBkR8U29sRM6DwuzjU7PwscFnBaGyU+eU73GwGkH3ozFfF
BQkFUAUCZ0nftQIdIAAKCRB8PHJ/BQkFUFHDEACtyNuUEjKCLAT5mSfow85PjFgo tllYhQrhP/kkN+0rEO5Xi+nR+4JCFRqrf3nJXAAPfiksURMp8er1dUOY2/e1ZSoL
o8kHjQr/IIQ7ZbBOHeJJcrxDuypssiLh5XUjF3x5BiBfZ6vCxSb81RRwsDMp0mA1 tS+nzUivV8CfE+pgj/5YtGwPC+KYHLATkKkMELCrbW4UO06VWOqQsvr6kivXuJQQ
qzv9G8sgW0HTQUnZ9oH6CYut2NgzAnQpmuacrunm9Zy0FJ3ejbmwUY/NqK6gJkle LdEAMpBlADmXFG45DmPKQzsBWUgvTwyGy3LX0nys8cgpex9BH8hhr01QmGyP469s
66duHKhAy7DWjj7amd0C8bPDR+PA44fI3MezDHkQNaauKZTRqd1TqH8Qk5PAl4cB N3cNrtFuu8U6RAsiCD/8mlBuD3EQEU5SF0lc7kCICAZk+wElmXnimEi0TOYsbz6k
o5gVzeZh/U7/usvtGhazAIUF5BqK6bTmDnYopg+2x8jjwrG4+08GrttZkNjBLXeA 90lteicX70rA9GNeyI76H+VSOYvWpkRwaJAgUdzrAM1o9SHASq+cZ6nD85OZioQk
Y/2U064yMz12LPv01qqAFdZ+coRy/ps/gOQTz34/VeW0CFy7TMqs4t3vSBWTqU7w DWM6+Q+sf2oen0qJnnGmUr93kJIC0PIdgrXRrtiNfeRa1Z/H0LmREyyEMoFiVivn
hnw/qj6cM33fdxctj6KDgJSCkZdx2fvwXgxiPqUa5+j9FlFBeD5RDAl6g6t8N1/K z1vVk85Oq6Sf3ltUwvmDzuuJOtsp2Qp6+x6Snn/yKauI4uf4Cf/wKUch4r6Bwgg5
Xca+zNYuSZgc297q1D+mtSD1C7uJNPxoAl+Bv5KNKpsjfQ+m04++CIFtGyX22aCA Dw49ky7lwlnALio4GIVoGLpLef93wWoDmp4Klyh3ZPf2nB0U91u3bHRUo7m+D7QJ
h2/tHwQZIXhOiMAKOoupidDVDhgxtCJ3Ps416xL0sTZfsPfg+j1Uv/Em9pzPClEl 98cyKtqLLzjg7szGf60pIWNWRsadYQT3bSncynqknAjOV3BCvx6/ivsnpj//QjYR
fX6+1O4DdSyZUQ4VsjMu/H5W/NQdbHgmqFrxQ6WX/0s5GMwO6GMDiPe8sOrwz9wD HtviUAcQ1DBB6UC6q23FIs0AEQEAAYkCPAQYAQoAJhYhBCSyWS6AGlqqhmbIunw8
WYtyjafxXOHEZ1OjYX5gr7bGaG4oKc2btTJN0B3Phg4dStnHCNjEYccxuV3507fj cn8FCQVQBQJlP16XAhsMBQkFo5qAAAoJEHw8cn8FCQVQzukP/iLxjOxT+UpPR//c
HnNotkpXF2nGLxy+PYkCVAQTAQoAPhYhBCSyWS6AGlqqhmbIunw8cn8FCQVQBQJl prDVSLkP4pF5bmw36U07jvqpS+/KTXsxiiQleffRabOpNLcd+K1ueavyt9nnIwHH
P16XAhsDBQkFo5qABQsJCAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJEHw8cn8FCQVQ tHS9kM9A7DBw3LnpEbXki46QDCCI6niGijlLOEeAWqnocwMNTT05wVVgCtO3DQP2
vt0P/3M7ve4jlVH5JbE8WYlb71Cw+JBtVG8bA1m6ty2ZDazY97S8dEQsV6oPmhi9 MoSCcqHpXDChvOyr5d5xjYLVJhlctIMSomcVzGryjknPu0Yj/TkC/4c+m86ZWQUD
LHYt6q7civ7Of8LeQMyQ2zjShtIvqMIGIs+TERjQmrM8z2NmvsSO+WJtZXd/oPGi HqMHQIuiEenvb62/F4c5OJIRZPEn70wdddkgJuJU3eHdHrnuhCkjCC93GQGbGj03
hEioTJ83nsjc4EaWpkTQzHuxC4JyBq7wLvQMX3nZCEpTBcLZyamYModUQSiKjXEZ Zqos6699y6hmPeD3U5IUv8ujwZYVCCuDm8gJfrp3R6WLfeZeK9WmTVBpCzsDg3fV
IPh1CEZqkIHfZFAa2do4Z8A71x6U0y9KrQun5onF4AKZYVjlPj/Lv0k9xPm9Kz/I hSwmOk6pp8DAq1/Dev3yRkFggCEyGK6c9b+a0CRBncl8e5Q0QQIzNiS/uExQP3h+
QhGZof9+famLBoUGwiqAckkzE7fvjQ/WEXyPspZShSh+4VoQAscGkSmNZM6Px6PX ELJs3P0MLP+6FWhNUry09n3lnWkr1hY+v1M0GAxbfdv/tsCN1Pq/VQEz+CTqXqya
CYWW77kX6NJWhzvzVZtlle7Thw4FecZOiDNLEykF71O0tvbzN3pbMWi0LbkpMjem ftWldOHWw6Hh+gtwxcHjG4MBOrO5oICQ3lh2hGwQ58cDgZYSK/OGgJ9BggFl1CcM
eAzNexAYsks7C+hkqJIsWM9GODWvChuYHdDcnZbkZhK96JrQIOxkY/6DzsZVV/+g 0uGC0/TRCI1zt/4y+7efSZQMZkHo7VC/3MFbp2hcNejpW+BxVuwKTunFvWK3TLhq
PYOOlrrzHJVjAhh4DpPcM11LvuoeJG4aJLiRZfJWMOo4gJuhHi4K+3T3OWQG4sN1 sSlQ5yyhqchooepsFHq9bosKFjLJC01uprBv1rinoNduOy43FbyS7JPRRspANN0R
NapZgBFS9mO2OzjD86Wt2sEjcDzy2Lx9GV1ho2m7XswTWKWk3jjIqgm2qJZzdOKy iC2pMbWdE0ZTQaFq6tPIg058pjqi
Tx5gt/HpzsVAXc+tG9qavsVm+EWxgwvkJ40UdMTM3pjeOMo3/rQO8J+h1adYUrjt =nqgX
PGSXxQBcZjZb0HR9apDBRWvy4u4zLIyIpt6i5+LusY6PoAwSuQINBGU/XpcBEADw
UaYPkne0zUE4zWeInFklN0Jq7MS3ESgEvRG6oYGRHxTb2xEzoPC7ONTs/CxwWcFo
bJT55TvcbAaQfejMV8W2WViFCuE/+SQ37SsQ7leL6dH7gkIVGqt/eclcAA9+KSxR
Eynx6vV1Q5jb97VlKgu1L6fNSK9XwJ8T6mCP/li0bA8L4pgcsBOQqQwQsKttbhQ7
TpVY6pCy+vqSK9e4lBAt0QAykGUAOZcUbjkOY8pDOwFZSC9PDIbLctfSfKzxyCl7
H0EfyGGvTVCYbI/jr2w3dw2u0W67xTpECyIIP/yaUG4PcRARTlIXSVzuQIgIBmT7
ASWZeeKYSLRM5ixvPqT3SW16JxfvSsD0Y17Ijvof5VI5i9amRHBokCBR3OsAzWj1
IcBKr5xnqcPzk5mKhCQNYzr5D6x/ah6fSomecaZSv3eQkgLQ8h2CtdGu2I195FrV
n8fQuZETLIQygWJWK+fPW9WTzk6rpJ/eW1TC+YPO64k62ynZCnr7HpKef/Ipq4ji
5/gJ//ApRyHivoHCCDkPDj2TLuXCWcAuKjgYhWgYukt5/3fBagOangqXKHdk9/ac
HRT3W7dsdFSjub4PtAn3xzIq2osvOODuzMZ/rSkhY1ZGxp1hBPdtKdzKeqScCM5X
cEK/Hr+K+yemP/9CNhEe2+JQBxDUMEHpQLqrbcUizQARAQABiQI8BBgBCgAmFiEE
JLJZLoAaWqqGZsi6fDxyfwUJBVAFAmU/XpcCGwwFCQWjmoAACgkQfDxyfwUJBVDO
6Q/+IvGM7FP5Sk9H/9ymsNVIuQ/ikXlubDfpTTuO+qlL78pNezGKJCV599Fps6k0
tx34rW55q/K32ecjAce0dL2Qz0DsMHDcuekRteSLjpAMIIjqeIaKOUs4R4Baqehz
Aw1NPTnBVWAK07cNA/YyhIJyoelcMKG87Kvl3nGNgtUmGVy0gxKiZxXMavKOSc+7
RiP9OQL/hz6bzplZBQMeowdAi6IR6e9vrb8Xhzk4khFk8SfvTB112SAm4lTd4d0e
ue6EKSMIL3cZAZsaPTdmqizrr33LqGY94PdTkhS/y6PBlhUIK4ObyAl+undHpYt9
5l4r1aZNUGkLOwODd9WFLCY6TqmnwMCrX8N6/fJGQWCAITIYrpz1v5rQJEGdyXx7
lDRBAjM2JL+4TFA/eH4Qsmzc/Qws/7oVaE1SvLT2feWdaSvWFj6/UzQYDFt92/+2
wI3U+r9VATP4JOperJp+1aV04dbDoeH6C3DFweMbgwE6s7mggJDeWHaEbBDnxwOB
lhIr84aAn0GCAWXUJwzS4YLT9NEIjXO3/jL7t59JlAxmQejtUL/cwVunaFw16Olb
4HFW7ApO6cW9YrdMuGqxKVDnLKGpyGih6mwUer1uiwoWMskLTW6msG/WuKeg1247
LjcVvJLsk9FGykA03RGILakxtZ0TRlNBoWrq08iDTnymOqI=
=QPTf
-----END PGP PUBLIC KEY BLOCK----- -----END PGP PUBLIC KEY BLOCK-----
``` ```
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-client" name = "nym-client"
version = "1.1.46" version = "1.1.42"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"] authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client" description = "Implementation of the Nym Client"
edition = "2021" edition = "2021"
+28 -1
View File
@@ -3,10 +3,13 @@
use crate::commands::try_load_current_config; use crate::commands::try_load_current_config;
use crate::{ use crate::{
client::SocketClient, client::{config::Config, SocketClient},
commands::{override_config, OverrideConfig}, commands::{override_config, OverrideConfig},
error::ClientError,
}; };
use clap::Args; use clap::Args;
use log::*;
use nym_bin_common::version_checker::is_minor_version_compatible;
use nym_client_core::cli_helpers::client_run::CommonClientRunArgs; use nym_client_core::cli_helpers::client_run::CommonClientRunArgs;
use std::error::Error; use std::error::Error;
use std::net::IpAddr; use std::net::IpAddr;
@@ -45,12 +48,36 @@ impl From<Run> for OverrideConfig {
} }
} }
// this only checks compatibility between config the binary. It does not take into consideration
// network version. It might do so in the future.
fn version_check(cfg: &Config) -> bool {
let binary_version = env!("CARGO_PKG_VERSION");
let config_version = &cfg.base.client.version;
if binary_version == config_version {
true
} else {
warn!("The native-client binary has different version than what is specified in config file! {} and {}", binary_version, config_version);
if is_minor_version_compatible(binary_version, config_version) {
info!("but they are still semver compatible. However, consider running the `upgrade` command");
true
} else {
error!("and they are semver incompatible! - please run the `upgrade` command before attempting `run` again");
false
}
}
}
pub(crate) async fn execute(args: Run) -> Result<(), Box<dyn Error + Send + Sync>> { pub(crate) async fn execute(args: Run) -> Result<(), Box<dyn Error + Send + Sync>> {
eprintln!("Starting client {}...", args.common_args.id); eprintln!("Starting client {}...", args.common_args.id);
let mut config = try_load_current_config(&args.common_args.id).await?; let mut config = try_load_current_config(&args.common_args.id).await?;
config = override_config(config, OverrideConfig::from(args.clone())); config = override_config(config, OverrideConfig::from(args.clone()));
if !version_check(&config) {
error!("failed the local version check");
return Err(Box::new(ClientError::FailedLocalVersionCheck));
}
SocketClient::new(config, args.common_args.custom_mixnet) SocketClient::new(config, args.common_args.custom_mixnet)
.run_socket_forever() .run_socket_forever()
.await .await
+3
View File
@@ -17,6 +17,9 @@ pub enum ClientError {
#[error("Failed to validate the loaded config")] #[error("Failed to validate the loaded config")]
ConfigValidationFailure, ConfigValidationFailure,
#[error("Failed local version check, client and config mismatch")]
FailedLocalVersionCheck,
#[error("Attempted to start the client in invalid socket mode")] #[error("Attempted to start the client in invalid socket mode")]
InvalidSocketMode, InvalidSocketMode,
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-socks5-client" name = "nym-socks5-client"
version = "1.1.46" version = "1.1.42"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"] authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address" description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021" edition = "2021"
+33 -1
View File
@@ -2,8 +2,14 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::commands::try_load_current_config; use crate::commands::try_load_current_config;
use crate::commands::{override_config, OverrideConfig}; use crate::config::Config;
use crate::{
commands::{override_config, OverrideConfig},
error::Socks5ClientError,
};
use clap::Args; use clap::Args;
use log::*;
use nym_bin_common::version_checker::is_minor_version_compatible;
use nym_client_core::cli_helpers::client_run::CommonClientRunArgs; use nym_client_core::cli_helpers::client_run::CommonClientRunArgs;
use nym_client_core::client::base_client::storage::OnDiskPersistent; use nym_client_core::client::base_client::storage::OnDiskPersistent;
use nym_client_core::client::topology_control::geo_aware_provider::CountryGroup; use nym_client_core::client::topology_control::geo_aware_provider::CountryGroup;
@@ -76,12 +82,38 @@ fn validate_country_group(s: &str) -> Result<CountryGroup, String> {
} }
} }
// this only checks compatibility between config the binary. It does not take into consideration
// network version. It might do so in the future.
fn version_check(cfg: &Config) -> bool {
let binary_version = env!("CARGO_PKG_VERSION");
let config_version = &cfg.core.base.client.version;
if binary_version == config_version {
true
} else {
warn!(
"The socks5-client binary has different version than what is specified in config file! {binary_version} and {config_version}",
);
if is_minor_version_compatible(binary_version, config_version) {
info!("but they are still semver compatible. However, consider running the `upgrade` command");
true
} else {
error!("and they are semver incompatible! - please run the `upgrade` command before attempting `run` again");
false
}
}
}
pub(crate) async fn execute(args: Run) -> Result<(), Box<dyn std::error::Error + Send + Sync>> { pub(crate) async fn execute(args: Run) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
eprintln!("Starting client {}...", args.common_args.id); eprintln!("Starting client {}...", args.common_args.id);
let mut config = try_load_current_config(&args.common_args.id).await?; let mut config = try_load_current_config(&args.common_args.id).await?;
config = override_config(config, OverrideConfig::from(args.clone())); config = override_config(config, OverrideConfig::from(args.clone()));
if !version_check(&config) {
error!("failed the local version check");
return Err(Box::new(Socks5ClientError::FailedLocalVersionCheck));
}
let storage = let storage =
OnDiskPersistent::from_paths(config.storage_paths.common_paths, &config.core.base.debug) OnDiskPersistent::from_paths(config.storage_paths.common_paths, &config.core.base.debug)
.await?; .await?;
+3
View File
@@ -14,6 +14,9 @@ pub enum Socks5ClientError {
#[error("Failed to validate the loaded config")] #[error("Failed to validate the loaded config")]
ConfigValidationFailure, ConfigValidationFailure,
#[error("Failed local version check, client and config mismatch")]
FailedLocalVersionCheck,
#[error("Fail to bind address")] #[error("Fail to bind address")]
FailToBindAddress, FailToBindAddress,
@@ -22,7 +22,4 @@ pub enum Error {
#[error("conversion: {0}")] #[error("conversion: {0}")]
Conversion(String), Conversion(String),
#[error("failed to serialize response packet: {source}")]
FailedToSerializeResponsePacket { source: Box<bincode::ErrorKind> },
} }
-2
View File
@@ -1,14 +1,12 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net> // Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
pub mod traits;
pub mod v1; pub mod v1;
pub mod v2; pub mod v2;
pub mod v3; pub mod v3;
pub mod v4; pub mod v4;
mod error; mod error;
mod util;
pub use error::Error; pub use error::Error;
pub use v4 as latest; pub use v4 as latest;
-343
View File
@@ -1,343 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_credentials_interface::CredentialSpendingData;
use nym_crypto::asymmetric::x25519::PrivateKey;
use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
use nym_sphinx::addressing::clients::Recipient;
use nym_wireguard_types::PeerPublicKey;
use crate::{
v1, v2, v3,
v4::{self, registration::IpPair},
Error,
};
#[derive(Copy, Clone, Debug)]
pub enum AuthenticatorVersion {
V1,
V2,
V3,
V4,
UNKNOWN,
}
impl From<Protocol> for AuthenticatorVersion {
fn from(value: Protocol) -> Self {
if value.service_provider_type != ServiceProviderType::Authenticator {
AuthenticatorVersion::UNKNOWN
} else if value.version == v1::VERSION {
AuthenticatorVersion::V1
} else if value.version == v2::VERSION {
AuthenticatorVersion::V2
} else if value.version == v3::VERSION {
AuthenticatorVersion::V3
} else if value.version == v4::VERSION {
AuthenticatorVersion::V4
} else {
AuthenticatorVersion::UNKNOWN
}
}
}
pub trait InitMessage {
fn pub_key(&self) -> PeerPublicKey;
}
impl InitMessage for v1::registration::InitMessage {
fn pub_key(&self) -> PeerPublicKey {
self.pub_key
}
}
impl InitMessage for v2::registration::InitMessage {
fn pub_key(&self) -> PeerPublicKey {
self.pub_key
}
}
impl InitMessage for v3::registration::InitMessage {
fn pub_key(&self) -> PeerPublicKey {
self.pub_key
}
}
impl InitMessage for v4::registration::InitMessage {
fn pub_key(&self) -> PeerPublicKey {
self.pub_key
}
}
pub trait FinalMessage {
fn pub_key(&self) -> PeerPublicKey;
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error>;
fn private_ips(&self) -> IpPair;
fn credential(&self) -> Option<CredentialSpendingData>;
}
impl FinalMessage for v1::GatewayClient {
fn pub_key(&self) -> PeerPublicKey {
self.pub_key
}
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
self.verify(private_key, nonce)
}
fn private_ips(&self) -> IpPair {
self.private_ip.into()
}
fn credential(&self) -> Option<CredentialSpendingData> {
None
}
}
impl FinalMessage for v2::registration::FinalMessage {
fn pub_key(&self) -> PeerPublicKey {
self.gateway_client.pub_key
}
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
self.gateway_client.verify(private_key, nonce)
}
fn private_ips(&self) -> IpPair {
self.gateway_client.private_ip.into()
}
fn credential(&self) -> Option<CredentialSpendingData> {
self.credential.clone()
}
}
impl FinalMessage for v3::registration::FinalMessage {
fn pub_key(&self) -> PeerPublicKey {
self.gateway_client.pub_key
}
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
self.gateway_client.verify(private_key, nonce)
}
fn private_ips(&self) -> IpPair {
self.gateway_client.private_ip.into()
}
fn credential(&self) -> Option<CredentialSpendingData> {
self.credential.clone()
}
}
impl FinalMessage for v4::registration::FinalMessage {
fn pub_key(&self) -> PeerPublicKey {
self.gateway_client.pub_key
}
fn verify(&self, private_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
self.gateway_client.verify(private_key, nonce)
}
fn private_ips(&self) -> IpPair {
self.gateway_client.private_ips
}
fn credential(&self) -> Option<CredentialSpendingData> {
self.credential.clone()
}
}
pub trait QueryBandwidthMessage {
fn pub_key(&self) -> PeerPublicKey;
}
impl QueryBandwidthMessage for PeerPublicKey {
fn pub_key(&self) -> PeerPublicKey {
*self
}
}
pub trait TopUpMessage {
fn pub_key(&self) -> PeerPublicKey;
fn credential(&self) -> CredentialSpendingData;
}
impl TopUpMessage for v3::topup::TopUpMessage {
fn pub_key(&self) -> PeerPublicKey {
self.pub_key
}
fn credential(&self) -> CredentialSpendingData {
self.credential.clone()
}
}
impl TopUpMessage for v4::topup::TopUpMessage {
fn pub_key(&self) -> PeerPublicKey {
self.pub_key
}
fn credential(&self) -> CredentialSpendingData {
self.credential.clone()
}
}
pub enum AuthenticatorRequest {
Initial {
msg: Box<dyn InitMessage + Send + Sync + 'static>,
protocol: Protocol,
reply_to: Recipient,
request_id: u64,
},
Final {
msg: Box<dyn FinalMessage + Send + Sync + 'static>,
protocol: Protocol,
reply_to: Recipient,
request_id: u64,
},
QueryBandwidth {
msg: Box<dyn QueryBandwidthMessage + Send + Sync + 'static>,
protocol: Protocol,
reply_to: Recipient,
request_id: u64,
},
TopUpBandwidth {
msg: Box<dyn TopUpMessage + Send + Sync + 'static>,
protocol: Protocol,
reply_to: Recipient,
request_id: u64,
},
}
impl From<v1::request::AuthenticatorRequest> for AuthenticatorRequest {
fn from(value: v1::request::AuthenticatorRequest) -> Self {
match value.data {
v1::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
msg: Box::new(init_message),
protocol: Protocol {
version: value.version,
service_provider_type: ServiceProviderType::Authenticator,
},
reply_to: value.reply_to,
request_id: value.request_id,
},
v1::request::AuthenticatorRequestData::Final(gateway_client) => Self::Final {
msg: Box::new(gateway_client),
protocol: Protocol {
version: value.version,
service_provider_type: ServiceProviderType::Authenticator,
},
reply_to: value.reply_to,
request_id: value.request_id,
},
v1::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
Self::QueryBandwidth {
msg: Box::new(peer_public_key),
protocol: Protocol {
version: value.version,
service_provider_type: ServiceProviderType::Authenticator,
},
reply_to: value.reply_to,
request_id: value.request_id,
}
}
}
}
}
impl From<v2::request::AuthenticatorRequest> for AuthenticatorRequest {
fn from(value: v2::request::AuthenticatorRequest) -> Self {
match value.data {
v2::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
msg: Box::new(init_message),
protocol: value.protocol,
reply_to: value.reply_to,
request_id: value.request_id,
},
v2::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
msg: final_message,
protocol: value.protocol,
reply_to: value.reply_to,
request_id: value.request_id,
},
v2::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
Self::QueryBandwidth {
msg: Box::new(peer_public_key),
protocol: value.protocol,
reply_to: value.reply_to,
request_id: value.request_id,
}
}
}
}
}
impl From<v3::request::AuthenticatorRequest> for AuthenticatorRequest {
fn from(value: v3::request::AuthenticatorRequest) -> Self {
match value.data {
v3::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
msg: Box::new(init_message),
protocol: value.protocol,
reply_to: value.reply_to,
request_id: value.request_id,
},
v3::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
msg: final_message,
protocol: value.protocol,
reply_to: value.reply_to,
request_id: value.request_id,
},
v3::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
Self::QueryBandwidth {
msg: Box::new(peer_public_key),
protocol: value.protocol,
reply_to: value.reply_to,
request_id: value.request_id,
}
}
v3::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => {
Self::TopUpBandwidth {
msg: top_up_message,
protocol: value.protocol,
reply_to: value.reply_to,
request_id: value.request_id,
}
}
}
}
}
impl From<v4::request::AuthenticatorRequest> for AuthenticatorRequest {
fn from(value: v4::request::AuthenticatorRequest) -> Self {
match value.data {
v4::request::AuthenticatorRequestData::Initial(init_message) => Self::Initial {
msg: Box::new(init_message),
protocol: value.protocol,
reply_to: value.reply_to,
request_id: value.request_id,
},
v4::request::AuthenticatorRequestData::Final(final_message) => Self::Final {
msg: final_message,
protocol: value.protocol,
reply_to: value.reply_to,
request_id: value.request_id,
},
v4::request::AuthenticatorRequestData::QueryBandwidth(peer_public_key) => {
Self::QueryBandwidth {
msg: Box::new(peer_public_key),
protocol: value.protocol,
reply_to: value.reply_to,
request_id: value.request_id,
}
}
v4::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => {
Self::TopUpBandwidth {
msg: top_up_message,
protocol: value.protocol,
reply_to: value.reply_to,
request_id: value.request_id,
}
}
}
}
}
-71
View File
@@ -1,71 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#[cfg(test)]
pub(crate) mod tests {
pub(crate) const CREDENTIAL_BYTES: [u8; 1245] = [
0, 0, 4, 133, 96, 179, 223, 185, 136, 23, 213, 166, 59, 203, 66, 69, 209, 181, 227, 254,
16, 102, 98, 237, 59, 119, 170, 111, 31, 194, 51, 59, 120, 17, 115, 229, 79, 91, 11, 139,
154, 2, 212, 23, 68, 70, 167, 3, 240, 54, 224, 171, 221, 1, 69, 48, 60, 118, 119, 249, 123,
35, 172, 227, 131, 96, 232, 209, 187, 123, 4, 197, 102, 90, 96, 45, 125, 135, 140, 99, 1,
151, 17, 131, 143, 157, 97, 107, 139, 232, 212, 87, 14, 115, 253, 255, 166, 167, 186, 43,
90, 96, 173, 105, 120, 40, 10, 163, 250, 224, 214, 200, 178, 4, 160, 16, 130, 59, 76, 193,
39, 240, 3, 101, 141, 209, 183, 226, 186, 207, 56, 210, 187, 7, 164, 240, 164, 205, 37, 81,
184, 214, 193, 195, 90, 205, 238, 225, 195, 104, 12, 123, 203, 57, 233, 243, 215, 145, 195,
196, 57, 38, 125, 172, 18, 47, 63, 165, 110, 219, 180, 40, 58, 116, 92, 254, 160, 98, 48,
92, 254, 232, 107, 184, 80, 234, 60, 160, 235, 249, 76, 41, 38, 165, 28, 40, 136, 74, 48,
166, 50, 245, 23, 201, 140, 101, 79, 93, 235, 128, 186, 146, 126, 180, 134, 43, 13, 186,
19, 195, 48, 168, 201, 29, 216, 95, 176, 198, 132, 188, 64, 39, 212, 150, 32, 52, 53, 38,
228, 199, 122, 226, 217, 75, 40, 191, 151, 48, 164, 242, 177, 79, 14, 122, 105, 151, 85,
88, 199, 162, 17, 96, 103, 83, 178, 128, 9, 24, 30, 74, 108, 241, 85, 240, 166, 97, 241,
85, 199, 11, 198, 226, 234, 70, 107, 145, 28, 208, 114, 51, 12, 234, 108, 101, 202, 112,
48, 185, 22, 159, 67, 109, 49, 27, 149, 90, 109, 32, 226, 112, 7, 201, 208, 209, 104, 31,
97, 134, 204, 145, 27, 181, 206, 181, 106, 32, 110, 136, 115, 249, 201, 111, 5, 245, 203,
71, 121, 169, 126, 151, 178, 236, 59, 221, 195, 48, 135, 115, 6, 50, 227, 74, 97, 107, 107,
213, 90, 2, 203, 154, 138, 47, 128, 52, 134, 128, 224, 51, 65, 240, 90, 8, 55, 175, 180,
178, 204, 206, 168, 110, 51, 57, 189, 169, 48, 169, 136, 121, 99, 51, 170, 178, 214, 74, 1,
96, 151, 167, 25, 173, 180, 171, 155, 10, 55, 142, 234, 190, 113, 90, 79, 80, 244, 71, 166,
30, 235, 113, 150, 133, 1, 218, 17, 109, 111, 223, 24, 216, 177, 41, 2, 204, 65, 221, 212,
207, 236, 144, 6, 65, 224, 55, 42, 1, 1, 161, 134, 118, 127, 111, 220, 110, 127, 240, 71,
223, 129, 12, 93, 20, 220, 60, 56, 71, 146, 184, 95, 132, 69, 28, 56, 53, 192, 213, 22,
119, 230, 152, 225, 182, 188, 163, 219, 37, 175, 247, 73, 14, 247, 38, 72, 243, 1, 48, 131,
59, 8, 13, 96, 143, 185, 127, 241, 161, 217, 24, 149, 193, 40, 16, 30, 202, 151, 28, 119,
240, 153, 101, 156, 61, 193, 72, 245, 199, 181, 12, 231, 65, 166, 67, 142, 121, 207, 202,
58, 197, 113, 188, 248, 42, 124, 105, 48, 161, 241, 55, 209, 36, 194, 27, 63, 233, 144,
189, 85, 117, 234, 9, 139, 46, 31, 206, 114, 95, 131, 29, 240, 13, 81, 142, 140, 133, 33,
30, 41, 141, 37, 80, 217, 95, 221, 76, 115, 86, 201, 165, 51, 252, 9, 28, 209, 1, 48, 150,
74, 248, 212, 187, 222, 66, 210, 3, 200, 19, 217, 171, 184, 42, 148, 53, 150, 57, 50, 6,
227, 227, 62, 49, 42, 148, 148, 157, 82, 191, 58, 24, 34, 56, 98, 120, 89, 105, 176, 85,
15, 253, 241, 41, 153, 195, 136, 1, 48, 142, 126, 213, 101, 223, 79, 133, 230, 105, 38,
161, 149, 2, 21, 136, 150, 42, 72, 218, 85, 146, 63, 223, 58, 108, 186, 183, 248, 62, 20,
47, 34, 113, 160, 177, 204, 181, 16, 24, 212, 224, 35, 84, 51, 168, 56, 136, 11, 1, 48,
135, 242, 62, 149, 230, 178, 32, 224, 119, 26, 234, 163, 237, 224, 114, 95, 112, 140, 170,
150, 96, 125, 136, 221, 180, 78, 18, 11, 12, 184, 2, 198, 217, 119, 43, 69, 4, 172, 109,
55, 183, 40, 131, 172, 161, 88, 183, 101, 1, 48, 173, 216, 22, 73, 42, 255, 211, 93, 249,
87, 159, 115, 61, 91, 55, 130, 17, 216, 60, 34, 122, 55, 8, 244, 244, 153, 151, 57, 5, 144,
178, 55, 249, 64, 211, 168, 34, 148, 56, 89, 92, 203, 70, 124, 219, 152, 253, 165, 0, 32,
203, 116, 63, 7, 240, 222, 82, 86, 11, 149, 167, 72, 224, 55, 190, 66, 201, 65, 168, 184,
96, 47, 194, 241, 168, 124, 7, 74, 214, 250, 37, 76, 32, 218, 69, 122, 103, 215, 145, 169,
24, 212, 229, 168, 106, 10, 144, 31, 13, 25, 178, 242, 250, 106, 159, 40, 48, 163, 165, 61,
130, 57, 146, 4, 73, 32, 254, 233, 125, 135, 212, 29, 111, 4, 177, 114, 15, 210, 170, 82,
108, 110, 62, 166, 81, 209, 106, 176, 156, 14, 133, 242, 60, 127, 120, 242, 28, 97, 0, 1,
32, 103, 93, 109, 89, 240, 91, 1, 84, 150, 50, 206, 157, 203, 49, 220, 120, 234, 175, 234,
150, 126, 225, 94, 163, 164, 199, 138, 114, 62, 99, 106, 112, 1, 32, 171, 40, 220, 82, 241,
203, 76, 146, 111, 139, 182, 179, 237, 182, 115, 75, 128, 201, 107, 43, 214, 0, 135, 217,
160, 68, 150, 232, 144, 114, 237, 98, 32, 30, 134, 232, 59, 93, 163, 253, 244, 13, 202, 52,
147, 168, 83, 121, 123, 95, 21, 210, 209, 225, 223, 143, 49, 10, 205, 238, 1, 22, 83, 81,
70, 1, 32, 26, 76, 6, 234, 160, 50, 139, 102, 161, 232, 155, 106, 130, 171, 226, 210, 233,
178, 85, 247, 71, 123, 55, 53, 46, 67, 148, 137, 156, 207, 208, 107, 1, 32, 102, 31, 4, 98,
110, 156, 144, 61, 229, 140, 198, 84, 196, 238, 128, 35, 131, 182, 137, 125, 241, 95, 69,
131, 170, 27, 2, 144, 75, 72, 242, 102, 3, 32, 121, 80, 45, 173, 56, 65, 218, 27, 40, 251,
197, 32, 169, 104, 123, 110, 90, 78, 153, 166, 38, 9, 129, 228, 99, 8, 1, 116, 142, 233,
162, 69, 32, 216, 169, 159, 116, 95, 12, 63, 176, 195, 6, 183, 123, 135, 75, 61, 112, 106,
83, 235, 176, 41, 27, 248, 48, 71, 165, 170, 12, 92, 103, 103, 81, 32, 58, 74, 75, 145,
192, 94, 153, 69, 80, 128, 241, 3, 16, 117, 192, 86, 161, 103, 44, 174, 211, 196, 182, 124,
55, 11, 107, 142, 49, 88, 6, 41, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 37, 139, 240, 0, 0,
0, 0, 0, 0, 0, 1,
];
pub(crate) const RECIPIENT: &str = "CytBseW6yFXUMzz4SGAKdNLGR7q3sJLLYxyBGvutNEQV.4QXYyEVc5fUDjmmi8PrHN9tdUFV4PCvSJE1278cHyvoe@4sBbL1ngf1vtNqykydQKTFh26sQCw888GpUqvPvyNB4f";
}
@@ -29,7 +29,7 @@ pub type Taken = Option<SystemTime>;
pub const BANDWIDTH_CAP_PER_DAY: u64 = 1024 * 1024 * 1024; // 1 GB pub const BANDWIDTH_CAP_PER_DAY: u64 = 1024 * 1024 * 1024; // 1 GB
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct InitMessage { pub struct InitMessage {
/// Base64 encoded x25519 public key /// Base64 encoded x25519 public key
pub pub_key: PeerPublicKey, pub pub_key: PeerPublicKey,
@@ -41,7 +41,7 @@ impl InitMessage {
} }
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct FinalMessage { pub struct FinalMessage {
/// Gateway client data /// Gateway client data
pub gateway_client: GatewayClient, pub gateway_client: GatewayClient,
@@ -50,28 +50,28 @@ pub struct FinalMessage {
pub credential: Option<CredentialSpendingData>, pub credential: Option<CredentialSpendingData>,
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RegistrationData { pub struct RegistrationData {
pub nonce: u64, pub nonce: u64,
pub gateway_data: GatewayClient, pub gateway_data: GatewayClient,
pub wg_port: u16, pub wg_port: u16,
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RegistredData { pub struct RegistredData {
pub pub_key: PeerPublicKey, pub pub_key: PeerPublicKey,
pub private_ip: IpAddr, pub private_ip: IpAddr,
pub wg_port: u16, pub wg_port: u16,
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RemainingBandwidthData { pub struct RemainingBandwidthData {
pub available_bandwidth: i64, pub available_bandwidth: i64,
} }
/// Client that wants to register sends its PublicKey bytes mac digest encrypted with a DH shared secret. /// Client that wants to register sends its PublicKey bytes mac digest encrypted with a DH shared secret.
/// Gateway/Nym node can then verify pub_key payload using the same process /// Gateway/Nym node can then verify pub_key payload using the same process
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct GatewayClient { pub struct GatewayClient {
/// Base64 encoded x25519 public key /// Base64 encoded x25519 public key
pub pub_key: PeerPublicKey, pub pub_key: PeerPublicKey,
@@ -147,7 +147,7 @@ impl GatewayClient {
// TODO: change the inner type into generic array of size HmacSha256::OutputSize // TODO: change the inner type into generic array of size HmacSha256::OutputSize
// TODO2: rely on our internal crypto/hmac // TODO2: rely on our internal crypto/hmac
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone)]
pub struct ClientMac(Vec<u8>); pub struct ClientMac(Vec<u8>);
impl fmt::Display for ClientMac { impl fmt::Display for ClientMac {
@@ -17,6 +17,11 @@ fn generate_random() -> u64 {
rng.next_u64() rng.next_u64()
} }
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct QueryMessage {
pub pub_key: PeerPublicKey,
}
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AuthenticatorRequest { pub struct AuthenticatorRequest {
pub protocol: Protocol, pub protocol: Protocol,
@@ -65,7 +70,7 @@ impl AuthenticatorRequest {
) )
} }
pub fn new_query_request(peer_public_key: PeerPublicKey, reply_to: Recipient) -> (Self, u64) { pub fn new_query_request(query_message: QueryMessage, reply_to: Recipient) -> (Self, u64) {
let request_id = generate_random(); let request_id = generate_random();
( (
Self { Self {
@@ -73,7 +78,7 @@ impl AuthenticatorRequest {
service_provider_type: ServiceProviderType::Authenticator, service_provider_type: ServiceProviderType::Authenticator,
version: VERSION, version: VERSION,
}, },
data: AuthenticatorRequestData::QueryBandwidth(peer_public_key), data: AuthenticatorRequestData::QueryBandwidth(query_message.pub_key),
reply_to, reply_to,
request_id, request_id,
}, },
@@ -87,7 +92,7 @@ impl AuthenticatorRequest {
} }
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum AuthenticatorRequestData { pub enum AuthenticatorRequestData {
Initial(InitMessage), Initial(InitMessage),
Final(Box<FinalMessage>), Final(Box<FinalMessage>),
@@ -100,28 +100,28 @@ impl AuthenticatorResponse {
} }
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum AuthenticatorResponseData { pub enum AuthenticatorResponseData {
PendingRegistration(PendingRegistrationResponse), PendingRegistration(PendingRegistrationResponse),
Registered(RegisteredResponse), Registered(RegisteredResponse),
RemainingBandwidth(RemainingBandwidthResponse), RemainingBandwidth(RemainingBandwidthResponse),
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PendingRegistrationResponse { pub struct PendingRegistrationResponse {
pub request_id: u64, pub request_id: u64,
pub reply_to: Recipient, pub reply_to: Recipient,
pub reply: RegistrationData, pub reply: RegistrationData,
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RegisteredResponse { pub struct RegisteredResponse {
pub request_id: u64, pub request_id: u64,
pub reply_to: Recipient, pub reply_to: Recipient,
pub reply: RegistredData, pub reply: RegistredData,
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RemainingBandwidthResponse { pub struct RemainingBandwidthResponse {
pub request_id: u64, pub request_id: u64,
pub reply_to: Recipient, pub reply_to: Recipient,
@@ -19,24 +19,6 @@ impl From<v2::request::AuthenticatorRequest> for v3::request::AuthenticatorReque
} }
} }
impl TryFrom<v3::request::AuthenticatorRequest> for v2::request::AuthenticatorRequest {
type Error = crate::Error;
fn try_from(
authenticator_request: v3::request::AuthenticatorRequest,
) -> Result<Self, Self::Error> {
Ok(Self {
protocol: Protocol {
version: 2,
service_provider_type: ServiceProviderType::Authenticator,
},
data: authenticator_request.data.try_into()?,
reply_to: authenticator_request.reply_to,
request_id: authenticator_request.request_id,
})
}
}
impl From<v2::request::AuthenticatorRequestData> for v3::request::AuthenticatorRequestData { impl From<v2::request::AuthenticatorRequestData> for v3::request::AuthenticatorRequestData {
fn from(authenticator_request_data: v2::request::AuthenticatorRequestData) -> Self { fn from(authenticator_request_data: v2::request::AuthenticatorRequestData) -> Self {
match authenticator_request_data { match authenticator_request_data {
@@ -53,29 +35,6 @@ impl From<v2::request::AuthenticatorRequestData> for v3::request::AuthenticatorR
} }
} }
impl TryFrom<v3::request::AuthenticatorRequestData> for v2::request::AuthenticatorRequestData {
type Error = crate::Error;
fn try_from(
authenticator_request_data: v3::request::AuthenticatorRequestData,
) -> Result<Self, Self::Error> {
match authenticator_request_data {
v3::request::AuthenticatorRequestData::Initial(init_msg) => Ok(
v2::request::AuthenticatorRequestData::Initial(init_msg.into()),
),
v3::request::AuthenticatorRequestData::Final(gw_client) => Ok(
v2::request::AuthenticatorRequestData::Final(gw_client.into()),
),
v3::request::AuthenticatorRequestData::QueryBandwidth(pub_key) => Ok(
v2::request::AuthenticatorRequestData::QueryBandwidth(pub_key),
),
v3::request::AuthenticatorRequestData::TopUpBandwidth(_) => Err(
Self::Error::Conversion("no top up bandwidth variant in v2".to_string()),
),
}
}
}
impl From<v2::registration::InitMessage> for v3::registration::InitMessage { impl From<v2::registration::InitMessage> for v3::registration::InitMessage {
fn from(init_msg: v2::registration::InitMessage) -> Self { fn from(init_msg: v2::registration::InitMessage) -> Self {
Self { Self {
@@ -84,14 +43,6 @@ impl From<v2::registration::InitMessage> for v3::registration::InitMessage {
} }
} }
impl From<v3::registration::InitMessage> for v2::registration::InitMessage {
fn from(init_msg: v3::registration::InitMessage) -> Self {
Self {
pub_key: init_msg.pub_key,
}
}
}
impl From<Box<v2::registration::FinalMessage>> for Box<v3::registration::FinalMessage> { impl From<Box<v2::registration::FinalMessage>> for Box<v3::registration::FinalMessage> {
fn from(gw_client: Box<v2::registration::FinalMessage>) -> Self { fn from(gw_client: Box<v2::registration::FinalMessage>) -> Self {
Box::new(v3::registration::FinalMessage { Box::new(v3::registration::FinalMessage {
@@ -101,15 +52,6 @@ impl From<Box<v2::registration::FinalMessage>> for Box<v3::registration::FinalMe
} }
} }
impl From<Box<v3::registration::FinalMessage>> for Box<v2::registration::FinalMessage> {
fn from(gw_client: Box<v3::registration::FinalMessage>) -> Self {
Box::new(v2::registration::FinalMessage {
gateway_client: gw_client.gateway_client.into(),
credential: gw_client.credential,
})
}
}
impl From<v2::registration::GatewayClient> for v3::registration::GatewayClient { impl From<v2::registration::GatewayClient> for v3::registration::GatewayClient {
fn from(gw_client: v2::registration::GatewayClient) -> Self { fn from(gw_client: v2::registration::GatewayClient) -> Self {
Self { Self {
@@ -151,27 +93,11 @@ impl TryFrom<v3::response::AuthenticatorResponse> for v2::response::Authenticato
Ok(Self { Ok(Self {
data: authenticator_response.data.try_into()?, data: authenticator_response.data.try_into()?,
reply_to: authenticator_response.reply_to, reply_to: authenticator_response.reply_to,
protocol: Protocol { protocol: authenticator_response.protocol,
version: 2,
service_provider_type: authenticator_response.protocol.service_provider_type,
},
}) })
} }
} }
impl From<v2::response::AuthenticatorResponse> for v3::response::AuthenticatorResponse {
fn from(value: v2::response::AuthenticatorResponse) -> Self {
Self {
protocol: Protocol {
version: 3,
service_provider_type: value.protocol.service_provider_type,
},
data: value.data.into(),
reply_to: value.reply_to,
}
}
}
impl TryFrom<v3::response::AuthenticatorResponseData> for v2::response::AuthenticatorResponseData { impl TryFrom<v3::response::AuthenticatorResponseData> for v2::response::AuthenticatorResponseData {
type Error = crate::Error; type Error = crate::Error;
@@ -203,22 +129,6 @@ impl TryFrom<v3::response::AuthenticatorResponseData> for v2::response::Authenti
} }
} }
impl From<v2::response::AuthenticatorResponseData> for v3::response::AuthenticatorResponseData {
fn from(value: v2::response::AuthenticatorResponseData) -> Self {
match value {
v2::response::AuthenticatorResponseData::PendingRegistration(
pending_registration_response,
) => Self::PendingRegistration(pending_registration_response.into()),
v2::response::AuthenticatorResponseData::Registered(registered_response) => {
Self::Registered(registered_response.into())
}
v2::response::AuthenticatorResponseData::RemainingBandwidth(
remaining_bandwidth_response,
) => Self::RemainingBandwidth(remaining_bandwidth_response.into()),
}
}
}
impl From<v3::response::PendingRegistrationResponse> for v2::response::PendingRegistrationResponse { impl From<v3::response::PendingRegistrationResponse> for v2::response::PendingRegistrationResponse {
fn from(value: v3::response::PendingRegistrationResponse) -> Self { fn from(value: v3::response::PendingRegistrationResponse) -> Self {
Self { Self {
@@ -229,16 +139,6 @@ impl From<v3::response::PendingRegistrationResponse> for v2::response::PendingRe
} }
} }
impl From<v2::response::PendingRegistrationResponse> for v3::response::PendingRegistrationResponse {
fn from(value: v2::response::PendingRegistrationResponse) -> Self {
Self {
request_id: value.request_id,
reply_to: value.reply_to,
reply: value.reply.into(),
}
}
}
impl From<v3::response::RegisteredResponse> for v2::response::RegisteredResponse { impl From<v3::response::RegisteredResponse> for v2::response::RegisteredResponse {
fn from(value: v3::response::RegisteredResponse) -> Self { fn from(value: v3::response::RegisteredResponse) -> Self {
Self { Self {
@@ -249,16 +149,6 @@ impl From<v3::response::RegisteredResponse> for v2::response::RegisteredResponse
} }
} }
impl From<v2::response::RegisteredResponse> for v3::response::RegisteredResponse {
fn from(value: v2::response::RegisteredResponse) -> Self {
Self {
request_id: value.request_id,
reply_to: value.reply_to,
reply: value.reply.into(),
}
}
}
impl From<v3::response::RemainingBandwidthResponse> for v2::response::RemainingBandwidthResponse { impl From<v3::response::RemainingBandwidthResponse> for v2::response::RemainingBandwidthResponse {
fn from(value: v3::response::RemainingBandwidthResponse) -> Self { fn from(value: v3::response::RemainingBandwidthResponse) -> Self {
Self { Self {
@@ -269,16 +159,6 @@ impl From<v3::response::RemainingBandwidthResponse> for v2::response::RemainingB
} }
} }
impl From<v2::response::RemainingBandwidthResponse> for v3::response::RemainingBandwidthResponse {
fn from(value: v2::response::RemainingBandwidthResponse) -> Self {
Self {
request_id: value.request_id,
reply_to: value.reply_to,
reply: value.reply.map(Into::into),
}
}
}
impl From<v3::registration::RegistrationData> for v2::registration::RegistrationData { impl From<v3::registration::RegistrationData> for v2::registration::RegistrationData {
fn from(value: v3::registration::RegistrationData) -> Self { fn from(value: v3::registration::RegistrationData) -> Self {
Self { Self {
@@ -289,16 +169,6 @@ impl From<v3::registration::RegistrationData> for v2::registration::Registration
} }
} }
impl From<v2::registration::RegistrationData> for v3::registration::RegistrationData {
fn from(value: v2::registration::RegistrationData) -> Self {
Self {
nonce: value.nonce,
gateway_data: value.gateway_data.into(),
wg_port: value.wg_port,
}
}
}
impl From<v3::registration::RegistredData> for v2::registration::RegistredData { impl From<v3::registration::RegistredData> for v2::registration::RegistredData {
fn from(value: v3::registration::RegistredData) -> Self { fn from(value: v3::registration::RegistredData) -> Self {
Self { Self {
@@ -309,16 +179,6 @@ 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 {
Self {
pub_key: value.pub_key,
private_ip: value.private_ip,
wg_port: value.wg_port,
}
}
}
impl From<v3::registration::RemainingBandwidthData> for v2::registration::RemainingBandwidthData { impl From<v3::registration::RemainingBandwidthData> for v2::registration::RemainingBandwidthData {
fn from(value: v3::registration::RemainingBandwidthData) -> Self { fn from(value: v3::registration::RemainingBandwidthData) -> Self {
Self { Self {
@@ -326,519 +186,3 @@ impl From<v3::registration::RemainingBandwidthData> for v2::registration::Remain
} }
} }
} }
impl From<v2::registration::RemainingBandwidthData> for v3::registration::RemainingBandwidthData {
fn from(value: v2::registration::RemainingBandwidthData) -> Self {
Self {
available_bandwidth: value.available_bandwidth,
}
}
}
#[cfg(test)]
mod tests {
use std::{net::IpAddr, str::FromStr};
use nym_credentials_interface::CredentialSpendingData;
use nym_crypto::asymmetric::encryption::PrivateKey;
use nym_sphinx::addressing::Recipient;
use nym_wireguard_types::PeerPublicKey;
use x25519_dalek::PublicKey;
use super::*;
use crate::util::tests::{CREDENTIAL_BYTES, RECIPIENT};
#[test]
fn upgrade_initial_req() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) = v2::request::AuthenticatorRequest::new_initial_request(
v2::registration::InitMessage::new(pub_key),
reply_to,
);
let upgraded_msg = v3::request::AuthenticatorRequest::from(msg);
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v3::request::AuthenticatorRequestData::Initial(v3::registration::InitMessage {
pub_key
})
);
}
#[test]
fn downgrade_initial_req() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) = v3::request::AuthenticatorRequest::new_initial_request(
v3::registration::InitMessage::new(pub_key),
reply_to,
);
let downgraded_msg = v2::request::AuthenticatorRequest::try_from(msg).unwrap();
assert_eq!(
downgraded_msg.protocol,
Protocol {
version: 2,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
downgraded_msg.data,
v2::request::AuthenticatorRequestData::Initial(v2::registration::InitMessage {
pub_key
})
);
}
#[test]
fn upgrade_final_req() {
let mut rng = rand::thread_rng();
let local_secret = PrivateKey::new(&mut rng);
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
let private_ip = IpAddr::from_str("10.10.10.10").unwrap();
let nonce = 42;
let gateway_client = v2::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
private_ip,
nonce,
);
let credential = Some(CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap());
let final_message = v2::registration::FinalMessage {
gateway_client,
credential: credential.clone(),
};
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) =
v2::request::AuthenticatorRequest::new_final_request(final_message, reply_to);
let upgraded_msg = v3::request::AuthenticatorRequest::from(msg);
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v3::request::AuthenticatorRequestData::Final(Box::new(
v3::registration::FinalMessage {
gateway_client: v3::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
private_ip,
nonce,
),
credential
}
))
);
}
#[test]
fn downgrade_final_req() {
let mut rng = rand::thread_rng();
let local_secret = PrivateKey::new(&mut rng);
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
let private_ip = IpAddr::from_str("10.10.10.10").unwrap();
let nonce = 42;
let gateway_client = v3::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
private_ip,
nonce,
);
let credential = Some(CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap());
let final_message = v3::registration::FinalMessage {
gateway_client,
credential: credential.clone(),
};
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) =
v3::request::AuthenticatorRequest::new_final_request(final_message, reply_to);
let upgraded_msg = v2::request::AuthenticatorRequest::try_from(msg).unwrap();
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 2,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v2::request::AuthenticatorRequestData::Final(Box::new(
v2::registration::FinalMessage {
gateway_client: v2::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
private_ip,
nonce,
),
credential
}
))
);
}
#[test]
fn upgrade_query_req() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) = v2::request::AuthenticatorRequest::new_query_request(pub_key, reply_to);
let upgraded_msg = v3::request::AuthenticatorRequest::from(msg);
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v3::request::AuthenticatorRequestData::QueryBandwidth(pub_key)
);
}
#[test]
fn downgrade_query_req() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) = v3::request::AuthenticatorRequest::new_query_request(pub_key, reply_to);
let downgraded_msg = v2::request::AuthenticatorRequest::try_from(msg).unwrap();
assert_eq!(
downgraded_msg.protocol,
Protocol {
version: 2,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
downgraded_msg.data,
v2::request::AuthenticatorRequestData::QueryBandwidth(pub_key)
);
}
#[test]
fn downgrade_topup_req() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let credential = CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap();
let top_up_message = v3::topup::TopUpMessage {
pub_key,
credential,
};
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) =
v3::request::AuthenticatorRequest::new_topup_request(top_up_message, reply_to);
assert!(v2::request::AuthenticatorRequest::try_from(msg).is_err());
}
#[test]
fn upgrade_pending_reg_resp() {
let mut rng = rand::thread_rng();
let local_secret = PrivateKey::new(&mut rng);
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
let private_ip = IpAddr::from_str("10.10.10.10").unwrap();
let nonce = 42;
let wg_port = 51822;
let gateway_data = v2::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
private_ip,
nonce,
);
let registration_data = v2::registration::RegistrationData {
nonce,
gateway_data,
wg_port,
};
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v2::response::AuthenticatorResponse::new_pending_registration_success(
registration_data,
request_id,
reply_to,
);
let upgraded_msg = v3::response::AuthenticatorResponse::from(msg);
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v3::response::AuthenticatorResponseData::PendingRegistration(
v3::response::PendingRegistrationResponse {
request_id,
reply_to,
reply: v3::registration::RegistrationData {
nonce,
gateway_data: v3::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
private_ip,
nonce,
),
wg_port,
}
}
)
);
}
#[test]
fn downgrade_pending_reg_resp() {
let mut rng = rand::thread_rng();
let local_secret = PrivateKey::new(&mut rng);
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
let private_ip = IpAddr::from_str("10.10.10.10").unwrap();
let nonce = 42;
let wg_port = 51822;
let gateway_data = v3::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
private_ip,
nonce,
);
let registration_data = v3::registration::RegistrationData {
nonce,
gateway_data,
wg_port,
};
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v3::response::AuthenticatorResponse::new_pending_registration_success(
registration_data,
request_id,
reply_to,
);
let downgraded_msg = v2::response::AuthenticatorResponse::try_from(msg).unwrap();
assert_eq!(
downgraded_msg.protocol,
Protocol {
version: 2,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
downgraded_msg.data,
v2::response::AuthenticatorResponseData::PendingRegistration(
v2::response::PendingRegistrationResponse {
request_id,
reply_to,
reply: v2::registration::RegistrationData {
nonce,
gateway_data: v2::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
private_ip,
nonce,
),
wg_port,
}
}
)
);
}
#[test]
fn upgrade_registered_resp() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let private_ip = IpAddr::from_str("10.10.10.10").unwrap();
let wg_port = 51822;
let registred_data = v2::registration::RegistredData {
pub_key,
private_ip,
wg_port,
};
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v2::response::AuthenticatorResponse::new_registered(
registred_data,
reply_to,
request_id,
);
let upgraded_msg = v3::response::AuthenticatorResponse::from(msg);
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v3::response::AuthenticatorResponseData::Registered(v3::response::RegisteredResponse {
request_id,
reply_to,
reply: v3::registration::RegistredData {
wg_port,
pub_key,
private_ip
}
})
);
}
#[test]
fn downgrade_registered_resp() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let private_ip = IpAddr::from_str("10.10.10.10").unwrap();
let wg_port = 51822;
let registred_data = v3::registration::RegistredData {
pub_key,
private_ip,
wg_port,
};
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v3::response::AuthenticatorResponse::new_registered(
registred_data,
reply_to,
request_id,
);
let downgraded_msg = v2::response::AuthenticatorResponse::try_from(msg).unwrap();
assert_eq!(
downgraded_msg.protocol,
Protocol {
version: 2,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
downgraded_msg.data,
v2::response::AuthenticatorResponseData::Registered(v2::response::RegisteredResponse {
request_id,
reply_to,
reply: v2::registration::RegistredData {
wg_port,
pub_key,
private_ip
}
})
);
}
#[test]
fn upgrade_remaining_bandwidth_resp() {
let available_bandwidth = 42;
let remaining_bandwidth_data = Some(v2::registration::RemainingBandwidthData {
available_bandwidth,
});
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v2::response::AuthenticatorResponse::new_remaining_bandwidth(
remaining_bandwidth_data,
reply_to,
request_id,
);
let upgraded_msg = v3::response::AuthenticatorResponse::from(msg);
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v3::response::AuthenticatorResponseData::RemainingBandwidth(
v3::response::RemainingBandwidthResponse {
request_id,
reply_to,
reply: Some(v3::registration::RemainingBandwidthData {
available_bandwidth,
})
}
)
);
}
#[test]
fn downgrade_remaining_bandwidth_resp() {
let available_bandwidth = 42;
let remaining_bandwidth_data = Some(v3::registration::RemainingBandwidthData {
available_bandwidth,
});
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v3::response::AuthenticatorResponse::new_remaining_bandwidth(
remaining_bandwidth_data,
reply_to,
request_id,
);
let downgraded_msg = v2::response::AuthenticatorResponse::try_from(msg).unwrap();
assert_eq!(
downgraded_msg.protocol,
Protocol {
version: 2,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
downgraded_msg.data,
v2::response::AuthenticatorResponseData::RemainingBandwidth(
v2::response::RemainingBandwidthResponse {
request_id,
reply_to,
reply: Some(v2::registration::RemainingBandwidthData {
available_bandwidth,
})
}
)
);
}
#[test]
fn downgrade_topup_resp() {
let available_bandwidth = 42;
let remaining_bandwidth_data = v3::registration::RemainingBandwidthData {
available_bandwidth,
};
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v3::response::AuthenticatorResponse::new_topup_bandwidth(
remaining_bandwidth_data,
reply_to,
request_id,
);
assert!(v2::response::AuthenticatorResponse::try_from(msg).is_err());
}
}
@@ -27,9 +27,9 @@ pub type HmacSha256 = Hmac<Sha256>;
pub type Nonce = u64; pub type Nonce = u64;
pub type Taken = Option<SystemTime>; pub type Taken = Option<SystemTime>;
pub const BANDWIDTH_CAP_PER_DAY: u64 = 250 * 1024 * 1024 * 1024; // 250 GB pub const BANDWIDTH_CAP_PER_DAY: u64 = 1024 * 1024 * 1024; // 1 GB
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct InitMessage { pub struct InitMessage {
/// Base64 encoded x25519 public key /// Base64 encoded x25519 public key
pub pub_key: PeerPublicKey, pub pub_key: PeerPublicKey,
@@ -41,7 +41,7 @@ impl InitMessage {
} }
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct FinalMessage { pub struct FinalMessage {
/// Gateway client data /// Gateway client data
pub gateway_client: GatewayClient, pub gateway_client: GatewayClient,
@@ -50,28 +50,28 @@ pub struct FinalMessage {
pub credential: Option<CredentialSpendingData>, pub credential: Option<CredentialSpendingData>,
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RegistrationData { pub struct RegistrationData {
pub nonce: u64, pub nonce: u64,
pub gateway_data: GatewayClient, pub gateway_data: GatewayClient,
pub wg_port: u16, pub wg_port: u16,
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RegistredData { pub struct RegistredData {
pub pub_key: PeerPublicKey, pub pub_key: PeerPublicKey,
pub private_ip: IpAddr, pub private_ip: IpAddr,
pub wg_port: u16, pub wg_port: u16,
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RemainingBandwidthData { pub struct RemainingBandwidthData {
pub available_bandwidth: i64, pub available_bandwidth: i64,
} }
/// Client that wants to register sends its PublicKey bytes mac digest encrypted with a DH shared secret. /// Client that wants to register sends its PublicKey bytes mac digest encrypted with a DH shared secret.
/// Gateway/Nym node can then verify pub_key payload using the same process /// Gateway/Nym node can then verify pub_key payload using the same process
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct GatewayClient { pub struct GatewayClient {
/// Base64 encoded x25519 public key /// Base64 encoded x25519 public key
pub pub_key: PeerPublicKey, pub pub_key: PeerPublicKey,
@@ -147,7 +147,7 @@ impl GatewayClient {
// TODO: change the inner type into generic array of size HmacSha256::OutputSize // TODO: change the inner type into generic array of size HmacSha256::OutputSize
// TODO2: rely on our internal crypto/hmac // TODO2: rely on our internal crypto/hmac
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone)]
pub struct ClientMac(Vec<u8>); pub struct ClientMac(Vec<u8>);
impl fmt::Display for ClientMac { impl fmt::Display for ClientMac {
@@ -20,6 +20,11 @@ fn generate_random() -> u64 {
rng.next_u64() rng.next_u64()
} }
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct QueryMessage {
pub pub_key: PeerPublicKey,
}
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AuthenticatorRequest { pub struct AuthenticatorRequest {
pub protocol: Protocol, pub protocol: Protocol,
@@ -68,7 +73,7 @@ impl AuthenticatorRequest {
) )
} }
pub fn new_query_request(peer_public_key: PeerPublicKey, reply_to: Recipient) -> (Self, u64) { pub fn new_query_request(query_message: QueryMessage, reply_to: Recipient) -> (Self, u64) {
let request_id = generate_random(); let request_id = generate_random();
( (
Self { Self {
@@ -76,7 +81,7 @@ impl AuthenticatorRequest {
service_provider_type: ServiceProviderType::Authenticator, service_provider_type: ServiceProviderType::Authenticator,
version: VERSION, version: VERSION,
}, },
data: AuthenticatorRequestData::QueryBandwidth(peer_public_key), data: AuthenticatorRequestData::QueryBandwidth(query_message.pub_key),
reply_to, reply_to,
request_id, request_id,
}, },
@@ -106,7 +111,7 @@ impl AuthenticatorRequest {
} }
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum AuthenticatorRequestData { pub enum AuthenticatorRequestData {
Initial(InitMessage), Initial(InitMessage),
Final(Box<FinalMessage>), Final(Box<FinalMessage>),
@@ -120,7 +120,7 @@ impl AuthenticatorResponse {
} }
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum AuthenticatorResponseData { pub enum AuthenticatorResponseData {
PendingRegistration(PendingRegistrationResponse), PendingRegistration(PendingRegistrationResponse),
Registered(RegisteredResponse), Registered(RegisteredResponse),
@@ -128,28 +128,28 @@ pub enum AuthenticatorResponseData {
TopUpBandwidth(TopUpBandwidthResponse), TopUpBandwidth(TopUpBandwidthResponse),
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PendingRegistrationResponse { pub struct PendingRegistrationResponse {
pub request_id: u64, pub request_id: u64,
pub reply_to: Recipient, pub reply_to: Recipient,
pub reply: RegistrationData, pub reply: RegistrationData,
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RegisteredResponse { pub struct RegisteredResponse {
pub request_id: u64, pub request_id: u64,
pub reply_to: Recipient, pub reply_to: Recipient,
pub reply: RegistredData, pub reply: RegistredData,
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RemainingBandwidthResponse { pub struct RemainingBandwidthResponse {
pub request_id: u64, pub request_id: u64,
pub reply_to: Recipient, pub reply_to: Recipient,
pub reply: Option<RemainingBandwidthData>, pub reply: Option<RemainingBandwidthData>,
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct TopUpBandwidthResponse { pub struct TopUpBandwidthResponse {
pub request_id: u64, pub request_id: u64,
pub reply_to: Recipient, pub reply_to: Recipient,
@@ -5,7 +5,7 @@ use nym_credentials_interface::CredentialSpendingData;
use nym_wireguard_types::PeerPublicKey; use nym_wireguard_types::PeerPublicKey;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TopUpMessage { pub struct TopUpMessage {
/// Base64 encoded x25519 public key /// Base64 encoded x25519 public key
pub pub_key: PeerPublicKey, pub pub_key: PeerPublicKey,
@@ -5,80 +5,35 @@ use nym_service_provider_requests_common::{Protocol, ServiceProviderType};
use crate::{v3, v4}; use crate::{v3, v4};
impl TryFrom<v3::request::AuthenticatorRequest> for v4::request::AuthenticatorRequest { impl From<v3::request::AuthenticatorRequest> for v4::request::AuthenticatorRequest {
type Error = crate::Error; fn from(authenticator_request: v3::request::AuthenticatorRequest) -> Self {
fn try_from( Self {
authenticator_request: v3::request::AuthenticatorRequest,
) -> Result<Self, Self::Error> {
Ok(Self {
protocol: Protocol { protocol: Protocol {
version: 4, version: 4,
service_provider_type: ServiceProviderType::Authenticator, service_provider_type: ServiceProviderType::Authenticator,
}, },
data: authenticator_request.data.try_into()?, data: authenticator_request.data.into(),
reply_to: authenticator_request.reply_to, reply_to: authenticator_request.reply_to,
request_id: authenticator_request.request_id, request_id: authenticator_request.request_id,
})
}
}
impl TryFrom<v4::request::AuthenticatorRequest> for v3::request::AuthenticatorRequest {
type Error = crate::Error;
fn try_from(
authenticator_request: v4::request::AuthenticatorRequest,
) -> Result<Self, Self::Error> {
Ok(Self {
protocol: Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator,
},
data: authenticator_request.data.try_into()?,
reply_to: authenticator_request.reply_to,
request_id: authenticator_request.request_id,
})
}
}
impl TryFrom<v3::request::AuthenticatorRequestData> for v4::request::AuthenticatorRequestData {
type Error = crate::Error;
fn try_from(
authenticator_request_data: v3::request::AuthenticatorRequestData,
) -> Result<Self, Self::Error> {
match authenticator_request_data {
v3::request::AuthenticatorRequestData::Initial(init_msg) => Ok(
v4::request::AuthenticatorRequestData::Initial(init_msg.into()),
),
v3::request::AuthenticatorRequestData::Final(_) => Err(Self::Error::Conversion(
"mac hash breaking change".to_string(),
)),
v3::request::AuthenticatorRequestData::QueryBandwidth(pub_key) => Ok(
v4::request::AuthenticatorRequestData::QueryBandwidth(pub_key),
),
v3::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => Ok(
v4::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message.into()),
),
} }
} }
} }
impl TryFrom<v4::request::AuthenticatorRequestData> for v3::request::AuthenticatorRequestData { impl From<v3::request::AuthenticatorRequestData> for v4::request::AuthenticatorRequestData {
type Error = crate::Error; fn from(authenticator_request_data: v3::request::AuthenticatorRequestData) -> Self {
fn try_from(
authenticator_request_data: v4::request::AuthenticatorRequestData,
) -> Result<Self, Self::Error> {
match authenticator_request_data { match authenticator_request_data {
v4::request::AuthenticatorRequestData::Initial(init_msg) => Ok( v3::request::AuthenticatorRequestData::Initial(init_msg) => {
v3::request::AuthenticatorRequestData::Initial(init_msg.into()), v4::request::AuthenticatorRequestData::Initial(init_msg.into())
), }
v4::request::AuthenticatorRequestData::Final(_) => Err(Self::Error::Conversion( v3::request::AuthenticatorRequestData::Final(gw_client) => {
"mac hash breaking change".to_string(), v4::request::AuthenticatorRequestData::Final(gw_client.into())
)), }
v4::request::AuthenticatorRequestData::QueryBandwidth(pub_key) => Ok( v3::request::AuthenticatorRequestData::QueryBandwidth(pub_key) => {
v3::request::AuthenticatorRequestData::QueryBandwidth(pub_key), v4::request::AuthenticatorRequestData::QueryBandwidth(pub_key)
), }
v4::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => Ok( v3::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message) => {
v3::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message.into()), v4::request::AuthenticatorRequestData::TopUpBandwidth(top_up_message.into())
), }
} }
} }
} }
@@ -91,11 +46,12 @@ impl From<v3::registration::InitMessage> for v4::registration::InitMessage {
} }
} }
impl From<v4::registration::InitMessage> for v3::registration::InitMessage { impl From<Box<v3::registration::FinalMessage>> for Box<v4::registration::FinalMessage> {
fn from(init_msg: v4::registration::InitMessage) -> Self { fn from(gw_client: Box<v3::registration::FinalMessage>) -> Self {
Self { Box::new(v4::registration::FinalMessage {
pub_key: init_msg.pub_key, gateway_client: gw_client.gateway_client.into(),
} credential: gw_client.credential,
})
} }
} }
@@ -108,26 +64,35 @@ impl From<Box<v3::topup::TopUpMessage>> for Box<v4::topup::TopUpMessage> {
} }
} }
impl From<Box<v4::topup::TopUpMessage>> for Box<v3::topup::TopUpMessage> { impl From<v3::registration::GatewayClient> for v4::registration::GatewayClient {
fn from(top_up_message: Box<v4::topup::TopUpMessage>) -> Self { fn from(gw_client: v3::registration::GatewayClient) -> Self {
Box::new(v3::topup::TopUpMessage { Self {
pub_key: top_up_message.pub_key, pub_key: gw_client.pub_key,
credential: top_up_message.credential, private_ips: gw_client.private_ip.into(),
}) mac: gw_client.mac.into(),
}
} }
} }
impl TryFrom<v3::response::AuthenticatorResponse> for v4::response::AuthenticatorResponse { impl From<v4::registration::GatewayClient> for v3::registration::GatewayClient {
type Error = crate::Error; fn from(gw_client: v4::registration::GatewayClient) -> Self {
fn try_from(value: v3::response::AuthenticatorResponse) -> Result<Self, Self::Error> { Self {
Ok(Self { pub_key: gw_client.pub_key,
protocol: Protocol { private_ip: gw_client.private_ips.ipv4.into(),
version: 4, mac: gw_client.mac.into(),
service_provider_type: value.protocol.service_provider_type, }
}, }
data: value.data.try_into()?, }
reply_to: value.reply_to,
}) impl From<v3::registration::ClientMac> for v4::registration::ClientMac {
fn from(mac: v3::registration::ClientMac) -> Self {
Self::new(mac.to_vec())
}
}
impl From<v4::registration::ClientMac> for v3::registration::ClientMac {
fn from(mac: v4::registration::ClientMac) -> Self {
Self::new(mac.to_vec())
} }
} }
@@ -140,40 +105,11 @@ impl TryFrom<v4::response::AuthenticatorResponse> for v3::response::Authenticato
Ok(Self { Ok(Self {
data: authenticator_response.data.try_into()?, data: authenticator_response.data.try_into()?,
reply_to: authenticator_response.reply_to, reply_to: authenticator_response.reply_to,
protocol: Protocol { protocol: authenticator_response.protocol,
version: 3,
service_provider_type: authenticator_response.protocol.service_provider_type,
},
}) })
} }
} }
impl TryFrom<v3::response::AuthenticatorResponseData> for v4::response::AuthenticatorResponseData {
type Error = crate::Error;
fn try_from(
authenticator_response_data: v3::response::AuthenticatorResponseData,
) -> Result<Self, Self::Error> {
match authenticator_response_data {
v3::response::AuthenticatorResponseData::PendingRegistration(_) => Err(
Self::Error::Conversion("mac hash breaking change".to_string()),
),
v3::response::AuthenticatorResponseData::Registered(registered_response) => Ok(
v4::response::AuthenticatorResponseData::Registered(registered_response.into()),
),
v3::response::AuthenticatorResponseData::RemainingBandwidth(
remaining_bandwidth_response,
) => Ok(v4::response::AuthenticatorResponseData::RemainingBandwidth(
remaining_bandwidth_response.into(),
)),
v3::response::AuthenticatorResponseData::TopUpBandwidth(top_up_response) => Ok(
v4::response::AuthenticatorResponseData::TopUpBandwidth(top_up_response.into()),
),
}
}
}
impl TryFrom<v4::response::AuthenticatorResponseData> for v3::response::AuthenticatorResponseData { impl TryFrom<v4::response::AuthenticatorResponseData> for v3::response::AuthenticatorResponseData {
type Error = crate::Error; type Error = crate::Error;
@@ -181,10 +117,13 @@ impl TryFrom<v4::response::AuthenticatorResponseData> for v3::response::Authenti
authenticator_response_data: v4::response::AuthenticatorResponseData, authenticator_response_data: v4::response::AuthenticatorResponseData,
) -> Result<Self, Self::Error> { ) -> Result<Self, Self::Error> {
match authenticator_response_data { match authenticator_response_data {
v4::response::AuthenticatorResponseData::PendingRegistration(_) => Err( v4::response::AuthenticatorResponseData::PendingRegistration(
Self::Error::Conversion("mac hash breaking change".to_string()), pending_registration_response,
) => Ok(
v3::response::AuthenticatorResponseData::PendingRegistration(
pending_registration_response.into(),
),
), ),
v4::response::AuthenticatorResponseData::Registered(registered_response) => Ok( v4::response::AuthenticatorResponseData::Registered(registered_response) => Ok(
v3::response::AuthenticatorResponseData::Registered(registered_response.into()), v3::response::AuthenticatorResponseData::Registered(registered_response.into()),
), ),
@@ -202,6 +141,16 @@ impl TryFrom<v4::response::AuthenticatorResponseData> for v3::response::Authenti
} }
} }
impl From<v4::response::PendingRegistrationResponse> for v3::response::PendingRegistrationResponse {
fn from(value: v4::response::PendingRegistrationResponse) -> Self {
Self {
request_id: value.request_id,
reply_to: value.reply_to,
reply: value.reply.into(),
}
}
}
impl From<v4::response::RegisteredResponse> for v3::response::RegisteredResponse { impl From<v4::response::RegisteredResponse> for v3::response::RegisteredResponse {
fn from(value: v4::response::RegisteredResponse) -> Self { fn from(value: v4::response::RegisteredResponse) -> Self {
Self { Self {
@@ -212,26 +161,6 @@ impl From<v4::response::RegisteredResponse> for v3::response::RegisteredResponse
} }
} }
impl From<v3::response::RegisteredResponse> for v4::response::RegisteredResponse {
fn from(value: v3::response::RegisteredResponse) -> Self {
Self {
request_id: value.request_id,
reply_to: value.reply_to,
reply: value.reply.into(),
}
}
}
impl From<v3::response::RemainingBandwidthResponse> for v4::response::RemainingBandwidthResponse {
fn from(value: v3::response::RemainingBandwidthResponse) -> Self {
Self {
request_id: value.request_id,
reply_to: value.reply_to,
reply: value.reply.map(Into::into),
}
}
}
impl From<v4::response::RemainingBandwidthResponse> for v3::response::RemainingBandwidthResponse { impl From<v4::response::RemainingBandwidthResponse> for v3::response::RemainingBandwidthResponse {
fn from(value: v4::response::RemainingBandwidthResponse) -> Self { fn from(value: v4::response::RemainingBandwidthResponse) -> Self {
Self { Self {
@@ -242,31 +171,11 @@ impl From<v4::response::RemainingBandwidthResponse> for v3::response::RemainingB
} }
} }
impl From<v3::response::TopUpBandwidthResponse> for v4::response::TopUpBandwidthResponse { impl From<v4::registration::RegistrationData> for v3::registration::RegistrationData {
fn from(value: v3::response::TopUpBandwidthResponse) -> Self { fn from(value: v4::registration::RegistrationData) -> Self {
Self { Self {
request_id: value.request_id, nonce: value.nonce,
reply_to: value.reply_to, gateway_data: value.gateway_data.into(),
reply: value.reply.into(),
}
}
}
impl From<v4::response::TopUpBandwidthResponse> for v3::response::TopUpBandwidthResponse {
fn from(value: v4::response::TopUpBandwidthResponse) -> Self {
Self {
request_id: value.request_id,
reply_to: value.reply_to,
reply: value.reply.into(),
}
}
}
impl From<v3::registration::RegistredData> for v4::registration::RegistredData {
fn from(value: v3::registration::RegistredData) -> Self {
Self {
pub_key: value.pub_key,
private_ips: value.private_ip.into(),
wg_port: value.wg_port, wg_port: value.wg_port,
} }
} }
@@ -282,14 +191,6 @@ impl From<v4::registration::RegistredData> for v3::registration::RegistredData {
} }
} }
impl From<v3::registration::RemainingBandwidthData> for v4::registration::RemainingBandwidthData {
fn from(value: v3::registration::RemainingBandwidthData) -> Self {
Self {
available_bandwidth: value.available_bandwidth,
}
}
}
impl From<v4::registration::RemainingBandwidthData> for v3::registration::RemainingBandwidthData { impl From<v4::registration::RemainingBandwidthData> for v3::registration::RemainingBandwidthData {
fn from(value: v4::registration::RemainingBandwidthData) -> Self { fn from(value: v4::registration::RemainingBandwidthData) -> Self {
Self { Self {
@@ -297,441 +198,3 @@ impl From<v4::registration::RemainingBandwidthData> for v3::registration::Remain
} }
} }
} }
#[cfg(test)]
mod tests {
use std::{
net::{Ipv4Addr, Ipv6Addr},
str::FromStr,
};
use nym_credentials_interface::CredentialSpendingData;
use nym_crypto::asymmetric::encryption::PrivateKey;
use nym_sphinx::addressing::Recipient;
use nym_wireguard_types::PeerPublicKey;
use x25519_dalek::PublicKey;
use super::*;
use crate::util::tests::{CREDENTIAL_BYTES, RECIPIENT};
#[test]
fn upgrade_initial_req() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) = v3::request::AuthenticatorRequest::new_initial_request(
v3::registration::InitMessage::new(pub_key),
reply_to,
);
let upgraded_msg = v4::request::AuthenticatorRequest::try_from(msg).unwrap();
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 4,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v4::request::AuthenticatorRequestData::Initial(v4::registration::InitMessage {
pub_key
})
);
}
#[test]
fn downgrade_initial_req() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) = v4::request::AuthenticatorRequest::new_initial_request(
v4::registration::InitMessage::new(pub_key),
reply_to,
);
let downgraded_msg = v3::request::AuthenticatorRequest::try_from(msg).unwrap();
assert_eq!(
downgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
downgraded_msg.data,
v3::request::AuthenticatorRequestData::Initial(v3::registration::InitMessage {
pub_key
})
);
}
#[test]
fn upgrade_final_req() {
let mut rng = rand::thread_rng();
let local_secret = PrivateKey::new(&mut rng);
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap();
let nonce = 42;
let gateway_client = v3::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
ipv4.into(),
nonce,
);
let credential = Some(CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap());
let final_message = v3::registration::FinalMessage {
gateway_client,
credential: credential.clone(),
};
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) =
v3::request::AuthenticatorRequest::new_final_request(final_message, reply_to);
assert!(v4::request::AuthenticatorRequest::try_from(msg).is_err());
}
#[test]
fn downgrade_final_req() {
let mut rng = rand::thread_rng();
let local_secret = PrivateKey::new(&mut rng);
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap();
let private_ips =
v4::registration::IpPair::new(ipv4, Ipv6Addr::from_str("fc01::10").unwrap());
let nonce = 42;
let gateway_client = v4::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
private_ips,
nonce,
);
let credential = Some(CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap());
let final_message = v4::registration::FinalMessage {
gateway_client,
credential: credential.clone(),
};
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) =
v4::request::AuthenticatorRequest::new_final_request(final_message, reply_to);
assert!(v3::request::AuthenticatorRequest::try_from(msg).is_err());
}
#[test]
fn upgrade_query_req() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) = v3::request::AuthenticatorRequest::new_query_request(pub_key, reply_to);
let upgraded_msg = v4::request::AuthenticatorRequest::try_from(msg).unwrap();
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 4,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v4::request::AuthenticatorRequestData::QueryBandwidth(pub_key)
);
}
#[test]
fn downgrade_query_req() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) = v4::request::AuthenticatorRequest::new_query_request(pub_key, reply_to);
let downgraded_msg = v3::request::AuthenticatorRequest::try_from(msg).unwrap();
assert_eq!(
downgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
downgraded_msg.data,
v3::request::AuthenticatorRequestData::QueryBandwidth(pub_key)
);
}
#[test]
fn downgrade_topup_req() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let credential = CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES).unwrap();
let top_up_message = v4::topup::TopUpMessage {
pub_key,
credential: credential.clone(),
};
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let (msg, _) =
v4::request::AuthenticatorRequest::new_topup_request(top_up_message, reply_to);
let downgraded_msg = v3::request::AuthenticatorRequest::try_from(msg).unwrap();
assert_eq!(
downgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
downgraded_msg.data,
v3::request::AuthenticatorRequestData::TopUpBandwidth(Box::new(
v3::topup::TopUpMessage {
pub_key,
credential
}
))
);
}
#[test]
fn upgrade_pending_reg_resp() {
let mut rng = rand::thread_rng();
let local_secret = PrivateKey::new(&mut rng);
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap();
let nonce = 42;
let wg_port = 51822;
let gateway_data = v3::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
ipv4.into(),
nonce,
);
let registration_data = v3::registration::RegistrationData {
nonce,
gateway_data,
wg_port,
};
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v3::response::AuthenticatorResponse::new_pending_registration_success(
registration_data,
request_id,
reply_to,
);
assert!(v4::response::AuthenticatorResponse::try_from(msg).is_err());
}
#[test]
fn downgrade_pending_reg_resp() {
let mut rng = rand::thread_rng();
let local_secret = PrivateKey::new(&mut rng);
let remote_secret = x25519_dalek::StaticSecret::random_from_rng(&mut rng);
let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap();
let private_ips =
v4::registration::IpPair::new(ipv4, Ipv6Addr::from_str("fc01::10").unwrap());
let nonce = 42;
let wg_port = 51822;
let gateway_data = v4::registration::GatewayClient::new(
&local_secret,
(&remote_secret).into(),
private_ips,
nonce,
);
let registration_data = v4::registration::RegistrationData {
nonce,
gateway_data,
wg_port,
};
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v4::response::AuthenticatorResponse::new_pending_registration_success(
registration_data,
request_id,
reply_to,
);
assert!(v3::response::AuthenticatorResponse::try_from(msg).is_err());
}
#[test]
fn upgrade_registered_resp() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let ipv4 = Ipv4Addr::from_str("10.1.10.10").unwrap();
let private_ips =
v4::registration::IpPair::new(ipv4, Ipv6Addr::from_str("fc01::a0a").unwrap());
let wg_port = 51822;
let registred_data = v3::registration::RegistredData {
pub_key,
private_ip: ipv4.into(),
wg_port,
};
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v3::response::AuthenticatorResponse::new_registered(
registred_data,
reply_to,
request_id,
);
let upgraded_msg = v4::response::AuthenticatorResponse::try_from(msg).unwrap();
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 4,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v4::response::AuthenticatorResponseData::Registered(v4::response::RegisteredResponse {
request_id,
reply_to,
reply: v4::registration::RegistredData {
wg_port,
pub_key,
private_ips
}
})
);
}
#[test]
fn downgrade_registered_resp() {
let pub_key = PeerPublicKey::new(PublicKey::from([0; 32]));
let ipv4 = Ipv4Addr::from_str("10.10.10.10").unwrap();
let private_ips =
v4::registration::IpPair::new(ipv4, Ipv6Addr::from_str("fc01::10").unwrap());
let wg_port = 51822;
let registred_data = v4::registration::RegistredData {
pub_key,
private_ips,
wg_port,
};
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v4::response::AuthenticatorResponse::new_registered(
registred_data,
reply_to,
request_id,
);
let downgraded_msg = v3::response::AuthenticatorResponse::try_from(msg).unwrap();
assert_eq!(
downgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
downgraded_msg.data,
v3::response::AuthenticatorResponseData::Registered(v3::response::RegisteredResponse {
request_id,
reply_to,
reply: v3::registration::RegistredData {
wg_port,
pub_key,
private_ip: ipv4.into()
}
})
);
}
#[test]
fn upgrade_remaining_bandwidth_resp() {
let available_bandwidth = 42;
let remaining_bandwidth_data = Some(v3::registration::RemainingBandwidthData {
available_bandwidth,
});
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v3::response::AuthenticatorResponse::new_remaining_bandwidth(
remaining_bandwidth_data,
reply_to,
request_id,
);
let upgraded_msg = v4::response::AuthenticatorResponse::try_from(msg).unwrap();
assert_eq!(
upgraded_msg.protocol,
Protocol {
version: 4,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
upgraded_msg.data,
v4::response::AuthenticatorResponseData::RemainingBandwidth(
v4::response::RemainingBandwidthResponse {
request_id,
reply_to,
reply: Some(v4::registration::RemainingBandwidthData {
available_bandwidth,
})
}
)
);
}
#[test]
fn downgrade_remaining_bandwidth_resp() {
let available_bandwidth = 42;
let remaining_bandwidth_data = Some(v4::registration::RemainingBandwidthData {
available_bandwidth,
});
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v4::response::AuthenticatorResponse::new_remaining_bandwidth(
remaining_bandwidth_data,
reply_to,
request_id,
);
let downgraded_msg = v3::response::AuthenticatorResponse::try_from(msg).unwrap();
assert_eq!(
downgraded_msg.protocol,
Protocol {
version: 3,
service_provider_type: ServiceProviderType::Authenticator
}
);
assert_eq!(
downgraded_msg.data,
v3::response::AuthenticatorResponseData::RemainingBandwidth(
v3::response::RemainingBandwidthResponse {
request_id,
reply_to,
reply: Some(v3::registration::RemainingBandwidthData {
available_bandwidth,
})
}
)
);
}
#[test]
fn downgrade_topup_resp() {
let available_bandwidth = 42;
let remaining_bandwidth_data = v4::registration::RemainingBandwidthData {
available_bandwidth,
};
let request_id = 123;
let reply_to = Recipient::try_from_base58_string(RECIPIENT).unwrap();
let msg = v4::response::AuthenticatorResponse::new_topup_bandwidth(
remaining_bandwidth_data,
reply_to,
request_id,
);
assert!(v3::response::AuthenticatorResponse::try_from(msg).is_err());
}
}
@@ -28,7 +28,7 @@ pub type HmacSha256 = Hmac<Sha256>;
pub type Nonce = u64; pub type Nonce = u64;
pub type Taken = Option<SystemTime>; pub type Taken = Option<SystemTime>;
pub const BANDWIDTH_CAP_PER_DAY: u64 = 250 * 1024 * 1024 * 1024; // 250 GB pub const BANDWIDTH_CAP_PER_DAY: u64 = 1024 * 1024 * 1024; // 1 GB
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct IpPair { pub struct IpPair {
@@ -42,12 +42,6 @@ impl IpPair {
} }
} }
impl From<(Ipv4Addr, Ipv6Addr)> for IpPair {
fn from((ipv4, ipv6): (Ipv4Addr, Ipv6Addr)) -> Self {
IpPair { ipv4, ipv6 }
}
}
impl fmt::Display for IpPair { impl fmt::Display for IpPair {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "({}, {})", self.ipv4, self.ipv6) write!(f, "({}, {})", self.ipv4, self.ipv6)
@@ -81,7 +75,7 @@ impl From<IpAddr> for IpPair {
} }
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct InitMessage { pub struct InitMessage {
/// Base64 encoded x25519 public key /// Base64 encoded x25519 public key
pub pub_key: PeerPublicKey, pub pub_key: PeerPublicKey,
@@ -93,7 +87,7 @@ impl InitMessage {
} }
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct FinalMessage { pub struct FinalMessage {
/// Gateway client data /// Gateway client data
pub gateway_client: GatewayClient, pub gateway_client: GatewayClient,
@@ -102,28 +96,28 @@ pub struct FinalMessage {
pub credential: Option<CredentialSpendingData>, pub credential: Option<CredentialSpendingData>,
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RegistrationData { pub struct RegistrationData {
pub nonce: u64, pub nonce: u64,
pub gateway_data: GatewayClient, pub gateway_data: GatewayClient,
pub wg_port: u16, pub wg_port: u16,
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RegistredData { pub struct RegistredData {
pub pub_key: PeerPublicKey, pub pub_key: PeerPublicKey,
pub private_ips: IpPair, pub private_ips: IpPair,
pub wg_port: u16, pub wg_port: u16,
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RemainingBandwidthData { pub struct RemainingBandwidthData {
pub available_bandwidth: i64, pub available_bandwidth: i64,
} }
/// Client that wants to register sends its PublicKey bytes mac digest encrypted with a DH shared secret. /// Client that wants to register sends its PublicKey bytes mac digest encrypted with a DH shared secret.
/// Gateway/Nym node can then verify pub_key payload using the same process /// Gateway/Nym node can then verify pub_key payload using the same process
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct GatewayClient { pub struct GatewayClient {
/// Base64 encoded x25519 public key /// Base64 encoded x25519 public key
pub pub_key: PeerPublicKey, pub pub_key: PeerPublicKey,
@@ -199,7 +193,7 @@ impl GatewayClient {
// TODO: change the inner type into generic array of size HmacSha256::OutputSize // TODO: change the inner type into generic array of size HmacSha256::OutputSize
// TODO2: rely on our internal crypto/hmac // TODO2: rely on our internal crypto/hmac
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone)]
pub struct ClientMac(Vec<u8>); pub struct ClientMac(Vec<u8>);
impl fmt::Display for ClientMac { impl fmt::Display for ClientMac {
@@ -20,7 +20,12 @@ fn generate_random() -> u64 {
rng.next_u64() rng.next_u64()
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct QueryMessage {
pub pub_key: PeerPublicKey,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AuthenticatorRequest { pub struct AuthenticatorRequest {
pub protocol: Protocol, pub protocol: Protocol,
pub data: AuthenticatorRequestData, pub data: AuthenticatorRequestData,
@@ -68,7 +73,7 @@ impl AuthenticatorRequest {
) )
} }
pub fn new_query_request(peer_public_key: PeerPublicKey, reply_to: Recipient) -> (Self, u64) { pub fn new_query_request(query_message: QueryMessage, reply_to: Recipient) -> (Self, u64) {
let request_id = generate_random(); let request_id = generate_random();
( (
Self { Self {
@@ -76,7 +81,7 @@ impl AuthenticatorRequest {
service_provider_type: ServiceProviderType::Authenticator, service_provider_type: ServiceProviderType::Authenticator,
version: VERSION, version: VERSION,
}, },
data: AuthenticatorRequestData::QueryBandwidth(peer_public_key), data: AuthenticatorRequestData::QueryBandwidth(query_message.pub_key),
reply_to, reply_to,
request_id, request_id,
}, },
@@ -106,7 +111,7 @@ impl AuthenticatorRequest {
} }
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum AuthenticatorRequestData { pub enum AuthenticatorRequestData {
Initial(InitMessage), Initial(InitMessage),
Final(Box<FinalMessage>), Final(Box<FinalMessage>),
@@ -10,7 +10,7 @@ use crate::make_bincode_serializer;
use super::VERSION; use super::VERSION;
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AuthenticatorResponse { pub struct AuthenticatorResponse {
pub protocol: Protocol, pub protocol: Protocol,
pub data: AuthenticatorResponseData, pub data: AuthenticatorResponseData,
@@ -120,7 +120,7 @@ impl AuthenticatorResponse {
} }
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum AuthenticatorResponseData { pub enum AuthenticatorResponseData {
PendingRegistration(PendingRegistrationResponse), PendingRegistration(PendingRegistrationResponse),
Registered(RegisteredResponse), Registered(RegisteredResponse),
@@ -128,28 +128,28 @@ pub enum AuthenticatorResponseData {
TopUpBandwidth(TopUpBandwidthResponse), TopUpBandwidth(TopUpBandwidthResponse),
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PendingRegistrationResponse { pub struct PendingRegistrationResponse {
pub request_id: u64, pub request_id: u64,
pub reply_to: Recipient, pub reply_to: Recipient,
pub reply: RegistrationData, pub reply: RegistrationData,
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RegisteredResponse { pub struct RegisteredResponse {
pub request_id: u64, pub request_id: u64,
pub reply_to: Recipient, pub reply_to: Recipient,
pub reply: RegistredData, pub reply: RegistredData,
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RemainingBandwidthResponse { pub struct RemainingBandwidthResponse {
pub request_id: u64, pub request_id: u64,
pub reply_to: Recipient, pub reply_to: Recipient,
pub reply: Option<RemainingBandwidthData>, pub reply: Option<RemainingBandwidthData>,
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct TopUpBandwidthResponse { pub struct TopUpBandwidthResponse {
pub request_id: u64, pub request_id: u64,
pub reply_to: Recipient, pub reply_to: Recipient,
@@ -5,7 +5,7 @@ use nym_credentials_interface::CredentialSpendingData;
use nym_wireguard_types::PeerPublicKey; use nym_wireguard_types::PeerPublicKey;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TopUpMessage { pub struct TopUpMessage {
/// Base64 encoded x25519 public key /// Base64 encoded x25519 public key
pub pub_key: PeerPublicKey, pub pub_key: PeerPublicKey,
@@ -17,7 +17,7 @@ use nym_validator_client::coconut::all_ecash_api_clients;
use nym_validator_client::nym_api::EpochId; use nym_validator_client::nym_api::EpochId;
use nym_validator_client::nyxd::contract_traits::EcashSigningClient; use nym_validator_client::nyxd::contract_traits::EcashSigningClient;
use nym_validator_client::nyxd::contract_traits::{DkgQueryClient, EcashQueryClient}; use nym_validator_client::nyxd::contract_traits::{DkgQueryClient, EcashQueryClient};
use nym_validator_client::nyxd::cosmwasm_client::ContractResponseData; use nym_validator_client::nyxd::cosmwasm_client::ToSingletonContractData;
use nym_validator_client::EcashApiClient; use nym_validator_client::EcashApiClient;
use rand::rngs::OsRng; use rand::rngs::OsRng;
+2 -1
View File
@@ -15,6 +15,7 @@ const-str = { workspace = true }
log = { workspace = true } log = { workspace = true }
pretty_env_logger = { workspace = true } pretty_env_logger = { workspace = true }
schemars = { workspace = true, features = ["preserve_order"], optional = true } schemars = { workspace = true, features = ["preserve_order"], optional = true }
semver.workspace = true
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true, optional = true } serde_json = { workspace = true, optional = true }
@@ -43,5 +44,5 @@ tracing = [
"tracing-opentelemetry", "tracing-opentelemetry",
"opentelemetry", "opentelemetry",
] ]
clap = ["dep:clap", "dep:clap_complete", "dep:clap_complete_fig"] clap = [ "dep:clap", "dep:clap_complete", "dep:clap_complete_fig" ]
models = [] models = []
+1
View File
@@ -3,6 +3,7 @@
pub mod build_information; pub mod build_information;
pub mod logging; pub mod logging;
pub mod version_checker;
#[cfg(feature = "clap")] #[cfg(feature = "clap")]
pub mod completions; pub mod completions;
-1
View File
@@ -47,7 +47,6 @@ pub fn setup_logging() {
#[cfg(feature = "basic_tracing")] #[cfg(feature = "basic_tracing")]
pub fn setup_tracing_logger() { pub fn setup_tracing_logger() {
let log_builder = tracing_subscriber::fmt() let log_builder = tracing_subscriber::fmt()
.with_writer(std::io::stderr)
// Use a more compact, abbreviated log format // Use a more compact, abbreviated log format
.compact() .compact()
// Display source code file paths // Display source code file paths
@@ -0,0 +1,78 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub use semver::Version;
/// Checks if the version is minor version compatible.
///
/// Checks whether given `version` is compatible with a given semantic version requirement `req`
/// according to major-minor semver rules. The semantic version requirement can be passed as a full,
/// concrete version number, because that's what we'll have in our Cargo.toml files (e.g. 0.3.2).
/// The patch number in the requirement gets dropped and replaced with a wildcard (0.3.*) as all
/// minor versions should be compatible with each other.
pub fn is_minor_version_compatible(version: &str, req: &str) -> bool {
let expected_version = match Version::parse(version) {
Ok(v) => v,
Err(_) => return false,
};
let req_version = match Version::parse(req) {
Ok(v) => v,
Err(_) => return false,
};
expected_version.major == req_version.major && expected_version.minor == req_version.minor
}
pub fn parse_version(raw_version: &str) -> Result<Version, semver::Error> {
Version::parse(raw_version)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn version_0_3_0_is_compatible_with_requirement_0_3_x() {
assert!(is_minor_version_compatible("0.3.0", "0.3.2"));
}
#[test]
fn version_0_3_1_is_compatible_with_minimum_requirement_0_3_x() {
assert!(is_minor_version_compatible("0.3.1", "0.3.2"));
}
#[test]
fn version_0_3_2_is_compatible_with_minimum_requirement_0_3_x() {
assert!(is_minor_version_compatible("0.3.2", "0.3.0"));
}
#[test]
fn version_0_2_0_is_not_compatible_with_requirement_0_3_x() {
assert!(!is_minor_version_compatible("0.2.0", "0.3.2"));
}
#[test]
fn version_0_4_0_is_not_compatible_with_requirement_0_3_x() {
assert!(!is_minor_version_compatible("0.4.0", "0.3.2"));
}
#[test]
fn version_1_3_2_is_not_compatible_with_requirement_0_3_x() {
assert!(!is_minor_version_compatible("1.3.2", "0.3.2"));
}
#[test]
fn version_0_4_0_rc_1_is_compatible_with_version_0_4_0_rc_1() {
assert!(is_minor_version_compatible("0.4.0-rc.1", "0.4.0-rc.1"));
}
#[test]
fn returns_false_on_foo_version() {
assert!(!is_minor_version_compatible("foo", "0.3.2"));
}
#[test]
fn returns_false_on_bar_version() {
assert!(!is_minor_version_compatible("0.3.2", "bar"));
}
}
+2 -3
View File
@@ -3,7 +3,7 @@ name = "nym-client-core"
version = "1.1.15" version = "1.1.15"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"] authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2021" edition = "2021"
rust-version = "1.76" rust-version = "1.70"
license.workspace = true license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -45,8 +45,7 @@ nym-nonexhaustive-delayqueue = { path = "../nonexhaustive-delayqueue" }
nym-sphinx = { path = "../nymsphinx" } nym-sphinx = { path = "../nymsphinx" }
nym-statistics-common = { path = "../statistics" } nym-statistics-common = { path = "../statistics" }
nym-pemstore = { path = "../pemstore" } nym-pemstore = { path = "../pemstore" }
nym-topology = { path = "../topology", features = ["persistence"] } nym-topology = { path = "../topology", features = ["serializable"] }
nym-mixnet-client = { path = "../client-libs/mixnet-client", default-features = false }
nym-validator-client = { path = "../client-libs/validator-client", default-features = false } nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
nym-task = { path = "../task" } nym-task = { path = "../task" }
nym-credentials-interface = { path = "../credentials-interface" } nym-credentials-interface = { path = "../credentials-interface" }
+1 -21
View File
@@ -393,20 +393,13 @@ pub struct Traffic {
/// poisson distribution. /// poisson distribution.
pub disable_main_poisson_packet_distribution: bool, pub disable_main_poisson_packet_distribution: bool,
/// Specify whether route selection should be determined by the packet header.
pub deterministic_route_selection: bool,
/// Specify how many times particular packet can be retransmitted
/// None - no limit
pub maximum_number_of_retransmissions: Option<u32>,
/// Specifies the packet size used for sent messages. /// Specifies the packet size used for sent messages.
/// Do not override it unless you understand the consequences of that change. /// Do not override it unless you understand the consequences of that change.
pub primary_packet_size: PacketSize, pub primary_packet_size: PacketSize,
/// Specifies the optional auxiliary packet size for optimizing message streams. /// Specifies the optional auxiliary packet size for optimizing message streams.
/// Note that its use decreases overall anonymity. /// Note that its use decreases overall anonymity.
/// Do not set it unless you understand the consequences of that change. /// Do not set it it unless you understand the consequences of that change.
pub secondary_packet_size: Option<PacketSize>, pub secondary_packet_size: Option<PacketSize>,
pub packet_type: PacketType, pub packet_type: PacketType,
@@ -431,8 +424,6 @@ impl Default for Traffic {
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY, average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY, message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
disable_main_poisson_packet_distribution: false, disable_main_poisson_packet_distribution: false,
deterministic_route_selection: false,
maximum_number_of_retransmissions: None,
primary_packet_size: PacketSize::RegularPacket, primary_packet_size: PacketSize::RegularPacket,
secondary_packet_size: None, secondary_packet_size: None,
packet_type: PacketType::Mix, packet_type: PacketType::Mix,
@@ -550,15 +541,6 @@ pub struct Topology {
/// Specifies a minimum performance of a gateway that is used on route construction. /// Specifies a minimum performance of a gateway that is used on route construction.
/// This setting is only applicable when `NymApi` topology is used. /// This setting is only applicable when `NymApi` topology is used.
pub minimum_gateway_performance: u8, pub minimum_gateway_performance: u8,
/// Specifies whether this client should attempt to retrieve all available network nodes
/// as opposed to just active mixnodes/gateways.
/// Useless without `ignore_epoch_roles = true`
pub use_extended_topology: bool,
/// Specifies whether this client should ignore the current epoch role of the target egress node
/// when constructing the final hop packets.
pub ignore_egress_epoch_role: bool,
} }
#[allow(clippy::large_enum_variant)] #[allow(clippy::large_enum_variant)]
@@ -595,8 +577,6 @@ impl Default for Topology {
topology_structure: TopologyStructure::default(), topology_structure: TopologyStructure::default(),
minimum_mixnode_performance: DEFAULT_MIN_MIXNODE_PERFORMANCE, minimum_mixnode_performance: DEFAULT_MIN_MIXNODE_PERFORMANCE,
minimum_gateway_performance: DEFAULT_MIN_GATEWAY_PERFORMANCE, minimum_gateway_performance: DEFAULT_MIN_GATEWAY_PERFORMANCE,
use_extended_topology: false,
ignore_egress_epoch_role: false,
} }
} }
} }
@@ -111,7 +111,6 @@ impl From<ConfigV5> for Config {
primary_packet_size: value.debug.traffic.primary_packet_size, primary_packet_size: value.debug.traffic.primary_packet_size,
secondary_packet_size: value.debug.traffic.secondary_packet_size, secondary_packet_size: value.debug.traffic.secondary_packet_size,
packet_type: value.debug.traffic.packet_type, packet_type: value.debug.traffic.packet_type,
..Default::default()
}, },
cover_traffic: CoverTraffic { cover_traffic: CoverTraffic {
loop_cover_traffic_average_delay: value loop_cover_traffic_average_delay: value
@@ -8,10 +8,7 @@ use crate::{
}, },
}; };
use log::{debug, error}; use log::{debug, error};
use sqlx::{ use sqlx::ConnectOptions;
sqlite::{SqliteAutoVacuum, SqliteSynchronous},
ConnectOptions,
};
use std::path::Path; use std::path::Path;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -33,9 +30,6 @@ impl StorageManager {
} }
let opts = sqlx::sqlite::SqliteConnectOptions::new() let opts = sqlx::sqlite::SqliteConnectOptions::new()
.journal_mode(sqlx::sqlite::SqliteJournalMode::Wal)
.synchronous(SqliteSynchronous::Normal)
.auto_vacuum(SqliteAutoVacuum::Incremental)
.filename(database_path) .filename(database_path)
.create_if_missing(true) .create_if_missing(true)
.disable_statement_logging(); .disable_statement_logging();
@@ -116,7 +110,7 @@ impl StorageManager {
) -> Result<(), sqlx::Error> { ) -> Result<(), sqlx::Error> {
sqlx::query!( sqlx::query!(
r#" r#"
INSERT INTO registered_gateway(gateway_id_bs58, registration_timestamp, gateway_type) INSERT INTO registered_gateway(gateway_id_bs58, registration_timestamp, gateway_type)
VALUES (?, ?, ?) VALUES (?, ?, ?)
"#, "#,
registered_gateway.gateway_id_bs58, registered_gateway.gateway_id_bs58,
@@ -230,7 +224,7 @@ impl StorageManager {
) -> Result<(), sqlx::Error> { ) -> Result<(), sqlx::Error> {
sqlx::query!( sqlx::query!(
r#" r#"
INSERT INTO custom_gateway_details(gateway_id_bs58, data) INSERT INTO custom_gateway_details(gateway_id_bs58, data)
VALUES (?, ?) VALUES (?, ?)
"#, "#,
custom.gateway_id_bs58, custom.gateway_id_bs58,
@@ -112,23 +112,18 @@ where
source, source,
} }
})?; })?;
hardcoded_topology.entry_capable_nodes().cloned().collect() hardcoded_topology.get_gateways()
} else { } else {
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
crate::init::helpers::current_gateways( crate::init::helpers::current_gateways(&mut rng, &core.client.nym_api_urls, user_agent)
&mut rng, .await?
&core.client.nym_api_urls,
user_agent,
core.debug.topology.minimum_gateway_performance,
)
.await?
}; };
// since we're registering with a brand new gateway, // since we're registering with a brand new gateway,
// make sure the list of available gateways doesn't overlap the list of known gateways // make sure the list of available gateways doesn't overlap the list of known gateways
let available_gateways = available_gateways let available_gateways = available_gateways
.into_iter() .into_iter()
.filter(|g| !registered_gateways.contains(&g.identity())) .filter(|g| !registered_gateways.contains(g.identity()))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if available_gateways.is_empty() { if available_gateways.is_empty() {
@@ -167,16 +167,11 @@ where
source, source,
} }
})?; })?;
hardcoded_topology.entry_capable_nodes().cloned().collect() hardcoded_topology.get_gateways()
} else { } else {
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
crate::init::helpers::current_gateways( crate::init::helpers::current_gateways(&mut rng, &core.client.nym_api_urls, user_agent)
&mut rng, .await?
&core.client.nym_api_urls,
user_agent,
core.debug.topology.minimum_gateway_performance,
)
.await?
}; };
let gateway_setup = GatewaySetup::New { let gateway_setup = GatewaySetup::New {
@@ -3,6 +3,7 @@
use super::received_buffer::ReceivedBufferMessage; use super::received_buffer::ReceivedBufferMessage;
use super::statistics_control::StatisticsControl; use super::statistics_control::StatisticsControl;
use super::topology_control::geo_aware_provider::GeoAwareTopologyProvider;
use crate::client::base_client::storage::helpers::store_client_keys; use crate::client::base_client::storage::helpers::store_client_keys;
use crate::client::base_client::storage::MixnetClientStorage; use crate::client::base_client::storage::MixnetClientStorage;
use crate::client::cover_traffic_stream::LoopCoverTrafficStream; use crate::client::cover_traffic_stream::LoopCoverTrafficStream;
@@ -23,7 +24,7 @@ use crate::client::replies::reply_storage::{
}; };
use crate::client::topology_control::nym_api_provider::NymApiTopologyProvider; use crate::client::topology_control::nym_api_provider::NymApiTopologyProvider;
use crate::client::topology_control::{ use crate::client::topology_control::{
TopologyAccessor, TopologyRefresher, TopologyRefresherConfig, nym_api_provider, TopologyAccessor, TopologyRefresher, TopologyRefresherConfig,
}; };
use crate::config::{Config, DebugConfig}; use crate::config::{Config, DebugConfig};
use crate::error::ClientCoreError; use crate::error::ClientCoreError;
@@ -31,7 +32,7 @@ use crate::init::{
setup_gateway, setup_gateway,
types::{GatewaySetup, InitialisationResult}, types::{GatewaySetup, InitialisationResult},
}; };
use crate::{config, spawn_future, ForgetMe}; use crate::{config, spawn_future};
use futures::channel::mpsc; use futures::channel::mpsc;
use log::*; use log::*;
use nym_bandwidth_controller::BandwidthController; use nym_bandwidth_controller::BandwidthController;
@@ -187,11 +188,6 @@ pub struct BaseClientBuilder<'a, C, S: MixnetClientStorage> {
user_agent: Option<UserAgent>, user_agent: Option<UserAgent>,
setup_method: GatewaySetup, setup_method: GatewaySetup,
#[cfg(unix)]
connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
forget_me: ForgetMe,
} }
impl<'a, C, S> BaseClientBuilder<'a, C, S> impl<'a, C, S> BaseClientBuilder<'a, C, S>
@@ -214,18 +210,9 @@ where
shutdown: None, shutdown: None,
user_agent: None, user_agent: None,
setup_method: GatewaySetup::MustLoad { gateway_id: None }, setup_method: GatewaySetup::MustLoad { gateway_id: None },
#[cfg(unix)]
connection_fd_callback: None,
forget_me: Default::default(),
} }
} }
#[must_use]
pub fn with_forget_me(mut self, forget_me: &ForgetMe) -> Self {
self.forget_me = forget_me.clone();
self
}
#[must_use] #[must_use]
pub fn with_gateway_setup(mut self, setup: GatewaySetup) -> Self { pub fn with_gateway_setup(mut self, setup: GatewaySetup) -> Self {
self.setup_method = setup; self.setup_method = setup;
@@ -274,15 +261,6 @@ where
Ok(self) Ok(self)
} }
#[cfg(unix)]
pub fn with_connection_fd_callback(
mut self,
callback: Arc<dyn Fn(RawFd) + Send + Sync>,
) -> Self {
self.connection_fd_callback = Some(callback);
self
}
// note: do **NOT** make this method public as its only valid usage is from within `start_base` // note: do **NOT** make this method public as its only valid usage is from within `start_base`
// because it relies on the crypto keys being already loaded // because it relies on the crypto keys being already loaded
fn mix_address(details: &InitialisationResult) -> Recipient { fn mix_address(details: &InitialisationResult) -> Recipient {
@@ -374,7 +352,6 @@ where
controller.start_with_shutdown(shutdown) controller.start_with_shutdown(shutdown)
} }
#[allow(clippy::too_many_arguments)]
async fn start_gateway_client( async fn start_gateway_client(
config: &Config, config: &Config,
initialisation_result: InitialisationResult, initialisation_result: InitialisationResult,
@@ -382,7 +359,6 @@ where
details_store: &S::GatewaysDetailsStore, details_store: &S::GatewaysDetailsStore,
packet_router: PacketRouter, packet_router: PacketRouter,
stats_reporter: ClientStatsSender, stats_reporter: ClientStatsSender,
#[cfg(unix)] connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
shutdown: TaskClient, shutdown: TaskClient,
) -> Result<GatewayClient<C, S::CredentialStore>, ClientCoreError> ) -> Result<GatewayClient<C, S::CredentialStore>, ClientCoreError>
where where
@@ -425,8 +401,6 @@ where
packet_router, packet_router,
bandwidth_controller, bandwidth_controller,
stats_reporter, stats_reporter,
#[cfg(unix)]
connection_fd_callback,
shutdown, shutdown,
) )
}; };
@@ -463,8 +437,8 @@ where
details_store details_store
.upgrade_stored_remote_gateway_key(gateway_client.gateway_identity(), &updated_key) .upgrade_stored_remote_gateway_key(gateway_client.gateway_identity(), &updated_key)
.await.map_err(|err| { .await.map_err(|err| {
error!("failed to store upgraded gateway key! this connection might be forever broken now: {err}"); error!("failed to store upgraded gateway key! this connection might be forever broken now: {err}");
ClientCoreError::GatewaysDetailsStoreError { source: Box::new(err) } ClientCoreError::GatewaysDetailsStoreError { source: Box::new(err) }
})? })?
} }
@@ -472,7 +446,6 @@ where
.claim_initial_bandwidth() .claim_initial_bandwidth()
.await .await
.map_err(gateway_failure)?; .map_err(gateway_failure)?;
gateway_client gateway_client
.start_listening_for_mixnet_messages() .start_listening_for_mixnet_messages()
.map_err(gateway_failure)?; .map_err(gateway_failure)?;
@@ -489,7 +462,6 @@ where
details_store: &S::GatewaysDetailsStore, details_store: &S::GatewaysDetailsStore,
packet_router: PacketRouter, packet_router: PacketRouter,
stats_reporter: ClientStatsSender, stats_reporter: ClientStatsSender,
#[cfg(unix)] connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
mut shutdown: TaskClient, mut shutdown: TaskClient,
) -> Result<Box<dyn GatewayTransceiver + Send>, ClientCoreError> ) -> Result<Box<dyn GatewayTransceiver + Send>, ClientCoreError>
where where
@@ -521,8 +493,6 @@ where
details_store, details_store,
packet_router, packet_router,
stats_reporter, stats_reporter,
#[cfg(unix)]
connection_fd_callback,
shutdown, shutdown,
) )
.await?; .await?;
@@ -539,15 +509,20 @@ where
// if no custom provider was ... provided ..., create one using nym-api // if no custom provider was ... provided ..., create one using nym-api
custom_provider.unwrap_or_else(|| match config_topology.topology_structure { custom_provider.unwrap_or_else(|| match config_topology.topology_structure {
config::TopologyStructure::NymApi => Box::new(NymApiTopologyProvider::new( config::TopologyStructure::NymApi => Box::new(NymApiTopologyProvider::new(
config_topology, nym_api_provider::Config {
min_mixnode_performance: config_topology.minimum_mixnode_performance,
min_gateway_performance: config_topology.minimum_gateway_performance,
},
nym_api_urls, nym_api_urls,
env!("CARGO_PKG_VERSION").to_string(),
user_agent, user_agent,
)), )),
config::TopologyStructure::GeoAware(group_by) => { config::TopologyStructure::GeoAware(group_by) => {
warn!("using deprecated 'GeoAware' topology provider - this option will be removed very soon"); Box::new(GeoAwareTopologyProvider::new(
nym_api_urls,
#[allow(deprecated)] env!("CARGO_PKG_VERSION").to_string(),
Box::new(crate::client::topology_control::GeoAwareTopologyProvider::new(nym_api_urls, group_by)) group_by,
))
} }
}) })
} }
@@ -558,7 +533,7 @@ where
topology_provider: Box<dyn TopologyProvider + Send + Sync>, topology_provider: Box<dyn TopologyProvider + Send + Sync>,
topology_config: config::Topology, topology_config: config::Topology,
topology_accessor: TopologyAccessor, topology_accessor: TopologyAccessor,
local_gateway: NodeIdentity, local_gateway: &NodeIdentity,
wait_for_gateway: bool, wait_for_gateway: bool,
mut shutdown: TaskClient, mut shutdown: TaskClient,
) -> Result<(), ClientCoreError> { ) -> Result<(), ClientCoreError> {
@@ -590,7 +565,7 @@ where
}; };
if let Err(err) = topology_refresher if let Err(err) = topology_refresher
.ensure_contains_routable_egress(local_gateway) .ensure_contains_gateway(local_gateway)
.await .await
{ {
if let Some(waiting_timeout) = gateway_wait_timeout { if let Some(waiting_timeout) = gateway_wait_timeout {
@@ -645,11 +620,9 @@ where
fn start_mix_traffic_controller( fn start_mix_traffic_controller(
gateway_transceiver: Box<dyn GatewayTransceiver + Send>, gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
shutdown: TaskClient, shutdown: TaskClient,
forget_me: ForgetMe,
) -> BatchMixMessageSender { ) -> BatchMixMessageSender {
info!("Starting mix traffic controller..."); info!("Starting mix traffic controller...");
let (mix_traffic_controller, mix_tx) = let (mix_traffic_controller, mix_tx) = MixTrafficController::new(gateway_transceiver);
MixTrafficController::new(gateway_transceiver, forget_me);
mix_traffic_controller.start_with_shutdown(shutdown); mix_traffic_controller.start_with_shutdown(shutdown);
mix_tx mix_tx
} }
@@ -740,8 +713,7 @@ where
// channels responsible for controlling ack messages // channels responsible for controlling ack messages
let (ack_sender, ack_receiver) = mpsc::unbounded(); let (ack_sender, ack_receiver) = mpsc::unbounded();
let shared_topology_accessor = let shared_topology_accessor = TopologyAccessor::new();
TopologyAccessor::new(self.config.debug.topology.ignore_egress_epoch_role);
// Shutdown notifier for signalling tasks to stop // Shutdown notifier for signalling tasks to stop
let shutdown = self let shutdown = self
@@ -805,8 +777,6 @@ where
&details_store, &details_store,
gateway_packet_router, gateway_packet_router,
stats_reporter.clone(), stats_reporter.clone(),
#[cfg(unix)]
self.connection_fd_callback,
shutdown.fork("gateway_transceiver"), shutdown.fork("gateway_transceiver"),
) )
.await?; .await?;
@@ -832,11 +802,9 @@ where
// that are to be sent to the mixnet. They are used by cover traffic stream and real // that are to be sent to the mixnet. They are used by cover traffic stream and real
// traffic stream. // traffic stream.
// The MixTrafficController then sends the actual traffic // The MixTrafficController then sends the actual traffic
let message_sender = Self::start_mix_traffic_controller( let message_sender = Self::start_mix_traffic_controller(
gateway_transceiver, gateway_transceiver,
shutdown.fork("mix_traffic_controller"), shutdown.fork("mix_traffic_controller"),
self.forget_me,
); );
// Channels that the websocket listener can use to signal downstream to the real traffic // Channels that the websocket listener can use to signal downstream to the real traffic
@@ -163,7 +163,6 @@ impl LoopCoverTrafficStream<OsRng> {
// poisson delay, but is it really a problem? // poisson delay, but is it really a problem?
let topology_permit = self.topology_access.get_read_permit().await; let topology_permit = self.topology_access.get_read_permit().await;
// the ack is sent back to ourselves (and then ignored) // the ack is sent back to ourselves (and then ignored)
let topology_ref = match topology_permit.try_get_valid_topology_ref( let topology_ref = match topology_permit.try_get_valid_topology_ref(
&self.our_full_destination, &self.our_full_destination,
Some(&self.our_full_destination), Some(&self.our_full_destination),
@@ -28,6 +28,7 @@ pub enum InputMessage {
recipient: Recipient, recipient: Recipient,
data: Vec<u8>, data: Vec<u8>,
lane: TransmissionLane, lane: TransmissionLane,
mix_hops: Option<u8>,
}, },
/// Creates a message used for a duplex anonymous communication where the recipient /// Creates a message used for a duplex anonymous communication where the recipient
@@ -43,6 +44,7 @@ pub enum InputMessage {
data: Vec<u8>, data: Vec<u8>,
reply_surbs: u32, reply_surbs: u32,
lane: TransmissionLane, lane: TransmissionLane,
mix_hops: Option<u8>,
}, },
/// Attempt to use our internally received and stored `ReplySurb` to send the message back /// Attempt to use our internally received and stored `ReplySurb` to send the message back
@@ -92,6 +94,29 @@ impl InputMessage {
recipient, recipient,
data, data,
lane, lane,
mix_hops: None,
};
if let Some(packet_type) = packet_type {
InputMessage::new_wrapper(message, packet_type)
} else {
message
}
}
// IMHO `new_regular` should take `mix_hops: Option<u8>` as an argument instead of creating
// this function, but that would potentially break backwards compatibility with the current API
pub fn new_regular_with_custom_hops(
recipient: Recipient,
data: Vec<u8>,
lane: TransmissionLane,
packet_type: Option<PacketType>,
mix_hops: Option<u8>,
) -> Self {
let message = InputMessage::Regular {
recipient,
data,
lane,
mix_hops,
}; };
if let Some(packet_type) = packet_type { if let Some(packet_type) = packet_type {
InputMessage::new_wrapper(message, packet_type) InputMessage::new_wrapper(message, packet_type)
@@ -112,6 +137,7 @@ impl InputMessage {
data, data,
reply_surbs, reply_surbs,
lane, lane,
mix_hops: None,
}; };
if let Some(packet_type) = packet_type { if let Some(packet_type) = packet_type {
InputMessage::new_wrapper(message, packet_type) InputMessage::new_wrapper(message, packet_type)
@@ -128,12 +154,14 @@ impl InputMessage {
reply_surbs: u32, reply_surbs: u32,
lane: TransmissionLane, lane: TransmissionLane,
packet_type: Option<PacketType>, packet_type: Option<PacketType>,
mix_hops: Option<u8>,
) -> Self { ) -> Self {
let message = InputMessage::Anonymous { let message = InputMessage::Anonymous {
recipient, recipient,
data, data,
reply_surbs, reply_surbs,
lane, lane,
mix_hops,
}; };
if let Some(packet_type) = packet_type { if let Some(packet_type) = packet_type {
InputMessage::new_wrapper(message, packet_type) InputMessage::new_wrapper(message, packet_type)
@@ -2,9 +2,8 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::client::mix_traffic::transceiver::GatewayTransceiver; use crate::client::mix_traffic::transceiver::GatewayTransceiver;
use crate::{spawn_future, ForgetMe}; use crate::spawn_future;
use log::*; use log::*;
use nym_gateway_requests::ClientRequest;
use nym_sphinx::forwarding::packet::MixPacket; use nym_sphinx::forwarding::packet::MixPacket;
pub type BatchMixMessageSender = tokio::sync::mpsc::Sender<Vec<MixPacket>>; pub type BatchMixMessageSender = tokio::sync::mpsc::Sender<Vec<MixPacket>>;
@@ -27,14 +26,10 @@ pub struct MixTrafficController {
// TODO: this is temporary work-around. // TODO: this is temporary work-around.
// in long run `gateway_client` will be moved away from `MixTrafficController` anyway. // in long run `gateway_client` will be moved away from `MixTrafficController` anyway.
consecutive_gateway_failure_count: usize, consecutive_gateway_failure_count: usize,
forget_me: ForgetMe,
} }
impl MixTrafficController { impl MixTrafficController {
pub fn new<T>( pub fn new<T>(gateway_transceiver: T) -> (MixTrafficController, BatchMixMessageSender)
gateway_transceiver: T,
forget_me: ForgetMe,
) -> (MixTrafficController, BatchMixMessageSender)
where where
T: GatewayTransceiver + Send + 'static, T: GatewayTransceiver + Send + 'static,
{ {
@@ -45,7 +40,6 @@ impl MixTrafficController {
gateway_transceiver: Box::new(gateway_transceiver), gateway_transceiver: Box::new(gateway_transceiver),
mix_rx: message_receiver, mix_rx: message_receiver,
consecutive_gateway_failure_count: 0, consecutive_gateway_failure_count: 0,
forget_me,
}, },
message_sender, message_sender,
) )
@@ -53,7 +47,6 @@ impl MixTrafficController {
pub fn new_dynamic( pub fn new_dynamic(
gateway_transceiver: Box<dyn GatewayTransceiver + Send>, gateway_transceiver: Box<dyn GatewayTransceiver + Send>,
forget_me: ForgetMe,
) -> (MixTrafficController, BatchMixMessageSender) { ) -> (MixTrafficController, BatchMixMessageSender) {
let (message_sender, message_receiver) = let (message_sender, message_receiver) =
tokio::sync::mpsc::channel(MIX_MESSAGE_RECEIVER_BUFFER_SIZE); tokio::sync::mpsc::channel(MIX_MESSAGE_RECEIVER_BUFFER_SIZE);
@@ -62,7 +55,6 @@ impl MixTrafficController {
gateway_transceiver, gateway_transceiver,
mix_rx: message_receiver, mix_rx: message_receiver,
consecutive_gateway_failure_count: 0, consecutive_gateway_failure_count: 0,
forget_me,
}, },
message_sender, message_sender,
) )
@@ -119,27 +111,7 @@ impl MixTrafficController {
} }
} }
shutdown.recv_timeout().await; shutdown.recv_timeout().await;
if self.forget_me.any() {
log::info!("Sending forget me request to the gateway");
match self
.gateway_transceiver
.send_client_request(ClientRequest::ForgetMe {
client: self.forget_me.client(),
stats: self.forget_me.stats(),
})
.await
{
Ok(_) => {
log::info!("Successfully sent forget me request to the gateway");
}
Err(err) => {
log::error!("Failed to send forget me request to the gateway: {err}");
}
}
}
log::debug!("MixTrafficController: Exiting"); log::debug!("MixTrafficController: Exiting");
}); })
} }
} }
@@ -5,10 +5,8 @@ use async_trait::async_trait;
use log::{debug, error}; use log::{debug, error};
use nym_credential_storage::storage::Storage as CredentialStorage; use nym_credential_storage::storage::Storage as CredentialStorage;
use nym_crypto::asymmetric::identity; use nym_crypto::asymmetric::identity;
use nym_gateway_client::error::GatewayClientError;
use nym_gateway_client::GatewayClient; use nym_gateway_client::GatewayClient;
pub use nym_gateway_client::{GatewayPacketRouter, PacketRouter}; pub use nym_gateway_client::{GatewayPacketRouter, PacketRouter};
use nym_gateway_requests::ClientRequest;
use nym_sphinx::forwarding::packet::MixPacket; use nym_sphinx::forwarding::packet::MixPacket;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient; use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
use std::fmt::Debug; use std::fmt::Debug;
@@ -16,7 +14,7 @@ use std::os::raw::c_int as RawFd;
use thiserror::Error; use thiserror::Error;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use futures::channel::oneshot; use futures::channel::{mpsc, oneshot};
// we need to type erase the error type since we can't have dynamic associated types alongside dynamic dispatch // we need to type erase the error type since we can't have dynamic associated types alongside dynamic dispatch
#[derive(Debug, Error)] #[derive(Debug, Error)]
@@ -28,14 +26,9 @@ fn erase_err<E: std::error::Error + Send + Sync + 'static>(err: E) -> ErasedGate
} }
/// This combines combines the functionalities of being able to send and receive mix packets. /// This combines combines the functionalities of being able to send and receive mix packets.
#[async_trait]
pub trait GatewayTransceiver: GatewaySender + GatewayReceiver { pub trait GatewayTransceiver: GatewaySender + GatewayReceiver {
fn gateway_identity(&self) -> identity::PublicKey; fn gateway_identity(&self) -> identity::PublicKey;
fn ws_fd(&self) -> Option<RawFd>; fn ws_fd(&self) -> Option<RawFd>;
async fn send_client_request(
&mut self,
message: ClientRequest,
) -> Result<(), GatewayClientError>;
} }
/// This trait defines the functionality of sending `MixPacket` into the mixnet, /// This trait defines the functionality of sending `MixPacket` into the mixnet,
@@ -72,7 +65,6 @@ pub trait GatewayReceiver {
} }
// to allow for dynamic dispatch // to allow for dynamic dispatch
#[async_trait]
impl<G: GatewayTransceiver + ?Sized + Send> GatewayTransceiver for Box<G> { impl<G: GatewayTransceiver + ?Sized + Send> GatewayTransceiver for Box<G> {
#[inline] #[inline]
fn gateway_identity(&self) -> identity::PublicKey { fn gateway_identity(&self) -> identity::PublicKey {
@@ -81,13 +73,6 @@ impl<G: GatewayTransceiver + ?Sized + Send> GatewayTransceiver for Box<G> {
fn ws_fd(&self) -> Option<RawFd> { fn ws_fd(&self) -> Option<RawFd> {
(**self).ws_fd() (**self).ws_fd()
} }
async fn send_client_request(
&mut self,
message: ClientRequest,
) -> Result<(), GatewayClientError> {
(**self).send_client_request(message).await
}
} }
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
@@ -106,6 +91,7 @@ impl<G: GatewaySender + ?Sized + Send> GatewaySender for Box<G> {
(**self).batch_send_mix_packets(packets).await (**self).batch_send_mix_packets(packets).await
} }
} }
impl<G: GatewayReceiver + ?Sized> GatewayReceiver for Box<G> { impl<G: GatewayReceiver + ?Sized> GatewayReceiver for Box<G> {
#[inline] #[inline]
fn set_packet_router(&mut self, packet_router: PacketRouter) -> Result<(), ErasedGatewayError> { fn set_packet_router(&mut self, packet_router: PacketRouter) -> Result<(), ErasedGatewayError> {
@@ -125,7 +111,6 @@ impl<C, St> RemoteGateway<C, St> {
} }
} }
#[async_trait]
impl<C, St> GatewayTransceiver for RemoteGateway<C, St> impl<C, St> GatewayTransceiver for RemoteGateway<C, St>
where where
C: DkgQueryClient + Send + Sync, C: DkgQueryClient + Send + Sync,
@@ -138,20 +123,6 @@ where
fn ws_fd(&self) -> Option<RawFd> { fn ws_fd(&self) -> Option<RawFd> {
self.gateway_client.ws_fd() self.gateway_client.ws_fd()
} }
async fn send_client_request(
&mut self,
message: ClientRequest,
) -> Result<(), GatewayClientError> {
if let Some(shared_key) = self.gateway_client.shared_key() {
self.gateway_client
.send_websocket_message(message.encrypt(&*shared_key)?)
.await?;
Ok(())
} else {
Err(GatewayClientError::ConnectionInInvalidState)
}
}
} }
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
@@ -199,7 +170,7 @@ pub struct LocalGateway {
// 'sender' part // 'sender' part
/// Channel responsible for taking mix packets and forwarding them further into the further mixnet layers. /// Channel responsible for taking mix packets and forwarding them further into the further mixnet layers.
packet_forwarder: nym_mixnet_client::forwarder::MixForwardingSender, packet_forwarder: mpsc::UnboundedSender<MixPacket>,
// 'receiver' part // 'receiver' part
packet_router_tx: Option<oneshot::Sender<PacketRouter>>, packet_router_tx: Option<oneshot::Sender<PacketRouter>>,
@@ -209,7 +180,7 @@ pub struct LocalGateway {
impl LocalGateway { impl LocalGateway {
pub fn new( pub fn new(
local_identity: identity::PublicKey, local_identity: identity::PublicKey,
packet_forwarder: nym_mixnet_client::forwarder::MixForwardingSender, packet_forwarder: mpsc::UnboundedSender<MixPacket>,
packet_router_tx: oneshot::Sender<PacketRouter>, packet_router_tx: oneshot::Sender<PacketRouter>,
) -> Self { ) -> Self {
LocalGateway { LocalGateway {
@@ -224,7 +195,6 @@ impl LocalGateway {
mod nonwasm_sealed { mod nonwasm_sealed {
use super::*; use super::*;
#[async_trait]
impl GatewayTransceiver for LocalGateway { impl GatewayTransceiver for LocalGateway {
fn gateway_identity(&self) -> identity::PublicKey { fn gateway_identity(&self) -> identity::PublicKey {
self.local_identity self.local_identity
@@ -232,20 +202,14 @@ mod nonwasm_sealed {
fn ws_fd(&self) -> Option<RawFd> { fn ws_fd(&self) -> Option<RawFd> {
None None
} }
async fn send_client_request(
&mut self,
_message: ClientRequest,
) -> Result<(), GatewayClientError> {
Ok(())
}
} }
#[async_trait] #[async_trait]
impl GatewaySender for LocalGateway { impl GatewaySender for LocalGateway {
async fn send_mix_packet(&mut self, packet: MixPacket) -> Result<(), ErasedGatewayError> { async fn send_mix_packet(&mut self, packet: MixPacket) -> Result<(), ErasedGatewayError> {
self.packet_forwarder self.packet_forwarder
.forward_packet(packet) .unbounded_send(packet)
.map_err(|err| err.into_send_error())
.map_err(erase_err) .map_err(erase_err)
} }
} }
@@ -306,7 +270,6 @@ impl GatewaySender for MockGateway {
} }
} }
#[async_trait]
impl GatewayTransceiver for MockGateway { impl GatewayTransceiver for MockGateway {
fn gateway_identity(&self) -> identity::PublicKey { fn gateway_identity(&self) -> identity::PublicKey {
self.dummy_identity self.dummy_identity
@@ -314,11 +277,4 @@ impl GatewayTransceiver for MockGateway {
fn ws_fd(&self) -> Option<RawFd> { fn ws_fd(&self) -> Option<RawFd> {
None None
} }
async fn send_client_request(
&mut self,
_message: ClientRequest,
) -> Result<(), GatewayClientError> {
Ok(())
}
} }
@@ -30,8 +30,7 @@ pub(crate) enum Action {
InsertPending(Vec<PendingAcknowledgement>), InsertPending(Vec<PendingAcknowledgement>),
/// Removes given `PendingAcknowledgement` from the 'shared' state. Also cancels the retransmission timer. /// Removes given `PendingAcknowledgement` from the 'shared' state. Also cancels the retransmission timer.
/// Initiated by `AcknowledgementListener` upon receiving the acknowledgement. Also by `RetransmissionRequestListener` /// Initiated by `AcknowledgementListener`
/// upon deciding to abandon the data.
RemovePending(FragmentIdentifier), RemovePending(FragmentIdentifier),
/// Starts the retransmission timer on given `PendingAcknowledgement` with the `Duration` based on /// Starts the retransmission timer on given `PendingAcknowledgement` with the `Duration` based on
@@ -42,7 +41,7 @@ pub(crate) enum Action {
/// Updates the expected delay of given `PendingAcknowledgement` with the new provided `SphinxDelay`. /// Updates the expected delay of given `PendingAcknowledgement` with the new provided `SphinxDelay`.
/// Initiated by `RetransmissionRequestListener` /// Initiated by `RetransmissionRequestListener`
UpdatePendingAck(FragmentIdentifier, SphinxDelay), UpdateDelay(FragmentIdentifier, SphinxDelay),
} }
impl Action { impl Action {
@@ -58,8 +57,8 @@ impl Action {
Action::StartTimer(frag_id) Action::StartTimer(frag_id)
} }
pub(crate) fn new_update_pending_ack(frag_id: FragmentIdentifier, delay: SphinxDelay) -> Self { pub(crate) fn new_update_delay(frag_id: FragmentIdentifier, delay: SphinxDelay) -> Self {
Action::UpdatePendingAck(frag_id, delay) Action::UpdateDelay(frag_id, delay)
} }
} }
@@ -136,7 +135,7 @@ impl ActionController {
} }
fn handle_start_timer(&mut self, frag_id: FragmentIdentifier) { fn handle_start_timer(&mut self, frag_id: FragmentIdentifier) {
trace!("{frag_id} is starting its timer"); trace!("{} is starting its timer", frag_id);
if let Some((pending_ack_data, queue_key)) = self.pending_acks_data.get_mut(&frag_id) { if let Some((pending_ack_data, queue_key)) = self.pending_acks_data.get_mut(&frag_id) {
// the fact that this branch is now POSSIBLE is a sign of a need to refactor this whole // the fact that this branch is now POSSIBLE is a sign of a need to refactor this whole
@@ -194,7 +193,7 @@ impl ActionController {
// initiated basically as a first step of retransmission. At first data has its delay updated // initiated basically as a first step of retransmission. At first data has its delay updated
// (as new sphinx packet was created with new expected delivery time) // (as new sphinx packet was created with new expected delivery time)
fn handle_update_pending_ack(&mut self, frag_id: FragmentIdentifier, delay: SphinxDelay) { fn handle_update_delay(&mut self, frag_id: FragmentIdentifier, delay: SphinxDelay) {
trace!("{} is updating its delay", frag_id); trace!("{} is updating its delay", frag_id);
// TODO: is it possible to solve this without either locking or temporarily removing the value? // TODO: is it possible to solve this without either locking or temporarily removing the value?
if let Some((pending_ack_data, queue_key)) = self.pending_acks_data.remove(&frag_id) { if let Some((pending_ack_data, queue_key)) = self.pending_acks_data.remove(&frag_id) {
@@ -203,7 +202,7 @@ impl ActionController {
// reference to this Arc. HOWEVER, before the Action was pushed onto the queue, the reference // reference to this Arc. HOWEVER, before the Action was pushed onto the queue, the reference
// was dropped hence this unwrap is safe. // was dropped hence this unwrap is safe.
let mut inner_data = Arc::try_unwrap(pending_ack_data).unwrap(); let mut inner_data = Arc::try_unwrap(pending_ack_data).unwrap();
inner_data.update_retransmitted(delay); inner_data.update_delay(delay);
self.pending_acks_data self.pending_acks_data
.insert(frag_id, (Arc::new(inner_data), queue_key)); .insert(frag_id, (Arc::new(inner_data), queue_key));
@@ -226,7 +225,7 @@ impl ActionController {
// about it. Perhaps just reschedule it at later point? // about it. Perhaps just reschedule it at later point?
let frag_id = expired_ack.into_inner(); let frag_id = expired_ack.into_inner();
trace!("{frag_id} has expired"); trace!("{} has expired", frag_id);
if let Some((pending_ack_data, queue_key)) = self.pending_acks_data.get_mut(&frag_id) { if let Some((pending_ack_data, queue_key)) = self.pending_acks_data.get_mut(&frag_id) {
if queue_key.is_none() { if queue_key.is_none() {
@@ -259,9 +258,7 @@ impl ActionController {
Action::InsertPending(pending_acks) => self.handle_insert(pending_acks), Action::InsertPending(pending_acks) => self.handle_insert(pending_acks),
Action::RemovePending(frag_id) => self.handle_remove(frag_id), Action::RemovePending(frag_id) => self.handle_remove(frag_id),
Action::StartTimer(frag_id) => self.handle_start_timer(frag_id), Action::StartTimer(frag_id) => self.handle_start_timer(frag_id),
Action::UpdatePendingAck(frag_id, delay) => { Action::UpdateDelay(frag_id, delay) => self.handle_update_delay(frag_id, delay),
self.handle_update_pending_ack(frag_id, delay)
}
} }
} }
@@ -73,10 +73,11 @@ where
content: Vec<u8>, content: Vec<u8>,
lane: TransmissionLane, lane: TransmissionLane,
packet_type: PacketType, packet_type: PacketType,
mix_hops: Option<u8>,
) { ) {
if let Err(err) = self if let Err(err) = self
.message_handler .message_handler
.try_send_plain_message(recipient, content, lane, packet_type) .try_send_plain_message(recipient, content, lane, packet_type, mix_hops)
.await .await
{ {
warn!("failed to send a plain message - {err}") warn!("failed to send a plain message - {err}")
@@ -90,10 +91,18 @@ where
reply_surbs: u32, reply_surbs: u32,
lane: TransmissionLane, lane: TransmissionLane,
packet_type: PacketType, packet_type: PacketType,
mix_hops: Option<u8>,
) { ) {
if let Err(err) = self if let Err(err) = self
.message_handler .message_handler
.try_send_message_with_reply_surbs(recipient, content, reply_surbs, lane, packet_type) .try_send_message_with_reply_surbs(
recipient,
content,
reply_surbs,
lane,
packet_type,
mix_hops,
)
.await .await
{ {
warn!("failed to send a repliable message - {err}") warn!("failed to send a repliable message - {err}")
@@ -106,8 +115,9 @@ where
recipient, recipient,
data, data,
lane, lane,
mix_hops,
} => { } => {
self.handle_plain_message(recipient, data, lane, PacketType::Mix) self.handle_plain_message(recipient, data, lane, PacketType::Mix, mix_hops)
.await .await
} }
InputMessage::Anonymous { InputMessage::Anonymous {
@@ -115,9 +125,17 @@ where
data, data,
reply_surbs, reply_surbs,
lane, lane,
mix_hops,
} => { } => {
self.handle_repliable_message(recipient, data, reply_surbs, lane, PacketType::Mix) self.handle_repliable_message(
.await recipient,
data,
reply_surbs,
lane,
PacketType::Mix,
mix_hops,
)
.await
} }
InputMessage::Reply { InputMessage::Reply {
recipient_tag, recipient_tag,
@@ -135,8 +153,9 @@ where
recipient, recipient,
data, data,
lane, lane,
mix_hops,
} => { } => {
self.handle_plain_message(recipient, data, lane, packet_type) self.handle_plain_message(recipient, data, lane, packet_type, mix_hops)
.await .await
} }
InputMessage::Anonymous { InputMessage::Anonymous {
@@ -144,9 +163,17 @@ where
data, data,
reply_surbs, reply_surbs,
lane, lane,
mix_hops,
} => { } => {
self.handle_repliable_message(recipient, data, reply_surbs, lane, packet_type) self.handle_repliable_message(
.await recipient,
data,
reply_surbs,
lane,
packet_type,
mix_hops,
)
.await
} }
InputMessage::Reply { InputMessage::Reply {
recipient_tag, recipient_tag,
@@ -70,7 +70,7 @@ pub(crate) struct PendingAcknowledgement {
message_chunk: Fragment, message_chunk: Fragment,
delay: SphinxDelay, delay: SphinxDelay,
destination: PacketDestination, destination: PacketDestination,
retransmissions: u32, mix_hops: Option<u8>,
} }
impl PendingAcknowledgement { impl PendingAcknowledgement {
@@ -79,12 +79,13 @@ impl PendingAcknowledgement {
message_chunk: Fragment, message_chunk: Fragment,
delay: SphinxDelay, delay: SphinxDelay,
recipient: Recipient, recipient: Recipient,
mix_hops: Option<u8>,
) -> Self { ) -> Self {
PendingAcknowledgement { PendingAcknowledgement {
message_chunk, message_chunk,
delay, delay,
destination: PacketDestination::KnownRecipient(recipient.into()), destination: PacketDestination::KnownRecipient(recipient.into()),
retransmissions: 0, mix_hops,
} }
} }
@@ -101,7 +102,9 @@ impl PendingAcknowledgement {
recipient_tag, recipient_tag,
extra_surb_request, extra_surb_request,
}, },
retransmissions: 0, // Messages sent using SURBs are using the number of mix hops set by the recipient when
// they provided the SURBs, so it doesn't make sense to include it here.
mix_hops: None,
} }
} }
@@ -113,9 +116,8 @@ impl PendingAcknowledgement {
self.message_chunk.clone() self.message_chunk.clone()
} }
fn update_retransmitted(&mut self, new_delay: SphinxDelay) { fn update_delay(&mut self, new_delay: SphinxDelay) {
self.delay = new_delay; self.delay = new_delay;
self.retransmissions += 1;
} }
} }
@@ -161,9 +163,6 @@ impl AcknowledgementControllerConnectors {
/// Configurable parameters of the `AcknowledgementController` /// Configurable parameters of the `AcknowledgementController`
pub(super) struct Config { pub(super) struct Config {
/// Specify how many times particular packet can be retransmitted
maximum_retransmissions: Option<u32>,
/// Given ack timeout in the form a * BASE_DELAY + b, it specifies the additive part `b` /// Given ack timeout in the form a * BASE_DELAY + b, it specifies the additive part `b`
ack_wait_addition: Duration, ack_wait_addition: Duration,
@@ -175,13 +174,8 @@ pub(super) struct Config {
} }
impl Config { impl Config {
pub(super) fn new( pub(super) fn new(ack_wait_addition: Duration, ack_wait_multiplier: f64) -> Self {
maximum_retransmissions: Option<u32>,
ack_wait_addition: Duration,
ack_wait_multiplier: f64,
) -> Self {
Config { Config {
maximum_retransmissions,
ack_wait_addition, ack_wait_addition,
ack_wait_multiplier, ack_wait_multiplier,
packet_size: Default::default(), packet_size: Default::default(),
@@ -244,7 +238,6 @@ where
// will listen for any ack timeouts and trigger retransmission // will listen for any ack timeouts and trigger retransmission
let retransmission_request_listener = RetransmissionRequestListener::new( let retransmission_request_listener = RetransmissionRequestListener::new(
config.maximum_retransmissions,
connectors.ack_action_sender.clone(), connectors.ack_action_sender.clone(),
message_handler, message_handler,
retransmission_rx, retransmission_rx,
@@ -20,7 +20,6 @@ use std::sync::{Arc, Weak};
// responsible for packet retransmission upon fired timer // responsible for packet retransmission upon fired timer
pub(super) struct RetransmissionRequestListener<R> { pub(super) struct RetransmissionRequestListener<R> {
maximum_retransmissions: Option<u32>,
action_sender: AckActionSender, action_sender: AckActionSender,
message_handler: MessageHandler<R>, message_handler: MessageHandler<R>,
request_receiver: RetransmissionRequestReceiver, request_receiver: RetransmissionRequestReceiver,
@@ -32,14 +31,12 @@ where
R: CryptoRng + Rng, R: CryptoRng + Rng,
{ {
pub(super) fn new( pub(super) fn new(
maximum_retransmissions: Option<u32>,
action_sender: AckActionSender, action_sender: AckActionSender,
message_handler: MessageHandler<R>, message_handler: MessageHandler<R>,
request_receiver: RetransmissionRequestReceiver, request_receiver: RetransmissionRequestReceiver,
reply_controller_sender: ReplyControllerSender, reply_controller_sender: ReplyControllerSender,
) -> Self { ) -> Self {
RetransmissionRequestListener { RetransmissionRequestListener {
maximum_retransmissions,
action_sender, action_sender,
message_handler, message_handler,
request_receiver, request_receiver,
@@ -52,12 +49,18 @@ where
packet_recipient: Recipient, packet_recipient: Recipient,
chunk_data: Fragment, chunk_data: Fragment,
packet_type: PacketType, packet_type: PacketType,
mix_hops: Option<u8>,
) -> Result<PreparedFragment, PreparationError> { ) -> Result<PreparedFragment, PreparationError> {
debug!("retransmitting normal packet..."); debug!("retransmitting normal packet...");
// TODO: Figure out retransmission packet type signaling // TODO: Figure out retransmission packet type signaling
self.message_handler self.message_handler
.try_prepare_single_chunk_for_sending(packet_recipient, chunk_data, packet_type) .try_prepare_single_chunk_for_sending(
packet_recipient,
chunk_data,
packet_type,
mix_hops,
)
.await .await
} }
@@ -74,18 +77,6 @@ where
} }
}; };
let frag_id = timed_out_ack.message_chunk.fragment_identifier();
if let Some(limit) = self.maximum_retransmissions {
if timed_out_ack.retransmissions >= limit {
warn!("reached maximum number of allowed retransmissions for the packet");
self.action_sender
.unbounded_send(Action::new_remove(frag_id))
.unwrap();
return;
}
}
let maybe_prepared_fragment = match &timed_out_ack.destination { let maybe_prepared_fragment = match &timed_out_ack.destination {
PacketDestination::Anonymous { PacketDestination::Anonymous {
recipient_tag, recipient_tag,
@@ -104,11 +95,14 @@ where
**recipient, **recipient,
timed_out_ack.message_chunk.clone(), timed_out_ack.message_chunk.clone(),
packet_type, packet_type,
timed_out_ack.mix_hops,
) )
.await .await
} }
}; };
let frag_id = timed_out_ack.message_chunk.fragment_identifier();
let prepared_fragment = match maybe_prepared_fragment { let prepared_fragment = match maybe_prepared_fragment {
Ok(prepared_fragment) => prepared_fragment, Ok(prepared_fragment) => prepared_fragment,
Err(err) => { Err(err) => {
@@ -142,7 +136,7 @@ where
// with the additional poisson delay. // with the additional poisson delay.
// And since Actions are executed in order `UpdateTimer` will HAVE TO be executed before `StartTimer` // And since Actions are executed in order `UpdateTimer` will HAVE TO be executed before `StartTimer`
self.action_sender self.action_sender
.unbounded_send(Action::new_update_pending_ack(frag_id, new_delay)) .unbounded_send(Action::new_update_delay(frag_id, new_delay))
.unwrap(); .unwrap();
// send to `OutQueueControl` to eventually send to the mix network // send to `OutQueueControl` to eventually send to the mix network
@@ -15,11 +15,11 @@ use nym_sphinx::anonymous_replies::requests::{AnonymousSenderTag, RepliableMessa
use nym_sphinx::anonymous_replies::{ReplySurb, SurbEncryptionKey}; use nym_sphinx::anonymous_replies::{ReplySurb, SurbEncryptionKey};
use nym_sphinx::chunking::fragment::{Fragment, FragmentIdentifier}; use nym_sphinx::chunking::fragment::{Fragment, FragmentIdentifier};
use nym_sphinx::message::NymMessage; use nym_sphinx::message::NymMessage;
use nym_sphinx::params::{PacketSize, PacketType}; use nym_sphinx::params::{PacketSize, PacketType, DEFAULT_NUM_MIX_HOPS};
use nym_sphinx::preparer::{MessagePreparer, PreparedFragment}; use nym_sphinx::preparer::{MessagePreparer, PreparedFragment};
use nym_sphinx::Delay; use nym_sphinx::Delay;
use nym_task::connections::TransmissionLane; use nym_task::connections::TransmissionLane;
use nym_topology::{NymRouteProvider, NymTopologyError}; use nym_topology::{NymTopology, NymTopologyError};
use rand::{CryptoRng, Rng}; use rand::{CryptoRng, Rng};
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
@@ -91,15 +91,16 @@ pub(crate) struct Config {
/// and surb-based are going to be sent. /// and surb-based are going to be sent.
sender_address: Recipient, sender_address: Recipient,
/// Specify whether route selection should be determined by the packet header.
deterministic_route_selection: bool,
/// Average delay a data packet is going to get delay at a single mixnode. /// Average delay a data packet is going to get delay at a single mixnode.
average_packet_delay: Duration, average_packet_delay: Duration,
/// Average delay an acknowledgement packet is going to get delay at a single mixnode. /// Average delay an acknowledgement packet is going to get delay at a single mixnode.
average_ack_delay: Duration, average_ack_delay: Duration,
/// Number of mix hops each packet ('real' message, ack, reply) is expected to take.
/// Note that it does not include gateway hops.
num_mix_hops: u8,
/// Primary predefined packet size used for the encapsulated messages. /// Primary predefined packet size used for the encapsulated messages.
primary_packet_size: PacketSize, primary_packet_size: PacketSize,
@@ -113,19 +114,25 @@ impl Config {
sender_address: Recipient, sender_address: Recipient,
average_packet_delay: Duration, average_packet_delay: Duration,
average_ack_delay: Duration, average_ack_delay: Duration,
deterministic_route_selection: bool,
) -> Self { ) -> Self {
Config { Config {
ack_key, ack_key,
sender_address, sender_address,
deterministic_route_selection,
average_packet_delay, average_packet_delay,
average_ack_delay, average_ack_delay,
num_mix_hops: DEFAULT_NUM_MIX_HOPS,
primary_packet_size: PacketSize::default(), primary_packet_size: PacketSize::default(),
secondary_packet_size: None, secondary_packet_size: None,
} }
} }
/// Allows setting non-default number of expected mix hops in the network.
#[allow(dead_code)]
pub fn with_mix_hops(mut self, hops: u8) -> Self {
self.num_mix_hops = hops;
self
}
/// Allows setting non-default size of the sphinx packets sent out. /// Allows setting non-default size of the sphinx packets sent out.
pub fn with_custom_primary_packet_size(mut self, packet_size: PacketSize) -> Self { pub fn with_custom_primary_packet_size(mut self, packet_size: PacketSize) -> Self {
self.primary_packet_size = packet_size; self.primary_packet_size = packet_size;
@@ -169,11 +176,12 @@ where
{ {
let message_preparer = MessagePreparer::new( let message_preparer = MessagePreparer::new(
rng, rng,
config.deterministic_route_selection,
config.sender_address, config.sender_address,
config.average_packet_delay, config.average_packet_delay,
config.average_ack_delay, config.average_ack_delay,
); )
.with_mix_hops(config.num_mix_hops);
MessageHandler { MessageHandler {
config, config,
rng, rng,
@@ -202,7 +210,7 @@ where
fn get_topology<'a>( fn get_topology<'a>(
&self, &self,
permit: &'a TopologyReadPermit<'a>, permit: &'a TopologyReadPermit<'a>,
) -> Result<&'a NymRouteProvider, PreparationError> { ) -> Result<&'a NymTopology, PreparationError> {
match permit.try_get_valid_topology_ref(&self.config.sender_address, None) { match permit.try_get_valid_topology_ref(&self.config.sender_address, None) {
Ok(topology_ref) => Ok(topology_ref), Ok(topology_ref) => Ok(topology_ref),
Err(err) => { Err(err) => {
@@ -219,8 +227,9 @@ where
return self.config.primary_packet_size; return self.config.primary_packet_size;
}; };
let primary_count = msg.required_packets(self.config.primary_packet_size); let primary_count =
let secondary_count = msg.required_packets(secondary_packet); msg.required_packets(self.config.primary_packet_size, self.config.num_mix_hops);
let secondary_count = msg.required_packets(secondary_packet, self.config.num_mix_hops);
trace!("This message would require: {primary_count} primary packets or {secondary_count} secondary packets..."); trace!("This message would require: {primary_count} primary packets or {secondary_count} secondary packets...");
// if there would be no benefit in using the secondary packet - use the primary (duh) // if there would be no benefit in using the secondary packet - use the primary (duh)
@@ -409,9 +418,10 @@ where
message: Vec<u8>, message: Vec<u8>,
lane: TransmissionLane, lane: TransmissionLane,
packet_type: PacketType, packet_type: PacketType,
mix_hops: Option<u8>,
) -> Result<(), PreparationError> { ) -> Result<(), PreparationError> {
let message = NymMessage::new_plain(message); let message = NymMessage::new_plain(message);
self.try_split_and_send_non_reply_message(message, recipient, lane, packet_type) self.try_split_and_send_non_reply_message(message, recipient, lane, packet_type, mix_hops)
.await .await
} }
@@ -421,6 +431,7 @@ where
recipient: Recipient, recipient: Recipient,
lane: TransmissionLane, lane: TransmissionLane,
packet_type: PacketType, packet_type: PacketType,
mix_hops: Option<u8>,
) -> Result<(), PreparationError> { ) -> Result<(), PreparationError> {
debug!("Sending non-reply message with packet type {packet_type}"); debug!("Sending non-reply message with packet type {packet_type}");
// TODO: I really dislike existence of this assertion, it implies code has to be re-organised // TODO: I really dislike existence of this assertion, it implies code has to be re-organised
@@ -453,6 +464,7 @@ where
&self.config.ack_key, &self.config.ack_key,
&recipient, &recipient,
packet_type, packet_type,
mix_hops,
)?; )?;
let real_message = RealMessage::new( let real_message = RealMessage::new(
@@ -460,7 +472,8 @@ where
Some(fragment.fragment_identifier()), Some(fragment.fragment_identifier()),
); );
let delay = prepared_fragment.total_delay; let delay = prepared_fragment.total_delay;
let pending_ack = PendingAcknowledgement::new_known(fragment, delay, recipient); let pending_ack =
PendingAcknowledgement::new_known(fragment, delay, recipient, mix_hops);
real_messages.push(real_message); real_messages.push(real_message);
pending_acks.push(pending_ack); pending_acks.push(pending_ack);
@@ -477,6 +490,7 @@ where
recipient: Recipient, recipient: Recipient,
amount: u32, amount: u32,
packet_type: PacketType, packet_type: PacketType,
mix_hops: Option<u8>,
) -> Result<(), PreparationError> { ) -> Result<(), PreparationError> {
debug!("Sending additional reply SURBs with packet type {packet_type}"); debug!("Sending additional reply SURBs with packet type {packet_type}");
let sender_tag = self.get_or_create_sender_tag(&recipient); let sender_tag = self.get_or_create_sender_tag(&recipient);
@@ -493,6 +507,7 @@ where
recipient, recipient,
TransmissionLane::AdditionalReplySurbs, TransmissionLane::AdditionalReplySurbs,
packet_type, packet_type,
mix_hops,
) )
.await?; .await?;
@@ -509,6 +524,7 @@ where
num_reply_surbs: u32, num_reply_surbs: u32,
lane: TransmissionLane, lane: TransmissionLane,
packet_type: PacketType, packet_type: PacketType,
mix_hops: Option<u8>,
) -> Result<(), SurbWrappedPreparationError> { ) -> Result<(), SurbWrappedPreparationError> {
debug!("Sending message with reply SURBs with packet type {packet_type}"); debug!("Sending message with reply SURBs with packet type {packet_type}");
let sender_tag = self.get_or_create_sender_tag(&recipient); let sender_tag = self.get_or_create_sender_tag(&recipient);
@@ -519,7 +535,7 @@ where
let message = let message =
NymMessage::new_repliable(RepliableMessage::new_data(message, sender_tag, reply_surbs)); NymMessage::new_repliable(RepliableMessage::new_data(message, sender_tag, reply_surbs));
self.try_split_and_send_non_reply_message(message, recipient, lane, packet_type) self.try_split_and_send_non_reply_message(message, recipient, lane, packet_type, mix_hops)
.await?; .await?;
log::trace!("storing {} reply keys", reply_keys.len()); log::trace!("storing {} reply keys", reply_keys.len());
@@ -533,18 +549,23 @@ where
recipient: Recipient, recipient: Recipient,
chunk: Fragment, chunk: Fragment,
packet_type: PacketType, packet_type: PacketType,
mix_hops: Option<u8>,
) -> Result<PreparedFragment, PreparationError> { ) -> Result<PreparedFragment, PreparationError> {
debug!("Sending single chunk with packet type {packet_type}"); debug!("Sending single chunk with packet type {packet_type}");
let topology_permit = self.topology_access.get_read_permit().await; let topology_permit = self.topology_access.get_read_permit().await;
let topology = self.get_topology(&topology_permit)?; let topology = self.get_topology(&topology_permit)?;
let prepared_fragment = self.message_preparer.prepare_chunk_for_sending( let prepared_fragment = self
chunk, .message_preparer
topology, .prepare_chunk_for_sending(
&self.config.ack_key, chunk,
&recipient, topology,
packet_type, &self.config.ack_key,
)?; &recipient,
packet_type,
mix_hops,
)
.unwrap();
Ok(prepared_fragment) Ok(prepared_fragment)
} }
@@ -597,20 +618,23 @@ where
Err(err) => return Err(err.return_surbs(vec![reply_surb])), Err(err) => return Err(err.return_surbs(vec![reply_surb])),
}; };
let prepared_fragment = self.message_preparer.prepare_reply_chunk_for_sending( let prepared_fragment = self
chunk, .message_preparer
topology, .prepare_reply_chunk_for_sending(
&self.config.ack_key, chunk,
reply_surb, topology,
PacketType::Mix, &self.config.ack_key,
)?; reply_surb,
PacketType::Mix,
)
.unwrap();
Ok(prepared_fragment) Ok(prepared_fragment)
} }
pub(crate) fn update_ack_delay(&self, id: FragmentIdentifier, new_delay: Delay) { pub(crate) fn update_ack_delay(&self, id: FragmentIdentifier, new_delay: Delay) {
self.action_sender self.action_sender
.unbounded_send(Action::UpdatePendingAck(id, new_delay)) .unbounded_send(Action::UpdateDelay(id, new_delay))
.expect("action control task has died") .expect("action control task has died")
} }
@@ -9,12 +9,10 @@ use self::{
acknowledgement_control::AcknowledgementController, real_traffic_stream::OutQueueControl, acknowledgement_control::AcknowledgementController, real_traffic_stream::OutQueueControl,
}; };
use crate::client::real_messages_control::message_handler::MessageHandler; use crate::client::real_messages_control::message_handler::MessageHandler;
use crate::client::replies::reply_controller;
use crate::client::replies::reply_controller::{ use crate::client::replies::reply_controller::{
ReplyController, ReplyControllerReceiver, ReplyControllerSender, ReplyController, ReplyControllerReceiver, ReplyControllerSender,
}; };
use crate::client::replies::reply_storage::CombinedReplyStorage; use crate::client::replies::reply_storage::CombinedReplyStorage;
use crate::config;
use crate::{ use crate::{
client::{ client::{
inbound_messages::InputMessageReceiver, mix_traffic::BatchMixMessageSender, inbound_messages::InputMessageReceiver, mix_traffic::BatchMixMessageSender,
@@ -29,13 +27,16 @@ use nym_gateway_client::AcknowledgementReceiver;
use nym_sphinx::acknowledgements::AckKey; use nym_sphinx::acknowledgements::AckKey;
use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::params::PacketType; use nym_sphinx::params::PacketType;
use nym_statistics_common::clients::ClientStatsSender;
use nym_task::connections::{ConnectionCommandReceiver, LaneQueueLengths}; use nym_task::connections::{ConnectionCommandReceiver, LaneQueueLengths};
use rand::{rngs::OsRng, CryptoRng, Rng}; use rand::{rngs::OsRng, CryptoRng, Rng};
use std::sync::Arc; use std::sync::Arc;
use crate::client::replies::reply_controller;
use crate::config;
pub(crate) use acknowledgement_control::{AckActionSender, Action}; pub(crate) use acknowledgement_control::{AckActionSender, Action};
use nym_statistics_common::clients::ClientStatsSender;
pub(crate) mod acknowledgement_control; pub(crate) mod acknowledgement_control;
pub(crate) mod message_handler; pub(crate) mod message_handler;
pub(crate) mod real_traffic_stream; pub(crate) mod real_traffic_stream;
@@ -64,7 +65,6 @@ pub struct Config {
impl<'a> From<&'a Config> for acknowledgement_control::Config { impl<'a> From<&'a Config> for acknowledgement_control::Config {
fn from(cfg: &'a Config) -> Self { fn from(cfg: &'a Config) -> Self {
acknowledgement_control::Config::new( acknowledgement_control::Config::new(
cfg.traffic.maximum_number_of_retransmissions,
cfg.acks.ack_wait_addition, cfg.acks.ack_wait_addition,
cfg.acks.ack_wait_multiplier, cfg.acks.ack_wait_multiplier,
) )
@@ -97,7 +97,6 @@ impl<'a> From<&'a Config> for message_handler::Config {
cfg.self_recipient, cfg.self_recipient,
cfg.traffic.average_packet_delay, cfg.traffic.average_packet_delay,
cfg.acks.average_ack_delay, cfg.acks.average_ack_delay,
cfg.traffic.deterministic_route_selection,
) )
.with_custom_primary_packet_size(cfg.traffic.primary_packet_size) .with_custom_primary_packet_size(cfg.traffic.primary_packet_size)
.with_custom_secondary_packet_size(cfg.traffic.secondary_packet_size) .with_custom_secondary_packet_size(cfg.traffic.secondary_packet_size)
@@ -230,7 +230,6 @@ where
// poisson delay, but is it really a problem? // poisson delay, but is it really a problem?
let topology_permit = self.topology_access.get_read_permit().await; let topology_permit = self.topology_access.get_read_permit().await;
// the ack is sent back to ourselves (and then ignored) // the ack is sent back to ourselves (and then ignored)
let topology_ref = match topology_permit.try_get_valid_topology_ref( let topology_ref = match topology_permit.try_get_valid_topology_ref(
&self.config.our_full_destination, &self.config.our_full_destination,
Some(&self.config.our_full_destination), Some(&self.config.our_full_destination),
@@ -70,10 +70,7 @@ impl SendingDelayController {
lower_bound, lower_bound,
multiplier_elevated_counter: 0, multiplier_elevated_counter: 0,
time_when_logged_about_elevated_multiplier: now time_when_logged_about_elevated_multiplier: now
.checked_sub(Duration::from_secs( - Duration::from_secs(INTERVAL_BETWEEN_WARNING_ABOUT_ELEVATED_MULTIPLIER_SECS),
INTERVAL_BETWEEN_WARNING_ABOUT_ELEVATED_MULTIPLIER_SECS,
))
.unwrap_or(now),
time_when_changed: now, time_when_changed: now,
time_when_backpressure_detected: now, time_when_backpressure_detected: now,
} }
@@ -516,6 +516,7 @@ where
recipient, recipient,
to_send, to_send,
nym_sphinx::params::PacketType::Mix, nym_sphinx::params::PacketType::Mix,
self.config.reply_surbs.surb_mix_hops,
) )
.await .await
{ {
@@ -16,14 +16,14 @@
#![warn(clippy::todo)] #![warn(clippy::todo)]
#![warn(clippy::dbg_macro)] #![warn(clippy::dbg_macro)]
use futures::StreamExt; use std::time::Duration;
use nym_client_core_config_types::StatsReporting; use nym_client_core_config_types::StatsReporting;
use nym_sphinx::addressing::Recipient; use nym_sphinx::addressing::Recipient;
use nym_statistics_common::clients::{ use nym_statistics_common::clients::{
ClientStatsController, ClientStatsReceiver, ClientStatsSender, ClientStatsController, ClientStatsReceiver, ClientStatsSender,
}; };
use nym_task::connections::TransmissionLane; use nym_task::connections::TransmissionLane;
use std::time::Duration;
use crate::{ use crate::{
client::inbound_messages::{InputMessage, InputMessageSender}, client::inbound_messages::{InputMessage, InputMessageSender},
@@ -94,32 +94,10 @@ impl StatisticsControl {
async fn run_with_shutdown(&mut self, mut task_client: nym_task::TaskClient) { async fn run_with_shutdown(&mut self, mut task_client: nym_task::TaskClient) {
log::debug!("Started StatisticsControl with graceful shutdown support"); log::debug!("Started StatisticsControl with graceful shutdown support");
#[cfg(not(target_arch = "wasm32"))] let mut stats_report_interval =
let mut stats_report_interval = tokio_stream::wrappers::IntervalStream::new( tokio::time::interval(self.reporting_config.reporting_interval);
tokio::time::interval(self.reporting_config.reporting_interval), let mut local_report_interval = tokio::time::interval(LOCAL_REPORT_INTERVAL);
); let mut snapshot_interval = tokio::time::interval(SNAPSHOT_INTERVAL);
#[cfg(not(target_arch = "wasm32"))]
let mut local_report_interval = tokio_stream::wrappers::IntervalStream::new(
tokio::time::interval(LOCAL_REPORT_INTERVAL),
);
#[cfg(not(target_arch = "wasm32"))]
let mut snapshot_interval =
tokio_stream::wrappers::IntervalStream::new(tokio::time::interval(SNAPSHOT_INTERVAL));
#[cfg(target_arch = "wasm32")]
let mut stats_report_interval = gloo_timers::future::IntervalStream::new(
self.reporting_config.reporting_interval.as_millis() as u32,
);
#[cfg(target_arch = "wasm32")]
let mut local_report_interval =
gloo_timers::future::IntervalStream::new(LOCAL_REPORT_INTERVAL.as_millis() as u32);
#[cfg(target_arch = "wasm32")]
let mut snapshot_interval =
gloo_timers::future::IntervalStream::new(SNAPSHOT_INTERVAL.as_millis() as u32);
loop { loop {
tokio::select! { tokio::select! {
@@ -130,20 +108,16 @@ impl StatisticsControl {
break; break;
} }
}, },
_ = snapshot_interval.next() => { _ = snapshot_interval.tick() => {
self.stats.snapshot(); self.stats.snapshot();
} }
_ = stats_report_interval.next() => { _ = stats_report_interval.tick(), if self.reporting_config.enabled && self.reporting_config.provider_address.is_some() => {
let Some(recipient) = self.reporting_config.provider_address else { // SAFTEY : this branch executes only if reporting is not none, so unwrapp is fine
continue #[allow(clippy::unwrap_used)]
}; self.report_stats(self.reporting_config.provider_address.unwrap()).await;
if self.reporting_config.enabled {
self.report_stats(recipient).await;
}
} }
_ = local_report_interval.next() => { _ = local_report_interval.tick() => {
self.stats.local_report(&mut task_client); self.stats.local_report(&mut task_client);
} }
_ = task_client.recv_with_delay() => { _ = task_client.recv_with_delay() => {
@@ -2,7 +2,8 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::addressing::clients::Recipient;
use nym_topology::{NymRouteProvider, NymTopology, NymTopologyError}; use nym_sphinx::params::DEFAULT_NUM_MIX_HOPS;
use nym_topology::{NymTopology, NymTopologyError};
use std::ops::Deref; use std::ops::Deref;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc; use std::sync::Arc;
@@ -16,36 +17,29 @@ pub struct TopologyAccessorInner {
// few seconds, while reads are needed every single packet generated. // few seconds, while reads are needed every single packet generated.
// However, proper benchmarks will be needed to determine if `RwLock` is indeed a better // However, proper benchmarks will be needed to determine if `RwLock` is indeed a better
// approach than a `Mutex` // approach than a `Mutex`
topology: RwLock<NymRouteProvider>, topology: RwLock<Option<NymTopology>>,
} }
impl TopologyAccessorInner { impl TopologyAccessorInner {
fn new(initial: NymRouteProvider) -> Self { fn new() -> Self {
TopologyAccessorInner { TopologyAccessorInner {
controlled_manually: AtomicBool::new(false), controlled_manually: AtomicBool::new(false),
released_manual_control: Notify::new(), released_manual_control: Notify::new(),
topology: RwLock::new(initial), topology: RwLock::new(None),
} }
} }
async fn update(&self, new: Option<NymTopology>) { async fn update(&self, new: Option<NymTopology>) {
let mut guard = self.topology.write().await; *self.topology.write().await = new;
match new {
Some(updated) => {
guard.update(updated);
}
None => guard.clear_topology(),
}
} }
} }
pub struct TopologyReadPermit<'a> { pub struct TopologyReadPermit<'a> {
permit: RwLockReadGuard<'a, NymRouteProvider>, permit: RwLockReadGuard<'a, Option<NymTopology>>,
} }
impl Deref for TopologyReadPermit<'_> { impl<'a> Deref for TopologyReadPermit<'a> {
type Target = NymRouteProvider; type Target = Option<NymTopology>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.permit &self.permit
@@ -59,31 +53,43 @@ impl<'a> TopologyReadPermit<'a> {
&'a self, &'a self,
ack_recipient: &Recipient, ack_recipient: &Recipient,
packet_recipient: Option<&Recipient>, packet_recipient: Option<&Recipient>,
) -> Result<&'a NymRouteProvider, NymTopologyError> { ) -> Result<&'a NymTopology, NymTopologyError> {
let route_provider = self.permit.deref();
let topology = &route_provider.topology;
// 1. Have we managed to get anything from the refresher, i.e. have the nym-api queries gone through? // 1. Have we managed to get anything from the refresher, i.e. have the nym-api queries gone through?
topology.ensure_not_empty()?; let topology = self
.permit
.as_ref()
.ok_or(NymTopologyError::EmptyNetworkTopology)?;
// 2. does the topology have a node on each mixing layer? // 2. does it have any mixnode at all?
topology.ensure_minimally_routable()?; // 3. does it have any gateways at all?
// 4. does it have a mixnode on each layer?
topology.ensure_can_construct_path_through(DEFAULT_NUM_MIX_HOPS)?;
// 3. does it contain OUR gateway (so that we could create an ack packet)? // 5. does it contain OUR gateway (so that we could create an ack packet)?
let _ = route_provider.egress_by_identity(ack_recipient.gateway())?; if !topology.gateway_exists(ack_recipient.gateway()) {
return Err(NymTopologyError::NonExistentGatewayError {
// 4. for our target recipient, does it contain THEIR gateway (so that we send anything over?) identity_key: ack_recipient.gateway().to_base58_string(),
if let Some(recipient) = packet_recipient { });
let _ = route_provider.egress_by_identity(recipient.gateway())?;
} }
Ok(route_provider) // 6. for our target recipient, does it contain THEIR gateway (so that we could create
if let Some(recipient) = packet_recipient {
if !topology.gateway_exists(recipient.gateway()) {
return Err(NymTopologyError::NonExistentGatewayError {
identity_key: recipient.gateway().to_base58_string(),
});
}
}
Ok(topology)
} }
} }
impl<'a> From<RwLockReadGuard<'a, NymRouteProvider>> for TopologyReadPermit<'a> { impl<'a> From<RwLockReadGuard<'a, Option<NymTopology>>> for TopologyReadPermit<'a> {
fn from(permit: RwLockReadGuard<'a, NymRouteProvider>) -> Self { fn from(read_permit: RwLockReadGuard<'a, Option<NymTopology>>) -> Self {
TopologyReadPermit { permit } TopologyReadPermit {
permit: read_permit,
}
} }
} }
@@ -93,11 +99,9 @@ pub struct TopologyAccessor {
} }
impl TopologyAccessor { impl TopologyAccessor {
pub fn new(ignore_egress_epoch_roles: bool) -> Self { pub fn new() -> Self {
TopologyAccessor { TopologyAccessor {
inner: Arc::new(TopologyAccessorInner::new(NymRouteProvider::new_empty( inner: Arc::new(TopologyAccessorInner::new()),
ignore_egress_epoch_roles,
))),
} }
} }
@@ -117,21 +121,8 @@ impl TopologyAccessor {
self.inner.released_manual_control.notified().await self.inner.released_manual_control.notified().await
} }
#[deprecated(note = "use .current_route_provider instead")]
pub async fn current_topology(&self) -> Option<NymTopology> { pub async fn current_topology(&self) -> Option<NymTopology> {
self.current_route_provider() self.inner.topology.read().await.clone()
.await
.as_ref()
.map(|p| p.topology.clone())
}
pub async fn current_route_provider(&self) -> Option<RwLockReadGuard<NymRouteProvider>> {
let provider = self.inner.topology.read().await;
if provider.topology.is_empty() {
None
} else {
Some(provider)
}
} }
pub async fn manually_change_topology(&self, new_topology: NymTopology) { pub async fn manually_change_topology(&self, new_topology: NymTopology) {
@@ -149,11 +140,15 @@ impl TopologyAccessor {
// only used by the client at startup to get a slightly more reasonable error message // only used by the client at startup to get a slightly more reasonable error message
// (currently displays as unused because health checker is disabled due to required changes) // (currently displays as unused because health checker is disabled due to required changes)
pub async fn ensure_is_routable(&self) -> Result<(), NymTopologyError> { pub async fn ensure_is_routable(&self) -> Result<(), NymTopologyError> {
self.inner match self.inner.topology.read().await.deref() {
.topology None => Err(NymTopologyError::EmptyNetworkTopology),
.read() Some(ref topology) => topology.ensure_can_construct_path_through(DEFAULT_NUM_MIX_HOPS),
.await }
.topology }
.ensure_minimally_routable() }
impl Default for TopologyAccessor {
fn default() -> Self {
TopologyAccessor::new()
} }
} }
@@ -3,6 +3,7 @@ use log::{debug, error};
use nym_explorer_client::{ExplorerClient, PrettyDetailedMixNodeBond}; use nym_explorer_client::{ExplorerClient, PrettyDetailedMixNodeBond};
use nym_network_defaults::var_names::EXPLORER_API; use nym_network_defaults::var_names::EXPLORER_API;
use nym_topology::{ use nym_topology::{
nym_topology_from_basic_info,
provider_trait::{async_trait, TopologyProvider}, provider_trait::{async_trait, TopologyProvider},
NymTopology, NymTopology,
}; };
@@ -14,6 +15,8 @@ use url::Url;
pub use nym_country_group::CountryGroup; pub use nym_country_group::CountryGroup;
const MIN_NODES_PER_LAYER: usize = 1;
fn create_explorer_client() -> Option<ExplorerClient> { fn create_explorer_client() -> Option<ExplorerClient> {
let Ok(explorer_api_url) = std::env::var(EXPLORER_API) else { let Ok(explorer_api_url) = std::env::var(EXPLORER_API) else {
error!("Missing EXPLORER_API"); error!("Missing EXPLORER_API");
@@ -60,22 +63,37 @@ fn log_mixnode_distribution(mixnodes: &HashMap<CountryGroup, Vec<NodeId>>) {
} }
fn check_layer_integrity(topology: NymTopology) -> Result<(), ()> { fn check_layer_integrity(topology: NymTopology) -> Result<(), ()> {
if topology.ensure_minimally_routable().is_err() { let mixes = topology.mixes();
if mixes.keys().len() < 3 {
error!("Layer is missing in topology!"); error!("Layer is missing in topology!");
return Err(()); return Err(());
} }
for (layer, mixnodes) in mixes {
debug!("Layer {:?} has {} mixnodes", layer, mixnodes.len());
if mixnodes.len() < MIN_NODES_PER_LAYER {
error!(
"There are only {} mixnodes in layer {:?}",
mixnodes.len(),
layer
);
return Err(());
}
}
Ok(()) Ok(())
} }
#[deprecated(note = "use NymApiTopologyProvider instead as explorer API will soon be removed")]
pub struct GeoAwareTopologyProvider { pub struct GeoAwareTopologyProvider {
validator_client: nym_validator_client::client::NymApiClient, validator_client: nym_validator_client::client::NymApiClient,
filter_on: GroupBy, filter_on: GroupBy,
client_version: String,
} }
#[allow(deprecated)]
impl GeoAwareTopologyProvider { impl GeoAwareTopologyProvider {
pub fn new(mut nym_api_urls: Vec<Url>, filter_on: GroupBy) -> GeoAwareTopologyProvider { pub fn new(
mut nym_api_urls: Vec<Url>,
client_version: String,
filter_on: GroupBy,
) -> GeoAwareTopologyProvider {
log::info!( log::info!(
"Creating geo-aware topology provider with filter on {}", "Creating geo-aware topology provider with filter on {}",
filter_on filter_on
@@ -87,22 +105,14 @@ impl GeoAwareTopologyProvider {
nym_api_urls[0].clone(), nym_api_urls[0].clone(),
), ),
filter_on, filter_on,
client_version,
} }
} }
async fn get_topology(&self) -> Option<NymTopology> { async fn get_topology(&self) -> Option<NymTopology> {
let rewarded_set = self
.validator_client
.get_current_rewarded_set()
.await
.inspect_err(|err| error!("failed to get current rewarded set: {err}"))
.ok()?;
let mut topology = NymTopology::new_empty(rewarded_set);
let mixnodes = match self let mixnodes = match self
.validator_client .validator_client
.get_all_basic_active_mixing_assigned_nodes() .get_all_basic_active_mixing_assigned_nodes(Some(self.client_version.clone()))
.await .await
{ {
Err(err) => { Err(err) => {
@@ -114,7 +124,7 @@ impl GeoAwareTopologyProvider {
let gateways = match self let gateways = match self
.validator_client .validator_client
.get_all_basic_entry_assigned_nodes() .get_all_basic_entry_assigned_nodes(Some(self.client_version.clone()))
.await .await
{ {
Err(err) => { Err(err) => {
@@ -183,8 +193,7 @@ impl GeoAwareTopologyProvider {
.filter(|m| filtered_mixnode_ids.contains(&m.node_id)) .filter(|m| filtered_mixnode_ids.contains(&m.node_id))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
topology.add_skimmed_nodes(&mixnodes); let topology = nym_topology_from_basic_info(&mixnodes, &gateways);
topology.add_skimmed_nodes(&gateways);
// TODO: return real error type // TODO: return real error type
check_layer_integrity(topology.clone()).ok()?; check_layer_integrity(topology.clone()).ok()?;
@@ -193,7 +202,6 @@ impl GeoAwareTopologyProvider {
} }
} }
#[allow(deprecated)]
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
#[async_trait] #[async_trait]
impl TopologyProvider for GeoAwareTopologyProvider { impl TopologyProvider for GeoAwareTopologyProvider {
@@ -203,7 +211,6 @@ impl TopologyProvider for GeoAwareTopologyProvider {
} }
} }
#[allow(deprecated)]
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
#[async_trait(?Send)] #[async_trait(?Send)]
impl TopologyProvider for GeoAwareTopologyProvider { impl TopologyProvider for GeoAwareTopologyProvider {
@@ -19,7 +19,6 @@ mod accessor;
pub mod geo_aware_provider; pub mod geo_aware_provider;
pub mod nym_api_provider; pub mod nym_api_provider;
#[allow(deprecated)]
pub use geo_aware_provider::GeoAwareTopologyProvider; pub use geo_aware_provider::GeoAwareTopologyProvider;
pub use nym_api_provider::{Config as NymApiTopologyProviderConfig, NymApiTopologyProvider}; pub use nym_api_provider::{Config as NymApiTopologyProviderConfig, NymApiTopologyProvider};
pub use nym_topology::provider_trait::TopologyProvider; pub use nym_topology::provider_trait::TopologyProvider;
@@ -28,7 +27,7 @@ pub use nym_topology::provider_trait::TopologyProvider;
const MAX_FAILURE_COUNT: usize = 10; const MAX_FAILURE_COUNT: usize = 10;
pub struct TopologyRefresherConfig { pub struct TopologyRefresherConfig {
pub refresh_rate: Duration, refresh_rate: Duration,
} }
impl TopologyRefresherConfig { impl TopologyRefresherConfig {
@@ -97,24 +96,28 @@ impl TopologyRefresher {
self.topology_accessor.ensure_is_routable().await self.topology_accessor.ensure_is_routable().await
} }
pub async fn ensure_contains_routable_egress( pub async fn ensure_contains_gateway(
&self, &self,
egress: NodeIdentity, gateway: &NodeIdentity,
) -> Result<(), NymTopologyError> { ) -> Result<(), NymTopologyError> {
let topology = self let topology = self
.topology_accessor .topology_accessor
.current_route_provider() .current_topology()
.await .await
.ok_or(NymTopologyError::EmptyNetworkTopology)?; .ok_or(NymTopologyError::EmptyNetworkTopology)?;
let _ = topology.egress_by_identity(egress)?; if !topology.gateway_exists(gateway) {
return Err(NymTopologyError::NonExistentGatewayError {
identity_key: gateway.to_base58_string(),
});
}
Ok(()) Ok(())
} }
pub async fn wait_for_gateway( pub async fn wait_for_gateway(
&mut self, &mut self,
gateway: NodeIdentity, gateway: &NodeIdentity,
timeout_duration: Duration, timeout_duration: Duration,
) -> Result<(), NymTopologyError> { ) -> Result<(), NymTopologyError> {
info!( info!(
@@ -132,7 +135,7 @@ impl TopologyRefresher {
}) })
} }
_ = self.try_refresh() => { _ = self.try_refresh() => {
if self.ensure_contains_routable_egress(gateway).await.is_ok() { if self.ensure_contains_gateway(gateway).await.is_ok() {
return Ok(()) return Ok(())
} }
info!("gateway '{gateway}' is still not online..."); info!("gateway '{gateway}' is still not online...");
@@ -4,51 +4,47 @@
use async_trait::async_trait; use async_trait::async_trait;
use log::{debug, error, warn}; use log::{debug, error, warn};
use nym_topology::provider_trait::TopologyProvider; use nym_topology::provider_trait::TopologyProvider;
use nym_topology::NymTopology; use nym_topology::{NymTopology, NymTopologyError};
use nym_validator_client::UserAgent; use nym_validator_client::UserAgent;
use rand::prelude::SliceRandom; use rand::prelude::SliceRandom;
use rand::thread_rng; use rand::thread_rng;
use std::cmp::min;
use url::Url; use url::Url;
// the same values as our current (10.06.24) blacklist
pub const DEFAULT_MIN_MIXNODE_PERFORMANCE: u8 = 50;
pub const DEFAULT_MIN_GATEWAY_PERFORMANCE: u8 = 50;
#[derive(Debug)] #[derive(Debug)]
pub struct Config { pub struct Config {
pub min_mixnode_performance: u8, pub min_mixnode_performance: u8,
pub min_gateway_performance: u8, pub min_gateway_performance: u8,
pub use_extended_topology: bool,
pub ignore_egress_epoch_role: bool,
} }
impl From<nym_client_core_config_types::Topology> for Config { impl Default for Config {
fn from(value: nym_client_core_config_types::Topology) -> Self { fn default() -> Self {
// old values that decided on blacklist membership
Config { Config {
min_mixnode_performance: value.minimum_mixnode_performance, min_mixnode_performance: DEFAULT_MIN_MIXNODE_PERFORMANCE,
min_gateway_performance: value.minimum_gateway_performance, min_gateway_performance: DEFAULT_MIN_GATEWAY_PERFORMANCE,
use_extended_topology: value.use_extended_topology,
ignore_egress_epoch_role: value.ignore_egress_epoch_role,
} }
} }
} }
impl Config {
// if we're using 'extended' topology, filter the nodes based on the lowest set performance
fn min_node_performance(&self) -> u8 {
min(self.min_mixnode_performance, self.min_gateway_performance)
}
}
pub struct NymApiTopologyProvider { pub struct NymApiTopologyProvider {
config: Config, config: Config,
validator_client: nym_validator_client::client::NymApiClient, validator_client: nym_validator_client::client::NymApiClient,
nym_api_urls: Vec<Url>, nym_api_urls: Vec<Url>,
client_version: String,
currently_used_api: usize, currently_used_api: usize,
} }
impl NymApiTopologyProvider { impl NymApiTopologyProvider {
pub fn new( pub fn new(
config: impl Into<Config>, config: Config,
mut nym_api_urls: Vec<Url>, mut nym_api_urls: Vec<Url>,
client_version: String,
user_agent: Option<UserAgent>, user_agent: Option<UserAgent>,
) -> Self { ) -> Self {
nym_api_urls.shuffle(&mut thread_rng()); nym_api_urls.shuffle(&mut thread_rng());
@@ -63,9 +59,10 @@ impl NymApiTopologyProvider {
}; };
NymApiTopologyProvider { NymApiTopologyProvider {
config: config.into(), config,
validator_client, validator_client,
nym_api_urls, nym_api_urls,
client_version,
currently_used_api: 0, currently_used_api: 0,
} }
} }
@@ -81,69 +78,70 @@ impl NymApiTopologyProvider {
.change_nym_api(self.nym_api_urls[self.currently_used_api].clone()) .change_nym_api(self.nym_api_urls[self.currently_used_api].clone())
} }
/// Verifies whether nodes a reasonably distributed among all mix layers.
///
/// In ideal world we would have 33% nodes on layer 1, 33% on layer 2 and 33% on layer 3.
/// However, this is a rather unrealistic expectation, instead we check whether there exists
/// a layer with more than 66% of nodes or with fewer than 15% and if so, we trigger a failure.
///
/// # Arguments
///
/// * `topology`: active topology constructed from validator api data
fn check_layer_distribution(
&self,
active_topology: &NymTopology,
) -> Result<(), NymTopologyError> {
let lower_threshold = 0.15;
let upper_threshold = 0.66;
active_topology.ensure_even_layer_distribution(lower_threshold, upper_threshold)
}
async fn get_current_compatible_topology(&mut self) -> Option<NymTopology> { async fn get_current_compatible_topology(&mut self) -> Option<NymTopology> {
let rewarded_set = self let mixnodes = match self
.validator_client .validator_client
.get_current_rewarded_set() .get_all_basic_active_mixing_assigned_nodes(Some(self.client_version.clone()))
.await .await
.inspect_err(|err| error!("failed to get current rewarded set: {err}")) {
.ok()?; Err(err) => {
error!("failed to get network mixnodes - {err}");
let mut topology = NymTopology::new_empty(rewarded_set); return None;
}
if self.config.use_extended_topology { Ok(mixes) => mixes,
let all_nodes = self
.validator_client
.get_all_basic_nodes()
.await
.inspect_err(|err| error!("failed to get network nodes: {err}"))
.ok()?;
debug!(
"there are {} nodes on the network (before filtering)",
all_nodes.len()
);
topology.add_additional_nodes(all_nodes.iter().filter(|n| {
n.performance.round_to_integer() >= self.config.min_node_performance()
}));
} else {
// if we're not using extended topology, we're only getting active set mixnodes and gateways
let mixnodes = self
.validator_client
.get_all_basic_active_mixing_assigned_nodes()
.await
.inspect_err(|err| error!("failed to get network mixnodes: {err}"))
.ok()?;
// TODO: we really should be getting ACTIVE gateways only
let gateways = self
.validator_client
.get_all_basic_entry_assigned_nodes()
.await
.inspect_err(|err| error!("failed to get network gateways: {err}"))
.ok()?;
debug!(
"there are {} mixnodes and {} gateways in total (before performance filtering)",
mixnodes.len(),
gateways.len()
);
topology.add_additional_nodes(mixnodes.iter().filter(|m| {
m.performance.round_to_integer() >= self.config.min_mixnode_performance
}));
topology.add_additional_nodes(gateways.iter().filter(|m| {
m.performance.round_to_integer() >= self.config.min_gateway_performance
}));
}; };
if !topology.is_minimally_routable() { let gateways = match self
error!("the current filtered active topology can't be used to construct any packets"); .validator_client
return None; .get_all_basic_entry_assigned_nodes(Some(self.client_version.clone()))
} .await
{
Err(err) => {
error!("failed to get network gateways - {err}");
return None;
}
Ok(gateways) => gateways,
};
Some(topology) debug!(
"there are {} mixnodes and {} gateways in total (before performance filtering)",
mixnodes.len(),
gateways.len()
);
let topology = NymTopology::from_unordered(
mixnodes.iter().filter(|m| {
m.performance.round_to_integer() >= self.config.min_mixnode_performance
}),
gateways.iter().filter(|g| {
g.performance.round_to_integer() >= self.config.min_gateway_performance
}),
);
if let Err(err) = self.check_layer_distribution(&topology) {
warn!("The current filtered active topology has extremely skewed layer distribution. It cannot be used: {err}");
self.use_next_nym_api();
None
} else {
Some(topology)
}
} }
} }
@@ -152,11 +150,7 @@ impl NymApiTopologyProvider {
#[async_trait] #[async_trait]
impl TopologyProvider for NymApiTopologyProvider { impl TopologyProvider for NymApiTopologyProvider {
async fn get_new_topology(&mut self) -> Option<NymTopology> { async fn get_new_topology(&mut self) -> Option<NymTopology> {
let Some(topology) = self.get_current_compatible_topology().await else { self.get_current_compatible_topology().await
self.use_next_nym_api();
return None;
};
Some(topology)
} }
} }
@@ -164,10 +158,6 @@ impl TopologyProvider for NymApiTopologyProvider {
#[async_trait(?Send)] #[async_trait(?Send)]
impl TopologyProvider for NymApiTopologyProvider { impl TopologyProvider for NymApiTopologyProvider {
async fn get_new_topology(&mut self) -> Option<NymTopology> { async fn get_new_topology(&mut self) -> Option<NymTopology> {
let Some(topology) = self.get_current_compatible_topology().await else { self.get_current_compatible_topology().await
self.use_next_nym_api();
return None;
};
Some(topology)
} }
} }
+4 -7
View File
@@ -4,8 +4,8 @@
use crate::client::mix_traffic::transceiver::ErasedGatewayError; use crate::client::mix_traffic::transceiver::ErasedGatewayError;
use nym_crypto::asymmetric::identity::Ed25519RecoveryError; use nym_crypto::asymmetric::identity::Ed25519RecoveryError;
use nym_gateway_client::error::GatewayClientError; use nym_gateway_client::error::GatewayClientError;
use nym_topology::node::RoutingNodeError; use nym_topology::gateway::GatewayConversionError;
use nym_topology::{NodeId, NymTopologyError}; use nym_topology::NymTopologyError;
use nym_validator_client::ValidatorClientError; use nym_validator_client::ValidatorClientError;
use std::error::Error; use std::error::Error;
use std::path::PathBuf; use std::path::PathBuf;
@@ -74,10 +74,10 @@ pub enum ClientCoreError {
#[error("the gateway id is invalid - {0}")] #[error("the gateway id is invalid - {0}")]
UnableToCreatePublicKeyFromGatewayId(Ed25519RecoveryError), UnableToCreatePublicKeyFromGatewayId(Ed25519RecoveryError),
#[error("the node is malformed: {source}")] #[error("The gateway is malformed: {source}")]
MalformedGateway { MalformedGateway {
#[from] #[from]
source: Box<RoutingNodeError>, source: GatewayConversionError,
}, },
#[error("failed to establish connection to gateway: {source}")] #[error("failed to establish connection to gateway: {source}")]
@@ -159,9 +159,6 @@ pub enum ClientCoreError {
#[error("the specified gateway '{gateway}' does not support the wss protocol")] #[error("the specified gateway '{gateway}' does not support the wss protocol")]
UnsupportedWssProtocol { gateway: String }, UnsupportedWssProtocol { gateway: String },
#[error("node {id} ({identity}) does not support mixnet entry mode")]
UnsupportedEntry { id: NodeId, identity: String },
#[error( #[error(
"failed to load custom topology using path '{}'. detailed message: {source}", file_path.display() "failed to load custom topology using path '{}'. detailed message: {source}", file_path.display()
)] )]
+45 -49
View File
@@ -7,7 +7,7 @@ use futures::{SinkExt, StreamExt};
use log::{debug, info, trace, warn}; use log::{debug, info, trace, warn};
use nym_crypto::asymmetric::identity; use nym_crypto::asymmetric::identity;
use nym_gateway_client::GatewayClient; use nym_gateway_client::GatewayClient;
use nym_topology::node::RoutingNode; use nym_topology::{gateway, mix};
use nym_validator_client::client::IdentityKeyRef; use nym_validator_client::client::IdentityKeyRef;
use nym_validator_client::UserAgent; use nym_validator_client::UserAgent;
use rand::{seq::SliceRandom, Rng}; use rand::{seq::SliceRandom, Rng};
@@ -15,7 +15,6 @@ use std::{sync::Arc, time::Duration};
use tungstenite::Message; use tungstenite::Message;
use url::Url; use url::Url;
use nym_topology::NodeId;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use tokio::net::TcpStream; use tokio::net::TcpStream;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
@@ -26,6 +25,7 @@ use tokio::time::Instant;
use tokio_tungstenite::connect_async; use tokio_tungstenite::connect_async;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream}; use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
use wasm_utils::websocket::JSWebsocket; use wasm_utils::websocket::JSWebsocket;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
@@ -48,30 +48,22 @@ const PING_TIMEOUT: Duration = Duration::from_millis(1000);
// The abstraction that some of these helpers use // The abstraction that some of these helpers use
pub trait ConnectableGateway { pub trait ConnectableGateway {
fn node_id(&self) -> NodeId; fn identity(&self) -> &identity::PublicKey;
fn identity(&self) -> identity::PublicKey; fn clients_address(&self) -> String;
fn clients_address(&self, prefer_ipv6: bool) -> Option<String>;
fn is_wss(&self) -> bool; fn is_wss(&self) -> bool;
} }
impl ConnectableGateway for RoutingNode { impl ConnectableGateway for gateway::LegacyNode {
fn node_id(&self) -> NodeId { fn identity(&self) -> &identity::PublicKey {
self.node_id self.identity()
} }
fn identity(&self) -> identity::PublicKey { fn clients_address(&self) -> String {
self.identity_key self.clients_address()
}
fn clients_address(&self, prefer_ipv6: bool) -> Option<String> {
self.ws_entry_address(prefer_ipv6)
} }
fn is_wss(&self) -> bool { fn is_wss(&self) -> bool {
self.entry self.clients_wss_port.is_some()
.as_ref()
.map(|e| e.clients_wss_port.is_some())
.unwrap_or_default()
} }
} }
@@ -90,8 +82,7 @@ pub async fn current_gateways<R: Rng>(
rng: &mut R, rng: &mut R,
nym_apis: &[Url], nym_apis: &[Url],
user_agent: Option<UserAgent>, user_agent: Option<UserAgent>,
minimum_performance: u8, ) -> Result<Vec<gateway::LegacyNode>, ClientCoreError> {
) -> Result<Vec<RoutingNode>, ClientCoreError> {
let nym_api = nym_apis let nym_api = nym_apis
.choose(rng) .choose(rng)
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?; .ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
@@ -103,27 +94,44 @@ pub async fn current_gateways<R: Rng>(
log::debug!("Fetching list of gateways from: {nym_api}"); log::debug!("Fetching list of gateways from: {nym_api}");
let gateways = client.get_all_basic_entry_assigned_nodes().await?; let gateways = client.get_all_basic_entry_assigned_nodes(None).await?;
info!("nym api reports {} gateways", gateways.len()); log::debug!("Found {} gateways", gateways.len());
log::trace!("Gateways: {:#?}", gateways); log::trace!("Gateways: {:#?}", gateways);
let valid_gateways = gateways let valid_gateways = gateways
.iter() .iter()
.filter(|g| g.performance.round_to_integer() >= minimum_performance)
.filter_map(|gateway| gateway.try_into().ok()) .filter_map(|gateway| gateway.try_into().ok())
.collect::<Vec<_>>(); .collect::<Vec<gateway::LegacyNode>>();
log::debug!("After checking validity: {}", valid_gateways.len()); log::debug!("After checking validity: {}", valid_gateways.len());
log::trace!("Valid gateways: {:#?}", valid_gateways); log::trace!("Valid gateways: {:#?}", valid_gateways);
log::info!( log::info!("nym-api reports {} valid gateways", valid_gateways.len());
"and {} after validity and performance filtering",
valid_gateways.len()
);
Ok(valid_gateways) Ok(valid_gateways)
} }
pub async fn current_mixnodes<R: Rng>(
rng: &mut R,
nym_apis: &[Url],
) -> Result<Vec<mix::LegacyNode>, ClientCoreError> {
let nym_api = nym_apis
.choose(rng)
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
let client = nym_validator_client::client::NymApiClient::new(nym_api.clone());
log::trace!("Fetching list of mixnodes from: {nym_api}");
let mixnodes = client
.get_all_basic_active_mixing_assigned_nodes(None)
.await?;
let valid_mixnodes = mixnodes
.iter()
.filter_map(|mixnode| mixnode.try_into().ok())
.collect::<Vec<mix::LegacyNode>>();
Ok(valid_mixnodes)
}
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
async fn connect(endpoint: &str) -> Result<WsConn, ClientCoreError> { async fn connect(endpoint: &str) -> Result<WsConn, ClientCoreError> {
match tokio::time::timeout(CONN_TIMEOUT, connect_async(endpoint)).await { match tokio::time::timeout(CONN_TIMEOUT, connect_async(endpoint)).await {
@@ -142,12 +150,7 @@ async fn measure_latency<G>(gateway: &G) -> Result<GatewayWithLatency<G>, Client
where where
G: ConnectableGateway, G: ConnectableGateway,
{ {
let Some(addr) = gateway.clients_address(false) else { let addr = gateway.clients_address();
return Err(ClientCoreError::UnsupportedEntry {
id: gateway.node_id(),
identity: gateway.identity().to_string(),
});
};
trace!( trace!(
"establishing connection to {} ({addr})...", "establishing connection to {} ({addr})...",
gateway.identity(), gateway.identity(),
@@ -203,7 +206,7 @@ where
Ok(GatewayWithLatency::new(gateway, avg)) Ok(GatewayWithLatency::new(gateway, avg))
} }
pub async fn choose_gateway_by_latency<R: Rng, G: ConnectableGateway + Clone>( pub async fn choose_gateway_by_latency<'a, R: Rng, G: ConnectableGateway + Clone>(
rng: &mut R, rng: &mut R,
gateways: &[G], gateways: &[G],
must_use_tls: bool, must_use_tls: bool,
@@ -218,7 +221,7 @@ pub async fn choose_gateway_by_latency<R: Rng, G: ConnectableGateway + Clone>(
let gateways_with_latency = Arc::new(tokio::sync::Mutex::new(Vec::new())); let gateways_with_latency = Arc::new(tokio::sync::Mutex::new(Vec::new()));
futures::stream::iter(gateways) futures::stream::iter(gateways)
.for_each_concurrent(CONCURRENT_GATEWAYS_MEASURED, |gateway| async { .for_each_concurrent(CONCURRENT_GATEWAYS_MEASURED, |gateway| async {
let id = gateway.identity(); let id = *gateway.identity();
trace!("measuring latency to {id}..."); trace!("measuring latency to {id}...");
match measure_latency(gateway).await { match measure_latency(gateway).await {
Ok(with_latency) => { Ok(with_latency) => {
@@ -265,9 +268,9 @@ fn filter_by_tls<G: ConnectableGateway>(
pub(super) fn uniformly_random_gateway<R: Rng>( pub(super) fn uniformly_random_gateway<R: Rng>(
rng: &mut R, rng: &mut R,
gateways: &[RoutingNode], gateways: &[gateway::LegacyNode],
must_use_tls: bool, must_use_tls: bool,
) -> Result<RoutingNode, ClientCoreError> { ) -> Result<gateway::LegacyNode, ClientCoreError> {
filter_by_tls(gateways, must_use_tls)? filter_by_tls(gateways, must_use_tls)?
.choose(rng) .choose(rng)
.ok_or(ClientCoreError::NoGatewaysOnNetwork) .ok_or(ClientCoreError::NoGatewaysOnNetwork)
@@ -276,9 +279,9 @@ pub(super) fn uniformly_random_gateway<R: Rng>(
pub(super) fn get_specified_gateway( pub(super) fn get_specified_gateway(
gateway_identity: IdentityKeyRef, gateway_identity: IdentityKeyRef,
gateways: &[RoutingNode], gateways: &[gateway::LegacyNode],
must_use_tls: bool, must_use_tls: bool,
) -> Result<RoutingNode, ClientCoreError> { ) -> Result<gateway::LegacyNode, ClientCoreError> {
log::debug!("Requesting specified gateway: {}", gateway_identity); log::debug!("Requesting specified gateway: {}", gateway_identity);
let user_gateway = identity::PublicKey::from_base58_string(gateway_identity) let user_gateway = identity::PublicKey::from_base58_string(gateway_identity)
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?; .map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
@@ -288,14 +291,7 @@ pub(super) fn get_specified_gateway(
.find(|gateway| gateway.identity_key == user_gateway) .find(|gateway| gateway.identity_key == user_gateway)
.ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_identity.to_string()))?; .ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_identity.to_string()))?;
let Some(entry_details) = gateway.entry.as_ref() else { if must_use_tls && gateway.clients_wss_port.is_none() {
return Err(ClientCoreError::UnsupportedEntry {
id: gateway.node_id,
identity: gateway.identity().to_string(),
});
};
if must_use_tls && entry_details.clients_wss_port.is_none() {
return Err(ClientCoreError::UnsupportedWssProtocol { return Err(ClientCoreError::UnsupportedWssProtocol {
gateway: gateway_identity.to_string(), gateway: gateway_identity.to_string(),
}); });
+2 -2
View File
@@ -19,7 +19,7 @@ use crate::init::types::{
use nym_client_core_gateways_storage::GatewaysDetailsStore; use nym_client_core_gateways_storage::GatewaysDetailsStore;
use nym_client_core_gateways_storage::{GatewayDetails, GatewayRegistration}; use nym_client_core_gateways_storage::{GatewayDetails, GatewayRegistration};
use nym_gateway_client::client::InitGatewayClient; use nym_gateway_client::client::InitGatewayClient;
use nym_topology::node::RoutingNode; use nym_topology::gateway;
use rand::rngs::OsRng; use rand::rngs::OsRng;
use rand::{CryptoRng, RngCore}; use rand::{CryptoRng, RngCore};
use serde::Serialize; use serde::Serialize;
@@ -50,7 +50,7 @@ async fn setup_new_gateway<K, D>(
key_store: &K, key_store: &K,
details_store: &D, details_store: &D,
selection_specification: GatewaySelectionSpecification, selection_specification: GatewaySelectionSpecification,
available_gateways: Vec<RoutingNode>, available_gateways: Vec<gateway::LegacyNode>,
) -> Result<InitialisationResult, ClientCoreError> ) -> Result<InitialisationResult, ClientCoreError>
where where
K: KeyStore, K: KeyStore,
+6 -41
View File
@@ -13,11 +13,11 @@ use nym_crypto::asymmetric::identity;
use nym_gateway_client::client::InitGatewayClient; use nym_gateway_client::client::InitGatewayClient;
use nym_gateway_requests::shared_key::SharedGatewayKey; use nym_gateway_requests::shared_key::SharedGatewayKey;
use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::addressing::clients::Recipient;
use nym_topology::node::RoutingNode; use nym_topology::gateway;
use nym_validator_client::client::IdentityKey; use nym_validator_client::client::IdentityKey;
use nym_validator_client::nyxd::AccountId; use nym_validator_client::nyxd::AccountId;
use serde::Serialize; use serde::Serialize;
use std::fmt::{Debug, Display}; use std::fmt::Display;
use std::sync::Arc; use std::sync::Arc;
use time::OffsetDateTime; use time::OffsetDateTime;
use url::Url; use url::Url;
@@ -38,23 +38,16 @@ pub enum SelectedGateway {
impl SelectedGateway { impl SelectedGateway {
pub fn from_topology_node( pub fn from_topology_node(
node: RoutingNode, node: gateway::LegacyNode,
must_use_tls: bool, must_use_tls: bool,
) -> Result<Self, ClientCoreError> { ) -> Result<Self, ClientCoreError> {
// for now, let's use 'old' behaviour, if you want to change it, you can pass it up the enum stack yourself : )
let prefer_ipv6 = false;
let gateway_listener = if must_use_tls { let gateway_listener = if must_use_tls {
node.ws_entry_address_tls() node.clients_address_tls()
.ok_or(ClientCoreError::UnsupportedWssProtocol { .ok_or(ClientCoreError::UnsupportedWssProtocol {
gateway: node.identity_key.to_base58_string(), gateway: node.identity_key.to_base58_string(),
})? })?
} else { } else {
node.ws_entry_address(prefer_ipv6) node.clients_address()
.ok_or(ClientCoreError::UnsupportedEntry {
id: node.node_id,
identity: node.identity_key.to_base58_string(),
})?
}; };
let gateway_listener = let gateway_listener =
@@ -207,7 +200,7 @@ pub enum GatewaySetup {
specification: GatewaySelectionSpecification, specification: GatewaySelectionSpecification,
// TODO: seems to be a bit inefficient to pass them by value // TODO: seems to be a bit inefficient to pass them by value
available_gateways: Vec<RoutingNode>, available_gateways: Vec<gateway::LegacyNode>,
}, },
ReuseConnection { ReuseConnection {
@@ -221,34 +214,6 @@ pub enum GatewaySetup {
}, },
} }
impl Debug for GatewaySetup {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GatewaySetup::MustLoad { gateway_id } => f
.debug_struct("GatewaySetup::MustLoad")
.field("gateway_id", gateway_id)
.finish(),
GatewaySetup::New {
specification,
available_gateways,
} => f
.debug_struct("GatewaySetup::New")
.field("specification", specification)
.field("available_gateways", available_gateways)
.field("gateways", specification)
.finish(),
GatewaySetup::ReuseConnection {
gateway_details, ..
} => f
.debug_struct("GatewaySetup::ReuseConnection")
.field("authenticated_ephemeral_client", &"***")
.field("gateway_details", gateway_details)
.field("client_keys", &"***")
.finish(),
}
}
}
impl GatewaySetup { impl GatewaySetup {
pub fn try_reuse_connection(init_res: InitialisationResult) -> Result<Self, ClientCoreError> { pub fn try_reuse_connection(init_res: InitialisationResult) -> Result<Self, ClientCoreError> {
if let Some(authenticated_ephemeral_client) = init_res.authenticated_ephemeral_client { if let Some(authenticated_ephemeral_client) = init_res.authenticated_ephemeral_client {
+2 -46
View File
@@ -14,7 +14,8 @@ pub mod error;
pub mod init; pub mod init;
pub use nym_topology::{ pub use nym_topology::{
HardcodedTopologyProvider, NymRouteProvider, NymTopology, NymTopologyError, TopologyProvider, HardcodedTopologyProvider, NymTopology, NymTopologyError, SerializableNymTopology,
SerializableTopologyError, TopologyProvider,
}; };
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
@@ -33,48 +34,3 @@ where
{ {
tokio::spawn(future); tokio::spawn(future);
} }
#[derive(Clone, Default, Debug)]
pub struct ForgetMe {
client: bool,
stats: bool,
}
impl ForgetMe {
pub fn new_all() -> Self {
Self {
client: true,
stats: true,
}
}
pub fn new_client() -> Self {
Self {
client: true,
stats: false,
}
}
pub fn new_stats() -> Self {
Self {
client: false,
stats: true,
}
}
pub fn new(client: bool, stats: bool) -> Self {
Self { client, stats }
}
pub fn any(&self) -> bool {
self.client || self.stats
}
pub fn client(&self) -> bool {
self.client
}
pub fn stats(&self) -> bool {
self.stats
}
}
@@ -9,10 +9,7 @@ use crate::backend::fs_backend::{
}, },
}; };
use log::{error, info}; use log::{error, info};
use sqlx::{ use sqlx::ConnectOptions;
sqlite::{SqliteAutoVacuum, SqliteSynchronous},
ConnectOptions,
};
use std::path::Path; use std::path::Path;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -34,9 +31,6 @@ impl StorageManager {
} }
let opts = sqlx::sqlite::SqliteConnectOptions::new() let opts = sqlx::sqlite::SqliteConnectOptions::new()
.journal_mode(sqlx::sqlite::SqliteJournalMode::Wal)
.synchronous(SqliteSynchronous::Normal)
.auto_vacuum(SqliteAutoVacuum::Incremental)
.filename(database_path) .filename(database_path)
.create_if_missing(fresh) .create_if_missing(fresh)
.disable_statement_logging(); .disable_statement_logging();
@@ -87,10 +87,8 @@ impl ClientBandwidth {
if remaining < 0 { if remaining < 0 {
tracing::warn!("OUT OF BANDWIDTH. remaining: {remaining_bi2}"); tracing::warn!("OUT OF BANDWIDTH. remaining: {remaining_bi2}");
} else if remaining < 1_000_000 {
tracing::info!("remaining bandwidth: {remaining_bi2}");
} else { } else {
tracing::debug!("remaining bandwidth: {remaining_bi2}"); tracing::info!("remaining bandwidth: {remaining_bi2}");
} }
self.inner self.inner
@@ -101,10 +101,6 @@ pub struct GatewayClient<C, St = EphemeralCredentialStorage> {
// currently unused (but populated) // currently unused (but populated)
negotiated_protocol: Option<u8>, negotiated_protocol: Option<u8>,
// Callback on the fd as soon as the connection has been established
#[cfg(unix)]
connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
/// Listen to shutdown messages and send notifications back to the task manager /// Listen to shutdown messages and send notifications back to the task manager
task_client: TaskClient, task_client: TaskClient,
} }
@@ -120,7 +116,6 @@ impl<C, St> GatewayClient<C, St> {
packet_router: PacketRouter, packet_router: PacketRouter,
bandwidth_controller: Option<BandwidthController<C, St>>, bandwidth_controller: Option<BandwidthController<C, St>>,
stats_reporter: ClientStatsSender, stats_reporter: ClientStatsSender,
#[cfg(unix)] connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
task_client: TaskClient, task_client: TaskClient,
) -> Self { ) -> Self {
GatewayClient { GatewayClient {
@@ -136,8 +131,6 @@ impl<C, St> GatewayClient<C, St> {
bandwidth_controller, bandwidth_controller,
stats_reporter, stats_reporter,
negotiated_protocol: None, negotiated_protocol: None,
#[cfg(unix)]
connection_fd_callback,
task_client, task_client,
} }
} }
@@ -146,10 +139,6 @@ impl<C, St> GatewayClient<C, St> {
self.gateway_identity self.gateway_identity
} }
pub fn shared_key(&self) -> Option<Arc<SharedGatewayKey>> {
self.shared_key.clone()
}
pub fn ws_fd(&self) -> Option<RawFd> { pub fn ws_fd(&self) -> Option<RawFd> {
match &self.connection { match &self.connection {
SocketState::Available(conn) => ws_fd(conn.as_ref()), SocketState::Available(conn) => ws_fd(conn.as_ref()),
@@ -212,12 +201,6 @@ impl<C, St> GatewayClient<C, St> {
}; };
self.connection = SocketState::Available(Box::new(ws_stream)); self.connection = SocketState::Available(Box::new(ws_stream));
#[cfg(unix)]
if let (Some(callback), Some(fd)) = (self.connection_fd_callback.as_ref(), self.ws_fd()) {
callback.as_ref()(fd);
}
Ok(()) Ok(())
} }
@@ -324,7 +307,7 @@ impl<C, St> GatewayClient<C, St> {
// If we want to send a message (with response), we need to have a full control over the socket, // If we want to send a message (with response), we need to have a full control over the socket,
// as we need to be able to write the request and read the subsequent response // as we need to be able to write the request and read the subsequent response
pub async fn send_websocket_message( async fn send_websocket_message(
&mut self, &mut self,
msg: impl Into<Message>, msg: impl Into<Message>,
) -> Result<ServerResponse, GatewayClientError> { ) -> Result<ServerResponse, GatewayClientError> {
@@ -425,7 +408,7 @@ impl<C, St> GatewayClient<C, St> {
} }
Some(_) => { Some(_) => {
debug!("the gateway is using exactly the same (or older) protocol version as we are. We're good to continue!"); info!("the gateway is using exactly the same (or older) protocol version as we are. We're good to continue!");
Ok(()) Ok(())
} }
} }
@@ -1009,6 +992,24 @@ impl<C, St> GatewayClient<C, St> {
} }
Ok(()) Ok(())
} }
#[deprecated(note = "this method does not deal with upgraded keys for legacy clients")]
pub async fn authenticate_and_start(
&mut self,
) -> Result<AuthenticationResponse, GatewayClientError>
where
C: DkgQueryClient + Send + Sync,
St: CredentialStorage,
<St as CredentialStorage>::StorageError: Send + Sync + 'static,
{
let shared_key = self.perform_initial_authentication().await?;
self.claim_initial_bandwidth().await?;
// this call is NON-blocking
self.start_listening_for_mixnet_messages()?;
Ok(shared_key)
}
} }
// type alias for an ease of use // type alias for an ease of use
@@ -1047,8 +1048,6 @@ impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
bandwidth_controller: None, bandwidth_controller: None,
stats_reporter: ClientStatsSender::new(None), stats_reporter: ClientStatsSender::new(None),
negotiated_protocol: None, negotiated_protocol: None,
#[cfg(unix)]
connection_fd_callback: None,
task_client, task_client,
} }
} }
@@ -1079,8 +1078,6 @@ impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
bandwidth_controller, bandwidth_controller,
stats_reporter, stats_reporter,
negotiated_protocol: self.negotiated_protocol, negotiated_protocol: self.negotiated_protocol,
#[cfg(unix)]
connection_fd_callback: self.connection_fd_callback,
task_client, task_client,
} }
} }
@@ -46,8 +46,7 @@ pub(crate) fn ws_fd(_conn: &WsConn) -> Option<RawFd> {
#[cfg(unix)] #[cfg(unix)]
match _conn.get_ref() { match _conn.get_ref() {
MaybeTlsStream::Plain(stream) => Some(stream.as_raw_fd()), MaybeTlsStream::Plain(stream) => Some(stream.as_raw_fd()),
MaybeTlsStream::Rustls(tls_stream) => Some(tls_stream.as_raw_fd()), &_ => None,
_ => None,
} }
#[cfg(not(unix))] #[cfg(not(unix))]
None None
@@ -111,11 +110,6 @@ impl PartiallyDelegatedRouter {
} }
}; };
if self.stream_return.is_canceled() {
// nothing to do, receiver has been dropped
return;
}
let return_res = match ret { let return_res = match ret {
Err(err) => self.stream_return.send(Err(err)), Err(err) => self.stream_return.send(Err(err)),
Ok(_) => { Ok(_) => {
+4 -10
View File
@@ -8,17 +8,11 @@ license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
dashmap = { workspace = true }
futures = { workspace = true } futures = { workspace = true }
tracing = { workspace = true } log = { workspace = true }
tokio = { workspace = true, features = ["time", "sync"] } tokio = { workspace = true, features = ["time", "net", "rt"] }
tokio-util = { workspace = true, features = ["codec"], optional = true } tokio-util = { workspace = true, features = ["codec"] }
tokio-stream = { workspace = true }
# internal # internal
nym-sphinx = { path = "../../nymsphinx" } nym-sphinx = { path = "../../nymsphinx" }
nym-task = { path = "../../task", optional = true } nym-task = { path = "../../task" }
[features]
default = ["client"]
client = ["tokio-util", "nym-task", "tokio/net", "tokio/rt"]
+94 -145
View File
@@ -1,33 +1,30 @@
// Copyright 2021-2024 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use dashmap::DashMap; use futures::channel::mpsc;
use futures::StreamExt; use futures::StreamExt;
use log::*;
use nym_sphinx::addressing::nodes::NymNodeRoutingAddress; use nym_sphinx::addressing::nodes::NymNodeRoutingAddress;
use nym_sphinx::framing::codec::NymCodec; use nym_sphinx::framing::codec::NymCodec;
use nym_sphinx::framing::packet::FramedNymPacket; use nym_sphinx::framing::packet::FramedNymPacket;
use nym_sphinx::params::PacketType; use nym_sphinx::params::PacketType;
use nym_sphinx::NymPacket; use nym_sphinx::NymPacket;
use std::collections::HashMap;
use std::io; use std::io;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::ops::Deref; use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::atomic::{AtomicU32, AtomicUsize, Ordering};
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use tokio::net::TcpStream; use tokio::net::TcpStream;
use tokio::sync::mpsc;
use tokio::sync::mpsc::error::TrySendError;
use tokio::time::sleep; use tokio::time::sleep;
use tokio_stream::wrappers::ReceiverStream;
use tokio_util::codec::Framed; use tokio_util::codec::Framed;
use tracing::*;
#[derive(Clone, Copy)]
pub struct Config { pub struct Config {
initial_reconnection_backoff: Duration, initial_reconnection_backoff: Duration,
maximum_reconnection_backoff: Duration, maximum_reconnection_backoff: Duration,
initial_connection_timeout: Duration, initial_connection_timeout: Duration,
maximum_connection_buffer_size: usize, maximum_connection_buffer_size: usize,
use_legacy_version: bool,
} }
impl Config { impl Config {
@@ -36,12 +33,14 @@ impl Config {
maximum_reconnection_backoff: Duration, maximum_reconnection_backoff: Duration,
initial_connection_timeout: Duration, initial_connection_timeout: Duration,
maximum_connection_buffer_size: usize, maximum_connection_buffer_size: usize,
use_legacy_version: bool,
) -> Self { ) -> Self {
Config { Config {
initial_reconnection_backoff, initial_reconnection_backoff,
maximum_reconnection_backoff, maximum_reconnection_backoff,
initial_connection_timeout, initial_connection_timeout,
maximum_connection_buffer_size, maximum_connection_buffer_size,
use_legacy_version,
} }
} }
} }
@@ -58,37 +57,11 @@ pub trait SendWithoutResponse {
} }
pub struct Client { pub struct Client {
active_connections: ActiveConnections, conn_new: HashMap<NymNodeRoutingAddress, ConnectionSender>,
connections_count: Arc<AtomicUsize>,
config: Config, config: Config,
} }
#[derive(Default, Clone)] struct ConnectionSender {
pub struct ActiveConnections {
inner: Arc<DashMap<NymNodeRoutingAddress, ConnectionSender>>,
}
impl ActiveConnections {
pub fn pending_packets(&self) -> usize {
self.inner
.iter()
.map(|sender| {
let max_capacity = sender.channel.max_capacity();
let capacity = sender.channel.capacity();
max_capacity - capacity
})
.sum()
}
}
impl Deref for ActiveConnections {
type Target = DashMap<NymNodeRoutingAddress, ConnectionSender>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
pub struct ConnectionSender {
channel: mpsc::Sender<FramedNymPacket>, channel: mpsc::Sender<FramedNymPacket>,
current_reconnection_attempt: Arc<AtomicU32>, current_reconnection_attempt: Arc<AtomicU32>,
} }
@@ -102,53 +75,46 @@ impl ConnectionSender {
} }
} }
struct ManagedConnection { impl Client {
address: SocketAddr, pub fn new(config: Config) -> Client {
message_receiver: ReceiverStream<FramedNymPacket>, Client {
connection_timeout: Duration, conn_new: HashMap::new(),
current_reconnection: Arc<AtomicU32>, config,
}
impl ManagedConnection {
fn new(
address: SocketAddr,
message_receiver: mpsc::Receiver<FramedNymPacket>,
connection_timeout: Duration,
current_reconnection: Arc<AtomicU32>,
) -> Self {
ManagedConnection {
address,
message_receiver: ReceiverStream::new(message_receiver),
connection_timeout,
current_reconnection,
} }
} }
async fn run(self) { async fn manage_connection(
let address = self.address; address: SocketAddr,
receiver: mpsc::Receiver<FramedNymPacket>,
connection_timeout: Duration,
current_reconnection: &AtomicU32,
) {
let connection_fut = TcpStream::connect(address); let connection_fut = TcpStream::connect(address);
let conn = match tokio::time::timeout(self.connection_timeout, connection_fut).await { let conn = match tokio::time::timeout(connection_timeout, connection_fut).await {
Ok(stream_res) => match stream_res { Ok(stream_res) => match stream_res {
Ok(stream) => { Ok(stream) => {
debug!("Managed to establish connection to {}", self.address); debug!("Managed to establish connection to {}", address);
// if we managed to connect, reset the reconnection count (whatever it might have been) // if we managed to connect, reset the reconnection count (whatever it might have been)
self.current_reconnection.store(0, Ordering::Release); current_reconnection.store(0, Ordering::Release);
Framed::new(stream, NymCodec) Framed::new(stream, NymCodec)
} }
Err(err) => { Err(err) => {
debug!("failed to establish connection to {address} (err: {err})",); debug!(
"failed to establish connection to {} (err: {})",
address, err
);
return; return;
} }
}, },
Err(_) => { Err(_) => {
debug!( debug!(
"failed to connect to {address} within {:?}", "failed to connect to {} within {:?}",
self.connection_timeout address, connection_timeout
); );
// we failed to connect - increase reconnection attempt // we failed to connect - increase reconnection attempt
self.current_reconnection.fetch_add(1, Ordering::SeqCst); current_reconnection.fetch_add(1, Ordering::SeqCst);
return; return;
} }
}; };
@@ -156,28 +122,15 @@ impl ManagedConnection {
// Take whatever the receiver channel produces and put it on the connection. // Take whatever the receiver channel produces and put it on the connection.
// We could have as well used conn.send_all(receiver.map(Ok)), but considering we don't care // We could have as well used conn.send_all(receiver.map(Ok)), but considering we don't care
// about neither receiver nor the connection, it doesn't matter which one gets consumed // about neither receiver nor the connection, it doesn't matter which one gets consumed
if let Err(err) = self.message_receiver.map(Ok).forward(conn).await { if let Err(err) = receiver.map(Ok).forward(conn).await {
warn!("Failed to forward packets to {address}: {err}"); warn!("Failed to forward packets to {} - {err}", address);
} }
debug!( debug!(
"connection manager to {address} is finished. Either the connection failed or mixnet client got dropped", "connection manager to {} is finished. Either the connection failed or mixnet client got dropped",
address
); );
} }
}
impl Client {
pub fn new(config: Config, connections_count: Arc<AtomicUsize>) -> Client {
Client {
active_connections: Default::default(),
connections_count,
config,
}
}
pub fn active_connections(&self) -> ActiveConnections {
self.active_connections.clone()
}
/// If we're trying to reconnect, determine how long we should wait. /// If we're trying to reconnect, determine how long we should wait.
fn determine_backoff(&self, current_attempt: u32) -> Option<Duration> { fn determine_backoff(&self, current_attempt: u32) -> Option<Duration> {
@@ -197,7 +150,7 @@ impl Client {
} }
fn make_connection(&mut self, address: NymNodeRoutingAddress, pending_packet: FramedNymPacket) { fn make_connection(&mut self, address: NymNodeRoutingAddress, pending_packet: FramedNymPacket) {
let (sender, receiver) = mpsc::channel(self.config.maximum_connection_buffer_size); let (mut sender, receiver) = mpsc::channel(self.config.maximum_connection_buffer_size);
// this CAN'T fail because we just created the channel which has a non-zero capacity // this CAN'T fail because we just created the channel which has a non-zero capacity
if self.config.maximum_connection_buffer_size > 0 { if self.config.maximum_connection_buffer_size > 0 {
@@ -205,16 +158,15 @@ impl Client {
} }
// if we already tried to connect to `address` before, grab the current attempt count // if we already tried to connect to `address` before, grab the current attempt count
let current_reconnection_attempt = let current_reconnection_attempt = if let Some(existing) = self.conn_new.get_mut(&address) {
if let Some(mut existing) = self.active_connections.get_mut(&address) { existing.channel = sender;
existing.channel = sender; Arc::clone(&existing.current_reconnection_attempt)
Arc::clone(&existing.current_reconnection_attempt) } else {
} else { let new_entry = ConnectionSender::new(sender);
let new_entry = ConnectionSender::new(sender); let current_attempt = Arc::clone(&new_entry.current_reconnection_attempt);
let current_attempt = Arc::clone(&new_entry.current_reconnection_attempt); self.conn_new.insert(address, new_entry);
self.active_connections.insert(address, new_entry); current_attempt
current_attempt };
};
// load the actual value. // load the actual value.
let reconnection_attempt = current_reconnection_attempt.load(Ordering::Acquire); let reconnection_attempt = current_reconnection_attempt.load(Ordering::Acquire);
@@ -223,7 +175,6 @@ impl Client {
// copy the value before moving into another task // copy the value before moving into another task
let initial_connection_timeout = self.config.initial_connection_timeout; let initial_connection_timeout = self.config.initial_connection_timeout;
let connections_count = self.connections_count.clone();
tokio::spawn(async move { tokio::spawn(async move {
// before executing the manager, wait for what was specified, if anything // before executing the manager, wait for what was specified, if anything
if let Some(backoff) = backoff { if let Some(backoff) = backoff {
@@ -231,16 +182,13 @@ impl Client {
sleep(backoff).await; sleep(backoff).await;
} }
connections_count.fetch_add(1, Ordering::SeqCst); Self::manage_connection(
ManagedConnection::new(
address.into(), address.into(),
receiver, receiver,
initial_connection_timeout, initial_connection_timeout,
current_reconnection_attempt, &current_reconnection_attempt,
) )
.run() .await
.await;
connections_count.fetch_sub(1, Ordering::SeqCst);
}); });
} }
} }
@@ -252,50 +200,53 @@ impl SendWithoutResponse for Client {
packet: NymPacket, packet: NymPacket,
packet_type: PacketType, packet_type: PacketType,
) -> io::Result<()> { ) -> io::Result<()> {
trace!("Sending packet to {address:?}"); trace!("Sending packet to {:?}", address);
let framed_packet = FramedNymPacket::new(packet, packet_type); let framed_packet =
FramedNymPacket::new(packet, packet_type, self.config.use_legacy_version);
let Some(sender) = self.active_connections.get_mut(&address) else { if let Some(sender) = self.conn_new.get_mut(&address) {
if let Err(err) = sender.channel.try_send(framed_packet) {
if err.is_full() {
debug!("Connection to {} seems to not be able to handle all the traffic - dropping the current packet", address);
// it's not a 'big' error, but we did not manage to send the packet
// if the queue is full, we can't really do anything but to drop the packet
Err(io::Error::new(
io::ErrorKind::WouldBlock,
"connection queue is full",
))
} else if err.is_disconnected() {
debug!(
"Connection to {} seems to be dead. attempting to re-establish it...",
address
);
// it's not a 'big' error, but we did not manage to send the packet, but queue
// it up to send it as soon as the connection is re-established
self.make_connection(address, err.into_inner());
Err(io::Error::new(
io::ErrorKind::ConnectionAborted,
"reconnection attempt is in progress",
))
} else {
// this can't really happen, but let's safe-guard against it in case something changes in futures library
Err(io::Error::new(
io::ErrorKind::Other,
"unknown connection buffer error",
))
}
} else {
Ok(())
}
} else {
// there was never a connection to begin with // there was never a connection to begin with
debug!("establishing initial connection to {}", address); debug!("establishing initial connection to {}", address);
// it's not a 'big' error, but we did not manage to send the packet, but queue the packet // it's not a 'big' error, but we did not manage to send the packet, but queue the packet
// for sending for as soon as the connection is created // for sending for as soon as the connection is created
self.make_connection(address, framed_packet); self.make_connection(address, framed_packet);
return Err(io::Error::new( Err(io::Error::new(
io::ErrorKind::NotConnected, io::ErrorKind::NotConnected,
"connection is in progress", "connection is in progress",
)); ))
}; }
let sending_res = sender.channel.try_send(framed_packet);
drop(sender);
sending_res.map_err(|err| {
match err {
TrySendError::Full(_) => {
debug!("Connection to {address} seems to not be able to handle all the traffic - dropping the current packet");
// it's not a 'big' error, but we did not manage to send the packet
// if the queue is full, we can't really do anything but to drop the packet
io::Error::new(
io::ErrorKind::WouldBlock,
"connection queue is full",
)
}
TrySendError::Closed(dropped) => {
debug!(
"Connection to {address} seems to be dead. attempting to re-establish it...",
);
// it's not a 'big' error, but we did not manage to send the packet, but queue
// it up to send it as soon as the connection is re-established
self.make_connection(address, dropped);
io::Error::new(
io::ErrorKind::ConnectionAborted,
"reconnection attempt is in progress",
)
}
}
} )
} }
} }
@@ -304,15 +255,13 @@ mod tests {
use super::*; use super::*;
fn dummy_client() -> Client { fn dummy_client() -> Client {
Client::new( Client::new(Config {
Config { initial_reconnection_backoff: Duration::from_millis(10_000),
initial_reconnection_backoff: Duration::from_millis(10_000), maximum_reconnection_backoff: Duration::from_millis(300_000),
maximum_reconnection_backoff: Duration::from_millis(300_000), initial_connection_timeout: Duration::from_millis(1_500),
initial_connection_timeout: Duration::from_millis(1_500), maximum_connection_buffer_size: 128,
maximum_connection_buffer_size: 128, use_legacy_version: false,
}, })
Default::default(),
)
} }
#[test] #[test]
@@ -1,72 +1,77 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::client::{Client, Config, SendWithoutResponse};
use futures::channel::mpsc; use futures::channel::mpsc;
use futures::channel::mpsc::SendError; use futures::StreamExt;
use log::*;
use nym_sphinx::forwarding::packet::MixPacket; use nym_sphinx::forwarding::packet::MixPacket;
use tokio::time::Instant; use std::time::Duration;
pub fn mix_forwarding_channels() -> (MixForwardingSender, MixForwardingReceiver) { pub type MixForwardingSender = mpsc::UnboundedSender<MixPacket>;
let (tx, rx) = mpsc::unbounded(); type MixForwardingReceiver = mpsc::UnboundedReceiver<MixPacket>;
(tx.into(), rx)
/// A specialisation of client such that it forwards any received packets on the channel into the
/// mix network immediately, i.e. will not try to listen for any responses.
pub struct PacketForwarder {
mixnet_client: Client,
packet_receiver: MixForwardingReceiver,
shutdown: nym_task::TaskClient,
} }
#[derive(Clone)] impl PacketForwarder {
pub struct MixForwardingSender(mpsc::UnboundedSender<PacketToForward>); pub fn new(
initial_reconnection_backoff: Duration,
maximum_reconnection_backoff: Duration,
initial_connection_timeout: Duration,
maximum_connection_buffer_size: usize,
use_legacy_version: bool,
shutdown: nym_task::TaskClient,
) -> (PacketForwarder, MixForwardingSender) {
let client_config = Config::new(
initial_reconnection_backoff,
maximum_reconnection_backoff,
initial_connection_timeout,
maximum_connection_buffer_size,
use_legacy_version,
);
impl From<mpsc::UnboundedSender<PacketToForward>> for MixForwardingSender { let (packet_sender, packet_receiver) = mpsc::unbounded();
fn from(tx: mpsc::UnboundedSender<PacketToForward>) -> Self {
MixForwardingSender(tx)
}
}
impl MixForwardingSender { (
pub fn forward_packet(&self, packet: impl Into<PacketToForward>) -> Result<(), SendError> { PacketForwarder {
self.0 mixnet_client: Client::new(client_config),
.unbounded_send(packet.into()) packet_receiver,
.map_err(|err| err.into_send_error()) shutdown,
},
packet_sender,
)
} }
#[allow(clippy::len_without_is_empty)] pub async fn run(&mut self) {
pub fn len(&self) -> usize { while !self.shutdown.is_shutdown() {
self.0.len() tokio::select! {
} biased;
} _ = self.shutdown.recv() => {
log::trace!("PacketForwarder: Received shutdown");
}
Some(mix_packet) = self.packet_receiver.next() => {
trace!("Going to forward packet to {}", mix_packet.next_hop());
pub type MixForwardingReceiver = mpsc::UnboundedReceiver<PacketToForward>; let next_hop = mix_packet.next_hop();
let packet_type = mix_packet.packet_type();
let packet = mix_packet.into_packet();
// we don't care about responses, we just want to fire packets
// as quickly as possible
pub struct PacketToForward { if let Err(err) =
pub packet: MixPacket, self.mixnet_client
pub forward_delay_target: Option<Instant>, .send_without_response(next_hop, packet, packet_type)
} {
debug!("failed to forward the packet - {err}")
impl From<MixPacket> for PacketToForward { }
fn from(packet: MixPacket) -> Self { }
PacketToForward::new_no_delay(packet) }
}
}
impl From<(MixPacket, Option<Instant>)> for PacketToForward {
fn from((packet, delay_until): (MixPacket, Option<Instant>)) -> Self {
PacketToForward::new(packet, delay_until)
}
}
impl From<(MixPacket, Instant)> for PacketToForward {
fn from((packet, delay_until): (MixPacket, Instant)) -> Self {
PacketToForward::new(packet, Some(delay_until))
}
}
impl PacketToForward {
pub fn new(packet: MixPacket, forward_delay_target: Option<Instant>) -> Self {
PacketToForward {
packet,
forward_delay_target,
} }
} }
pub fn new_no_delay(packet: MixPacket) -> Self {
Self::new(packet, None)
}
} }
@@ -1,9 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#[cfg(feature = "client")]
pub mod client; pub mod client;
pub mod forwarder; pub mod forwarder;
#[cfg(feature = "client")]
pub use client::{Client, Config, SendWithoutResponse}; pub use client::{Client, Config, SendWithoutResponse};
@@ -1,6 +1,7 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
pub use crate::nym_api::NymApiClientExt;
use crate::nyxd::{self, NyxdClient}; use crate::nyxd::{self, NyxdClient};
use crate::signing::direct_wallet::DirectSecp256k1HdWallet; use crate::signing::direct_wallet::DirectSecp256k1HdWallet;
use crate::signing::signer::{NoSigner, OfflineSigner}; use crate::signing::signer::{NoSigner, OfflineSigner};
@@ -19,8 +20,8 @@ use nym_api_requests::ecash::{
PartialExpirationDateSignatureResponse, VerificationKeyResponse, PartialExpirationDateSignatureResponse, VerificationKeyResponse,
}; };
use nym_api_requests::models::{ use nym_api_requests::models::{
ApiHealthResponse, GatewayBondAnnotated, GatewayCoreStatusResponse, MixnodeCoreStatusResponse, ApiHealthResponse, GatewayCoreStatusResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse,
MixnodeStatusResponse, NymNodeDescription, RewardEstimationResponse, StakeSaturationResponse, NymNodeDescription, RewardEstimationResponse, StakeSaturationResponse,
}; };
use nym_api_requests::models::{LegacyDescribedGateway, MixNodeBondAnnotated}; use nym_api_requests::models::{LegacyDescribedGateway, MixNodeBondAnnotated};
use nym_api_requests::nym_nodes::SkimmedNode; use nym_api_requests::nym_nodes::SkimmedNode;
@@ -31,11 +32,10 @@ use nym_network_defaults::NymNetworkDetails;
use time::Date; use time::Date;
use url::Url; use url::Url;
pub use crate::nym_api::NymApiClientExt;
use nym_mixnet_contract_common::EpochRewardedSet;
pub use nym_mixnet_contract_common::{ pub use nym_mixnet_contract_common::{
mixnode::MixNodeDetails, GatewayBond, IdentityKey, IdentityKeyRef, NodeId, NymNodeDetails, mixnode::MixNodeDetails, GatewayBond, IdentityKey, IdentityKeyRef, NodeId, NymNodeDetails,
}; };
// re-export the type to not break existing imports // re-export the type to not break existing imports
pub use crate::coconut::EcashApiClient; pub use crate::coconut::EcashApiClient;
@@ -257,13 +257,6 @@ impl<C, S> Client<C, S> {
Ok(self.nym_api.get_gateways().await?) Ok(self.nym_api.get_gateways().await?)
} }
#[deprecated]
pub async fn get_cached_gateways_detailed_unfiltered(
&self,
) -> Result<Vec<GatewayBondAnnotated>, ValidatorClientError> {
Ok(self.nym_api.get_gateways_detailed_unfiltered().await?)
}
// TODO: combine with NymApiClient... // TODO: combine with NymApiClient...
pub async fn get_all_cached_described_nodes( pub async fn get_all_cached_described_nodes(
&self, &self,
@@ -339,10 +332,10 @@ impl NymApiClient {
NymApiClient { nym_api } NymApiClient { nym_api }
} }
pub fn new_with_user_agent(api_url: Url, user_agent: impl Into<UserAgent>) -> Self { pub fn new_with_user_agent(api_url: Url, user_agent: UserAgent) -> Self {
let nym_api = nym_api::Client::builder::<_, ValidatorClientError>(api_url) let nym_api = nym_api::Client::builder::<_, ValidatorClientError>(api_url)
.expect("invalid api url") .expect("invalid api url")
.with_user_agent(user_agent.into()) .with_user_agent(user_agent)
.build::<ValidatorClientError>() .build::<ValidatorClientError>()
.expect("failed to build nym api client"); .expect("failed to build nym api client");
@@ -358,23 +351,34 @@ impl NymApiClient {
} }
#[deprecated(note = "use get_all_basic_active_mixing_assigned_nodes instead")] #[deprecated(note = "use get_all_basic_active_mixing_assigned_nodes instead")]
pub async fn get_basic_mixnodes(&self) -> Result<Vec<SkimmedNode>, ValidatorClientError> { pub async fn get_basic_mixnodes(
Ok(self.nym_api.get_basic_mixnodes().await?.nodes) &self,
semver_compatibility: Option<String>,
) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
Ok(self
.nym_api
.get_basic_mixnodes(semver_compatibility)
.await?
.nodes)
} }
#[deprecated(note = "use get_all_basic_entry_assigned_nodes instead")] #[deprecated(note = "use get_all_basic_entry_assigned_nodes instead")]
pub async fn get_basic_gateways(&self) -> Result<Vec<SkimmedNode>, ValidatorClientError> { pub async fn get_basic_gateways(
Ok(self.nym_api.get_basic_gateways().await?.nodes) &self,
} semver_compatibility: Option<String>,
) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
pub async fn get_current_rewarded_set(&self) -> Result<EpochRewardedSet, ValidatorClientError> { Ok(self
Ok(self.nym_api.get_rewarded_set().await?.into()) .nym_api
.get_basic_gateways(semver_compatibility)
.await?
.nodes)
} }
/// retrieve basic information for nodes are capable of operating as an entry gateway /// retrieve basic information for nodes are capable of operating as an entry gateway
/// this includes legacy gateways and nym-nodes /// this includes legacy gateways and nym-nodes
pub async fn get_all_basic_entry_assigned_nodes( pub async fn get_all_basic_entry_assigned_nodes(
&self, &self,
semver_compatibility: Option<String>,
) -> Result<Vec<SkimmedNode>, ValidatorClientError> { ) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere // TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
let mut page = 0; let mut page = 0;
@@ -383,7 +387,12 @@ impl NymApiClient {
loop { loop {
let mut res = self let mut res = self
.nym_api .nym_api
.get_basic_entry_assigned_nodes(false, Some(page), None) .get_basic_entry_assigned_nodes(
semver_compatibility.clone(),
false,
Some(page),
None,
)
.await?; .await?;
nodes.append(&mut res.nodes.data); nodes.append(&mut res.nodes.data);
@@ -401,6 +410,7 @@ impl NymApiClient {
/// this includes legacy mixnodes and nym-nodes /// this includes legacy mixnodes and nym-nodes
pub async fn get_all_basic_active_mixing_assigned_nodes( pub async fn get_all_basic_active_mixing_assigned_nodes(
&self, &self,
semver_compatibility: Option<String>,
) -> Result<Vec<SkimmedNode>, ValidatorClientError> { ) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere // TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
let mut page = 0; let mut page = 0;
@@ -409,7 +419,12 @@ impl NymApiClient {
loop { loop {
let mut res = self let mut res = self
.nym_api .nym_api
.get_basic_active_mixing_assigned_nodes(false, Some(page), None) .get_basic_active_mixing_assigned_nodes(
semver_compatibility.clone(),
false,
Some(page),
None,
)
.await?; .await?;
nodes.append(&mut res.nodes.data); nodes.append(&mut res.nodes.data);
@@ -427,6 +442,7 @@ impl NymApiClient {
/// this includes legacy mixnodes and nym-nodes /// this includes legacy mixnodes and nym-nodes
pub async fn get_all_basic_mixing_capable_nodes( pub async fn get_all_basic_mixing_capable_nodes(
&self, &self,
semver_compatibility: Option<String>,
) -> Result<Vec<SkimmedNode>, ValidatorClientError> { ) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere // TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
let mut page = 0; let mut page = 0;
@@ -435,7 +451,12 @@ impl NymApiClient {
loop { loop {
let mut res = self let mut res = self
.nym_api .nym_api
.get_basic_mixing_capable_nodes(false, Some(page), None) .get_basic_mixing_capable_nodes(
semver_compatibility.clone(),
false,
Some(page),
None,
)
.await?; .await?;
nodes.append(&mut res.nodes.data); nodes.append(&mut res.nodes.data);
@@ -450,7 +471,10 @@ impl NymApiClient {
} }
/// retrieve basic information for all bonded nodes on the network /// retrieve basic information for all bonded nodes on the network
pub async fn get_all_basic_nodes(&self) -> Result<Vec<SkimmedNode>, ValidatorClientError> { pub async fn get_all_basic_nodes(
&self,
semver_compatibility: Option<String>,
) -> Result<Vec<SkimmedNode>, ValidatorClientError> {
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere // TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
let mut page = 0; let mut page = 0;
let mut nodes = Vec::new(); let mut nodes = Vec::new();
@@ -458,7 +482,7 @@ impl NymApiClient {
loop { loop {
let mut res = self let mut res = self
.nym_api .nym_api
.get_basic_nodes(false, Some(page), None) .get_basic_nodes(semver_compatibility.clone(), false, Some(page), None)
.await?; .await?;
nodes.append(&mut res.nodes.data); nodes.append(&mut res.nodes.data);
@@ -13,7 +13,7 @@ use nym_api_requests::ecash::models::{
use nym_api_requests::ecash::VerificationKeyResponse; use nym_api_requests::ecash::VerificationKeyResponse;
use nym_api_requests::models::{ use nym_api_requests::models::{
AnnotationResponse, ApiHealthResponse, LegacyDescribedMixNode, NodePerformanceResponse, AnnotationResponse, ApiHealthResponse, LegacyDescribedMixNode, NodePerformanceResponse,
NodeRefreshBody, NymNodeDescription, RewardedSetResponse, NymNodeDescription,
}; };
use nym_api_requests::nym_nodes::PaginatedCachedNodesResponse; use nym_api_requests::nym_nodes::PaginatedCachedNodesResponse;
use nym_api_requests::pagination::PaginatedResponse; use nym_api_requests::pagination::PaginatedResponse;
@@ -102,23 +102,6 @@ pub trait NymApiClientExt: ApiClient {
.await .await
} }
#[deprecated]
#[instrument(level = "debug", skip(self))]
async fn get_gateways_detailed_unfiltered(
&self,
) -> Result<Vec<GatewayBondAnnotated>, NymAPIError> {
self.get_json(
&[
routes::API_VERSION,
routes::STATUS,
routes::GATEWAYS,
routes::DETAILED_UNFILTERED,
],
NO_PARAMS,
)
.await
}
#[deprecated] #[deprecated]
#[instrument(level = "debug", skip(self))] #[instrument(level = "debug", skip(self))]
async fn get_mixnodes_detailed_unfiltered( async fn get_mixnodes_detailed_unfiltered(
@@ -205,7 +188,16 @@ pub trait NymApiClientExt: ApiClient {
#[deprecated] #[deprecated]
#[tracing::instrument(level = "debug", skip_all)] #[tracing::instrument(level = "debug", skip_all)]
async fn get_basic_mixnodes(&self) -> Result<CachedNodesResponse<SkimmedNode>, NymAPIError> { async fn get_basic_mixnodes(
&self,
semver_compatibility: Option<String>,
) -> Result<CachedNodesResponse<SkimmedNode>, NymAPIError> {
let params = if let Some(semver_compatibility) = &semver_compatibility {
vec![("semver_compatibility", semver_compatibility.as_str())]
} else {
vec![]
};
self.get_json( self.get_json(
&[ &[
routes::API_VERSION, routes::API_VERSION,
@@ -214,14 +206,23 @@ pub trait NymApiClientExt: ApiClient {
"mixnodes", "mixnodes",
"skimmed", "skimmed",
], ],
NO_PARAMS, &params,
) )
.await .await
} }
#[deprecated] #[deprecated]
#[instrument(level = "debug", skip(self))] #[instrument(level = "debug", skip(self))]
async fn get_basic_gateways(&self) -> Result<CachedNodesResponse<SkimmedNode>, NymAPIError> { async fn get_basic_gateways(
&self,
semver_compatibility: Option<String>,
) -> Result<CachedNodesResponse<SkimmedNode>, NymAPIError> {
let params = if let Some(semver_compatibility) = &semver_compatibility {
vec![("semver_compatibility", semver_compatibility.as_str())]
} else {
vec![]
};
self.get_json( self.get_json(
&[ &[
routes::API_VERSION, routes::API_VERSION,
@@ -230,16 +231,7 @@ pub trait NymApiClientExt: ApiClient {
"gateways", "gateways",
"skimmed", "skimmed",
], ],
NO_PARAMS, &params,
)
.await
}
#[instrument(level = "debug", skip(self))]
async fn get_rewarded_set(&self) -> Result<RewardedSetResponse, NymAPIError> {
self.get_json(
&[routes::API_VERSION, "nym-nodes", "rewarded-set"],
NO_PARAMS,
) )
.await .await
} }
@@ -249,12 +241,17 @@ pub trait NymApiClientExt: ApiClient {
#[instrument(level = "debug", skip(self))] #[instrument(level = "debug", skip(self))]
async fn get_basic_entry_assigned_nodes( async fn get_basic_entry_assigned_nodes(
&self, &self,
semver_compatibility: Option<String>,
no_legacy: bool, no_legacy: bool,
page: Option<u32>, page: Option<u32>,
per_page: Option<u32>, per_page: Option<u32>,
) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> { ) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> {
let mut params = Vec::new(); let mut params = Vec::new();
if let Some(arg) = &semver_compatibility {
params.push(("semver_compatibility", arg.clone()))
}
if no_legacy { if no_legacy {
params.push(("no_legacy", "true".to_string())) params.push(("no_legacy", "true".to_string()))
} }
@@ -286,12 +283,17 @@ pub trait NymApiClientExt: ApiClient {
#[instrument(level = "debug", skip(self))] #[instrument(level = "debug", skip(self))]
async fn get_basic_active_mixing_assigned_nodes( async fn get_basic_active_mixing_assigned_nodes(
&self, &self,
semver_compatibility: Option<String>,
no_legacy: bool, no_legacy: bool,
page: Option<u32>, page: Option<u32>,
per_page: Option<u32>, per_page: Option<u32>,
) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> { ) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> {
let mut params = Vec::new(); let mut params = Vec::new();
if let Some(arg) = &semver_compatibility {
params.push(("semver_compatibility", arg.clone()))
}
if no_legacy { if no_legacy {
params.push(("no_legacy", "true".to_string())) params.push(("no_legacy", "true".to_string()))
} }
@@ -323,12 +325,17 @@ pub trait NymApiClientExt: ApiClient {
#[instrument(level = "debug", skip(self))] #[instrument(level = "debug", skip(self))]
async fn get_basic_mixing_capable_nodes( async fn get_basic_mixing_capable_nodes(
&self, &self,
semver_compatibility: Option<String>,
no_legacy: bool, no_legacy: bool,
page: Option<u32>, page: Option<u32>,
per_page: Option<u32>, per_page: Option<u32>,
) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> { ) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> {
let mut params = Vec::new(); let mut params = Vec::new();
if let Some(arg) = &semver_compatibility {
params.push(("semver_compatibility", arg.clone()))
}
if no_legacy { if no_legacy {
params.push(("no_legacy", "true".to_string())) params.push(("no_legacy", "true".to_string()))
} }
@@ -358,12 +365,17 @@ pub trait NymApiClientExt: ApiClient {
#[instrument(level = "debug", skip(self))] #[instrument(level = "debug", skip(self))]
async fn get_basic_nodes( async fn get_basic_nodes(
&self, &self,
semver_compatibility: Option<String>,
no_legacy: bool, no_legacy: bool,
page: Option<u32>, page: Option<u32>,
per_page: Option<u32>, per_page: Option<u32>,
) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> { ) -> Result<PaginatedCachedNodesResponse<SkimmedNode>, NymAPIError> {
let mut params = Vec::new(); let mut params = Vec::new();
if let Some(arg) = &semver_compatibility {
params.push(("semver_compatibility", arg.clone()))
}
if no_legacy { if no_legacy {
params.push(("no_legacy", "true".to_string())) params.push(("no_legacy", "true".to_string()))
} }
@@ -683,32 +695,16 @@ pub trait NymApiClientExt: ApiClient {
&self, &self,
node_id: NodeId, node_id: NodeId,
) -> Result<NodePerformanceResponse, NymAPIError> { ) -> Result<NodePerformanceResponse, NymAPIError> {
self.get_json( self.get_json_from(format!("/v1/nym-nodes/performance/{node_id}"))
&[ .await
routes::API_VERSION,
"nym-nodes",
"performance",
&node_id.to_string(),
],
NO_PARAMS,
)
.await
} }
async fn get_node_annotation( async fn get_node_annotation(
&self, &self,
node_id: NodeId, node_id: NodeId,
) -> Result<AnnotationResponse, NymAPIError> { ) -> Result<AnnotationResponse, NymAPIError> {
self.get_json( self.get_json_from(format!("/v1/nym-nodes/annotation/{node_id}"))
&[ .await
routes::API_VERSION,
"nym-nodes",
"annotation",
&node_id.to_string(),
],
NO_PARAMS,
)
.await
} }
#[deprecated] #[deprecated]
@@ -921,20 +917,6 @@ pub trait NymApiClientExt: ApiClient {
.await .await
} }
#[instrument(level = "debug", skip(self))]
async fn force_refresh_describe_cache(
&self,
request: &NodeRefreshBody,
) -> Result<(), NymAPIError> {
self.post_json(
&[routes::API_VERSION, "nym-nodes", "refresh-described"],
NO_PARAMS,
request,
)
.await
}
#[instrument(level = "debug", skip(self))]
async fn issued_ticketbooks_for( async fn issued_ticketbooks_for(
&self, &self,
expiration_date: Date, expiration_date: Date,
@@ -951,7 +933,6 @@ pub trait NymApiClientExt: ApiClient {
.await .await
} }
#[instrument(level = "debug", skip(self))]
async fn issued_ticketbooks_challenge( async fn issued_ticketbooks_challenge(
&self, &self,
expiration_date: Date, expiration_date: Date,
@@ -32,7 +32,7 @@ impl Div<GasPrice> for Coin {
} }
} }
impl Div<GasPrice> for &Coin { impl<'a> Div<GasPrice> for &'a Coin {
type Output = Gas; type Output = Gas;
fn div(self, rhs: GasPrice) -> Self::Output { fn div(self, rhs: GasPrice) -> Self::Output {
@@ -10,10 +10,10 @@ use cosmrs::AccountId;
use nym_contracts_common::signing::Nonce; use nym_contracts_common::signing::Nonce;
use nym_mixnet_contract_common::gateway::{PreassignedGatewayIdsResponse, PreassignedId}; use nym_mixnet_contract_common::gateway::{PreassignedGatewayIdsResponse, PreassignedId};
use nym_mixnet_contract_common::nym_node::{ use nym_mixnet_contract_common::nym_node::{
EpochAssignmentResponse, NodeDetailsByIdentityResponse, NodeDetailsResponse, EpochAssignmentResponse, NodeDetailsByIdentityResponse, NodeOwnershipResponse,
NodeOwnershipResponse, NodeRewardingDetailsResponse, PagedNymNodeBondsResponse, NodeRewardingDetailsResponse, PagedNymNodeBondsResponse, PagedNymNodeDetailsResponse,
PagedNymNodeDetailsResponse, PagedUnbondedNymNodesResponse, Role, RolesMetadataResponse, PagedUnbondedNymNodesResponse, Role, RolesMetadataResponse, StakeSaturationResponse,
StakeSaturationResponse, UnbondedNodeResponse, UnbondedNymNode, UnbondedNodeResponse, UnbondedNymNode,
}; };
use nym_mixnet_contract_common::reward_params::WorkFactor; use nym_mixnet_contract_common::reward_params::WorkFactor;
use nym_mixnet_contract_common::{ use nym_mixnet_contract_common::{
@@ -26,11 +26,10 @@ use nym_mixnet_contract_common::{
reward_params::{Performance, RewardingParams}, reward_params::{Performance, RewardingParams},
rewarding::{EstimatedCurrentEpochRewardResponse, PendingRewardResponse}, rewarding::{EstimatedCurrentEpochRewardResponse, PendingRewardResponse},
ContractBuildInformation, ContractState, ContractStateParams, CurrentIntervalResponse, ContractBuildInformation, ContractState, ContractStateParams, CurrentIntervalResponse,
CurrentNymNodeVersionResponse, Delegation, EpochEventId, EpochRewardedSet, EpochStatus, Delegation, EpochEventId, EpochStatus, GatewayBond, GatewayBondResponse,
GatewayBond, GatewayBondResponse, GatewayOwnershipResponse, HistoricalNymNodeVersionEntry, GatewayOwnershipResponse, IdentityKey, IdentityKeyRef, IntervalEventId, MixNodeBond,
IdentityKey, IdentityKeyRef, IntervalEventId, MixNodeBond, MixNodeDetails, MixNodeDetails, MixOwnershipResponse, MixnodeDetailsByIdentityResponse, MixnodeDetailsResponse,
MixOwnershipResponse, MixnodeDetailsByIdentityResponse, MixnodeDetailsResponse, NodeId, NodeId, NumberOfPendingEventsResponse, NymNodeBond, NymNodeDetails,
NumberOfPendingEventsResponse, NymNodeBond, NymNodeDetails, NymNodeVersionHistoryResponse,
PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse, PagedGatewayResponse, PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse, PagedGatewayResponse,
PagedMixnodeBondsResponse, PagedNodeDelegationsResponse, PendingEpochEvent, PagedMixnodeBondsResponse, PagedNodeDelegationsResponse, PendingEpochEvent,
PendingEpochEventResponse, PendingEpochEventsResponse, PendingIntervalEvent, PendingEpochEventResponse, PendingEpochEventsResponse, PendingIntervalEvent,
@@ -72,22 +71,6 @@ pub trait MixnetQueryClient {
.await .await
} }
async fn get_nym_node_version_history_paged(
&self,
start_after: Option<u32>,
limit: Option<u32>,
) -> Result<NymNodeVersionHistoryResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetNymNodeVersionHistory { limit, start_after })
.await
}
async fn get_current_nym_node_version(
&self,
) -> Result<CurrentNymNodeVersionResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetCurrentNymNodeVersion {})
.await
}
async fn get_mixnet_contract_state(&self) -> Result<ContractState, NyxdError> { async fn get_mixnet_contract_state(&self) -> Result<ContractState, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetState {}) self.query_mixnet_contract(MixnetQueryMsg::GetState {})
.await .await
@@ -328,7 +311,10 @@ pub trait MixnetQueryClient {
.await .await
} }
async fn get_nymnode_details(&self, node_id: NodeId) -> Result<NodeDetailsResponse, NyxdError> { async fn get_nymnode_details(
&self,
node_id: NodeId,
) -> Result<NodeOwnershipResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetNymNodeDetails { node_id }) self.query_mixnet_contract(MixnetQueryMsg::GetNymNodeDetails { node_id })
.await .await
} }
@@ -655,12 +641,6 @@ pub trait PagedMixnetQueryClient: MixnetQueryClient {
) -> Result<Vec<PendingIntervalEvent>, NyxdError> { ) -> Result<Vec<PendingIntervalEvent>, NyxdError> {
collect_paged!(self, get_pending_interval_events_paged, events) collect_paged!(self, get_pending_interval_events_paged, events)
} }
async fn get_full_nym_node_version_history(
&self,
) -> Result<Vec<HistoricalNymNodeVersionEntry>, NyxdError> {
collect_paged!(self, get_nym_node_version_history_paged, history)
}
} }
#[async_trait] #[async_trait]
@@ -670,7 +650,7 @@ impl<T> PagedMixnetQueryClient for T where T: MixnetQueryClient {}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)] #[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait MixnetQueryClientExt: MixnetQueryClient { pub trait MixnetQueryClientExt: MixnetQueryClient {
async fn get_rewarded_set(&self) -> Result<EpochRewardedSet, NyxdError> { async fn get_rewarded_set(&self) -> Result<RewardedSet, NyxdError> {
let error_response = |message| Err(NyxdError::extension_query_failure("mixnet", message)); let error_response = |message| Err(NyxdError::extension_query_failure("mixnet", message));
let metadata = self.get_rewarded_set_metadata().await?; let metadata = self.get_rewarded_set_metadata().await?;
@@ -711,16 +691,13 @@ pub trait MixnetQueryClientExt: MixnetQueryClient {
return error_response("the nodes assigned for 'standby' returned unexpected epoch_id"); return error_response("the nodes assigned for 'standby' returned unexpected epoch_id");
} }
Ok(EpochRewardedSet { Ok(RewardedSet {
epoch_id: expected_epoch_id, entry_gateways: entry.nodes,
assignment: RewardedSet { exit_gateways: exit.nodes,
entry_gateways: entry.nodes, layer1: layer1.nodes,
exit_gateways: exit.nodes, layer2: layer2.nodes,
layer1: layer1.nodes, layer3: layer3.nodes,
layer2: layer2.nodes, standby: standby.nodes,
layer3: layer3.nodes,
standby: standby.nodes,
},
}) })
} }
} }
@@ -750,7 +727,6 @@ where
mod tests { mod tests {
use super::*; use super::*;
use crate::nyxd::contract_traits::tests::IgnoreValue; use crate::nyxd::contract_traits::tests::IgnoreValue;
use nym_mixnet_contract_common::QueryMsg;
// it's enough that this compiles and clippy is happy about it // it's enough that this compiles and clippy is happy about it
#[allow(dead_code)] #[allow(dead_code)]
@@ -951,10 +927,6 @@ mod tests {
MixnetQueryMsg::GetRewardedSetMetadata {} => { MixnetQueryMsg::GetRewardedSetMetadata {} => {
client.get_rewarded_set_metadata().ignore() client.get_rewarded_set_metadata().ignore()
} }
QueryMsg::GetCurrentNymNodeVersion {} => client.get_current_nym_node_version().ignore(),
QueryMsg::GetNymNodeVersionHistory { limit, start_after } => client
.get_nym_node_version_history_paged(start_after, limit)
.ignore(),
} }
} }
} }
@@ -13,44 +13,6 @@ use tracing::error;
pub use cosmrs::abci::MsgResponse; pub use cosmrs::abci::MsgResponse;
pub fn parse_singleton_u32_from_contract_response(b: Vec<u8>) -> Result<u32, NyxdError> {
if b.len() != 4 {
return Err(NyxdError::MalformedResponseData {
got: b.len(),
expected: 4,
});
}
Ok(u32::from_be_bytes([b[0], b[1], b[2], b[3]]))
}
pub fn parse_singleton_u64_from_contract_response(b: Vec<u8>) -> Result<u64, NyxdError> {
if b.len() != 8 {
return Err(NyxdError::MalformedResponseData {
got: b.len(),
expected: 8,
});
}
Ok(u64::from_be_bytes([
b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
]))
}
#[derive(Debug, Clone)]
pub struct ParsedContractResponse {
pub message_index: usize,
pub response: Vec<u8>,
}
impl ParsedContractResponse {
pub fn parse_singleton_u32_contract_data(self) -> Result<u32, NyxdError> {
parse_singleton_u32_from_contract_response(self.response)
}
pub fn parse_singleton_u64_contract_data(self) -> Result<u64, NyxdError> {
parse_singleton_u64_from_contract_response(self.response)
}
}
pub fn parse_msg_responses(data: Bytes) -> Vec<MsgResponse> { pub fn parse_msg_responses(data: Bytes) -> Vec<MsgResponse> {
// it seems that currently, on wasmd 0.43 + tendermint-rs 0.37 + cosmrs 0.17.0-pre // it seems that currently, on wasmd 0.43 + tendermint-rs 0.37 + cosmrs 0.17.0-pre
// the data is left in undecoded base64 form, but I'd imagine this might change so if the decoding fails, // the data is left in undecoded base64 form, but I'd imagine this might change so if the decoding fails,
@@ -72,25 +34,35 @@ pub fn parse_msg_responses(data: Bytes) -> Vec<MsgResponse> {
} }
// requires there's a single response message // requires there's a single response message
pub trait ContractResponseData: Sized { pub trait ToSingletonContractData: Sized {
fn parse_singleton_u32_contract_data(&self) -> Result<u32, NyxdError> { fn parse_singleton_u32_contract_data(&self) -> Result<u32, NyxdError> {
let b = self.to_singleton_contract_data()?; let b = self.to_singleton_contract_data()?;
parse_singleton_u32_from_contract_response(b) if b.len() != 4 {
return Err(NyxdError::MalformedResponseData {
got: b.len(),
expected: 4,
});
}
Ok(u32::from_be_bytes([b[0], b[1], b[2], b[3]]))
} }
fn parse_singleton_u64_contract_data(&self) -> Result<u64, NyxdError> { fn parse_singleton_u64_contract_data(&self) -> Result<u64, NyxdError> {
let b = self.to_singleton_contract_data()?; let b = self.to_singleton_contract_data()?;
parse_singleton_u64_from_contract_response(b) if b.len() != 8 {
return Err(NyxdError::MalformedResponseData {
got: b.len(),
expected: 8,
});
}
Ok(u64::from_be_bytes([
b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
]))
} }
fn to_singleton_contract_data(&self) -> Result<Vec<u8>, NyxdError>; fn to_singleton_contract_data(&self) -> Result<Vec<u8>, NyxdError>;
fn to_unchecked_contract_data(&self) -> Result<Vec<Vec<u8>>, NyxdError>;
fn to_contract_data(&self) -> Result<Vec<ParsedContractResponse>, NyxdError>;
} }
impl ContractResponseData for ExecuteResult { impl ToSingletonContractData for ExecuteResult {
fn to_singleton_contract_data(&self) -> Result<Vec<u8>, NyxdError> { fn to_singleton_contract_data(&self) -> Result<Vec<u8>, NyxdError> {
if self.msg_responses.len() != 1 { if self.msg_responses.len() != 1 {
return Err(NyxdError::UnexpectedNumberOfMsgResponses { return Err(NyxdError::UnexpectedNumberOfMsgResponses {
@@ -100,30 +72,6 @@ impl ContractResponseData for ExecuteResult {
self.msg_responses[0].to_contract_response_data() self.msg_responses[0].to_contract_response_data()
} }
fn to_unchecked_contract_data(&self) -> Result<Vec<Vec<u8>>, NyxdError> {
self.msg_responses
.iter()
.map(ToContractResponseData::to_contract_response_data)
.collect()
}
fn to_contract_data(&self) -> Result<Vec<ParsedContractResponse>, NyxdError> {
let mut response = Vec::new();
for (message_index, msg) in self.msg_responses.iter().enumerate() {
// unfortunately `Name` trait has not been derived for `MsgExecuteContractResponse`,
// so we have to make an explicit string comparison instead
if msg.type_url == "/cosmwasm.wasm.v1.MsgExecuteContractResponse" {
response.push(ParsedContractResponse {
message_index,
response: msg.to_contract_response_data()?,
})
}
}
Ok(response)
}
} }
pub trait ToContractResponseData: Sized { pub trait ToContractResponseData: Sized {
@@ -23,7 +23,7 @@ use tendermint_rpc::endpoint::*;
use tendermint_rpc::query::Query; use tendermint_rpc::query::Query;
use tendermint_rpc::{Error as TendermintRpcError, Order, Paging, SimpleRequest}; use tendermint_rpc::{Error as TendermintRpcError, Order, Paging, SimpleRequest};
pub use helpers::{ContractResponseData, ToContractResponseData}; pub use helpers::{ToContractResponseData, ToSingletonContractData};
#[cfg(feature = "http-client")] #[cfg(feature = "http-client")]
use crate::http_client; use crate::http_client;
@@ -22,7 +22,7 @@ pub struct GasPrice {
pub denom: String, pub denom: String,
} }
impl Mul<Gas> for &GasPrice { impl<'a> Mul<Gas> for &'a GasPrice {
type Output = Coin; type Output = Coin;
fn mul(self, gas_limit: Gas) -> Self::Output { fn mul(self, gas_limit: Gas) -> Self::Output {
@@ -98,13 +98,6 @@ impl DirectSecp256k1HdWallet {
Ok((private_key, public_key)) Ok((private_key, public_key))
} }
pub fn derive_extended_private_key(
&self,
hd_path: &DerivationPath,
) -> Result<XPrv, DirectSecp256k1HdWalletError> {
Ok(XPrv::derive_from_path(self.seed, hd_path)?)
}
pub fn try_derive_accounts(&self) -> Result<Vec<AccountData>, DirectSecp256k1HdWalletError> { pub fn try_derive_accounts(&self) -> Result<Vec<AccountData>, DirectSecp256k1HdWalletError> {
let mut accounts = Vec::with_capacity(self.accounts.len()); let mut accounts = Vec::with_capacity(self.accounts.len());
for derivation_info in &self.accounts { for derivation_info in &self.accounts {
-2
View File
@@ -48,7 +48,6 @@ nym-vesting-contract-common = { path = "../cosmwasm-smart-contracts/vesting-cont
nym-coconut-dkg-common = { path = "../cosmwasm-smart-contracts/coconut-dkg" } nym-coconut-dkg-common = { path = "../cosmwasm-smart-contracts/coconut-dkg" }
nym-multisig-contract-common = { path = "../cosmwasm-smart-contracts/multisig-contract" } nym-multisig-contract-common = { path = "../cosmwasm-smart-contracts/multisig-contract" }
nym-ecash-contract-common = { path = "../cosmwasm-smart-contracts/ecash-contract" } nym-ecash-contract-common = { path = "../cosmwasm-smart-contracts/ecash-contract" }
nym-ecash-time = { path = "../../common/ecash-time" }
nym-sphinx = { path = "../../common/nymsphinx" } nym-sphinx = { path = "../../common/nymsphinx" }
nym-client-core = { path = "../../common/client-core" } nym-client-core = { path = "../../common/client-core" }
nym-config = { path = "../../common/config" } nym-config = { path = "../../common/config" }
@@ -57,7 +56,6 @@ nym-credentials-interface = { path = "../../common/credentials-interface" }
nym-credential-storage = { path = "../../common/credential-storage" } nym-credential-storage = { path = "../../common/credential-storage" }
nym-credential-utils = { path = "../../common/credential-utils" } nym-credential-utils = { path = "../../common/credential-utils" }
nym-id = { path = "../nym-id" } nym-id = { path = "../nym-id" }
nym-credential-proxy-requests = { path = "../../nym-credential-proxy/nym-credential-proxy-requests" }
nym-pemstore = { path = "../../common/pemstore", version = "0.3.0" } nym-pemstore = { path = "../../common/pemstore", version = "0.3.0" }
nym-types = { path = "../../common/types" } nym-types = { path = "../../common/types" }

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