Compare commits

...

81 Commits

Author SHA1 Message Date
Tommy Verrall 600bf42a95 conflicts 2025-04-09 12:51:31 +02:00
Tommy Verrall 748e3e4248 fix remaining lint and cargo clippy errors 2025-04-09 12:46:03 +02:00
dependabot[bot] 8cf1b6427a build(deps): bump tokio from 1.44.0 to 1.44.2 in /nym-wallet (#5694) 2025-04-09 12:40:37 +02:00
Tommy Verrall 7a888c6fdf fix wallet ci 2025-04-09 12:17:02 +02:00
Tommy Verrall 9a9bb89d89 fix lint again 2025-04-09 12:14:49 +02:00
Tommy Verrall 4cc14ddcc4 cargo fmt
- hopefully the last
2025-04-09 11:53:47 +02:00
Tommy Verrall 2dbf9d97cb yarn lint fix 2025-04-09 11:47:10 +02:00
Tommy Verrall 91b6f3cc3e paste not working from currency form
- removed shellhelper too
2025-04-09 11:22:09 +02:00
Tommy Verrall 84cccffcbd Fix PR comments
- removed the shell open in favour of the tauri plugin for opening
- cleaned up some code
- added a few packages
2025-04-09 10:27:25 +02:00
Tommy Verrall af16b3f059 first code review comments 2025-04-09 09:12:21 +02:00
Tommy Verrall b1cde0716e Fix delegation list 2025-04-08 20:10:05 +02:00
Tommy Verrall 45bcdb03d8 fix delegations page - after overflow 2025-04-08 19:29:32 +02:00
Tommy Verrall 44682b5ef0 removed duplicates and reverted back to 1.2.18 as a version 2025-04-08 18:46:52 +02:00
Tommy Verrall 51c9b012e2 merge conflicts 2025-04-08 16:50:45 +02:00
Tommy Verrall 50b1175622 Merge branch 'develop' into feature/test-v2 2025-04-08 16:40:00 +02:00
Tommy Verrall 29ee5984fb fix all workflows 2025-04-08 16:21:15 +02:00
Tommy Verrall e542b25ffc bump to version 2.0.0
- it's a big release therefore let's semver it correctly
2025-04-08 16:03:36 +02:00
Tommy Verrall 516d3f04cf No need to publish these to the build server just use the artifacts 2025-04-08 15:57:20 +02:00
Tommy Verrall 08c09781c7 Fixing all yarn lint errors 2025-04-08 14:36:42 +02:00
Tommy Verrall c92de832e4 remove arg 2025-04-08 12:12:13 +02:00
Tommy Verrall d9d62195cb try again 2025-04-08 12:05:28 +02:00
Tommy Verrall da9115d51b format 2025-04-08 11:58:48 +02:00
Tommy Verrall 1367cad99d another attempt 2025-04-08 11:54:47 +02:00
Tommy Verrall 4f6d65ab95 revert previous add more logging 2025-04-08 11:50:27 +02:00
Tommy Verrall 4292d8ac03 update windows build 2025-04-08 11:40:50 +02:00
Tommy Verrall dcb6de2421 tauri path 2025-04-08 11:22:38 +02:00
Tommy Verrall 1f5ed41bb3 correct tauri path 2025-04-08 11:21:53 +02:00
Tommy Verrall 091e98aa74 attempt windows build 2025-04-08 11:14:19 +02:00
Jędrzej Stuczyński 0e38126fc5 Feature/replay protection (#5682)
* remove old packettype + fix: apply routing filter BEFORE delaying

* updated sphinx crate for allow usage of reply tags

* full pipeline for placeholder checking of packet replay

* replay protection with batched insertion

* running background task for clearing/flushing the BF

* allow disabling the replay detection + cleanup

* allow unwrap in bench code
2025-04-08 09:50:25 +01:00
Tommy Verrall ecbe192a88 try 22.04 2025-04-08 10:20:50 +02:00
Tommy Verrall f0ee49788c test old runner first 2025-04-08 10:18:32 +02:00
Tommy Verrall d2ff3cb88d fix app deps 2025-04-08 10:15:27 +02:00
Tommy Verrall 873d15a5e1 update runner platform 2025-04-08 10:13:30 +02:00
Tommy Verrall 53792cc839 Update runner for linux 2025-04-08 10:00:22 +02:00
Tommy Verrall 415ef1bf13 attempt to push to ci 2025-04-08 09:53:35 +02:00
Tommy Verrall a4f6426bf9 Update account display 2025-04-08 09:32:46 +02:00
dependabot[bot] 0870911b3c build(deps): bump tokio from 1.44.1 to 1.44.2 (#5693) 2025-04-08 08:01:40 +02:00
Tommy Verrall 9f23887cc0 Input fields 2025-04-07 20:07:15 +02:00
Tommy Verrall 8ab269fa05 Jazz up receive modal 2025-04-07 17:16:22 +02:00
Tommy Verrall 7b75f22a8e Remove legacy 2025-04-07 15:27:54 +02:00
Tommy Verrall ca0449e03d Init clipboard manager 2025-04-07 14:22:55 +02:00
Tommy Verrall 224e63d275 Rename and update 2025-04-07 11:37:22 +02:00
Tommy Verrall 3d77283056 Add pruning warning errors 2025-04-07 10:29:03 +02:00
Tommy Verrall 7cc473005b More permissions errors
- fix more perm errors
- enabled the version in the wallet
2025-04-07 10:09:47 +02:00
Tommy Verrall f874284850 - Update beyond tauri v2
- use the latest and greatest
- fixed links to use the command shell
- app version changes, need to be fixed to allow the auto updater too work
2025-04-04 18:47:35 +02:00
Tommy Verrall 7b6077ba64 update to log in
- next up fix hyperlinks
2025-04-04 13:56:20 +02:00
Jędrzej Stuczyński 12026305d5 chore: clippy for 1.86 (#5685)
* chore: clippy for 1.86

* clippy inside wallet
2025-04-04 10:43:21 +01:00
import this 257e36ddcb Featrure: Bash scripts to init and configure VMs conveniently and update docs (#5681)
* create VM init and config scripts

* PR ready for review

* address review comments

* syntax fix
2025-04-04 09:17:30 +00:00
Jon Häggblad ad81c6d27e Move all workflows on ubuntu-20 to ubuntu-22 (#5455)
* Move all workflows on ubuntu-20 to ubuntu-22

* Add missing -y for installing rsync in ci-docs

* Install rsync with --yes

* Switch two jobs to github hosted free tier runners
2025-04-04 11:05:02 +02:00
Tommy Verrall ae52b7b71f Merge pull request #5483 from nymtech/dependabot/npm_and_yarn/elliptic-6.6.1
build(deps): bump elliptic from 6.5.5 to 6.6.1
2025-04-04 08:48:29 +00:00
Tommy Verrall 854d3cceac Merge pull request #5665 from nymtech/dependabot/npm_and_yarn/sdk/typescript/tests/integration-tests/mix-fetch/multi-eeeba236cb
build(deps): bump tar-fs and puppeteer in /sdk/typescript/tests/integration-tests/mix-fetch
2025-04-04 08:45:49 +00:00
benedetta davico 1bdf867fdb Merge pull request #5684 from nymtech/benny/fix-mac-build
Fix the mac build of the wallet
2025-04-04 10:45:05 +02:00
benedetta davico 5a88b5b6a8 upper case 2025-04-04 10:39:40 +02:00
benedettadavico 5ab4d3c22c bump wallet version 2025-04-04 10:12:00 +02:00
benedetta davico b529883b81 Update package.json 2025-04-04 10:11:03 +02:00
benedetta davico 07f624660c Update Cargo.toml 2025-04-04 10:10:37 +02:00
benedetta davico 71f8e736d8 Update publish-nym-wallet-macos.yml 2025-04-04 10:09:58 +02:00
benedetta davico d3573e78e0 Merge pull request #5677 from nymtech/benny/update-node-versions
Update node versions in CI
2025-04-04 09:26:47 +02:00
import this e6e74855af [DOCs/operators]: Release notes 2025.6-chuckles (#5678)
* release notes finished

* add explorer info
2025-04-02 14:28:31 +00:00
Tommy Verrall b4865520a4 Revert "add the base points back in"
This reverts commit 400aa6ba6d.
2025-04-02 15:36:49 +02:00
Tommy Verrall f52ebfb9c3 Merge remote-tracking branch 'origin/feature/test-v2' into feature/test-v2 2025-04-02 15:34:12 +02:00
Tommy Verrall 6ca2a3c539 migrate to v2
- lots to check and do
2025-04-02 15:22:27 +02:00
Tommy Verrall 717c9066d6 Merge remote-tracking branch 'origin/feature/test-v2' into feature/test-v2 2025-04-02 15:18:26 +02:00
Tommy Verrall 2760a17323 add the base points back in
- now i've reverted back to the original two here, as the compiler is failing around `tauri::api::path` however, looking into the new design for the path resolver in tower this tasks, requires pratically changing the whole wallet_strorage and config set up
- it seems pretty straight forward https://v2.tauri.app/start/migrate/from-tauri-1/#migrate-path-to-tauri-manager - however, I would need a second set of eyes on this
2025-04-02 15:18:11 +02:00
Tommy Verrall 4e9f1bc0ed migrate to v2
- lots to check and do
2025-04-02 15:17:44 +02:00
Tommy Verrall d35023d14b Merge remote-tracking branch 'origin/feature/test-v2' into feature/test-v2 2025-04-02 15:14:02 +02:00
Tommy Verrall 400aa6ba6d add the base points back in
- now i've reverted back to the original two here, as the compiler is failing around `tauri::api::path` however, looking into the new design for the path resolver in tower this tasks, requires pratically changing the whole wallet_strorage and config set up
- it seems pretty straight forward https://v2.tauri.app/start/migrate/from-tauri-1/#migrate-path-to-tauri-manager - however, I would need a second set of eyes on this
2025-04-02 15:13:42 +02:00
Tommy Verrall 2ba74ae120 migrate to v2
- lots to check and do
2025-04-02 15:13:42 +02:00
fmtabbara 99d8aebea9 fix build 2025-04-02 13:08:16 +01:00
benedettadavico 0bde4dfc84 update to node v20 2025-04-02 11:41:27 +02:00
benedetta davico a56068e28a Merge pull request #5671 from nymtech/release/2025.6-chuckles
Merge release/2025.6-chuckles into develop
2025-04-02 10:35:16 +02:00
Tommy Verrall 9a4293a5b9 add the base points back in
- now i've reverted back to the original two here, as the compiler is failing around `tauri::api::path` however, looking into the new design for the path resolver in tower this tasks, requires pratically changing the whole wallet_strorage and config set up
- it seems pretty straight forward https://v2.tauri.app/start/migrate/from-tauri-1/#migrate-path-to-tauri-manager - however, I would need a second set of eyes on this
2025-04-02 08:53:40 +02:00
Tommy Verrall cdddb44099 migrate to v2
- lots to check and do
2025-04-01 17:06:21 +02:00
Jack Wampler d309b44ad7 Minor fixes involving key cloning and hashing (#5664) 2025-04-01 08:34:39 -06:00
Jędrzej Stuczyński d062524d32 mix throughput tester (#5661)
* wip: sending with single client

* tag packets to measure latency

* constantly logging rates

* concurrency

* adjusting some values

* write results to files upon completion
2025-03-31 15:57:24 +01:00
dependabot[bot] 89eea3100e build(deps): bump the patch-updates group across 1 directory with 8 updates (#5668)
Bumps the patch-updates group with 7 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [clap](https://github.com/clap-rs/clap) | `4.5.32` | `4.5.34` |
| [clap_complete](https://github.com/clap-rs/clap) | `4.5.46` | `4.5.47` |
| [once_cell](https://github.com/matklad/once_cell) | `1.21.1` | `1.21.3` |
| [reqwest](https://github.com/seanmonstar/reqwest) | `0.12.4` | `0.12.15` |
| [tempfile](https://github.com/Stebalien/tempfile) | `3.19.0` | `3.19.1` |
| [time](https://github.com/time-rs/time) | `0.3.39` | `0.3.41` |
| [uniffi](https://github.com/mozilla/uniffi-rs) | `0.29.0` | `0.29.1` |



Updates `clap` from 4.5.32 to 4.5.34
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.32...clap_complete-v4.5.34)

Updates `clap_complete` from 4.5.46 to 4.5.47
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.46...clap_complete-v4.5.47)

Updates `once_cell` from 1.21.1 to 1.21.3
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.21.1...v1.21.3)

Updates `reqwest` from 0.12.4 to 0.12.15
- [Release notes](https://github.com/seanmonstar/reqwest/releases)
- [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/seanmonstar/reqwest/compare/v0.12.4...v0.12.15)

Updates `tempfile` from 3.19.0 to 3.19.1
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/compare/v3.19.0...v3.19.1)

Updates `time` from 0.3.39 to 0.3.41
- [Release notes](https://github.com/time-rs/time/releases)
- [Changelog](https://github.com/time-rs/time/blob/main/CHANGELOG.md)
- [Commits](https://github.com/time-rs/time/compare/v0.3.39...v0.3.41)

Updates `uniffi` from 0.29.0 to 0.29.1
- [Changelog](https://github.com/mozilla/uniffi-rs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mozilla/uniffi-rs/compare/v0.29.0...v0.29.1)

Updates `uniffi_build` from 0.29.0 to 0.29.1
- [Changelog](https://github.com/mozilla/uniffi-rs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mozilla/uniffi-rs/compare/v0.29.0...v0.29.1)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: clap_complete
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: once_cell
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: reqwest
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: tempfile
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: time
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: uniffi
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
- dependency-name: uniffi_build
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: patch-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-31 15:51:06 +02:00
Jon Häggblad d893c806c2 Update log crate (#5667) 2025-03-31 14:44:47 +02:00
dependabot[bot] 7846058802 build(deps): bump blake3 from 1.6.1 to 1.7.0 (#5658)
Bumps [blake3](https://github.com/BLAKE3-team/BLAKE3) from 1.6.1 to 1.7.0.
- [Release notes](https://github.com/BLAKE3-team/BLAKE3/releases)
- [Commits](https://github.com/BLAKE3-team/BLAKE3/compare/1.6.1...1.7.0)

---
updated-dependencies:
- dependency-name: blake3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-31 13:57:26 +02:00
dependabot[bot] 3c98c9021e build(deps): bump tar-fs and puppeteer
Bumps [tar-fs](https://github.com/mafintosh/tar-fs) to 3.0.8 and updates ancestor dependency [puppeteer](https://github.com/puppeteer/puppeteer). These dependencies need to be updated together.


Updates `tar-fs` from 3.0.4 to 3.0.8
- [Commits](https://github.com/mafintosh/tar-fs/compare/v3.0.4...v3.0.8)

Updates `puppeteer` from 21.1.1 to 24.4.0
- [Release notes](https://github.com/puppeteer/puppeteer/releases)
- [Changelog](https://github.com/puppeteer/puppeteer/blob/main/CHANGELOG.md)
- [Commits](https://github.com/puppeteer/puppeteer/compare/puppeteer-v21.1.1...puppeteer-v24.4.0)

---
updated-dependencies:
- dependency-name: tar-fs
  dependency-type: indirect
- dependency-name: puppeteer
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-28 22:42:24 +00:00
import this f705884a53 [DOCs/operators]: fix typo and add url (#5662) 2025-03-27 11:50:16 +00:00
dependabot[bot] 84b6068ac9 build(deps): bump elliptic from 6.5.5 to 6.6.1
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.5 to 6.6.1.
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.5...v6.6.1)

---
updated-dependencies:
- dependency-name: elliptic
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-17 23:26:04 +00:00
211 changed files with 18153 additions and 5934 deletions
+2 -2
View File
@@ -5,7 +5,7 @@ on:
jobs:
build:
runs-on: arc-ubuntu-20.04
runs-on: arc-ubuntu-22.04
defaults:
run:
working-directory: documentation/docs
@@ -18,7 +18,7 @@ jobs:
- name: Install Python3 modules
run: sudo pip3 install pandas tabulate
- name: Install rsync
run: sudo apt-get install rsync
run: sudo apt-get install -y rsync
- uses: rlespinasse/github-slug-action@v3.x
- name: Setup pnpm
uses: pnpm/action-setup@v4.0.0
@@ -33,7 +33,7 @@ jobs:
strategy:
fail-fast: false
matrix:
platform: [arc-ubuntu-20.04]
platform: [arc-ubuntu-22.04]
runs-on: ${{ matrix.platform }}
steps:
+2 -2
View File
@@ -10,7 +10,7 @@ on:
jobs:
build:
runs-on: arc-ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Install rsync
@@ -19,7 +19,7 @@ jobs:
- uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v4
with:
node-version: 18
node-version: 20
- name: Setup yarn
run: npm install -g yarn
- name: Build
@@ -21,7 +21,7 @@ jobs:
strategy:
fail-fast: false
matrix:
platform: [ arc-ubuntu-20.04 ]
platform: [ arc-ubuntu-22.04 ]
runs-on: ${{ matrix.platform }}
env:
+1 -1
View File
@@ -38,7 +38,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ arc-ubuntu-20.04, custom-windows-11, custom-runner-mac-m1 ]
os: [ arc-ubuntu-22.04, custom-windows-11, custom-runner-mac-m1 ]
runs-on: ${{ matrix.os }}
env:
CARGO_TERM_COLOR: always
+1 -1
View File
@@ -11,7 +11,7 @@ on:
jobs:
check-schema:
name: Generate and check schema
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
env:
CARGO_TERM_COLOR: always
steps:
@@ -11,7 +11,7 @@ jobs:
strategy:
fail-fast: false
matrix:
platform: [ arc-ubuntu-20.04 ]
platform: [ arc-ubuntu-22.04 ]
runs-on: ${{ matrix.platform }}
env:
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
build:
# since it's going to be compiled into wasm, there's absolutely
# no point in running CI on different OS-es
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
env:
CARGO_TERM_COLOR: always
steps:
+2 -2
View File
@@ -10,7 +10,7 @@ on:
jobs:
build:
runs-on: arc-ubuntu-20.04
runs-on: arc-ubuntu-22.04
env:
RUSTUP_PERMIT_COPY_RENAME: 1
defaults:
@@ -25,7 +25,7 @@ jobs:
- name: Install Python3 modules
run: sudo pip3 install pandas tabulate
- name: Install rsync
run: sudo apt-get install rsync
run: sudo apt-get install -y rsync
- uses: rlespinasse/github-slug-action@v3.x
- name: Setup pnpm
uses: pnpm/action-setup@v4.0.0
+2 -2
View File
@@ -15,7 +15,7 @@ on:
jobs:
build:
runs-on: arc-ubuntu-20.04
runs-on: ubuntu-22.04
env:
RUSTUP_PERMIT_COPY_RENAME: 1
steps:
@@ -24,7 +24,7 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 18
node-version: 20
- name: Setup yarn
run: npm install -g yarn
@@ -1,92 +0,0 @@
name: ci-nym-network-explorer
on:
workflow_dispatch:
push:
paths:
- 'explorer/**'
- '.github/workflows/ci-nym-network-explorer.yml'
defaults:
run:
working-directory: explorer
jobs:
build:
runs-on: custom-linux
steps:
- uses: actions/checkout@v4
- name: Install rsync
run: sudo apt-get install rsync
continue-on-error: true
- uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v4
with:
node-version: 18
- name: Setup yarn
run: npm install -g yarn
continue-on-error: true
- name: Build shared packages
run: cd .. && yarn && yarn build
- name: Set environment from the example
run: cp .env.prod .env
# - run: yarn test
# continue-on-error: true
- run: yarn && yarn build
continue-on-error: true
- run: yarn storybook:build
name: Build storybook
- name: Deploy branch to CI www
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "explorer/dist/"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/network-explorer-${{ env.GITHUB_REF_SLUG }}
EXCLUDE: "/dist/, /node_modules/"
- name: Deploy storybook to CI www
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "explorer/storybook-static/"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/ne-sb-${{ env.GITHUB_REF_SLUG }}
EXCLUDE: "/dist/, /node_modules/"
- name: Matrix - Node Install
run: npm install
working-directory: .github/workflows/support-files
- name: Matrix - Send Notification
env:
NYM_NOTIFICATION_KIND: network-explorer
NYM_PROJECT_NAME: "Network Explorer"
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
NYM_CI_WWW_LOCATION: "network-explorer-${{ env.GITHUB_REF_SLUG }}"
NYM_CI_WWW_LOCATION_STORYBOOK: "ne-sb-${{ env.GITHUB_REF_SLUG }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "${GITHUB_REF##*/}"
IS_SUCCESS: "${{ job.status == 'success' }}"
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}"
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
- name: Deploy
if: github.event_name == 'workflow_dispatch'
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CD_PROD_NE_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "explorer/dist/"
REMOTE_HOST: ${{ secrets.CD_PROD_NE_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CD_PROD_NE_REMOTE_USER }}
TARGET: ${{ secrets.CD_PROD_NE_REMOTE_TARGET }}
EXCLUDE: "/dist/, /node_modules/"
+7 -3
View File
@@ -11,13 +11,17 @@ on:
jobs:
build:
runs-on: arc-ubuntu-20.04
runs-on: arc-ubuntu-22.04
env:
CARGO_TERM_COLOR: always
RUSTUP_PERMIT_COPY_RENAME: 1
steps:
- 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 squashfs-tools
- name: Install system dependencies
run: |
sudo apt-get update && sudo apt-get install -y libdbus-1-dev libmnl-dev libnftnl-dev \
libwebkit2gtk-4.1-dev build-essential curl wget libssl-dev jq \
libgtk-3-dev squashfs-tools libayatana-appindicator3-dev make libfuse2 unzip librsvg2-dev file \
libsoup-3.0-dev libjavascriptcoregtk-4.1-dev
continue-on-error: true
- name: Check out repository code
+2 -2
View File
@@ -11,7 +11,7 @@ on:
jobs:
wasm:
runs-on: arc-ubuntu-20.04
runs-on: arc-ubuntu-22.04
env:
CARGO_TERM_COLOR: always
RUSTUP_PERMIT_COPY_RENAME: 1
@@ -20,7 +20,7 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 18
node-version: 20
- uses: actions-rs/toolchain@v1
with:
+4 -4
View File
@@ -11,7 +11,7 @@ jobs:
fail-fast: false
matrix:
rust: [stable, beta]
os: [ubuntu-20.04, windows-latest, macos-latest]
os: [ubuntu-22.04, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
env:
CARGO_TERM_COLOR: always
@@ -23,7 +23,7 @@ jobs:
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get install -y build-essential curl wget libssl-dev libudev-dev squashfs-tools protobuf-compiler
if: matrix.os == 'ubuntu-20.04'
if: matrix.os == 'ubuntu-22.04'
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
@@ -59,7 +59,7 @@ jobs:
# To avoid running out of disk space, skip generating debug symbols
- name: Set debug to false (unix)
if: matrix.os == 'ubuntu-20.04' || matrix.os == 'macos-latest'
if: matrix.os == 'ubuntu-22.04' || matrix.os == 'macos-latest'
run: |
sed -i.bak 's/\[profile.dev\]/\[profile.dev\]\ndebug = false/' Cargo.toml
git diff
@@ -106,7 +106,7 @@ jobs:
uses: actions/setup-node@v4
if: env.WORKFLOW_CONCLUSION == 'failure'
with:
node-version: 18
node-version: 20
- name: Matrix - Node Install
if: env.WORKFLOW_CONCLUSION == 'failure'
run: npm install
@@ -10,7 +10,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-20.04, macos-latest, windows-latest]
os: [ubuntu-22.04, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
env:
CARGO_TERM_COLOR: always
@@ -22,7 +22,7 @@ jobs:
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get install -y libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools
if: matrix.os == 'ubuntu-20.04'
if: matrix.os == 'ubuntu-22.04'
- name: Install rust toolchain
uses: actions-rs/toolchain@v1
@@ -68,7 +68,7 @@ jobs:
uses: actions/setup-node@v4
if: env.WORKFLOW_CONCLUSION == 'failure'
with:
node-version: 18
node-version: 20
- name: Matrix - Node Install
if: env.WORKFLOW_CONCLUSION == 'failure'
run: npm install
+2 -2
View File
@@ -5,7 +5,7 @@ on:
- cron: '5 9 * * *'
jobs:
cargo-deny:
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- name: Checkout repository code
uses: actions/checkout@v4
@@ -38,7 +38,7 @@ jobs:
- name: install npm
uses: actions/setup-node@v4
with:
node-version: 18
node-version: 20
- name: Matrix - Node Install
run: npm install
working-directory: .github/workflows/support-files
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
strategy:
fail-fast: false
matrix:
platform: [custom-ubuntu-20.04]
platform: [custom-ubuntu-22.04]
runs-on: ${{ matrix.platform }}
outputs:
+41 -27
View File
@@ -18,11 +18,7 @@ jobs:
runs-on: ${{ matrix.platform }}
outputs:
release_id: ${{ steps.create-release.outputs.id }}
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].created_at }}
version: ${{ steps.release-info.outputs.version }}
filename: ${{ steps.release-info.outputs.filename }}
file_hash: ${{ steps.release-info.outputs.file_hash }}
release_tag: ${{ github.ref_name }}
steps:
- uses: actions/checkout@v4
@@ -31,10 +27,18 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: 21
- name: Install Rust stable
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
- name: Add Rust target for x86_64-apple-darwin
run: rustup target add x86_64-apple-darwin
- name: Set Cargo build target to x86_64
run: echo "CARGO_BUILD_TARGET=x86_64-apple-darwin" >> $GITHUB_ENV
- name: Install the Apple developer certificate for code signing
env:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
@@ -64,11 +68,19 @@ jobs:
fileName: '.env'
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
- name: Yarn cache clean
shell: bash
run: cd .. && yarn cache clean
- name: Install project dependencies
shell: bash
run: cd .. && yarn --network-timeout 100000
- name: Install app dependencies and build it
- name: Yarn build
shell: bash
run: cd .. && yarn build
- name: Install dependencies and build it
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ENABLE_CODE_SIGNING: ${{ secrets.APPLE_CERTIFICATE }}
@@ -78,46 +90,48 @@ jobs:
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_IDENTITY_ID }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
run: yarn && yarn build
# Tauri v2 specific environment variables
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
TAURI_NOTARIZATION_USERNAME: ${{ secrets.APPLE_ID }}
TAURI_NOTARIZATION_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
TAURI_NOTARIZATION_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: |
yarn build-macx86
- name: Create app tarball
run: |
# Navigate to where the app bundle is and create the tarball
cd target/x86_64-apple-darwin/release/bundle/macos
echo "Creating tarball from app bundle"
tar -czf nym-wallet.app.tar.gz NymWallet.app
cd -
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: nym-wallet.app.tar.gz
path: nym-wallet/target/release/bundle/macos/nym-wallet.app.tar.gz
path: nym-wallet/target/x86_64-apple-darwin/release/bundle/macos/nym-wallet.app.tar.gz
retention-days: 5
- name: Clean up keychain
if: ${{ always() }}
run: |
security delete-keychain $RUNNER_TEMP/app-signing.keychain-db
- id: create-release
name: Upload to release based on tag name
uses: softprops/action-gh-release@v2
if: github.event_name == 'release'
with:
files: |
nym-wallet/target/release/bundle/dmg/*.dmg
nym-wallet/target/release/bundle/macos/*.app.tar.gz*
- name: Deploy artifacts to CI www
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-avzr"
SOURCE: "nym-wallet/target/release/bundle/macos/nym-wallet.app.tar.gz"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/builds/${{ github.ref_name }}/nym-wallet
EXCLUDE: "/dist/, /node_modules/"
nym-wallet/target/x86_64-apple-darwin/release/bundle/dmg/*.dmg
nym-wallet/target/x86_64-apple-darwin/release/bundle/macos/*.app.tar.gz*
push-release-data:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/release-calculate-hash.yml
needs: publish-tauri
with:
release_tag: ${{ github.ref_name }}
secrets: inherit
release_tag: ${{ needs.publish-tauri.outputs.release_tag || github.ref_name }}
secrets: inherit
+81 -42
View File
@@ -3,71 +3,108 @@ on:
workflow_dispatch:
release:
types: [created]
defaults:
run:
working-directory: nym-wallet
jobs:
publish-tauri:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
strategy:
fail-fast: false
matrix:
platform: [custom-ubuntu-20.04]
platform: [ubuntu-22.04]
runs-on: ${{ matrix.platform }}
outputs:
release_id: ${{ steps.create-release.outputs.id }}
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].created_at }}
version: ${{ steps.release-info.outputs.version }}
filename: ${{ steps.release-info.outputs.filename }}
file_hash: ${{ steps.release-info.outputs.file_hash }}
release_tag: ${{ github.ref_name }}
steps:
- uses: actions/checkout@v4
- name: Tauri dependencies
run: >
sudo apt-get update &&
sudo apt-get install -y webkit2gtk-4.0
continue-on-error: true
- name: Install system dependencies
run: |
sudo apt-get update && sudo apt-get install -y libdbus-1-dev libmnl-dev libnftnl-dev \
libwebkit2gtk-4.1-dev build-essential curl wget libssl-dev jq \
libgtk-3-dev squashfs-tools libayatana-appindicator3-dev make libfuse2 unzip librsvg2-dev file \
libsoup-3.0-dev libjavascriptcoregtk-4.1-dev
- name: Node
uses: actions/setup-node@v4
with:
node-version: 21
cache: 'yarn'
- name: Install Rust stable
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
- name: Install project dependencies
shell: bash
run: cd .. && yarn --network-timeout 100000
- name: Install app dependencies
run: yarn
- name: Create env file
uses: timheuer/base64-to-file@v1.2
with:
fileName: '.env'
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
- name: Build app
run: yarn build
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
- name: Check bundle directory
run: |
echo "Checking bundle directory structure"
ls -la target/release/bundle || echo "Bundle directory not found"
if [ -d "target/release/bundle/appimage" ]; then
echo "AppImage bundle directory exists, checking contents:"
ls -la target/release/bundle/appimage
else
echo "AppImage bundle directory not found, checking alternatives:"
find target/release/bundle -type d -name "*appimage*" -o -name "*AppImage*" || echo "No AppImage directories found"
find target/release/bundle -name "*.AppImage" -o -name "*.appimage" || echo "No AppImage files found"
fi
- name: Create AppImage tarball if needed
run: |
# Find the AppImage file
APPIMAGE_FILE=$(find target/release/bundle -name "*.AppImage" | head -n 1)
if [ -n "$APPIMAGE_FILE" ]; then
echo "Found AppImage file: $APPIMAGE_FILE"
APPIMAGE_DIR=$(dirname "$APPIMAGE_FILE")
APPIMAGE_NAME=$(basename "$APPIMAGE_FILE")
# Create tarball if it doesn't exist
if [ ! -f "${APPIMAGE_FILE}.tar.gz" ]; then
echo "Creating tarball for $APPIMAGE_NAME"
cd "$APPIMAGE_DIR"
tar -czf "${APPIMAGE_NAME}.tar.gz" "$APPIMAGE_NAME"
cd -
echo "Created tarball: ${APPIMAGE_FILE}.tar.gz"
else
echo "Tarball already exists: ${APPIMAGE_FILE}.tar.gz"
fi
else
echo "WARNING: No AppImage file found!"
fi
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: nym-wallet_1.0.0_amd64.AppImage.tar.gz
path: nym-wallet/target/release/bundle/appimage/nym-wallet*.AppImage.tar.gz
name: nym-wallet-appimage.tar.gz
path: |
nym-wallet/target/release/bundle/appimage/*.AppImage.tar.gz
nym-wallet/target/release/bundle/*/nym-wallet*.AppImage.tar.gz
retention-days: 30
- id: create-release
name: Upload to release based on tag name
uses: softprops/action-gh-release@v2
@@ -75,24 +112,26 @@ jobs:
with:
files: |
nym-wallet/target/release/bundle/appimage/*.AppImage
nym-wallet/target/release/bundle/appimage/*.AppImage.tar.gz*
- name: Deploy artifacts to CI www
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-avzr"
SOURCE: "nym-wallet/target/release/bundle/appimage/nym-wallet*.AppImage.tar.gz"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/builds/${{ github.ref_name }}/nym-wallet
EXCLUDE: "/dist/, /node_modules/"
nym-wallet/target/release/bundle/appimage/*.AppImage.tar.gz
nym-wallet/target/release/bundle/*/nym-wallet*.AppImage
nym-wallet/target/release/bundle/*/nym-wallet*.AppImage.tar.gz
- name: Find AppImage tarball path for deployment
id: find-appimage
run: |
APPIMAGE_TARBALL=$(find target/release/bundle -name "*.AppImage.tar.gz" | head -n 1)
if [ -n "$APPIMAGE_TARBALL" ]; then
echo "Found AppImage tarball: $APPIMAGE_TARBALL"
echo "appimage_path=$APPIMAGE_TARBALL" >> $GITHUB_OUTPUT
else
echo "WARNING: No AppImage tarball found for deployment!"
echo "appimage_path=target/release/bundle/appimage/nym-wallet*.AppImage.tar.gz" >> $GITHUB_OUTPUT
fi
push-release-data:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/release-calculate-hash.yml
needs: publish-tauri
with:
release_tag: ${{ github.ref_name }}
secrets: inherit
release_tag: ${{ needs.publish-tauri.outputs.release_tag || github.ref_name }}
secrets: inherit
+110 -61
View File
@@ -1,6 +1,12 @@
name: publish-nym-wallet-win11
on:
workflow_dispatch:
inputs:
sign:
description: "Sign this build using SSL.com. Signing is billed per signature so be careful"
required: false
type: boolean
default: true
release:
types: [created]
@@ -18,53 +24,61 @@ jobs:
runs-on: ${{ matrix.platform }}
outputs:
release_id: ${{ steps.create-release.outputs.id }}
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].created_at }}
version: ${{ steps.release-info.outputs.version }}
filename: ${{ steps.release-info.outputs.filename }}
file_hash: ${{ steps.release-info.outputs.file_hash }}
release_tag: ${{ github.ref_name }}
steps:
- name: Clean up first
continue-on-error: true
working-directory: .
run: |
cd ..
del /s /q /A:H nym
rmdir /s /q nym
- uses: actions/checkout@v4
- name: Import signing certificate
env:
WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }}
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
run: |
New-Item -ItemType directory -Path certificate
Set-Content -Path certificate/tempCert.txt -Value $env:WINDOWS_CERTIFICATE
certutil -decode certificate/tempCert.txt certificate/certificate.pfx
Remove-Item -path certificate -include tempCert.txt
Import-PfxCertificate -FilePath certificate/certificate.pfx -CertStoreLocation Cert:\CurrentUser\My -Password (ConvertTo-SecureString -String $env:WINDOWS_CERTIFICATE_PASSWORD -Force -AsPlainText)
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
- name: Setup MSBuild.exe
uses: microsoft/setup-msbuild@v2
- name: Node
uses: actions/setup-node@v4
with:
node-version: 21
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Create env file
uses: timheuer/base64-to-file@v1.2
with:
fileName: '.env'
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
- name: Install Yarn
run: npm install -g yarn
- name: Download EV CodeSignTool from ssl.com
working-directory: nym-wallet/src-tauri
if: ${{ inputs.sign }}
shell: bash
run: |
curl -L0 https://www.ssl.com/download/codesigntool-for-linux-and-macos/ -o codesigntool.zip
unzip codesigntool.zip
- name: Get EV certificate credential id
working-directory: nym-wallet/src-tauri
if: ${{ inputs.sign }}
id: get_credential_ids
shell: bash
run: |
echo "SSL_COM_CREDENTIAL_ID=$(./CodeSignTool.sh get_credential_ids -username=${{ secrets.SSL_COM_USERNAME }} -password=${{ secrets.SSL_COM_PASSWORD }} | sed -n '1!p' | sed 's/- //')" >> "$GITHUB_OUTPUT"
- name: Add custom sign command to tauri.conf.json
working-directory: nym-wallet/src-tauri
if: ${{ inputs.sign }}
shell: bash
run: |
yq eval --inplace '.bundle.windows +=
{
"signCommand": {
"cmd": "C:\Program Files\Git\bin\bash.EXE",
"args": [
"/c/actions-runner/_work/nym/nym/nym-wallet/src-tauri/CodeSignTool.sh",
"sign",
"-username ${{ secrets.SSL_COM_USERNAME }}",
"-password ${{ secrets.SSL_COM_PASSWORD }}",
"-credential_id ${{ steps.get_credential_ids.outputs.SSL_COM_CREDENTIAL_ID }}",
"-totp_secret ${{ secrets.SSL_COM_TOTP_SECRET }}",
"-program_name NymWallet",
"-input_file_path",
"%1",
"-override"
]
}
}' tauri.conf.json
- name: Install project dependencies
shell: bash
run: cd .. && yarn --network-timeout 100000
@@ -77,18 +91,50 @@ jobs:
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ENABLE_CODE_SIGNING: ${{ secrets.WINDOWS_CERTIFICATE }}
WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }}
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
run: yarn build
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
SSL_COM_USERNAME: ${{ inputs.sign && secrets.SSL_COM_USERNAME }}
SSL_COM_PASSWORD: ${{ inputs.sign && secrets.SSL_COM_PASSWORD }}
SSL_COM_CREDENTIAL_ID: ${{ inputs.sign && steps.get_credential_ids.outputs.SSL_COM_CREDENTIAL_ID }}
SSL_COM_TOTP_SECRET: ${{ inputs.sign && secrets.SSL_COM_TOTP_SECRET }}
run: |
echo "Starting build process..."
yarn build
- name: Check bundle directory
shell: bash
run: |
echo "Checking bundle directory structure"
# Check standard location
if [ -d "target/release/bundle" ]; then
echo "Found bundle directory at standard location"
ls -la target/release/bundle || echo "Failed to list bundle directory"
fi
# Check src-tauri location
if [ -d "src-tauri/target/release/bundle" ]; then
echo "Found bundle directory in src-tauri"
ls -la src-tauri/target/release/bundle || echo "Failed to list src-tauri bundle directory"
# Use this path for future steps
echo "BUNDLE_PATH=src-tauri/target/release/bundle" >> $GITHUB_ENV
else
echo "Using standard bundle path"
echo "BUNDLE_PATH=target/release/bundle" >> $GITHUB_ENV
fi
# Check for MSI files in any location
find . -name "*.msi" -type f
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: nym-wallet_1.0.0_x64_en-US.msi
path: nym-wallet/target/release/bundle/msi/nym-wallet_1.*.msi
name: nym-wallet.msi
path: |
nym-wallet/${{ env.BUNDLE_PATH }}/msi/*.msi
nym-wallet/${{ env.BUNDLE_PATH }}/*/nym-wallet*.msi
nym-wallet/src-tauri/target/release/bundle/msi/*.msi
retention-days: 30
- id: create-release
@@ -97,25 +143,28 @@ jobs:
if: github.event_name == 'release'
with:
files: |
nym-wallet/target/release/bundle/msi/*.msi
nym-wallet/target/release/bundle/msi/*.msi.zip*
- name: Deploy artifacts to CI www
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-avzr"
SOURCE: "nym-wallet/target/release/bundle/msi/nym-wallet_1.*.msi"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/builds/${{ github.ref_name }}/nym-wallet
EXCLUDE: "/dist/, /node_modules/"
nym-wallet/${{ env.BUNDLE_PATH }}/msi/*.msi
nym-wallet/${{ env.BUNDLE_PATH }}/msi/*.msi.zip*
nym-wallet/${{ env.BUNDLE_PATH }}/*/nym-wallet*.msi
nym-wallet/src-tauri/target/release/bundle/msi/*.msi
- name: Find MSI path for deployment
id: find-msi
shell: bash
run: |
MSI_FILE=$(find . -name "*.msi" -type f | head -n 1)
if [ -n "$MSI_FILE" ]; then
echo "Found MSI file: $MSI_FILE"
echo "msi_path=$MSI_FILE" >> $GITHUB_OUTPUT
else
echo "WARNING: No MSI file found for deployment!"
echo "msi_path=${{ env.BUNDLE_PATH }}/msi/nym-wallet*.msi" >> $GITHUB_OUTPUT
fi
push-release-data:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/release-calculate-hash.yml
needs: publish-tauri
with:
release_tag: ${{ github.ref_name }}
secrets: inherit
release_tag: ${{ needs.publish-tauri.outputs.release_tag || github.ref_name }}
secrets: inherit
@@ -12,7 +12,7 @@ on:
jobs:
build:
name: Build APK
runs-on: custom-ubuntu-20.04
runs-on: custom-ubuntu-22.04
env:
ANDROID_HOME: ${{ github.workspace }}/android-sdk
NDK_VERSION: 25.2.9519653
+2 -2
View File
@@ -4,14 +4,14 @@ on:
jobs:
publish:
runs-on: arc-ubuntu-20.04
runs-on: arc-ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: 18
node-version: 20
registry-url: "https://registry.npmjs.org"
- name: Setup yarn
+1 -1
View File
@@ -23,7 +23,7 @@ jobs:
uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
node-version: 20
- uses: nymtech/nym/.github/actions/nym-hash-releases@develop
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Generated
+343 -94
View File
@@ -869,9 +869,9 @@ dependencies = [
[[package]]
name = "blake3"
version = "1.6.1"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "675f87afced0413c9bb02843499dbbd3882a237645883f71a2b59644a6d2f753"
checksum = "b17679a8d69b6d7fd9cd9801a536cec9fa5e5970b69f9d4747f70b39b031f5e7"
dependencies = [
"arrayref",
"arrayvec",
@@ -908,6 +908,16 @@ dependencies = [
"generic-array 0.14.7",
]
[[package]]
name = "bloomfilter"
version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f6d7f06817e48ea4e17532fa61bc4e8b9a101437f0623f69d2ea54284f3a817"
dependencies = [
"getrandom 0.2.15",
"siphasher 1.0.1",
]
[[package]]
name = "bls12_381"
version = "0.8.0"
@@ -1184,9 +1194,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.32"
version = "4.5.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83"
checksum = "e958897981290da2a852763fe9cdb89cd36977a5d729023127095fa94d95e2ff"
dependencies = [
"clap_builder",
"clap_derive",
@@ -1194,9 +1204,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.32"
version = "4.5.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8"
checksum = "83b0f35019843db2160b5bb19ae09b4e6411ac33fc6a712003c33e03090e2489"
dependencies = [
"anstream",
"anstyle",
@@ -1206,9 +1216,9 @@ dependencies = [
[[package]]
name = "clap_complete"
version = "4.5.46"
version = "4.5.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5c5508ea23c5366f77e53f5a0070e5a84e51687ec3ef9e0464c86dc8d13ce98"
checksum = "c06f5378ea264ad4f82bbc826628b5aad714a75abf6ece087e923010eb937fb6"
dependencies = [
"clap",
]
@@ -1564,6 +1574,7 @@ dependencies = [
"ciborium",
"clap",
"criterion-plot",
"futures",
"is-terminal",
"itertools 0.10.5",
"num-traits",
@@ -1576,6 +1587,7 @@ dependencies = [
"serde_derive",
"serde_json",
"tinytemplate",
"tokio",
"walkdir",
]
@@ -2016,9 +2028,9 @@ dependencies = [
[[package]]
name = "deranged"
version = "0.3.11"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
checksum = "28cfac68e08048ae1883171632c2aef3ebc555621ae56fbccce1cbf22dd7f058"
dependencies = [
"powerfmt",
"serde",
@@ -2805,8 +2817,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi 0.13.3+wasi-0.2.2",
"wasm-bindgen",
"windows-targets 0.52.6",
]
@@ -3402,19 +3416,20 @@ dependencies = [
[[package]]
name = "hyper-rustls"
version = "0.26.0"
version = "0.27.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c"
checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2"
dependencies = [
"futures-util",
"http 1.3.1",
"hyper 1.6.0",
"hyper-util",
"rustls 0.22.4",
"rustls 0.23.25",
"rustls-pki-types",
"tokio",
"tokio-rustls 0.25.0",
"tokio-rustls 0.26.2",
"tower-service",
"webpki-roots 0.26.8",
]
[[package]]
@@ -3728,6 +3743,7 @@ dependencies = [
"number_prefix",
"portable-atomic",
"unicode-width 0.2.0",
"vt100",
"web-time",
]
@@ -3816,7 +3832,7 @@ dependencies = [
"socket2",
"widestring",
"windows-sys 0.48.0",
"winreg 0.50.0",
"winreg",
]
[[package]]
@@ -4129,9 +4145,9 @@ checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e"
[[package]]
name = "log"
version = "0.4.26"
version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "loom"
@@ -4740,7 +4756,7 @@ dependencies = [
"pin-project",
"rand 0.8.5",
"rand_chacha 0.3.1",
"reqwest 0.12.4",
"reqwest 0.12.15",
"schemars",
"semver 1.0.26",
"serde",
@@ -4908,6 +4924,7 @@ dependencies = [
"schemars",
"serde",
"serde_json",
"tracing",
"tracing-opentelemetry",
"tracing-subscriber",
"tracing-tree",
@@ -5311,7 +5328,7 @@ dependencies = [
"nym-network-defaults",
"nym-validator-client",
"rand 0.8.5",
"reqwest 0.12.4",
"reqwest 0.12.15",
"serde",
"serde_json",
"sqlx",
@@ -5342,7 +5359,7 @@ dependencies = [
"nym-http-api-client",
"nym-http-api-common",
"nym-serde-helpers",
"reqwest 0.12.4",
"reqwest 0.12.15",
"schemars",
"serde",
"serde_json",
@@ -5538,7 +5555,7 @@ dependencies = [
name = "nym-exit-policy"
version = "0.1.0"
dependencies = [
"reqwest 0.12.4",
"reqwest 0.12.15",
"serde",
"serde_json",
"thiserror 2.0.12",
@@ -5563,7 +5580,7 @@ name = "nym-explorer-client"
version = "0.1.0"
dependencies = [
"nym-explorer-api-requests",
"reqwest 0.12.4",
"reqwest 0.12.15",
"serde",
"thiserror 2.0.12",
"tokio",
@@ -5774,7 +5791,7 @@ dependencies = [
"mime",
"nym-bin-common",
"once_cell",
"reqwest 0.12.4",
"reqwest 0.12.15",
"serde",
"serde_json",
"thiserror 2.0.12",
@@ -5889,7 +5906,7 @@ dependencies = [
"nym-wireguard",
"nym-wireguard-types",
"rand 0.8.5",
"reqwest 0.12.4",
"reqwest 0.12.15",
"serde",
"serde_json",
"thiserror 2.0.12",
@@ -6038,7 +6055,7 @@ dependencies = [
"petgraph",
"rand 0.8.5",
"rand_chacha 0.3.1",
"reqwest 0.12.4",
"reqwest 0.12.15",
"serde",
"serde_json",
"tokio",
@@ -6085,7 +6102,7 @@ dependencies = [
"publicsuffix",
"rand 0.8.5",
"regex",
"reqwest 0.12.4",
"reqwest 0.12.15",
"serde",
"serde_json",
"sqlx",
@@ -6105,22 +6122,28 @@ version = "1.8.0"
dependencies = [
"anyhow",
"arc-swap",
"arrayref",
"async-trait",
"axum 0.7.9",
"axum-extra",
"bip39",
"blake2 0.8.1",
"bloomfilter",
"bs58",
"cargo_metadata 0.18.1",
"celes",
"chacha",
"clap",
"colored",
"criterion",
"csv",
"cupid",
"dashmap",
"futures",
"headers",
"hkdf",
"human-repr",
"humantime-serde",
"indicatif",
"ipnetwork",
"lioness",
"nym-authenticator",
"nym-bin-common",
"nym-client-core-config-types",
@@ -6142,6 +6165,8 @@ dependencies = [
"nym-sphinx-addressing",
"nym-sphinx-forwarding",
"nym-sphinx-framing",
"nym-sphinx-params",
"nym-sphinx-routing",
"nym-sphinx-types",
"nym-task",
"nym-topology",
@@ -6151,10 +6176,9 @@ dependencies = [
"nym-wireguard",
"nym-wireguard-types",
"rand 0.8.5",
"semver 1.0.26",
"serde",
"serde_json",
"si-scale",
"sha2 0.10.8",
"sysinfo",
"thiserror 2.0.12",
"time",
@@ -6163,6 +6187,7 @@ dependencies = [
"toml 0.8.20",
"tower-http",
"tracing",
"tracing-indicatif",
"tracing-subscriber",
"url",
"utoipa",
@@ -6255,7 +6280,7 @@ dependencies = [
"rand 0.8.5",
"rand_chacha 0.3.1",
"regex",
"reqwest 0.12.4",
"reqwest 0.12.15",
"serde",
"serde_json",
"serde_json_path",
@@ -6284,7 +6309,7 @@ dependencies = [
"chrono",
"nym-crypto",
"nym-http-api-client",
"reqwest 0.12.4",
"reqwest 0.12.15",
"serde",
"serde_json",
"tracing",
@@ -6433,7 +6458,7 @@ dependencies = [
"parking_lot",
"pretty_env_logger",
"rand 0.8.5",
"reqwest 0.12.4",
"reqwest 0.12.15",
"serde",
"tap",
"tempfile",
@@ -6542,7 +6567,7 @@ dependencies = [
"nym-validator-client",
"pin-project",
"rand 0.8.5",
"reqwest 0.12.4",
"reqwest 0.12.15",
"schemars",
"serde",
"tap",
@@ -6704,8 +6729,6 @@ name = "nym-sphinx-framing"
version = "0.1.0"
dependencies = [
"bytes",
"log",
"nym-metrics",
"nym-sphinx-acknowledgements",
"nym-sphinx-addressing",
"nym-sphinx-forwarding",
@@ -6714,6 +6737,7 @@ dependencies = [
"thiserror 2.0.12",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
@@ -6827,7 +6851,7 @@ dependencies = [
"nym-sphinx-routing",
"nym-sphinx-types",
"rand 0.8.5",
"reqwest 0.12.4",
"reqwest 0.12.15",
"serde",
"serde_json",
"thiserror 2.0.12",
@@ -6865,7 +6889,7 @@ dependencies = [
"nym-mixnet-contract-common",
"nym-validator-client",
"nym-vesting-contract-common",
"reqwest 0.12.4",
"reqwest 0.12.15",
"schemars",
"serde",
"serde_json",
@@ -6913,7 +6937,7 @@ dependencies = [
"nym-serde-helpers",
"nym-vesting-contract-common",
"prost 0.13.5",
"reqwest 0.12.4",
"reqwest 0.12.15",
"serde",
"serde_json",
"sha2 0.10.8",
@@ -7108,7 +7132,7 @@ dependencies = [
"nym-bin-common",
"nym-config",
"nym-task",
"reqwest 0.12.4",
"reqwest 0.12.15",
"serde",
"serde_json",
"sha2 0.10.8",
@@ -7135,7 +7159,7 @@ dependencies = [
"nym-task",
"nym-validator-client",
"nyxd-scraper",
"reqwest 0.12.4",
"reqwest 0.12.15",
"schemars",
"serde",
"sqlx",
@@ -7186,9 +7210,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.21.1"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "oorandom"
@@ -7934,6 +7958,60 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]]
name = "quinn"
version = "0.11.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3bd15a6f2967aef83887dcb9fec0014580467e33720d073560cf015a5683012"
dependencies = [
"bytes",
"cfg_aliases",
"pin-project-lite",
"quinn-proto",
"quinn-udp",
"rustc-hash",
"rustls 0.23.25",
"socket2",
"thiserror 2.0.12",
"tokio",
"tracing",
"web-time",
]
[[package]]
name = "quinn-proto"
version = "0.11.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b820744eb4dc9b57a3398183639c511b5a26d2ed702cedd3febaa1393caa22cc"
dependencies = [
"bytes",
"getrandom 0.3.1",
"rand 0.9.0",
"ring",
"rustc-hash",
"rustls 0.23.25",
"rustls-pki-types",
"slab",
"thiserror 2.0.12",
"tinyvec",
"tracing",
"web-time",
]
[[package]]
name = "quinn-udp"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "541d0f57c6ec747a90738a52741d3221f7960e8ac2f0ff4b1a63680e033b4ab5"
dependencies = [
"cfg_aliases",
"libc",
"once_cell",
"socket2",
"tracing",
"windows-sys 0.59.0",
]
[[package]]
name = "quote"
version = "1.0.40"
@@ -8142,14 +8220,14 @@ dependencies = [
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"winreg 0.50.0",
"winreg",
]
[[package]]
name = "reqwest"
version = "0.12.4"
version = "0.12.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10"
checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb"
dependencies = [
"async-compression",
"base64 0.22.1",
@@ -8160,7 +8238,7 @@ dependencies = [
"http-body 1.0.1",
"http-body-util",
"hyper 1.6.0",
"hyper-rustls 0.26.0",
"hyper-rustls 0.27.5",
"hyper-util",
"ipnet",
"js-sys",
@@ -8169,17 +8247,19 @@ dependencies = [
"once_cell",
"percent-encoding",
"pin-project-lite",
"rustls 0.22.4",
"quinn",
"rustls 0.23.25",
"rustls-pemfile 2.2.0",
"rustls-pki-types",
"serde",
"serde_json",
"serde_urlencoded",
"sync_wrapper 0.1.2",
"sync_wrapper 1.0.2",
"tokio",
"tokio-rustls 0.25.0",
"tokio-rustls 0.26.2",
"tokio-socks",
"tokio-util",
"tower 0.5.2",
"tower-service",
"url",
"wasm-bindgen",
@@ -8187,7 +8267,7 @@ dependencies = [
"wasm-streams",
"web-sys",
"webpki-roots 0.26.8",
"winreg 0.52.0",
"windows-registry",
]
[[package]]
@@ -8465,6 +8545,20 @@ dependencies = [
"zeroize",
]
[[package]]
name = "rustls"
version = "0.23.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "822ee9188ac4ec04a2f0531e55d035fb2de73f18b41a63c70c2712503b6fb13c"
dependencies = [
"once_cell",
"ring",
"rustls-pki-types",
"rustls-webpki 0.103.1",
"subtle 2.6.1",
"zeroize",
]
[[package]]
name = "rustls-native-certs"
version = "0.6.3"
@@ -8513,6 +8607,9 @@ name = "rustls-pki-types"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c"
dependencies = [
"web-time",
]
[[package]]
name = "rustls-webpki"
@@ -8535,6 +8632,17 @@ dependencies = [
"untrusted",
]
[[package]]
name = "rustls-webpki"
version = "0.103.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03"
dependencies = [
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]]
name = "rustversion"
version = "1.0.19"
@@ -9153,9 +9261,9 @@ dependencies = [
[[package]]
name = "sphinx-packet"
version = "0.3.2"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c23047e0cf36ff6904603f499fd13153425cdf5ba47bfbaedbc999da0bd92f4e"
checksum = "b63a72efe7dce8a546d5cb855e60699ae69203d0d7e4335a654eb87e93d7d141"
dependencies = [
"aes",
"arrayref",
@@ -9174,6 +9282,7 @@ dependencies = [
"sha2 0.10.8",
"subtle 2.6.1",
"x25519-dalek",
"zeroize",
]
[[package]]
@@ -9578,6 +9687,9 @@ name = "sync_wrapper"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
dependencies = [
"futures-core",
]
[[package]]
name = "synstructure"
@@ -9650,9 +9762,9 @@ dependencies = [
[[package]]
name = "tempfile"
version = "3.19.0"
version = "3.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "488960f40a3fd53d72c2a29a58722561dee8afdd175bd88e3db4677d7b2ba600"
checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf"
dependencies = [
"fastrand 2.3.0",
"getrandom 0.3.1",
@@ -9896,9 +10008,9 @@ dependencies = [
[[package]]
name = "time"
version = "0.3.39"
version = "0.3.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dad298b01a40a23aac4580b67e3dbedb7cc8402f3592d7f49469de2ea4aecdd8"
checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40"
dependencies = [
"deranged",
"itoa",
@@ -9914,15 +10026,15 @@ dependencies = [
[[package]]
name = "time-core"
version = "0.1.3"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "765c97a5b985b7c11d7bc27fa927dc4fe6af3a6dfb021d28deb60d3bf51e76ef"
checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
[[package]]
name = "time-macros"
version = "0.2.20"
version = "0.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8093bc3e81c3bc5f7879de09619d06c9a5a5e45ca44dfeeb7225bae38005c5c"
checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49"
dependencies = [
"num-conv",
"time-core",
@@ -9965,9 +10077,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.44.1"
version = "1.44.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a"
checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48"
dependencies = [
"backtrace",
"bytes",
@@ -10050,6 +10162,16 @@ dependencies = [
"tokio",
]
[[package]]
name = "tokio-rustls"
version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b"
dependencies = [
"rustls 0.23.25",
"tokio",
]
[[package]]
name = "tokio-socks"
version = "0.5.2"
@@ -10319,6 +10441,18 @@ dependencies = [
"tracing",
]
[[package]]
name = "tracing-indicatif"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8201ca430e0cd893ef978226fd3516c06d9c494181c8bf4e5b32e30ed4b40aa1"
dependencies = [
"indicatif",
"tracing",
"tracing-core",
"tracing-subscriber",
]
[[package]]
name = "tracing-log"
version = "0.1.4"
@@ -10603,9 +10737,9 @@ checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
[[package]]
name = "uniffi"
version = "0.29.0"
version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba62a57e90f9baed5ad02a71a0870180fa1cc35499093b2d21be2edfb68ec0f7"
checksum = "fe34585ac0275accf6c284d0080cc2840f3898c551cda869ec291b5a4218712c"
dependencies = [
"anyhow",
"camino",
@@ -10619,9 +10753,9 @@ dependencies = [
[[package]]
name = "uniffi_bindgen"
version = "0.29.0"
version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2242f35214f1e0e3b47c495d340c69f649f9a9ece3a943a29e275686cc884533"
checksum = "1a792af1424cc8b3c43b44c1a6cb7935ed1fbe5584a74f70e8bab9799740266d"
dependencies = [
"anyhow",
"camino",
@@ -10642,9 +10776,9 @@ dependencies = [
[[package]]
name = "uniffi_build"
version = "0.29.0"
version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c887a6c9a2857d8dc2ab0c8d578e8aa4978145b4fd65ed44296341e89aebc3cc"
checksum = "00c4138211f2ae951018fcce6a978e1fcd1a47c3fd0bc0d5472a520520060db1"
dependencies = [
"anyhow",
"camino",
@@ -10653,22 +10787,21 @@ dependencies = [
[[package]]
name = "uniffi_core"
version = "0.29.0"
version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cad9fbdeb7ae4daf8d0f7704a3b638c37018eb16bb701e30fa17a2dd3e2d39c1"
checksum = "c18baace68a52666d33d12d73ca335ecf27a302202cefb53b1f974512bb72417"
dependencies = [
"anyhow",
"bytes",
"once_cell",
"paste",
"static_assertions",
]
[[package]]
name = "uniffi_internal_macros"
version = "0.29.0"
version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22a9dba1d78b9ce429439891089c223478043d52a1c3176a0fcea2b5573a7fcf"
checksum = "f9902d4ed16c65e6c0222241024dd0bfeed07ea3deb7c470eb175e5f5ef406cd"
dependencies = [
"quote",
"syn 2.0.98",
@@ -10676,9 +10809,9 @@ dependencies = [
[[package]]
name = "uniffi_macros"
version = "0.29.0"
version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78dd5f8eefba5898b901086f5e7916da67b9a5286a01cc44e910cd75fa37c630"
checksum = "9d82c82ef945c51082d8763635334b994e63e77650f09d0fae6d28dd08b1de83"
dependencies = [
"camino",
"fs-err",
@@ -10693,9 +10826,9 @@ dependencies = [
[[package]]
name = "uniffi_meta"
version = "0.29.0"
version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d5965b1d4ffacef1eaa72fef9c00d2491641e87ad910f6c5859b9c503ddb16a"
checksum = "8d6027b971c2aa86350dd180aee9819729c7b99bacd381534511ff29d2c09cea"
dependencies = [
"anyhow",
"siphasher 0.3.11",
@@ -10704,9 +10837,9 @@ dependencies = [
[[package]]
name = "uniffi_udl"
version = "0.29.0"
version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "279b82bac9a382c796a0d210bb8354a0b813499b28aa1de046c85d78ca389805"
checksum = "52300b7a4ab02dc159a038a13d5bfe27aefbad300d91b0b501b3dda094c1e0a2"
dependencies = [
"anyhow",
"textwrap",
@@ -10914,6 +11047,39 @@ version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "vt100"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84cd863bf0db7e392ba3bd04994be3473491b31e66340672af5d11943c6274de"
dependencies = [
"itoa",
"log",
"unicode-width 0.1.14",
"vte",
]
[[package]]
name = "vte"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197"
dependencies = [
"arrayvec",
"utf8parse",
"vte_generate_state_changes",
]
[[package]]
name = "vte_generate_state_changes"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e369bee1b05d510a7b4ed645f5faa90619e05437111783ea5848f28d97d3c2e"
dependencies = [
"proc-macro2",
"quote",
]
[[package]]
name = "waker-fn"
version = "1.2.0"
@@ -11298,7 +11464,7 @@ dependencies = [
"windows-implement 0.58.0",
"windows-interface 0.58.0",
"windows-result 0.2.0",
"windows-strings",
"windows-strings 0.1.0",
"windows-targets 0.52.6",
]
@@ -11352,6 +11518,17 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3"
[[package]]
name = "windows-registry"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3"
dependencies = [
"windows-result 0.3.1",
"windows-strings 0.3.1",
"windows-targets 0.53.0",
]
[[package]]
name = "windows-result"
version = "0.1.2"
@@ -11370,6 +11547,15 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-result"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06374efe858fab7e4f881500e6e86ec8bc28f9462c47e5a9941a0142ad86b189"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
version = "0.1.0"
@@ -11380,6 +11566,15 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-strings"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-sys"
version = "0.45.0"
@@ -11455,13 +11650,29 @@ dependencies = [
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm",
"windows_i686_gnullvm 0.52.6",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b"
dependencies = [
"windows_aarch64_gnullvm 0.53.0",
"windows_aarch64_msvc 0.53.0",
"windows_i686_gnu 0.53.0",
"windows_i686_gnullvm 0.53.0",
"windows_i686_msvc 0.53.0",
"windows_x86_64_gnu 0.53.0",
"windows_x86_64_gnullvm 0.53.0",
"windows_x86_64_msvc 0.53.0",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
@@ -11480,6 +11691,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
@@ -11498,6 +11715,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_aarch64_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
@@ -11516,12 +11739,24 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnu"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
@@ -11540,6 +11775,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_i686_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
@@ -11558,6 +11799,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnu"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
@@ -11576,6 +11823,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
@@ -11594,6 +11847,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "windows_x86_64_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
[[package]]
name = "winnow"
version = "0.7.3"
@@ -11613,16 +11872,6 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "winreg"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5"
dependencies = [
"cfg-if",
"windows-sys 0.48.0",
]
[[package]]
name = "wit-bindgen-rt"
version = "0.33.0"
@@ -11842,7 +12091,7 @@ dependencies = [
"nym-crypto",
"nym-http-api-client",
"rand 0.8.5",
"reqwest 0.12.4",
"reqwest 0.12.15",
"serde",
"thiserror 2.0.12",
"tokio",
+9 -11
View File
@@ -203,8 +203,8 @@ bincode = "1.3.3"
bip39 = { version = "2.0.0", features = ["zeroize"] }
bit-vec = "0.7.0" # can we unify those?
bitvec = "1.0.0"
blake3 = "1.6.1"
bloomfilter = "1.0.14"
blake3 = "1.7.0"
bloomfilter = "3.0.1"
bs58 = "0.5.1"
bytecodec = "0.4.15"
bytes = "1.10.1"
@@ -215,7 +215,7 @@ chacha20 = "0.9.0"
chacha20poly1305 = "0.10.1"
chrono = "0.4.40"
cipher = "0.4.3"
clap = "4.5.32"
clap = "4.5.34"
clap_complete = "4.5"
clap_complete_fig = "4.5"
colored = "2.2"
@@ -283,7 +283,7 @@ moka = { version = "0.12", features = ["future"] }
nix = "0.27.1"
notify = "5.1.0"
okapi = "0.7.0"
once_cell = "1.21.1"
once_cell = "1.21.3"
opentelemetry = "0.19.0"
opentelemetry-jaeger = "0.18.0"
parking_lot = "0.12.3"
@@ -302,10 +302,7 @@ rand_pcg = "0.3.1"
rand_seeder = "0.2.3"
rayon = "1.5.1"
regex = "1.10.6"
reqwest = { version = "0.12.4", default-features = false }
rocket = "0.5.0"
rocket_cors = "0.6.0"
rocket_okapi = "0.8.0"
reqwest = { version = "0.12.15", default-features = false }
rs_merkle = "1.5.0"
safer-ffi = "0.1.13"
schemars = "0.8.22"
@@ -320,7 +317,7 @@ serde_with = "3.9.0"
serde_yaml = "0.9.25"
sha2 = "0.10.8"
si-scale = "0.2.3"
sphinx-packet = "=0.3.2"
sphinx-packet = "=0.5.0"
sqlx = "0.7.4"
strum = "0.26"
strum_macros = "0.26"
@@ -331,7 +328,7 @@ tap = "1.0.1"
tar = "0.4.44"
tempfile = "3.19"
thiserror = "2.0"
time = "0.3.39"
time = "0.3.41"
tokio = "1.44"
tokio-postgres = "0.7"
tokio-stream = "0.1.17"
@@ -347,9 +344,10 @@ tracing-log = "0.2"
tracing-opentelemetry = "0.19.0"
tracing-subscriber = "0.3.19"
tracing-tree = "0.2.2"
tracing-indicatif = "0.3.9"
ts-rs = "10.1.0"
tungstenite = { version = "0.20.1", default-features = false }
uniffi = "0.29.0"
uniffi = "0.29.1"
uniffi_build = "0.29.0"
url = "2.5"
utoipa = "5.2"
@@ -13,7 +13,7 @@ use std::{fmt, ops::Deref, str::FromStr};
#[cfg(feature = "verify")]
use hmac::{Hmac, Mac};
#[cfg(feature = "verify")]
use nym_crypto::asymmetric::encryption::PrivateKey;
use nym_crypto::asymmetric::encryption::{PrivateKey, PublicKey};
#[cfg(feature = "verify")]
use sha2::Sha256;
@@ -82,16 +82,14 @@ impl GatewayClient {
private_ip: IpAddr,
nonce: u64,
) -> Self {
// convert from 1.0 x25519-dalek private key into 2.0 x25519-dalek
#[allow(clippy::expect_used)]
let static_secret = x25519_dalek::StaticSecret::from(local_secret.to_bytes());
let local_public: x25519_dalek::PublicKey = (&static_secret).into();
let local_public = PublicKey::from(local_secret);
let remote_public = PublicKey::from(remote_public);
let dh = static_secret.diffie_hellman(&remote_public);
let dh = local_secret.diffie_hellman(&remote_public);
// TODO: change that to use our nym_crypto::hmac module instead
#[allow(clippy::expect_used)]
let mut mac = HmacSha256::new_from_slice(dh.as_bytes())
let mut mac = HmacSha256::new_from_slice(&dh[..])
.expect("x25519 shared secret is always 32 bytes long");
mac.update(local_public.as_bytes());
@@ -99,7 +97,7 @@ impl GatewayClient {
mac.update(&nonce.to_le_bytes());
GatewayClient {
pub_key: PeerPublicKey::new(local_public),
pub_key: PeerPublicKey::new(local_public.into()),
private_ip,
mac: ClientMac(mac.finalize().into_bytes().to_vec()),
}
@@ -109,11 +107,8 @@ impl GatewayClient {
// Client should perform this step when generating its payload, using its own WG PK
#[cfg(feature = "verify")]
pub fn verify(&self, gateway_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
// convert from 1.0 x25519-dalek private key into 2.0 x25519-dalek
#[allow(clippy::expect_used)]
let static_secret = x25519_dalek::StaticSecret::from(gateway_key.to_bytes());
let dh = static_secret.diffie_hellman(&self.pub_key);
// use gateways key as a ref to an x25519_dalek key
let dh = (gateway_key.as_ref()).diffie_hellman(&self.pub_key);
// TODO: change that to use our nym_crypto::hmac module instead
#[allow(clippy::expect_used)]
@@ -14,7 +14,7 @@ use std::{fmt, ops::Deref, str::FromStr};
#[cfg(feature = "verify")]
use hmac::{Hmac, Mac};
#[cfg(feature = "verify")]
use nym_crypto::asymmetric::encryption::PrivateKey;
use nym_crypto::asymmetric::encryption::{PrivateKey, PublicKey};
#[cfg(feature = "verify")]
use sha2::Sha256;
@@ -91,16 +91,14 @@ impl GatewayClient {
private_ip: IpAddr,
nonce: u64,
) -> Self {
// convert from 1.0 x25519-dalek private key into 2.0 x25519-dalek
#[allow(clippy::expect_used)]
let static_secret = x25519_dalek::StaticSecret::from(local_secret.to_bytes());
let local_public: x25519_dalek::PublicKey = (&static_secret).into();
let local_public = PublicKey::from(local_secret);
let remote_public = PublicKey::from(remote_public);
let dh = static_secret.diffie_hellman(&remote_public);
let dh = local_secret.diffie_hellman(&remote_public);
// TODO: change that to use our nym_crypto::hmac module instead
#[allow(clippy::expect_used)]
let mut mac = HmacSha256::new_from_slice(dh.as_bytes())
let mut mac = HmacSha256::new_from_slice(&dh[..])
.expect("x25519 shared secret is always 32 bytes long");
mac.update(local_public.as_bytes());
@@ -108,7 +106,7 @@ impl GatewayClient {
mac.update(&nonce.to_le_bytes());
GatewayClient {
pub_key: PeerPublicKey::new(local_public),
pub_key: PeerPublicKey::new(local_public.into()),
private_ip,
mac: ClientMac(mac.finalize().into_bytes().to_vec()),
}
@@ -118,11 +116,8 @@ impl GatewayClient {
// Client should perform this step when generating its payload, using its own WG PK
#[cfg(feature = "verify")]
pub fn verify(&self, gateway_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
// convert from 1.0 x25519-dalek private key into 2.0 x25519-dalek
#[allow(clippy::expect_used)]
let static_secret = x25519_dalek::StaticSecret::from(gateway_key.to_bytes());
let dh = static_secret.diffie_hellman(&self.pub_key);
// use gateways key as a ref to an x25519_dalek key
let dh = (gateway_key.as_ref()).diffie_hellman(&self.pub_key);
// TODO: change that to use our nym_crypto::hmac module instead
#[allow(clippy::expect_used)]
@@ -14,7 +14,7 @@ use std::{fmt, ops::Deref, str::FromStr};
#[cfg(feature = "verify")]
use hmac::{Hmac, Mac};
#[cfg(feature = "verify")]
use nym_crypto::asymmetric::encryption::PrivateKey;
use nym_crypto::asymmetric::encryption::{PrivateKey, PublicKey};
#[cfg(feature = "verify")]
use sha2::Sha256;
@@ -91,16 +91,14 @@ impl GatewayClient {
private_ip: IpAddr,
nonce: u64,
) -> Self {
// convert from 1.0 x25519-dalek private key into 2.0 x25519-dalek
#[allow(clippy::expect_used)]
let static_secret = x25519_dalek::StaticSecret::from(local_secret.to_bytes());
let local_public: x25519_dalek::PublicKey = (&static_secret).into();
let local_public = PublicKey::from(local_secret);
let remote_public = PublicKey::from(remote_public);
let dh = static_secret.diffie_hellman(&remote_public);
let dh = local_secret.diffie_hellman(&remote_public);
// TODO: change that to use our nym_crypto::hmac module instead
#[allow(clippy::expect_used)]
let mut mac = HmacSha256::new_from_slice(dh.as_bytes())
let mut mac = HmacSha256::new_from_slice(&dh[..])
.expect("x25519 shared secret is always 32 bytes long");
mac.update(local_public.as_bytes());
@@ -108,7 +106,7 @@ impl GatewayClient {
mac.update(&nonce.to_le_bytes());
GatewayClient {
pub_key: PeerPublicKey::new(local_public),
pub_key: PeerPublicKey::new(local_public.into()),
private_ip,
mac: ClientMac(mac.finalize().into_bytes().to_vec()),
}
@@ -118,11 +116,8 @@ impl GatewayClient {
// Client should perform this step when generating its payload, using its own WG PK
#[cfg(feature = "verify")]
pub fn verify(&self, gateway_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
// convert from 1.0 x25519-dalek private key into 2.0 x25519-dalek
#[allow(clippy::expect_used)]
let static_secret = x25519_dalek::StaticSecret::from(gateway_key.to_bytes());
let dh = static_secret.diffie_hellman(&self.pub_key);
// use gateways key as a ref to an x25519_dalek key
let dh = (gateway_key.as_ref()).diffie_hellman(&self.pub_key);
// TODO: change that to use our nym_crypto::hmac module instead
#[allow(clippy::expect_used)]
@@ -15,7 +15,7 @@ use std::{fmt, ops::Deref, str::FromStr};
#[cfg(feature = "verify")]
use hmac::{Hmac, Mac};
#[cfg(feature = "verify")]
use nym_crypto::asymmetric::encryption::PrivateKey;
use nym_crypto::asymmetric::encryption::{PrivateKey, PublicKey};
#[cfg(feature = "verify")]
use sha2::Sha256;
@@ -143,16 +143,14 @@ impl GatewayClient {
private_ips: IpPair,
nonce: u64,
) -> Self {
// convert from 1.0 x25519-dalek private key into 2.0 x25519-dalek
#[allow(clippy::expect_used)]
let static_secret = x25519_dalek::StaticSecret::from(local_secret.to_bytes());
let local_public: x25519_dalek::PublicKey = (&static_secret).into();
let local_public = PublicKey::from(local_secret);
let remote_public = PublicKey::from(remote_public);
let dh = static_secret.diffie_hellman(&remote_public);
let dh = local_secret.diffie_hellman(&remote_public);
// TODO: change that to use our nym_crypto::hmac module instead
#[allow(clippy::expect_used)]
let mut mac = HmacSha256::new_from_slice(dh.as_bytes())
let mut mac = HmacSha256::new_from_slice(&dh[..])
.expect("x25519 shared secret is always 32 bytes long");
mac.update(local_public.as_bytes());
@@ -160,7 +158,7 @@ impl GatewayClient {
mac.update(&nonce.to_le_bytes());
GatewayClient {
pub_key: PeerPublicKey::new(local_public),
pub_key: PeerPublicKey::new(local_public.into()),
private_ips,
mac: ClientMac(mac.finalize().into_bytes().to_vec()),
}
@@ -170,11 +168,8 @@ impl GatewayClient {
// Client should perform this step when generating its payload, using its own WG PK
#[cfg(feature = "verify")]
pub fn verify(&self, gateway_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
// convert from 1.0 x25519-dalek private key into 2.0 x25519-dalek
#[allow(clippy::expect_used)]
let static_secret = x25519_dalek::StaticSecret::from(gateway_key.to_bytes());
let dh = static_secret.diffie_hellman(&self.pub_key);
// use gateways key as a ref to an x25519_dalek key
let dh = (gateway_key.as_ref()).diffie_hellman(&self.pub_key);
// TODO: change that to use our nym_crypto::hmac module instead
#[allow(clippy::expect_used)]
@@ -15,7 +15,7 @@ use std::{fmt, ops::Deref, str::FromStr};
#[cfg(feature = "verify")]
use hmac::{Hmac, Mac};
#[cfg(feature = "verify")]
use nym_crypto::asymmetric::encryption::PrivateKey;
use nym_crypto::asymmetric::encryption::{PrivateKey, PublicKey};
#[cfg(feature = "verify")]
use sha2::Sha256;
@@ -143,16 +143,14 @@ impl GatewayClient {
private_ips: IpPair,
nonce: u64,
) -> Self {
// convert from 1.0 x25519-dalek private key into 2.0 x25519-dalek
#[allow(clippy::expect_used)]
let static_secret = x25519_dalek::StaticSecret::from(local_secret.to_bytes());
let local_public: x25519_dalek::PublicKey = (&static_secret).into();
let local_public = PublicKey::from(local_secret);
let remote_public = PublicKey::from(remote_public);
let dh = static_secret.diffie_hellman(&remote_public);
let dh = local_secret.diffie_hellman(&remote_public);
// TODO: change that to use our nym_crypto::hmac module instead
#[allow(clippy::expect_used)]
let mut mac = HmacSha256::new_from_slice(dh.as_bytes())
let mut mac = HmacSha256::new_from_slice(&dh[..])
.expect("x25519 shared secret is always 32 bytes long");
mac.update(local_public.as_bytes());
@@ -160,7 +158,7 @@ impl GatewayClient {
mac.update(&nonce.to_le_bytes());
GatewayClient {
pub_key: PeerPublicKey::new(local_public),
pub_key: PeerPublicKey::new(local_public.into()),
private_ips,
mac: ClientMac(mac.finalize().into_bytes().to_vec()),
}
@@ -170,11 +168,8 @@ impl GatewayClient {
// Client should perform this step when generating its payload, using its own WG PK
#[cfg(feature = "verify")]
pub fn verify(&self, gateway_key: &PrivateKey, nonce: u64) -> Result<(), Error> {
// convert from 1.0 x25519-dalek private key into 2.0 x25519-dalek
#[allow(clippy::expect_used)]
let static_secret = x25519_dalek::StaticSecret::from(gateway_key.to_bytes());
let dh = static_secret.diffie_hellman(&self.pub_key);
// use gateways key as a ref to an x25519_dalek key
let dh = (gateway_key.as_ref()).diffie_hellman(&self.pub_key);
// TODO: change that to use our nym_crypto::hmac module instead
#[allow(clippy::expect_used)]
+2 -1
View File
@@ -21,6 +21,7 @@ serde_json = { workspace = true, optional = true }
## tracing
tracing-subscriber = { workspace = true, features = ["env-filter"], optional = true }
tracing-tree = { workspace = true, optional = true }
tracing = { workspace = true, optional = true }
opentelemetry-jaeger = { workspace = true, features = ["rt-tokio", "collector_client", "isahc_collector_client"], optional = true }
tracing-opentelemetry = { workspace = true, optional = true }
utoipa = { workspace = true, optional = true }
@@ -35,7 +36,7 @@ default = []
openapi = ["utoipa"]
output_format = ["serde_json", "dep:clap"]
bin_info_schema = ["schemars"]
basic_tracing = ["tracing-subscriber"]
basic_tracing = ["dep:tracing", "tracing-subscriber"]
tracing = [
"basic_tracing",
"tracing-tree",
+37 -14
View File
@@ -44,10 +44,38 @@ pub fn setup_logging() {
.init();
}
// don't call init so that we could attach additional layers
#[cfg(feature = "basic_tracing")]
pub fn setup_tracing_logger() {
let log_builder = tracing_subscriber::fmt()
.with_writer(std::io::stderr)
pub fn build_tracing_logger() -> impl tracing_subscriber::layer::SubscriberExt {
use tracing_subscriber::prelude::*;
tracing_subscriber::registry()
.with(default_tracing_fmt_layer(std::io::stderr))
.with(default_tracing_env_filter())
}
#[cfg(feature = "basic_tracing")]
pub fn default_tracing_env_filter() -> tracing_subscriber::filter::EnvFilter {
if ::std::env::var("RUST_LOG").is_ok() {
tracing_subscriber::filter::EnvFilter::from_default_env()
} else {
// if the env value was not found, default to `INFO` level rather than `ERROR`
tracing_subscriber::filter::EnvFilter::builder()
.with_default_directive(tracing_subscriber::filter::LevelFilter::INFO.into())
.parse_lossy("")
}
}
#[cfg(feature = "basic_tracing")]
pub fn default_tracing_fmt_layer<S, W>(
writer: W,
) -> impl tracing_subscriber::Layer<S> + Sync + Send + 'static
where
S: tracing::Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a>,
W: for<'writer> tracing_subscriber::fmt::MakeWriter<'writer> + Sync + Send + 'static,
{
tracing_subscriber::fmt::layer()
.with_writer(writer)
// Use a more compact, abbreviated log format
.compact()
// Display source code file paths
@@ -55,18 +83,13 @@ pub fn setup_tracing_logger() {
// Display source code line numbers
.with_line_number(true)
// Don't display the event's target (module path)
.with_target(false);
.with_target(false)
}
if ::std::env::var("RUST_LOG").is_ok() {
log_builder
.with_env_filter(tracing_subscriber::filter::EnvFilter::from_default_env())
.init()
} else {
// default to 'Info
log_builder
.with_max_level(tracing_subscriber::filter::LevelFilter::INFO)
.init()
}
#[cfg(feature = "basic_tracing")]
pub fn setup_tracing_logger() {
use tracing_subscriber::util::SubscriberInitExt;
build_tracing_logger().init()
}
// TODO: This has to be a macro, running it as a function does not work for the file_appender for some reason
@@ -24,10 +24,10 @@ use tracing::*;
#[derive(Clone, Copy)]
pub struct Config {
initial_reconnection_backoff: Duration,
maximum_reconnection_backoff: Duration,
initial_connection_timeout: Duration,
maximum_connection_buffer_size: usize,
pub initial_reconnection_backoff: Duration,
pub maximum_reconnection_backoff: Duration,
pub initial_connection_timeout: Duration,
pub maximum_connection_buffer_size: usize,
}
impl Config {
@@ -50,7 +50,7 @@ pub trait SendWithoutResponse {
// Without response in this context means we will not listen for anything we might get back (not
// that we should get anything), including any possible io errors
fn send_without_response(
&mut self,
&self,
address: NymNodeRoutingAddress,
packet: NymPacket,
packet_type: PacketType,
@@ -196,7 +196,7 @@ impl Client {
}
}
fn make_connection(&mut self, address: NymNodeRoutingAddress, pending_packet: FramedNymPacket) {
fn make_connection(&self, address: NymNodeRoutingAddress, pending_packet: FramedNymPacket) {
let (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
@@ -247,7 +247,7 @@ impl Client {
impl SendWithoutResponse for Client {
fn send_without_response(
&mut self,
&self,
address: NymNodeRoutingAddress,
packet: NymPacket,
packet_type: PacketType,
@@ -219,7 +219,7 @@ impl Epoch {
EpochState::VerificationKeyFinalization { .. } => {
time_configuration.verification_key_finalization_time_secs
}
EpochState::InProgress { .. } => 0,
EpochState::InProgress => 0,
};
finish += adding;
}
@@ -132,6 +132,10 @@ impl PublicKey {
*self.0.as_bytes()
}
pub fn as_bytes(&self) -> &[u8; PUBLIC_KEY_SIZE] {
self.0.as_bytes()
}
pub fn from_bytes(b: &[u8]) -> Result<Self, KeyRecoveryError> {
if b.len() != PUBLIC_KEY_SIZE {
return Err(KeyRecoveryError::InvalidSizePublicKey {
@@ -228,7 +232,6 @@ impl<'a> From<&'a PrivateKey> for PublicKey {
PublicKey((&pk.0).into())
}
}
impl FromStr for PrivateKey {
type Err = KeyRecoveryError;
+1 -10
View File
@@ -6,7 +6,6 @@ use ed25519_dalek::{SecretKey, Signer, SigningKey};
pub use ed25519_dalek::{Verifier, PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, SIGNATURE_LENGTH};
use nym_pemstore::traits::{PemStorableKey, PemStorableKeyPair};
use std::fmt::{self, Debug, Display, Formatter};
use std::hash::{Hash, Hasher};
use std::str::FromStr;
use thiserror::Error;
use zeroize::{Zeroize, ZeroizeOnDrop};
@@ -154,17 +153,9 @@ impl PemStorableKeyPair for KeyPair {
}
/// ed25519 EdDSA Public Key
#[derive(Copy, Clone, Eq, PartialEq)]
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct PublicKey(ed25519_dalek::VerifyingKey);
impl Hash for PublicKey {
fn hash<H: Hasher>(&self, state: &mut H) {
// each public key has unique bytes representation which can be used
// for the hash implementation
self.to_bytes().hash(state)
}
}
impl Display for PublicKey {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&self.to_base58_string(), f)
@@ -10,9 +10,6 @@ pub enum MixProcessingError {
#[error("failed to recover the expected SURB-Ack packet: {0}")]
MalformedSurbAck(#[from] SurbAckRecoveryError),
#[error("the received packet was set to use the very old and very much deprecated 'VPN' mode")]
ReceivedOldTypeVpnPacket,
#[error("failed to process received Nym packet: {0}")]
NymPacketProcessingError(#[from] PacketProcessingError),
}
-29
View File
@@ -37,32 +37,3 @@ impl TicketTypeRepr {
}
}
}
// Constants for bloom filter for double spending detection
//Chosen for FP of
//Calculator at https://hur.st/bloomfilter/
pub const ECASH_DS_BLOOMFILTER_PARAMS: BloomfilterParameters = BloomfilterParameters {
num_hashes: 10,
bitmap_size: 1_500_000_000,
sip_keys: [
(12345678910111213141, 1415926535897932384),
(7182818284590452353, 3571113171923293137),
],
};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct BloomfilterParameters {
pub num_hashes: u32,
pub bitmap_size: u64,
pub sip_keys: [(u64, u64); 2],
}
impl BloomfilterParameters {
pub const fn byte_size(&self) -> u64 {
self.bitmap_size / 8
}
pub const fn default_ecash() -> Self {
ECASH_DS_BLOOMFILTER_PARAMS
}
}
+1 -1
View File
@@ -64,7 +64,7 @@ pub enum NodeType {
impl NodeType {
pub fn is_mixnode(&self) -> bool {
matches!(self, NodeType::Mixnode { .. })
matches!(self, NodeType::Mixnode)
}
}
+17
View File
@@ -363,6 +363,7 @@ impl MetricsController {
buffer
}
#[inline(always)]
pub fn to_writer(&self, writer: &mut dyn std::io::Write) {
let metrics = self.gather();
match writer.write_all(&metrics) {
@@ -371,6 +372,7 @@ impl MetricsController {
}
}
#[inline(always)]
pub fn register_int_gauge<'a>(&self, name: &str, help: impl Into<Option<&'a str>>) {
let Some(metric) = Metric::new_int_gauge(name, help.into().unwrap_or(name)) else {
return;
@@ -378,6 +380,7 @@ impl MetricsController {
self.register_metric(metric);
}
#[inline(always)]
pub fn register_float_gauge<'a>(&self, name: &str, help: impl Into<Option<&'a str>>) {
let Some(metric) = Metric::new_float_gauge(name, help.into().unwrap_or(name)) else {
return;
@@ -385,6 +388,7 @@ impl MetricsController {
self.register_metric(metric);
}
#[inline(always)]
pub fn register_int_counter<'a>(&self, name: &str, help: impl Into<Option<&'a str>>) {
let Some(metric) = Metric::new_int_counter(name, help.into().unwrap_or(name)) else {
return;
@@ -392,6 +396,7 @@ impl MetricsController {
self.register_metric(metric);
}
#[inline(always)]
pub fn register_histogram<'a>(
&self,
name: &str,
@@ -404,6 +409,7 @@ impl MetricsController {
self.register_metric(metric);
}
#[inline(always)]
pub fn set(&self, name: &str, value: i64) -> bool {
if let Some(metric) = self.registry_index.get(name) {
metric.set(value);
@@ -413,6 +419,7 @@ impl MetricsController {
}
}
#[inline(always)]
pub fn set_float(&self, name: &str, value: f64) -> bool {
if let Some(metric) = self.registry_index.get(name) {
metric.set_float(value);
@@ -422,6 +429,7 @@ impl MetricsController {
}
}
#[inline(always)]
pub fn add_to_histogram(&self, name: &str, value: f64) -> bool {
if let Some(metric) = self.registry_index.get(name) {
metric.add_histogram_observation(value);
@@ -431,12 +439,14 @@ impl MetricsController {
}
}
#[inline(always)]
pub fn start_timer(&self, name: &str) -> Option<HistogramTimer> {
self.registry_index
.get(name)
.and_then(|metric| metric.start_timer())
}
#[inline(always)]
pub fn inc(&self, name: &str) -> bool {
if let Some(metric) = self.registry_index.get(name) {
metric.inc();
@@ -446,6 +456,7 @@ impl MetricsController {
}
}
#[inline(always)]
pub fn inc_by(&self, name: &str, value: i64) -> bool {
if let Some(metric) = self.registry_index.get(name) {
metric.inc_by(value);
@@ -455,6 +466,7 @@ impl MetricsController {
}
}
#[inline(always)]
pub fn maybe_register_and_set<'a>(
&self,
name: &str,
@@ -468,6 +480,7 @@ impl MetricsController {
}
}
#[inline(always)]
pub fn maybe_register_and_set_float<'a>(
&self,
name: &str,
@@ -481,6 +494,7 @@ impl MetricsController {
}
}
#[inline(always)]
pub fn maybe_register_and_add_to_histogram<'a>(
&self,
name: &str,
@@ -495,6 +509,7 @@ impl MetricsController {
}
}
#[inline(always)]
pub fn maybe_register_and_inc<'a>(&self, name: &str, help: impl Into<Option<&'a str>>) {
if !self.inc(name) {
let help = help.into();
@@ -503,6 +518,7 @@ impl MetricsController {
}
}
#[inline(always)]
pub fn maybe_register_and_inc_by<'a>(
&self,
name: &str,
@@ -516,6 +532,7 @@ impl MetricsController {
}
}
#[inline(always)]
pub fn register_metric(&self, metric: impl Into<Metric>) {
let m = metric.into();
let fq_name = m.fq_name();
@@ -403,7 +403,7 @@ pub fn issue(
/// * `sk_user` - Secret key of the user.
/// * `blind_signature` - Blinded signature received from the authority.
/// * `req_info` - Information associated with the request, including the joined commitment hash,
/// private attributes openings, v, and expiration date.
/// private attributes openings, v, and expiration date.
///
/// # Returns
///
@@ -57,8 +57,6 @@ impl SurbAck {
let packet_size = match packet_type {
PacketType::Outfox => surb_ack_payload.len().max(MIN_PACKET_SIZE),
PacketType::Mix => PacketSize::AckPacket.payload_size(),
#[allow(deprecated)]
PacketType::Vpn => PacketSize::AckPacket.payload_size(),
};
let surb_ack_packet = match packet_type {
@@ -75,14 +73,6 @@ impl SurbAck {
&destination,
&delays,
)?,
#[allow(deprecated)]
PacketType::Vpn => NymPacket::sphinx_build(
packet_size,
surb_ack_payload,
&route,
&destination,
&delays,
)?,
};
// in our case, the last hop is a gateway that does NOT do any delays
@@ -106,8 +96,6 @@ impl SurbAck {
PacketSize::OutfoxAckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN
}
PacketType::Mix => PacketSize::AckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN,
#[allow(deprecated)]
PacketType::Vpn => PacketSize::AckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN,
}
}
@@ -139,8 +127,6 @@ impl SurbAck {
let packet = match packet_type {
PacketType::Outfox => NymPacket::outfox_from_bytes(&b[address_offset..])?,
PacketType::Mix => NymPacket::sphinx_from_bytes(&b[address_offset..])?,
#[allow(deprecated)]
PacketType::Vpn => NymPacket::sphinx_from_bytes(&b[address_offset..])?,
};
Ok((address, packet))
+2 -9
View File
@@ -132,14 +132,6 @@ where
&destination,
&delays,
)?,
#[allow(deprecated)]
PacketType::Vpn => NymPacket::sphinx_build(
packet_size.payload_size(),
packet_payload,
&route,
&destination,
&delays,
)?,
PacketType::Outfox => NymPacket::outfox_build(
packet_payload,
&route,
@@ -171,6 +163,7 @@ pub fn is_cover(data: &[u8]) -> bool {
#[cfg(test)]
mod tests {
use super::*;
use std::iter::repeat_n;
#[test]
fn is_cover_works_for_identical_input() {
@@ -182,7 +175,7 @@ mod tests {
let input: Vec<_> = LOOP_COVER_MESSAGE_PAYLOAD
.iter()
.cloned()
.chain(std::iter::repeat(42).take(100))
.chain(repeat_n(42, 100))
.collect();
assert!(is_cover(&input))
}
+1 -2
View File
@@ -11,12 +11,11 @@ repository = { workspace = true }
bytes = { workspace = true }
tokio-util = { workspace = true, features = ["codec"] }
thiserror = { workspace = true }
log = { workspace = true }
tracing = { workspace = true }
nym-sphinx-types = { path = "../types", features = ["sphinx", "outfox"] }
nym-sphinx-params = { path = "../params", features = ["sphinx", "outfox"] }
nym-sphinx-forwarding = { path = "../forwarding" }
nym-metrics = { path = "../../nym-metrics" }
nym-sphinx-addressing = { path = "../addressing" }
nym-sphinx-acknowledgements = { path = "../acknowledgements" }
-2
View File
@@ -85,8 +85,6 @@ impl Decoder for NymCodec {
match header.packet_type {
PacketType::Outfox => NymPacket::outfox_from_bytes(slice)?,
PacketType::Mix => NymPacket::sphinx_from_bytes(slice)?,
#[allow(deprecated)]
PacketType::Vpn => NymPacket::sphinx_from_bytes(slice)?,
}
} else {
return Ok(None);
+8
View File
@@ -47,6 +47,14 @@ impl FramedNymPacket {
pub fn into_inner(self) -> NymPacket {
self.packet
}
pub fn packet(&self) -> &NymPacket {
&self.packet
}
pub fn is_sphinx(&self) -> bool {
self.packet.is_sphinx()
}
}
// Contains any metadata that might be useful for sending between mix nodes.
+192 -91
View File
@@ -1,18 +1,20 @@
use log::{debug, error, info, trace};
// Copyright 2021-2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::packet::FramedNymPacket;
use nym_sphinx_acknowledgements::surb_ack::{SurbAck, SurbAckRecoveryError};
use nym_sphinx_addressing::nodes::{NymNodeRoutingAddress, NymNodeRoutingAddressError};
use nym_sphinx_forwarding::packet::MixPacket;
use nym_sphinx_params::{PacketSize, PacketType};
use nym_sphinx_types::header::shared_secret::ExpandedSharedSecret;
use nym_sphinx_types::{
Delay as SphinxDelay, DestinationAddressBytes, NodeAddressBytes, NymPacket, NymPacketError,
NymProcessedPacket, OutfoxError, PrivateKey, ProcessedPacketData, SphinxError,
Version as SphinxPacketVersion,
NymProcessedPacket, OutfoxError, OutfoxProcessedPacket, PrivateKey, ProcessedPacketData,
SphinxError, Version as SphinxPacketVersion, REPLAY_TAG_SIZE,
};
use std::fmt::Display;
use thiserror::Error;
use crate::packet::FramedNymPacket;
use nym_metrics::nanos;
use nym_sphinx_forwarding::packet::MixPacket;
use tracing::{debug, error, info, trace};
#[derive(Debug)]
pub enum MixProcessingResultData {
@@ -49,6 +51,26 @@ pub struct MixProcessingResult {
pub processing_data: MixProcessingResultData,
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug)]
pub enum PartialMixProcessingResult {
Sphinx {
expanded_shared_secret: ExpandedSharedSecret,
},
Outfox,
}
impl PartialMixProcessingResult {
pub fn replay_tag(&self) -> Option<&[u8; REPLAY_TAG_SIZE]> {
match self {
PartialMixProcessingResult::Sphinx {
expanded_shared_secret,
} => Some(expanded_shared_secret.replay_tag()),
PartialMixProcessingResult::Outfox => None,
}
}
}
type ForwardAck = MixPacket;
#[derive(Debug)]
@@ -75,59 +97,192 @@ pub enum PacketProcessingError {
#[error("failed to recover the expected SURB-Ack packet: {0}")]
MalformedSurbAck(#[from] SurbAckRecoveryError),
#[error("the received packet was set to use the very old and very much deprecated 'VPN' mode")]
ReceivedOldTypeVpnPacket,
#[error("failed to process received outfox packet: {0}")]
OutfoxProcessingError(#[from] OutfoxError),
#[error("attempted to partially process an outfox packet")]
PartialOutfoxProcessing,
#[error("this packet has already been processed before")]
PacketReplay,
}
pub struct PartiallyUnwrappedPacket {
received_data: FramedNymPacket,
partial_result: PartialMixProcessingResult,
}
impl PartiallyUnwrappedPacket {
/// Attempt to partially unwrap received packet to derive relevant keys
/// to allow us to reject it for obvious bad behaviour (like replay or invalid mac)
/// without performing full processing
pub fn new(
received_data: FramedNymPacket,
sphinx_key: &PrivateKey,
) -> Result<Self, PacketProcessingError> {
let partial_result = match received_data.packet() {
NymPacket::Sphinx(packet) => {
let expanded_shared_secret =
packet.header.compute_expanded_shared_secret(sphinx_key);
// don't continue if the header is malformed
packet
.header
.ensure_header_integrity(&expanded_shared_secret)?;
PartialMixProcessingResult::Sphinx {
expanded_shared_secret,
}
}
NymPacket::Outfox(_) => PartialMixProcessingResult::Outfox,
};
Ok(PartiallyUnwrappedPacket {
received_data,
partial_result,
})
}
pub fn finalise_unwrapping(self) -> Result<MixProcessingResult, PacketProcessingError> {
let packet_size = self.received_data.packet_size();
let packet_type = self.received_data.packet_type();
let packet = self.received_data.into_inner();
// currently partial unwrapping is only implemented for sphinx packets.
// attempting to call it for anything else should result in a failure
let (
NymPacket::Sphinx(packet),
PartialMixProcessingResult::Sphinx {
expanded_shared_secret,
},
) = (packet, self.partial_result)
else {
return Err(PacketProcessingError::PartialOutfoxProcessing);
};
let processed_packet = packet.process_with_expanded_secret(&expanded_shared_secret)?;
wrap_processed_sphinx_packet(processed_packet, packet_size, packet_type)
}
pub fn replay_tag(&self) -> Option<&[u8; REPLAY_TAG_SIZE]> {
self.partial_result.replay_tag()
}
}
impl From<(FramedNymPacket, PartialMixProcessingResult)> for PartiallyUnwrappedPacket {
fn from(
(received_data, partial_result): (FramedNymPacket, PartialMixProcessingResult),
) -> Self {
PartiallyUnwrappedPacket {
received_data,
partial_result,
}
}
}
pub fn process_framed_packet(
received: FramedNymPacket,
sphinx_key: &PrivateKey,
) -> Result<MixProcessingResult, PacketProcessingError> {
nanos!("process_received", {
let packet_size = received.packet_size();
let packet_type = received.packet_type();
let packet_size = received.packet_size();
let packet_type = received.packet_type();
// unwrap the sphinx packet and if possible and appropriate, cache keys
let processed_packet = perform_framed_unwrapping(received, sphinx_key)?;
// unwrap the sphinx packet
let processed_packet = perform_framed_unwrapping(received, sphinx_key)?;
// for forward packets, extract next hop and set delay (but do NOT delay here)
// for final packets, extract SURBAck
let final_processing_result =
perform_final_processing(processed_packet, packet_size, packet_type);
if final_processing_result.is_err() {
error!("{:?}", final_processing_result)
}
final_processing_result
})
// for forward packets, extract next hop and set delay (but do NOT delay here)
// for final packets, extract SURBAck
perform_final_processing(processed_packet, packet_size, packet_type)
}
fn perform_framed_unwrapping(
received: FramedNymPacket,
sphinx_key: &PrivateKey,
) -> Result<NymProcessedPacket, PacketProcessingError> {
nanos!("perform_initial_unwrapping", {
let packet = received.into_inner();
perform_framed_packet_processing(packet, sphinx_key)
})
let packet = received.into_inner();
perform_framed_packet_processing(packet, sphinx_key)
}
fn perform_framed_packet_processing(
packet: NymPacket,
sphinx_key: &PrivateKey,
) -> Result<NymProcessedPacket, PacketProcessingError> {
nanos!("perform_initial_packet_processing", {
packet.process(sphinx_key).map_err(|err| {
debug!("Failed to unwrap NymPacket packet: {err}");
PacketProcessingError::NymPacketProcessingError(err)
})
packet.process(sphinx_key).map_err(|err| {
debug!("Failed to unwrap NymPacket packet: {err}");
PacketProcessingError::NymPacketProcessingError(err)
})
}
fn wrap_processed_sphinx_packet(
packet: nym_sphinx_types::ProcessedPacket,
packet_size: PacketSize,
packet_type: PacketType,
) -> Result<MixProcessingResult, PacketProcessingError> {
let processing_data = match packet.data {
ProcessedPacketData::ForwardHop {
next_hop_packet,
next_hop_address,
delay,
} => process_forward_hop(
NymPacket::Sphinx(next_hop_packet),
next_hop_address,
delay,
packet_type,
),
// right now there's no use for the surb_id included in the header - probably it should get removed from the
// sphinx all together?
ProcessedPacketData::FinalHop {
destination,
identifier: _,
payload,
} => process_final_hop(
destination,
payload.recover_plaintext()?,
packet_size,
packet_type,
),
}?;
Ok(MixProcessingResult {
packet_version: MixPacketVersion::Sphinx(packet.version),
processing_data,
})
}
fn wrap_processed_outfox_packet(
packet: OutfoxProcessedPacket,
packet_size: PacketSize,
packet_type: PacketType,
) -> Result<MixProcessingResult, PacketProcessingError> {
let next_address = *packet.next_address();
let packet = packet.into_packet();
if packet.is_final_hop() {
let processing_data = process_final_hop(
DestinationAddressBytes::from_bytes(next_address),
packet.recover_plaintext()?.to_vec(),
packet_size,
packet_type,
)?;
Ok(MixProcessingResult {
packet_version: MixPacketVersion::Outfox,
processing_data,
})
} else {
let packet = MixPacket::new(
NymNodeRoutingAddress::try_from_bytes(&next_address)?,
NymPacket::Outfox(packet),
PacketType::Outfox,
);
Ok(MixProcessingResult {
packet_version: MixPacketVersion::Outfox,
processing_data: MixProcessingResultData::ForwardHop {
packet,
delay: None,
},
})
}
}
fn perform_final_processing(
packet: NymProcessedPacket,
packet_size: PacketSize,
@@ -135,64 +290,10 @@ fn perform_final_processing(
) -> Result<MixProcessingResult, PacketProcessingError> {
match packet {
NymProcessedPacket::Sphinx(packet) => {
let processing_data = match packet.data {
ProcessedPacketData::ForwardHop {
next_hop_packet,
next_hop_address,
delay,
} => process_forward_hop(
NymPacket::Sphinx(next_hop_packet),
next_hop_address,
delay,
packet_type,
),
// right now there's no use for the surb_id included in the header - probably it should get removed from the
// sphinx all together?
ProcessedPacketData::FinalHop {
destination,
identifier: _,
payload,
} => process_final_hop(
destination,
payload.recover_plaintext()?,
packet_size,
packet_type,
),
}?;
Ok(MixProcessingResult {
packet_version: MixPacketVersion::Sphinx(packet.version),
processing_data,
})
wrap_processed_sphinx_packet(packet, packet_size, packet_type)
}
NymProcessedPacket::Outfox(packet) => {
let next_address = *packet.next_address();
let packet = packet.into_packet();
if packet.is_final_hop() {
let processing_data = process_final_hop(
DestinationAddressBytes::from_bytes(next_address),
packet.recover_plaintext()?.to_vec(),
packet_size,
packet_type,
)?;
Ok(MixProcessingResult {
packet_version: MixPacketVersion::Outfox,
processing_data,
})
} else {
let packet = MixPacket::new(
NymNodeRoutingAddress::try_from_bytes(&next_address)?,
NymPacket::Outfox(packet),
PacketType::Outfox,
);
Ok(MixProcessingResult {
packet_version: MixPacketVersion::Outfox,
processing_data: MixProcessingResultData::ForwardHop {
packet,
delay: None,
},
})
}
wrap_processed_outfox_packet(packet, packet_size, packet_type)
}
}
}
@@ -272,9 +272,6 @@ impl PacketSize {
let overhead = match packet_type {
#[cfg(feature = "sphinx")]
PacketType::Mix => SPHINX_PACKET_OVERHEAD,
#[allow(deprecated)]
#[cfg(feature = "sphinx")]
PacketType::Vpn => SPHINX_PACKET_OVERHEAD,
#[cfg(feature = "outfox")]
PacketType::Outfox => OUTFOX_PACKET_OVERHEAD,
_ => 0,
@@ -27,11 +27,6 @@ pub enum PacketType {
#[serde(alias = "sphinx")]
Mix = 0,
/// Represents a packet that should be sent through the network as fast as possible.
#[deprecated]
#[serde(rename = "unsupported-mix-vpn")]
Vpn = 1,
/// Abusing this to add Outfox support
#[serde(rename = "outfox")]
Outfox = 2,
@@ -41,8 +36,6 @@ impl fmt::Display for PacketType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
PacketType::Mix => write!(f, "Mix"),
#[allow(deprecated)]
PacketType::Vpn => write!(f, "Vpn"),
PacketType::Outfox => write!(f, "Outfox"),
}
}
+1 -1
View File
@@ -252,7 +252,7 @@ impl NymMessage {
bytes
.into_iter()
.chain(std::iter::once(1u8))
.chain(std::iter::repeat(0u8).take(space_left))
.chain(std::iter::repeat_n(0u8, space_left))
.collect::<Vec<_>>()
.into()
}
+1 -9
View File
@@ -100,7 +100,7 @@ pub trait FragmentPreparer {
/// - compute SURB_ACK
/// - generate (x, g^x)
/// - obtain key k from the reply-surb which was computed as follows:
/// k = KDF(remote encryption key ^ x) this is equivalent to KDF( dh(remote, x) )
/// k = KDF(remote encryption key ^ x) this is equivalent to KDF( dh(remote, x) )
/// - compute v_b = AES-128-CTR(k, serialized_fragment)
/// - compute vk_b = H(k) || v_b
/// - compute sphinx_plaintext = SURB_ACK || H(k) || v_b
@@ -270,14 +270,6 @@ pub trait FragmentPreparer {
&destination,
&delays,
)?,
#[allow(deprecated)]
PacketType::Vpn => NymPacket::sphinx_build(
packet_size.payload_size(),
packet_payload,
&route,
&destination,
&delays,
)?,
};
// from the previously constructed route extract the first hop
+24 -3
View File
@@ -5,10 +5,10 @@ use std::{array::TryFromSliceError, fmt};
use thiserror::Error;
#[cfg(feature = "outfox")]
use nym_outfox::packet::{OutfoxPacket, OutfoxProcessedPacket};
pub use nym_outfox::packet::{OutfoxPacket, OutfoxProcessedPacket};
#[cfg(feature = "sphinx")]
use sphinx_packet::{SphinxPacket, SphinxPacketBuilder};
pub use sphinx_packet::{SphinxPacket, SphinxPacketBuilder};
#[cfg(feature = "outfox")]
pub use nym_outfox::{
@@ -21,7 +21,7 @@ pub use nym_outfox::{
pub use sphinx_packet::{
constants::{
self, DESTINATION_ADDRESS_LENGTH, IDENTIFIER_LENGTH, MAX_PATH_LENGTH, NODE_ADDRESS_LENGTH,
PAYLOAD_KEY_SIZE,
PAYLOAD_KEY_SIZE, REPLAY_TAG_SIZE,
},
crypto::{self, PrivateKey, PublicKey},
header::{self, delays, delays::Delay, ProcessedHeader, SphinxHeader, HEADER_SIZE},
@@ -166,4 +166,25 @@ impl NymPacket {
}
}
}
#[cfg(feature = "sphinx")]
pub fn sphinx_packet_ref(&self) -> Option<&SphinxPacket> {
match self {
NymPacket::Sphinx(packet) => Some(packet),
_ => None,
}
}
#[cfg(feature = "sphinx")]
pub fn to_sphinx_packet(self) -> Option<SphinxPacket> {
match self {
NymPacket::Sphinx(packet) => Some(packet),
_ => None,
}
}
#[cfg(feature = "sphinx")]
pub fn is_sphinx(&self) -> bool {
matches!(self, NymPacket::Sphinx(_))
}
}
+1 -1
View File
@@ -209,7 +209,7 @@ impl ShutdownManager {
legacy_task_manager: None,
shutdown_signals: Default::default(),
tracker: Default::default(),
max_shutdown_duration: Default::default(),
max_shutdown_duration: Duration::from_secs(10),
};
// we need to add an explicit watcher for the cancellation token being cancelled
+1 -1
View File
@@ -363,7 +363,7 @@ impl TaskClient {
"unnamed-TaskClient".to_string()
};
log!(target: target, level, "{}", format!("[{target}] {msg}"))
log!(target: target, level, "{}", format_args!("[{target}] {msg}"))
}
#[must_use]
+1 -8
View File
@@ -6,13 +6,12 @@ use base64::engine::general_purpose;
use base64::Engine;
use serde::Serialize;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::ops::Deref;
use std::str::FromStr;
use x25519_dalek::PublicKey;
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct PeerPublicKey(PublicKey);
impl PeerPublicKey {
@@ -36,12 +35,6 @@ impl fmt::Display for PeerPublicKey {
}
}
impl Hash for PeerPublicKey {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.as_bytes().hash(state)
}
}
impl Deref for PeerPublicKey {
type Target = PublicKey;
+2 -2
View File
@@ -102,7 +102,7 @@ impl PeerHandle {
if SystemTime::now().duration_since(self.startup_timestamp)? >= AUTO_REMOVE_AFTER {
log::debug!(
"Peer {} has been present for 30 days, removing it",
self.public_key.to_string()
self.public_key
);
let success = self.remove_peer().await?;
return Ok(!success);
@@ -111,7 +111,7 @@ impl PeerHandle {
if spent_bandwidth >= BANDWIDTH_CAP_PER_DAY {
log::debug!(
"Peer {} doesn't have bandwidth anymore, removing it",
self.public_key.to_string()
self.public_key
);
let success = self.remove_peer().await?;
return Ok(!success);
+8 -8
View File
@@ -667,9 +667,9 @@ dependencies = [
[[package]]
name = "deranged"
version = "0.3.11"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
checksum = "28cfac68e08048ae1883171632c2aef3ebc555621ae56fbccce1cbf22dd7f058"
dependencies = [
"powerfmt",
]
@@ -1851,9 +1851,9 @@ dependencies = [
[[package]]
name = "time"
version = "0.3.39"
version = "0.3.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dad298b01a40a23aac4580b67e3dbedb7cc8402f3592d7f49469de2ea4aecdd8"
checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40"
dependencies = [
"deranged",
"itoa",
@@ -1868,15 +1868,15 @@ dependencies = [
[[package]]
name = "time-core"
version = "0.1.3"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "765c97a5b985b7c11d7bc27fa927dc4fe6af3a6dfb021d28deb60d3bf51e76ef"
checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
[[package]]
name = "time-macros"
version = "0.2.20"
version = "0.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8093bc3e81c3bc5f7879de09619d06c9a5a5e45ca44dfeeb7225bae38005c5c"
checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49"
dependencies = [
"num-conv",
"time-core",
@@ -1 +1 @@
Wednesday, March 19th 2025, 18:56:53 UTC
Thursday, April 3rd 2025, 13:40:32 UTC
@@ -47,6 +47,166 @@ This page displays a full list of all the changes during our release cycle from
<VarInfo />
## `v2025.6-chuckles`
- [Release Binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2025.8-chuckles)
- [`nym-node`](nodes/nym-node.mdx) version `1.8.0`
```shell
nym-node
Binary Name: nym-node
Build Timestamp: 2025-04-01T09:55:02.982234741Z
Build Version: 1.8.0
Commit SHA: a429d6528e99b878a310b71bdbe6d31923c52d84
Commit Date: 2025-04-01T11:41:15.000000000+02:00
Commit Branch: HEAD
rustc Version: 1.85.1
rustc Channel: stable
cargo Profile: release
```
### Operators Updates & Tools
- [**Nym Wallet v1.2.16**](https://github.com/nymtech/nym/releases/tag/nym-wallet-v1.2.16): Operators can change their parameters in the GUI interface again. Remember that while changing node settings will take effect in the next epoch (max 60 min), *Operator cost* and *Profit margin* will be updated with the change of interval (720 epochs). You can track the state of epochs and intervals [here](https://explorer.nym.spectredao.net/dashboard). Currently we are less than 24h from the end of interval, **if you were to change OC or PM, do it now!**
- [**Nym Explorer v2.1.0**](https://nym.com/explorer) is out as a beta release
- Since last release [**Wireguard exit policy is out!**](nodes/nym-node/configuration#wireguard-exit-policy-configuration). Make sure to apply it on the servers hosting `nym-node` in `exit-gateway` mode.
#### Community Tools
Nym operators are coming with diverse backgrounds. While for some running a `nym-node` is an introduction to sys-administration, others are well seasoned builders and hackers. It's amazing to see the effort people put in order to lift each other by day-to-day mutual problem solving support in [Matrix channel](https://matrix.to/#/#operators:nymtech.chat). More and more people take it further and build tools to improve network monitoring and node administration.
While we are planning to make a page aggregating these tools together, here are some tools released lately by the technical community:
- [SpectreDAO token dashboard](https://explorer.nym.spectredao.net/token): Including stats, distribution, alongside all fundamental stats
- [SpectreDAO privacy check](https://explorer.nym.spectredao.net/privacy-check): Testing users browser, IP info, WebRTC leaks, fingerprinting, extensions and most importantly whether you access via [NymVPN](https://nym.com/)
- Service status dashboards: Uptime dashboards showing status of Nym Nodes, either squads using self-hosted [Uptime Kuma](https://uptime.kuma.pet/) or a custom dashboard, like the one from [Oceanus](https://oceanus.p17o.com/)
- [Nym Node Widget](https://github.com/koutakou/NymNodeWidget): A MacOS widget following status of your nodes by [koutakou](https://github.com/koutakou/)
- [Nymesis](https://nymesis.vercel.app/): Beta version of an explorer tracking node rewards in a simple graph
### Features
- [Bump `http-proxy-middleware` from `2.0.6` to `2.0.7`](https://github.com/nymtech/nym/pull/5019): Bumps [`http-proxy-middleware`](https://github.com/chimurai/http-proxy-middleware)
- [Bump `next` from `13.5.7` to `14.2.15` in `/documentation/docs`](https://github.com/nymtech/nym/pull/5281): Bumps [`next`](https://github.com/vercel/next.js)
- [Bump `next` from `14.1.4` to `14.2.21` in `/explorer-nextjs`](https://github.com/nymtech/nym/pull/5308): Bumps [`next`](https://github.com/vercel/next.js)
- [Bump `nanoid` from `3.3.7` to `3.3.8` in `/documentation/docs`](https://github.com/nymtech/nym/pull/5335): Bumps [`nanoid`](https://github.com/ai/nanoid)
- [Bump `store2` from `2.14.3` to `2.14.4`](https://github.com/nymtech/nym/pull/5391): Bumps [`store2`](https://github.com/nbubna/store)
- [Bump `@octokit/plugin-paginate-rest` and `@actions/github` in `/.github/actions/nym-hash-releases/src`](https://github.com/nymtech/nym/pull/5488): Bumps [`@octokit/plugin-paginate-rest`](https://github.com/octokit/plugin-paginate-rest.js)
- [Clean stale partially received buffers](https://github.com/nymtech/nym/pull/5536): Clean up old `ReconstructionBuffer`s that have not received fragments in a long time. The motivation is that we want to allow the client to cap the number of re-transmissions done, meaning we can't assume that these will eventually arrive
- [Explorer V2](https://github.com/nymtech/nym/pull/5548)
- [Bump `braces` from `3.0.2` to `3.0.3` in `/sdk/typescript/packages/mix-fetch-node`](https://github.com/nymtech/nym/pull/5612): Bumps [`braces`](https://github.com/micromatch/braces)
- [Bump `golang.org/x/net` from `0.23.0` to `0.36.0` in `/wasm/mix-fetch/go-mix-conn`](https://github.com/nymtech/nym/pull/5613): Bumps [golang.org/x/net](https://github.com/golang/net)
- [Bump `ws` from `8.13.0` to `8.18.1` in `/wasm/client/internal-dev`](https://github.com/nymtech/nym/pull/5614): Bumps [`ws`](https://github.com/websockets/ws)
- [Bump webpack from `5.77.0` to `5.98.0` in `/wasm/client/internal-dev`](https://github.com/nymtech/nym/pull/5615): Bumps [`webpack`](https://github.com/webpack/webpack)
- [Feature/paginated ticketbooks challenge](https://github.com/nymtech/nym/pull/5619): This PR makes changes to how ticketbook rewarding works and in particular the challenge portion of the exchange. Rather than requesting merkle proof alongside all binary data of the associated ticketbooks in a single `/issued-ticketbooks-challenge` query (that could have potentially lead to ddos of nym-apis as it made them load all of the data into the memory at once) the query got split into `/issued-ticketbooks-challenge-commitment` for obtaining the merkle proof and `/issued-ticketbooks-data` for obtaining the binary data. Note that the latter query might have to be called multiple times with different arguments due to the enforced limit on the maximum number of values returned at once.
<AccordionTemplate name="nym-api routes">
- added:
- `/issued-ticketbooks-count`
- `/issued-ticketbooks-for-count/:expiration_date`
- `/issued-ticketbooks-on-count/:issuance_date`
- `/issued-ticketbooks-challenge-commitment`
- `/issued-ticketbooks-data`
- removed:
- `/issued-ticketbooks-challenge`
</AccordionTemplate>
<AccordionTemplate name={<TestingSteps/>}>
Testing Steps Performed:
New issue ticketbook related endpoints work - tickets are issued and consumed as before.
Redeeming tickets is happening as normal on the gateway side.
Notes (if any):
Chuckles will not require the rewarder binary to be updated; as there will be additional changes to rewarding, this will be extensively tested in the future.
</AccordionTemplate>
- [Bump `@babel/runtime` from `7.16.3` to `7.26.10` in `/testnet-faucet`](https://github.com/nymtech/nym/pull/5621): Bumps [`@babel/runtime`](https://github.com/babel/babel/tree/HEAD/packages/babel-runtime)
- [Bump the `patch-updates` group with 8 updates](https://github.com/nymtech/nym/pull/5624):
| Package | From | To |
| --- | --- | --- |
| [`async-trait`](https://github.com/dtolnay/async-trait) | `0.1.87` | `0.1.88` |
| [`clap`](https://github.com/clap-rs/clap) | `4.5.31` | `4.5.32` |
| [`env_logger`](https://github.com/rust-cli/env_logger) | `0.11.6` | `0.11.7` |
| [`http-body-util`](https://github.com/hyperium/http-body) | `0.1.2` | `0.1.3` |
| [`quote`](https://github.com/dtolnay/quote) | `1.0.39` | `1.0.40` |
| [`tokio`](https://github.com/tokio-rs/tokio) | `1.44.0` | `1.44.1` |
| [`tokio-util`](https://github.com/tokio-rs/tokio) | `0.7.13` | `0.7.14` |
| [`indexed_db_futures`](https://github.com/Alorel/rust-indexed-db) | `0.6.0` | `0.6.1` |
- [Bump `humantime` from `2.1.0` to `2.2.0`](https://github.com/nymtech/nym/pull/5625): Bumps [`humantime`](https://github.com/chronotope/humantime)
- [Bump `http` from `1.2.0` to `1.3.1`](https://github.com/nymtech/nym/pull/5626): Bumps [`http`](https://github.com/hyperium/http)
- [Bump `celes` from `2.5.0` to `2.6.0`](https://github.com/nymtech/nym/pull/5627)
- [Bump `uuid` from `1.15.1` to `1.16.0`](https://github.com/nymtech/nym/pull/5628): Bumps [`uuid`](https://github.com/uuid-rs/uuid)
- [Bump `once_cell` from `1.20.3` to `1.21.1`](https://github.com/nymtech/nym/pull/5629): Bumps [`once_cell`](https://github.com/matklad/once_cell)
- [Bump `zeroize` from `1.6.0` to `1.8.1`](https://github.com/nymtech/nym/pull/5630): Bumps [`zeroize`](https://github.com/RustCrypto/utils)
- [Bump `tempfile` from `3.18.0` to `3.19.0`](https://github.com/nymtech/nym/pull/5631): Bumps [`tempfile`](https://github.com/Stebalien/tempfile)
- [Rework IPR codec to extract out timer and implement AsyncWrite](https://github.com/nymtech/nym/pull/5632): The goal is to be able to handle back pressure in the vpn client. For that we need to extract out the timer from the codec since we want to move control over that to the mixnet processor. Once the timer is removed we can reformulate the mixnet sender as a `Sink` wrapped in a `FramedWrite`
- Extract out timer from IPR codec
- Implement AsyncWrite on mixnet client sender
- Add functions to wait for lanes to clear on `LaneQueueLenghts`
- [Remove `explorer-api` from the main workspace](https://github.com/nymtech/nym/pull/5635)
- [Bump `dtolnay/rust-toolchain` from `1.90.0` to `1.100.0`](https://github.com/nymtech/nym/pull/5638): Bumps [`dtolnay/rust-toolchain`](https://github.com/dtolnay/rust-toolchain)
- [Bump `zip` from `2.2.2` to `2.4.1`](https://github.com/nymtech/nym/pull/5639): Bumps [`zip`](https://github.com/zip-rs/zip2)
- [Add `max_retransmissions` flag on each message](https://github.com/nymtech/nym/pull/5642): Add an optional `max_retransmissions` field on `InputMessage`, to be able to selectively disable or turn down the number of `retransmissions` done for specific packets
- [Upgrade `sha2` to workspace version for validator-client](https://github.com/nymtech/nym/pull/5644)
- [Add `RUSTUP_PERMIT_COPY_RENAME` in two workflows that we forgot about](https://github.com/nymtech/nym/pull/5646)
- [Remove `UNIQUE` constraint on node pubkey](https://github.com/nymtech/nym/pull/5649): This accounts for the case when a node rebonds with the same key remove nodes that weren't updated in a week
- [Add concurrency limit to CI](https://github.com/nymtech/nym/pull/5651): For `ci-build` workflow only
- [Update wallet to include Interval Operator Cost and Profit Margin](https://github.com/nymtech/nym/pull/5652): Created a new modal for Interval Operator Cost and Profit Margin changes
- Uses existing update node operating cost functionality for old mixnodes (If it works it works)
- Prompts a warning that a user "Can send the request as many times as they want but it will only update the last message when the interval changes"
- [Wallet-revamp to be in line with new nym-theming](https://github.com/nymtech/nym/pull/5653): Updating colour palette to match the [nym.com](https://nym.com) sites:
- Used the same font too
- Updated icons
- [Add `fd` callback for initial authentication](https://github.com/nymtech/nym/pull/5654): One thing that was missed was the registration `ws` connection that happens before the actual connection, if it's the first time connecting to a gateway. This PR extends connection `fd` callback to that initial connection, so that it can also be allowed by firewalls.
- [Revert using `AsyncWrite` sink in IPR](https://github.com/nymtech/nym/pull/5656): Revert the use of the `AsyncWrite` sink in the IPR until it's fully ready.
- [Remove Google public DNS](https://github.com/nymtech/nym/pull/5660): We had a lot of complaints about Google, so we're removing this in VPN client, Nym should follow to avoid issues with firewall.
### Bugfix
- [Corrected typos](https://github.com/nymtech/nym/pull/5497) in `CHANGELOG.md`
## `v2025.5-chokito`
- [Release Binaries](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2025.5-chokito)
@@ -67,18 +227,14 @@ cargo Profile: release
### Release Summary
1. WebSocket Connection Counter Fix (this is the fix for which we have a [pre-release branch](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2025.4-dorina-nym-node-patch) out already which will be replaced by this release)
- Prevents incorrect session tracking for WebSockets
1. WebSocket Connection Counter Fix (this is the fix for which we have a [pre-release branch](https://github.com/nymtech/nym/releases/tag/nym-binaries-v2025.4-dorina-nym-node-patch) out already which will be replaced by this release): Prevents incorrect session tracking for WebSockets
2. Security Fixes in Dependencies
- Address known security vulnerabilities, making updating highly recommended
2. Security Fixes in Dependencies: Address known security vulnerabilities, making updating highly recommended
3. Authorisation & Timestamp Changes
- Modifies how timestamps are validated in Auth v2 - this is going to be what the clients use. For example: the clients will start to use v2 variants of everything (prepping the network for the breaking change to come on April 1st which will force clients to only use v2).
3. Authorisation & Timestamp Changes: Modifies how timestamps are validated in Auth v2 - this is going to be what the clients use. For example: the clients will start to use v2 variants of everything (prepping the network for the breaking change to come on April 1st which will force clients to only use v2).
4. Wireguard exit policy [scripts](https://github.com/nymtech/nym/tree/develop/scripts/wireguard-exit-policy)
### Operators Updates & Tools
- [**Wireguard exit policy is out!**](nodes/nym-node/configuration#wireguard-exit-policy-configuration) Operators can use a new guide with our scripts to setup wireguard exit policy using IP tables rules.
@@ -20,12 +20,12 @@ This documentation page provides a guide on how to set up and run a [NYM NODE](.
```sh
nym-node
Binary Name: nym-node
Build Timestamp: 2025-03-18T12:17:10.782554174Z
Build Version: 1.7.0
Commit SHA: 82e82943aa14e530ab4eb3aaab28ce86d47e5298
Commit Date: 2025-03-18T10:39:55.000000000+01:00
Build Timestamp: 2025-04-01T09:55:02.982234741Z
Build Version: 1.8.0
Commit SHA: a429d6528e99b878a310b71bdbe6d31923c52d84
Commit Date: 2025-04-01T11:41:15.000000000+02:00
Commit Branch: HEAD
rustc Version: 1.85.0
rustc Version: 1.85.1
rustc Channel: stable
cargo Profile: release
```
@@ -23,13 +23,13 @@ This guide explains how to prepare your server in order to be able to host multi
This guide is based on Ubuntu 22.04, in case you prefer another OS, you may have to do a bit of your own research to troubleshoot networking configuration and other parameters.
</Callout>
### Installing KVM on a Server with Ubuntu 22.04
## Installing KVM on a Server with Ubuntu 22.04
**KVM** stands for **Kernel-based Virtual Machine**. It is a virtualization technology for Linux that allows a user to run multiple virtual machines (VMs) on a single physical machine. KVM turns the Linux kernel into a hypervisor, enabling it to manage multiple virtualised systems.
Follow the steps below to install KVM on Ubuntu 22.04 LTS.
#### Prerequisites
### Prerequisites
<Callout type="warning">
Operators aiming to run Nym node as mixnet [Exit Gateway](../../../community-counsel/exit-gateway) or with wireguard enabled should familiarize themselves with the challenges possibly coming along `nym-node` operation, described in our [community counsel](../../../community-counsel) and follow up with [legal suggestions](../../../community-counsel/legal). Particularly important is to [introduce yourself](../../../community-counsel/legal#introduce-nym-node-to-your-provider) and your intentions to run a Nym node to your provider.
@@ -510,16 +510,56 @@ Your KVM installation is now ready to deploy and manage VMs.
</Steps>
### Setting Up Virtual Machines
## Setting Up Virtual Machines
After finishing the [installation of KVM](#installing-kvm-on-a-server-with-ubuntu-2204), we can move to the virtualisation configuration.
**After finishing the [installation of KVM](#installing-kvm-on-a-server-with-ubuntu-2204), we can move to the virtualisation configuration.**
> **The steps below will guide you through a setup of one VM, therefore you will have to repeat this process for each VM**. That also means that you have to be mindful of space and memory allocation.
> **This tutorial will guide you through a setup of one VM, therefore you will have to repeat this process for each VM**.
<Callout>
A good practice before initialising any VMs, is to calculate space and memory allocation for each of them and your host machine.
</Callout>
You can choose in between using [bash scripts](#setting-up-vm-using-scripts) or [manual steps](#setting-up-vm-step-by-step) approach. While scripts may be faster, they give you much less flexibility. Therefore if you prefer to have room for customisation coming along installing software, do it step by step.
Remember, that anytime you execute a script on your computer, make sure to read and understand the script first.
### Seting up VM Using Scripts
<Callout type="info" emoji="️">
In case you want to initialise and configure your VMs manually - skip this chapter and go directly to the [*step-by-step* part](#setting-up-vm-step-by-step).
</Callout>
Using the scripts is a two-step process. First, initialisation part is done from the host root using [`initialise-vm.sh`](https://github.com/nymtech/nym/blob/develop/scripts/kvm-setup/initialise-vm.sh) and second, configuration part is done from the VM itself using [`configure-vm.sh`](https://github.com/nymtech/nym/blob/develop/scripts/kvm-setup/configure-vm.sh).
<Steps>
##### 1. Initialise VM from the host machine
- Log in to your host as `root`
- Run this block and follow the prompts carefully:
```bash
wget "https://raw.githubusercontent.com/nymtech/nym/refs/heads/develop/scripts/kvm-setup/initialise-vm.sh"
chmod +x ./initialise-vm.sh
./initialise-vm.sh
```
##### 2. Configure VM from within
- After logging into your VM run this block and follow the prompts carefully:
```bash
wget "https://raw.githubusercontent.com/nymtech/nym/refs/heads/develop/scripts/kvm-setup/configure-vm.sh"
chmod +x ./configure-vm.sh
./configure-vm.sh
```
</ Steps>
### Setting up VM Step-by-step
In case you did't use the scripts and prefer manual approach, follow the steps below carefully.
<Steps>
##### 1. Install OS for VMs
This is the OS on which the nodes themselves will run. You can chose any GNU/Linux of your preference. For this guide we are going to be using Ubuntu 24.04 LTS (Noble Numbat) cloud image from [cloud-images.ubuntu.com](https://cloud-images.ubuntu.com/noble/current/).
This is the OS on which the nodes themselves will run. You can chose any GNU/Linux of your preference. For this guide we are going to be using Ubuntu 24.04 LTS (Noble Numbat) cloud image from [cloud-images.ubuntu.com](https://cloud-images.ubuntu.com/noble/current/).
- Download Ubuntu Cloud image:
```bash
@@ -651,7 +691,7 @@ network:
addresses:
- 1.1.1.1 # Cloudflare IPv4 DNS
- 8.8.8.8 # Google IPv4 DNS
- 8.8.4.4 # Secondary Google IPv4 DNS
- 8.8.4.4 # Secondary Google IPv4 DNS
- 2606:4700:4700::1111 # Cloudflare IPv6 DNS
- 2001:4860:4860::8888 # Google IPv6 DNS
```
@@ -57,7 +57,7 @@ This is a quick summary, to understand the full picture, please see detailed [*R
* NymVPN users can chose to route through Nym Network in two ways:
- Mixnet: 5 layers routing and mixing - full privacy
- Wireguard: 2 layers routing, skipping 3 mixing layers - fast mode
* **The current reward system is *Native rewarding* - an intermediate step - where each layer get's rewarded the same**
* **The current reward system is [*Naive rewarding*](#naive-rewarding) - an intermediate step - where each layer get's rewarded the same**
* In the final model, nodes will get rewarded based on their layer position and the work they do (collected user tickets), where and the reward distribution per layer will be according to a [decision made by the operators](https://forum.nymtech.net/t/poll-what-should-be-the-split-of-mixmining-rewards-among-the-layers-of-the-nym-mixnet/407) as follows:
- 5-hop: 16%-16%-16%-16%-36%
- 2-hop: 33%-67%
+23 -7
View File
@@ -19,20 +19,22 @@ anyhow.workspace = true
arc-swap = { workspace = true }
bip39 = { workspace = true, features = ["zeroize"] }
bs58.workspace = true
bloomfilter = { workspace = true }
celes = { workspace = true } # country codes
colored = { workspace = true }
csv = { workspace = true }
clap = { workspace = true, features = ["cargo", "env"] }
dashmap = { workspace = true }
futures = { workspace = true }
humantime-serde = { workspace = true }
human-repr = { workspace = true }
ipnetwork = { workspace = true }
indicatif = { workspace = true }
rand = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
si-scale = { workspace = true }
thiserror.workspace = true
tracing.workspace = true
tracing-indicatif = { workspace = true }
tracing-subscriber.workspace = true
tokio = { workspace = true, features = ["macros", "sync", "rt-multi-thread"] }
tokio-util = { workspace = true, features = ["codec"] }
@@ -40,9 +42,6 @@ toml = { workspace = true }
url = { workspace = true, features = ["serde"] }
zeroize = { workspace = true, features = ["zeroize_derive"] }
# temporary bonding information v1 (to grab and parse nym-mixnode and nym-gateway package versions)
semver = { workspace = true }
# system info:
cupid = { workspace = true }
sysinfo = { workspace = true }
@@ -62,6 +61,8 @@ nym-sphinx-addressing = { path = "../common/nymsphinx/addressing" }
nym-sphinx-framing = { path = "../common/nymsphinx/framing" }
nym-sphinx-types = { path = "../common/nymsphinx/types" }
nym-sphinx-forwarding = { path = "../common/nymsphinx/forwarding" }
nym-sphinx-routing = { path = "../common/nymsphinx/routing" }
nym-sphinx-params = { path = "../common/nymsphinx/params" }
nym-task = { path = "../common/task" }
nym-types = { path = "../common/types" }
nym-validator-client = { path = "../common/client-libs/validator-client" }
@@ -77,8 +78,6 @@ nym-http-api-client = { path = "../common/http-api-client" }
# useful for `#[axum_macros::debug_handler]`
#axum-macros = "0.3.8"
axum.workspace = true
axum-extra = { workspace = true, features = ["typed-header"] }
headers.workspace = true
time = { workspace = true, features = ["serde"] }
tower-http = { workspace = true, features = ["fs"] }
utoipa = { workspace = true, features = ["axum_extras", "time"] }
@@ -95,10 +94,27 @@ nym-network-requester = { path = "../service-providers/network-requester" }
nym-ip-packet-router = { path = "../service-providers/ip-packet-router" }
# throughput tester to recreate lioness
# we don't care about particular versions - just pull whatever is used by sphinx
lioness = "*"
chacha = "*"
arrayref = "*"
blake2 = "=0.8.1"
sha2 = { workspace = true }
hkdf = { workspace = true }
[[bench]]
name = "benchmarks"
harness = false
[build-dependencies]
# temporary bonding information v1 (to grab and parse nym-mixnode and nym-gateway package versions)
cargo_metadata = { workspace = true }
[dev-dependencies]
criterion = { workspace = true, features = ["async_tokio"] }
[lints]
workspace = true
+78
View File
@@ -0,0 +1,78 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
// unwraps in tests/benches are fine...
#![allow(clippy::unwrap_used)]
use bloomfilter::Bloom;
use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion};
use nym_sphinx_types::REPLAY_TAG_SIZE;
use rand::{thread_rng, Rng};
use std::sync::Mutex;
pub fn uncontested_bloomfilter_check(c: &mut Criterion) {
let mut bloomfilter = Bloom::new_for_fp_rate(725760000, 1e-5).unwrap();
c.bench_function("bf_725760000_1e-5_check", |b| {
b.iter_batched(
|| {
let mut rng = thread_rng();
let mut reply_tag = [0; REPLAY_TAG_SIZE];
rng.fill(&mut reply_tag);
reply_tag
},
|replay_tag| {
black_box(bloomfilter.check_and_set(&replay_tag));
},
BatchSize::SmallInput,
)
});
}
pub fn uncontested_bloomfilter_check_with_exclusive_mutex(c: &mut Criterion) {
let bloomfilter = Mutex::new(Bloom::new_for_fp_rate(725760000, 1e-5).unwrap());
c.bench_function("bf_725760000_1e-5_uncontested_std_mutex_check", |b| {
b.iter_batched(
|| {
let mut rng = thread_rng();
let mut reply_tag = [0; REPLAY_TAG_SIZE];
rng.fill(&mut reply_tag);
reply_tag
},
|replay_tag| {
black_box(bloomfilter.lock().unwrap().check_and_set(&replay_tag));
},
BatchSize::SmallInput,
)
});
}
pub fn uncontested_bloomfilter_check_with_exclusive_tokio_mutex(c: &mut Criterion) {
let bloomfilter = tokio::sync::Mutex::new(Bloom::new_for_fp_rate(725760000, 1e-5).unwrap());
let runtime = tokio::runtime::Runtime::new().unwrap();
c.bench_function("bf_725760000_1e-5_uncontested_tokio_mutex_check", |b| {
b.to_async(&runtime).iter_batched(
|| {
let mut rng = thread_rng();
let mut reply_tag = [0; REPLAY_TAG_SIZE];
rng.fill(&mut reply_tag);
reply_tag
},
async |replay_tag| {
black_box(bloomfilter.lock().await.check_and_set(&replay_tag));
},
BatchSize::SmallInput,
)
});
}
criterion_group!(
nym_node_benches,
uncontested_bloomfilter_check,
uncontested_bloomfilter_check_with_exclusive_mutex,
uncontested_bloomfilter_check_with_exclusive_tokio_mutex
);
// TODO: somehow bench heavily contested cases...
criterion_main!(nym_node_benches);
+15
View File
@@ -46,6 +46,13 @@ impl MixingStats {
.store(update_timestamp, Ordering::Release);
}
pub fn ingress_replayed_packet(&self, source: IpAddr) {
self.ingress
.replayed_packets_received
.fetch_add(1, Ordering::Relaxed);
self.ingress.senders.entry(source).or_default().replayed += 1;
}
pub fn ingress_malformed_packet(&self, source: IpAddr) {
self.ingress
.malformed_packets_received
@@ -197,6 +204,7 @@ pub struct IngressRecipientStats {
pub forward_packets: IngressPacketsStats,
pub final_hop_packets: IngressPacketsStats,
pub malformed: usize,
pub replayed: usize,
}
#[derive(Debug, Default, Copy, Clone, Hash, PartialEq, Eq)]
@@ -232,6 +240,9 @@ pub struct IngressMixingStats {
// packets that failed to get unwrapped
malformed_packets_received: AtomicUsize,
// packets that were already received and processed before
replayed_packets_received: AtomicUsize,
// (forward) packets that had invalid, i.e. too large, delays
excessive_delay_packets: AtomicUsize,
@@ -253,6 +264,10 @@ impl IngressMixingStats {
self.final_hop_packets_received.load(Ordering::Relaxed)
}
pub fn replayed_packets_received(&self) -> usize {
self.replayed_packets_received.load(Ordering::Relaxed)
}
pub fn malformed_packets_received(&self) -> usize {
self.malformed_packets_received.load(Ordering::Relaxed)
}
@@ -39,6 +39,9 @@ pub enum PrometheusMetric {
#[strum(props(help = "The number of ingress final hop sphinx packets received"))]
MixnetIngressFinalHopPacketsReceived,
#[strum(props(help = "The number of ingress replayed sphinx packets received"))]
MixnetIngressReplayedPacketsReceived,
#[strum(props(help = "The number of ingress malformed sphinx packets received"))]
MixnetIngressMalformedPacketsReceived,
@@ -208,6 +211,9 @@ impl PrometheusMetric {
PrometheusMetric::MixnetIngressFinalHopPacketsReceived => {
Metric::new_int_gauge(&name, help)
}
PrometheusMetric::MixnetIngressReplayedPacketsReceived => {
Metric::new_int_gauge(&name, help)
}
PrometheusMetric::MixnetIngressMalformedPacketsReceived => {
Metric::new_int_gauge(&name, help)
}
@@ -382,7 +388,7 @@ mod tests {
// a sanity check for anyone adding new metrics. if this test fails,
// make sure any methods on `PrometheusMetric` enum don't need updating
// or require custom Display impl
assert_eq!(38, PrometheusMetric::COUNT)
assert_eq!(39, PrometheusMetric::COUNT)
}
#[test]
@@ -40,6 +40,9 @@ pub mod packets {
// packets that failed to get unwrapped
pub malformed_packets_received: usize,
// packets that were already received and processed before
pub replayed_packets_received: usize,
// (forward) packets that had invalid, i.e. too large, delays
pub excessive_delay_packets: usize,
@@ -3,7 +3,6 @@
use crate::cli::helpers::ConfigArgs;
use crate::config::upgrade_helpers::try_load_current_config;
use crate::error::NymNodeError;
use crate::node::bonding_information::BondingInformation;
use nym_bin_common::output_format::OutputFormat;
@@ -21,7 +20,7 @@ pub struct Args {
pub(crate) output: OutputFormat,
}
pub async fn execute(args: Args) -> Result<(), NymNodeError> {
pub async fn execute(args: Args) -> anyhow::Result<()> {
let config = try_load_current_config(args.config.config_path()).await?;
let info = BondingInformation::try_load(&config)?;
args.output.to_stdout(&info);
+1 -2
View File
@@ -1,7 +1,6 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::error::NymNodeError;
use nym_bin_common::bin_info_owned;
use nym_bin_common::output_format::OutputFormat;
@@ -11,7 +10,7 @@ pub(crate) struct Args {
output: OutputFormat,
}
pub(crate) fn execute(args: Args) -> Result<(), NymNodeError> {
pub(crate) fn execute(args: Args) -> anyhow::Result<()> {
println!("{}", args.output.format(&bin_info_owned!()));
Ok(())
}
+1 -1
View File
@@ -11,7 +11,7 @@ pub(crate) struct Args {
_args: Vec<String>,
}
pub(crate) async fn execute(_args: Args) -> Result<(), NymNodeError> {
pub(crate) fn execute(_args: Args) -> Result<(), NymNodeError> {
let orange = TrueColor {
r: 251,
g: 110,
+1
View File
@@ -7,3 +7,4 @@ pub(super) mod migrate;
pub(crate) mod node_details;
pub(super) mod run;
pub(super) mod sign;
pub(crate) mod test_throughput;
+1 -2
View File
@@ -3,7 +3,6 @@
use crate::cli::helpers::ConfigArgs;
use crate::config::upgrade_helpers::try_load_current_config;
use crate::error::NymNodeError;
use crate::node::NymNode;
use nym_bin_common::output_format::OutputFormat;
@@ -21,7 +20,7 @@ pub(crate) struct Args {
pub(crate) output: OutputFormat,
}
pub async fn execute(args: Args) -> Result<(), NymNodeError> {
pub async fn execute(args: Args) -> anyhow::Result<()> {
let config = try_load_current_config(args.config.config_path()).await?;
let details = NymNode::new(config).await?.display_details()?;
args.output.to_stdout(&details);
+1 -1
View File
@@ -167,7 +167,7 @@ impl Args {
)
.with_host(self.host.build_config_section())
.with_http(self.http.build_config_section())
.with_mixnet(self.mixnet.build_config_section())
.with_mixnet(self.mixnet.build_config_section(&data_dir))
.with_wireguard(self.wireguard.build_config_section(&data_dir))
.with_storage_paths(NymNodePaths::new(&data_dir))
.with_verloc(self.verloc.build_config_section())
+1 -2
View File
@@ -3,7 +3,6 @@
use crate::cli::helpers::ConfigArgs;
use crate::config::upgrade_helpers::try_load_current_config;
use crate::error::NymNodeError;
use crate::node::helpers::load_ed25519_identity_keypair;
use nym_bin_common::output_format::OutputFormat;
use nym_crypto::asymmetric::identity;
@@ -70,7 +69,7 @@ fn print_signed_contract_msg(
// SAFETY: clippy ArgGroup ensures only a single branch is actually called
#[allow(clippy::unreachable)]
pub async fn execute(args: Args) -> Result<(), NymNodeError> {
pub async fn execute(args: Args) -> anyhow::Result<()> {
let config = try_load_current_config(args.config.config_path()).await?;
let identity_keypair =
load_ed25519_identity_keypair(config.storage_paths.keys.ed25519_identity_storage_paths())?;
@@ -0,0 +1,87 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::cli::helpers::ConfigArgs;
use crate::logging::granual_filtered_env;
use crate::throughput_tester::test_mixing_throughput;
use anyhow::bail;
use humantime_serde::re::humantime;
use indicatif::ProgressStyle;
use nym_bin_common::logging::default_tracing_fmt_layer;
use std::env::temp_dir;
use std::path::PathBuf;
use std::time::Duration;
use time::OffsetDateTime;
use tracing_indicatif::IndicatifLayer;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
#[derive(Debug, clap::Args)]
pub struct Args {
#[clap(flatten)]
config: ConfigArgs,
#[clap(long, default_value_t = 10)]
senders: usize,
/// target packet latency, if current value is below threshold, clients will increase their sending rates
/// and similarly if it's above it, they will decrease it
#[clap(long, default_value = "15ms", value_parser = humantime::parse_duration)]
packet_latency_threshold: Duration,
#[clap(long, default_value_t = 50)]
starting_sending_batch_size: usize,
#[clap(long, default_value = "50ms", value_parser = humantime::parse_duration)]
starting_sending_delay: Duration,
#[clap(long, short)]
output_directory: Option<PathBuf>,
}
fn init_test_logger() -> anyhow::Result<()> {
let indicatif_layer = IndicatifLayer::new()
.with_progress_style(ProgressStyle::with_template(
"{span_child_prefix}{spinner} {span_fields} -- {span_name} {wide_msg}",
)?)
.with_span_child_prefix_symbol("")
.with_span_child_prefix_indent(" ");
tracing_subscriber::registry()
.with(default_tracing_fmt_layer(
indicatif_layer.get_stderr_writer(),
))
.with(indicatif_layer)
.with(granual_filtered_env()?)
.init();
Ok(())
}
pub fn execute(args: Args) -> anyhow::Result<()> {
init_test_logger()?;
let output_dir = match args.output_directory {
Some(output_dir) => {
if !output_dir.is_dir() {
bail!("'{}' is not a directory", output_dir.display());
}
output_dir
}
None => {
let now = OffsetDateTime::now_utc().unix_timestamp();
temp_dir()
.join("nym-node-throughput-testing")
.join(now.to_string())
}
};
test_mixing_throughput(
args.config.config_path(),
args.senders,
args.packet_latency_threshold,
args.starting_sending_batch_size,
args.starting_sending_delay,
output_dir,
)
}
+13 -2
View File
@@ -220,12 +220,20 @@ pub(crate) struct MixnetArgs {
env = NYMNODE_UNSAFE_DISABLE_NOISE
)]
pub(crate) unsafe_disable_noise: bool,
/// Specifies whether this node should **NOT** be using replay protection
#[clap(
hide = true,
long,
env = NYMNODE_UNSAFE_DISABLE_REPLAY_PROTECTION
)]
pub(crate) unsafe_disable_replay_protection: bool,
}
impl MixnetArgs {
// TODO: could we perhaps make a clap error here and call `safe_exit` instead?
pub(crate) fn build_config_section(self) -> config::Mixnet {
self.override_config_section(config::Mixnet::default())
pub(crate) fn build_config_section<P: AsRef<Path>>(self, data_dir: P) -> config::Mixnet {
self.override_config_section(config::Mixnet::new_default(data_dir))
}
pub(crate) fn override_config_section(self, mut section: config::Mixnet) -> config::Mixnet {
@@ -244,6 +252,9 @@ impl MixnetArgs {
if self.unsafe_disable_noise {
section.debug.unsafe_disable_noise = true
}
if self.unsafe_disable_replay_protection {
section.replay_protection.debug.unsafe_disabled = true
}
section
}
}
+35 -11
View File
@@ -1,14 +1,17 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::cli::commands::{bonding_information, build_info, migrate, node_details, run, sign};
use crate::cli::commands::{
bonding_information, build_info, migrate, node_details, run, sign, test_throughput,
};
use crate::env::vars::{NYMNODE_CONFIG_ENV_FILE_ARG, NYMNODE_NO_BANNER_ARG};
use crate::error::NymNodeError;
use crate::logging::setup_tracing_logger;
use clap::{Parser, Subcommand};
use nym_bin_common::bin_info;
use std::future::Future;
use std::sync::OnceLock;
mod commands;
pub(crate) mod commands;
mod helpers;
pub const DEFAULT_NYMNODE_ID: &str = "default-nym-node";
@@ -42,15 +45,31 @@ pub(crate) struct Cli {
}
impl Cli {
pub(crate) async fn execute(self) -> Result<(), NymNodeError> {
match self.command {
Commands::BuildInfo(args) => build_info::execute(args),
Commands::BondingInformation(args) => bonding_information::execute(args).await,
Commands::NodeDetails(args) => node_details::execute(args).await,
Commands::Run(args) => run::execute(*args).await,
Commands::Migrate(args) => migrate::execute(*args).await,
Commands::Sign(args) => sign::execute(args).await,
fn execute_async<F: Future>(fut: F) -> anyhow::Result<F::Output> {
Ok(tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()?
.block_on(fut))
}
pub(crate) fn execute(self) -> anyhow::Result<()> {
// NOTE: `test_throughput` sets up its own logger as it has to include additional layers
if !matches!(self.command, Commands::TestThroughput(..)) {
setup_tracing_logger()?;
}
match self.command {
Commands::BuildInfo(args) => build_info::execute(args)?,
Commands::BondingInformation(args) => {
{ Self::execute_async(bonding_information::execute(args))? }?
}
Commands::NodeDetails(args) => { Self::execute_async(node_details::execute(args))? }?,
Commands::Run(args) => { Self::execute_async(run::execute(*args))? }?,
Commands::Migrate(args) => migrate::execute(*args)?,
Commands::Sign(args) => { Self::execute_async(sign::execute(args))? }?,
Commands::TestThroughput(args) => test_throughput::execute(args)?,
}
Ok(())
}
}
@@ -73,6 +92,11 @@ pub(crate) enum Commands {
/// Use identity key of this node to sign provided message.
Sign(sign::Args),
/// Attempt to approximate the maximum mixnet throughput if nym-node
/// was running on this machine in mixnet mode
#[clap(hide = true)]
TestThroughput(test_throughput::Args),
}
#[cfg(test)]
+181 -6
View File
@@ -1,11 +1,12 @@
// Copyright 2023-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::config::persistence::NymNodePaths;
use crate::config::persistence::{NymNodePaths, ReplayProtectionPaths};
use crate::config::template::CONFIG_TEMPLATE;
use crate::error::NymNodeError;
use celes::Country;
use clap::ValueEnum;
use human_repr::HumanCount;
use nym_bin_common::logging::LoggingSettings;
use nym_config::defaults::{
mainnet, var_names, DEFAULT_MIX_LISTENING_PORT, DEFAULT_NYM_NODE_HTTP_PORT,
@@ -26,6 +27,7 @@ use std::fmt::{Display, Formatter};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use std::path::{Path, PathBuf};
use std::time::Duration;
use sysinfo::System;
use tracing::{debug, error};
use url::Url;
@@ -42,6 +44,7 @@ pub mod upgrade_helpers;
pub use crate::config::gateway_tasks::GatewayTasksConfig;
pub use crate::config::metrics::MetricsConfig;
pub use crate::config::service_providers::ServiceProvidersConfig;
use crate::node::replay_protection::{bitmap_size, items_in_bloomfilter};
const DEFAULT_NYMNODES_DIR: &str = "nym-nodes";
@@ -264,7 +267,9 @@ impl ConfigBuilder {
modes: self.modes,
host: self.host.unwrap_or_default(),
http: self.http.unwrap_or_default(),
mixnet: self.mixnet.unwrap_or_default(),
mixnet: self
.mixnet
.unwrap_or_else(|| Mixnet::new_default(&self.data_dir)),
verloc: self.verloc.unwrap_or_default(),
wireguard: self
.wireguard
@@ -417,6 +422,12 @@ impl Config {
pub fn read_from_toml_file<P: AsRef<Path>>(path: P) -> Result<Self, NymNodeError> {
Self::read_from_path(path)
}
pub fn validate(&self) -> Result<(), NymNodeError> {
self.mixnet.validate()?;
Ok(())
}
}
// TODO: this is very much a WIP. we need proper ssl certificate support here
@@ -496,7 +507,6 @@ impl Default for Http {
}
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(default)]
#[serde(deny_unknown_fields)]
pub struct Mixnet {
/// Address this node will bind to for listening for mixnet packets
@@ -516,13 +526,35 @@ pub struct Mixnet {
/// Addresses to nyxd which the node uses to interact with the nyx chain.
pub nyxd_urls: Vec<Url>,
/// Settings for controlling replay detection
pub replay_protection: ReplayProtection,
#[serde(default)]
pub debug: MixnetDebug,
}
impl Mixnet {
pub fn validate(&self) -> Result<(), NymNodeError> {
if self.nym_api_urls.is_empty() {
return Err(NymNodeError::config_validation_failure(
"no nym api urls provided",
));
}
if self.nyxd_urls.is_empty() {
return Err(NymNodeError::config_validation_failure(
"no nyxd urls provided",
));
}
self.replay_protection.validate()?;
Ok(())
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(default)]
#[serde(deny_unknown_fields)]
pub struct MixnetDebug {
/// Specifies the duration of time this node is willing to delay a forward packet for.
#[serde(with = "humantime_serde")]
@@ -549,6 +581,148 @@ pub struct MixnetDebug {
pub unsafe_disable_noise: bool,
}
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
pub struct ReplayProtection {
/// Paths for current bloomfilters
pub storage_paths: persistence::ReplayProtectionPaths,
#[serde(default)]
pub debug: ReplayProtectionDebug,
}
impl ReplayProtection {
pub fn new_default<P: AsRef<Path>>(data_dir: P) -> Self {
ReplayProtection {
storage_paths: ReplayProtectionPaths::new(data_dir),
debug: Default::default(),
}
}
}
impl ReplayProtection {
pub fn validate(&self) -> Result<(), NymNodeError> {
self.debug.validate()?;
Ok(())
}
}
#[derive(Debug, Copy, Clone, Deserialize, PartialEq, Serialize)]
#[serde(default)]
pub struct ReplayProtectionDebug {
/// Specifies whether this node should **NOT** use replay protection
pub unsafe_disabled: bool,
/// How long the processing task is willing to skip mutex acquisition before it will block the thread
/// until it actually obtains it
pub maximum_replay_detection_deferral: Duration,
/// How many packets the processing task is willing to queue before it will block the thread
/// until it obtains the mutex
pub maximum_replay_detection_pending_packets: usize,
/// Probability of false positives, fraction between 0 and 1 or a number indicating 1-in-p
pub false_positive_rate: f64,
/// Defines initial expected number of packets this node will process a second,
/// so that an initial bloomfilter could be established.
/// As the node is running and BF are cleared, the value will be adjusted dynamically
pub initial_expected_packets_per_second: usize,
/// Defines minimum expected number of packets this node will process a second
/// when used for calculating the BF size after reset.
/// This is to avoid degenerate cases where node receives 0 packets (because say it's misconfigured)
/// and it constructs an empty bloomfilter.
pub bloomfilter_minimum_packets_per_second_size: usize,
/// Specifies the amount the bloomfilter size is going to get multiplied by after each reset.
/// It's performed in case the traffic rates increase before the next bloomfilter update.
pub bloomfilter_size_multiplier: f64,
// NOTE: this field is temporary until replay detection bloomfilter rotation is tied
// to key rotation
/// Specifies how often the bloomfilter is cleared
#[serde(with = "humantime_serde")]
pub bloomfilter_reset_rate: Duration,
/// Specifies how often the bloomfilter is flushed to disk for recovery in case of a crash
#[serde(with = "humantime_serde")]
pub bloomfilter_disk_flushing_rate: Duration,
}
impl ReplayProtectionDebug {
pub const DEFAULT_MAXIMUM_REPLAY_DETECTION_DEFERRAL: Duration = Duration::from_millis(50);
pub const DEFAULT_MAXIMUM_REPLAY_DETECTION_PENDING_PACKETS: usize = 100;
// 12% (completely arbitrary)
pub const DEFAULT_BLOOMFILTER_SIZE_MULTIPLIER: f64 = 1.12;
// 10^-5
pub const DEFAULT_REPLAY_DETECTION_FALSE_POSITIVE_RATE: f64 = 1e-5;
// 25h (key rotation will be happening every 24h + 1h of overlap)
pub const DEFAULT_REPLAY_DETECTION_BF_RESET_RATE: Duration = Duration::from_secs(25 * 60 * 60);
// we must have some reasonable balance between losing values and trashing the disk.
// since on average HDD it would take ~30s to save a 2GB bloomfilter
pub const DEFAULT_BF_DISK_FLUSHING_RATE: Duration = Duration::from_secs(10 * 60);
// this value will have to be adjusted in the future
pub const DEFAULT_INITIAL_EXPECTED_PACKETS_PER_SECOND: usize = 2000;
pub const DEFAULT_BLOOMFILTER_MINIMUM_PACKETS_PER_SECOND_SIZE: usize = 200;
pub fn validate(&self) -> Result<(), NymNodeError> {
if self.false_positive_rate >= 1.0 || self.false_positive_rate <= 0.0 {
return Err(NymNodeError::config_validation_failure(
"false positive rate for replay detection can't be larger than (or equal to) 1 or smaller than (or equal to) 0",
));
}
let items_in_filter = items_in_bloomfilter(
self.bloomfilter_reset_rate,
self.initial_expected_packets_per_second,
);
let bitmap_size = bitmap_size(self.false_positive_rate, items_in_filter);
let bloomfilter_size = bitmap_size / 8;
let mut sys_info = System::new();
sys_info.refresh_memory();
// we'll need 2x size of the bloomfilter
// as during key transition we'll have to simultaneously use two filters
// plus we also need to make a memcopy during disk flush
let required_memory = 2 * bloomfilter_size;
let memory = sys_info.available_memory();
if (memory as usize) < required_memory {
return Err(NymNodeError::config_validation_failure(
format!("system does not have sufficient memory to allocate required replay protection bloomfilters. {} is available whilst at least {} is needed",memory.human_count_bytes(), required_memory.human_count_bytes())));
}
Ok(())
}
}
impl Default for ReplayProtectionDebug {
fn default() -> Self {
ReplayProtectionDebug {
unsafe_disabled: false,
maximum_replay_detection_deferral: Self::DEFAULT_MAXIMUM_REPLAY_DETECTION_DEFERRAL,
maximum_replay_detection_pending_packets:
Self::DEFAULT_MAXIMUM_REPLAY_DETECTION_PENDING_PACKETS,
false_positive_rate: Self::DEFAULT_REPLAY_DETECTION_FALSE_POSITIVE_RATE,
initial_expected_packets_per_second: Self::DEFAULT_INITIAL_EXPECTED_PACKETS_PER_SECOND,
bloomfilter_minimum_packets_per_second_size:
Self::DEFAULT_BLOOMFILTER_MINIMUM_PACKETS_PER_SECOND_SIZE,
bloomfilter_size_multiplier: Self::DEFAULT_BLOOMFILTER_SIZE_MULTIPLIER,
bloomfilter_reset_rate: Self::DEFAULT_REPLAY_DETECTION_BF_RESET_RATE,
bloomfilter_disk_flushing_rate: Self::DEFAULT_BF_DISK_FLUSHING_RATE,
}
}
}
impl MixnetDebug {
// given that genuine clients are using mean delay of 50ms,
// the probability of them delaying for over 10s is 10^-87
@@ -574,8 +748,8 @@ impl Default for MixnetDebug {
}
}
impl Default for Mixnet {
fn default() -> Self {
impl Mixnet {
pub fn new_default<P: AsRef<Path>>(data_dir: P) -> Self {
// SAFETY:
// our hardcoded values should always be valid
#[allow(clippy::expect_used)]
@@ -598,6 +772,7 @@ impl Default for Mixnet {
announce_port: None,
nym_api_urls,
nyxd_urls,
replay_protection: ReplayProtection::new_default(data_dir),
debug: Default::default(),
}
}
+2
View File
@@ -8,6 +8,7 @@ mod old_config_v4;
mod old_config_v5;
mod old_config_v6;
mod old_config_v7;
mod old_config_v8;
pub use old_config_v1::try_upgrade_config_v1;
pub use old_config_v2::try_upgrade_config_v2;
@@ -16,3 +17,4 @@ pub use old_config_v4::try_upgrade_config_v4;
pub use old_config_v5::try_upgrade_config_v5;
pub use old_config_v6::try_upgrade_config_v6;
pub use old_config_v7::try_upgrade_config_v7;
pub use old_config_v8::try_upgrade_config_v8;
+41 -340
View File
@@ -1,22 +1,20 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
#![allow(dead_code)]
use crate::config::authenticator::{Authenticator, AuthenticatorDebug};
use crate::config::gateway_tasks::ZkNymTicketHandlerDebug;
use crate::config::service_providers::{
IpPacketRouter, IpPacketRouterDebug, NetworkRequester, NetworkRequesterDebug,
use crate::config::old_configs::old_config_v8::{
AuthenticatorDebugV8, AuthenticatorPathsV8, AuthenticatorV8, ConfigV8,
GatewayTasksConfigDebugV8, GatewayTasksConfigV8, GatewayTasksPathsV8, HostV8, HttpV8,
IpPacketRouterDebugV8, IpPacketRouterPathsV8, IpPacketRouterV8, KeysPathsV8, LoggingSettingsV8,
MixnetDebugV8, MixnetV8, NetworkRequesterDebugV8, NetworkRequesterPathsV8, NetworkRequesterV8,
NodeModesV8, NymNodePathsV8, ServiceProvidersConfigDebugV8, ServiceProvidersConfigV8,
ServiceProvidersPathsV8, VerlocDebugV8, VerlocV8, WireguardPathsV8, WireguardV8,
ZkNymTicketHandlerDebugV8,
};
use crate::config::*;
use crate::error::{EntryGatewayError, NymNodeError};
use crate::error::NymNodeError;
use celes::Country;
use clap::ValueEnum;
use gateway_tasks::DEFAULT_WS_PORT;
use nym_client_core_config_types::{
disk_persistence::{ClientKeysPaths, CommonClientPaths},
DebugConfig as ClientDebugConfig,
};
use nym_client_core_config_types::DebugConfig as ClientDebugConfig;
use nym_config::defaults::{mainnet, var_names};
use nym_config::helpers::inaddr_any;
use nym_config::{
@@ -24,17 +22,13 @@ use nym_config::{
serde_helpers::{de_maybe_port, de_maybe_stringified},
};
use nym_config::{parse_urls, read_config_from_toml_file};
use persistence::*;
use serde::{Deserialize, Serialize};
use std::fs::create_dir_all;
use std::env;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use std::path::{Path, PathBuf};
use std::time::Duration;
use std::{env, fs, io};
use tracing::info;
use tracing::{debug, instrument};
use url::Url;
use zeroize::Zeroizing;
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
@@ -132,14 +126,6 @@ impl From<&[NodeModeV7]> for NodeModesV7 {
}
impl NodeModesV7 {
pub fn any_enabled(&self) -> bool {
self.mixnode || self.entry || self.exit
}
pub fn standalone_exit(&self) -> bool {
!self.mixnode && !self.entry && self.exit
}
pub fn with_mode(&mut self, mode: NodeModeV7) -> &mut Self {
match mode {
NodeModeV7::Mixnode => self.with_mixnode(),
@@ -149,10 +135,6 @@ impl NodeModesV7 {
}
}
pub fn expects_final_hop_traffic(&self) -> bool {
self.entry || self.exit
}
pub fn with_mixnode(&mut self) -> &mut Self {
self.mixnode = true;
self
@@ -320,45 +302,6 @@ pub struct KeysPathsV7 {
pub public_x25519_noise_key_file: PathBuf,
}
impl KeysPathsV7 {
pub fn new<P: AsRef<Path>>(data_dir: P) -> Self {
let data_dir = data_dir.as_ref();
KeysPathsV7 {
private_ed25519_identity_key_file: data_dir
.join(DEFAULT_ED25519_PRIVATE_IDENTITY_KEY_FILENAME),
public_ed25519_identity_key_file: data_dir
.join(DEFAULT_ED25519_PUBLIC_IDENTITY_KEY_FILENAME),
private_x25519_sphinx_key_file: data_dir
.join(DEFAULT_X25519_PRIVATE_SPHINX_KEY_FILENAME),
public_x25519_sphinx_key_file: data_dir.join(DEFAULT_X25519_PUBLIC_SPHINX_KEY_FILENAME),
private_x25519_noise_key_file: data_dir.join(DEFAULT_X25519_PRIVATE_NOISE_KEY_FILENAME),
public_x25519_noise_key_file: data_dir.join(DEFAULT_X25519_PUBLIC_NOISE_KEY_FILENAME),
}
}
pub fn ed25519_identity_storage_paths(&self) -> nym_pemstore::KeyPairPath {
nym_pemstore::KeyPairPath::new(
&self.private_ed25519_identity_key_file,
&self.public_ed25519_identity_key_file,
)
}
pub fn x25519_sphinx_storage_paths(&self) -> nym_pemstore::KeyPairPath {
nym_pemstore::KeyPairPath::new(
&self.private_x25519_sphinx_key_file,
&self.public_x25519_sphinx_key_file,
)
}
pub fn x25519_noise_storage_paths(&self) -> nym_pemstore::KeyPairPath {
nym_pemstore::KeyPairPath::new(
&self.private_x25519_noise_key_file,
&self.public_x25519_noise_key_file,
)
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct NymNodePathsV7 {
@@ -701,56 +644,6 @@ pub struct NetworkRequesterPathsV7 {
// it's possible we might have to add credential storage here for return tickets
}
impl NetworkRequesterPathsV7 {
pub fn new<P: AsRef<Path>>(data_dir: P) -> Self {
let data_dir = data_dir.as_ref();
NetworkRequesterPathsV7 {
private_ed25519_identity_key_file: data_dir
.join(DEFAULT_ED25519_NR_PRIVATE_IDENTITY_KEY_FILENAME),
public_ed25519_identity_key_file: data_dir
.join(DEFAULT_ED25519_NR_PUBLIC_IDENTITY_KEY_FILENAME),
private_x25519_diffie_hellman_key_file: data_dir
.join(DEFAULT_X25519_NR_PRIVATE_DH_KEY_FILENAME),
public_x25519_diffie_hellman_key_file: data_dir
.join(DEFAULT_X25519_NR_PUBLIC_DH_KEY_FILENAME),
ack_key_file: data_dir.join(DEFAULT_NR_ACK_KEY_FILENAME),
reply_surb_database: data_dir.join(DEFAULT_NR_REPLY_SURB_DB_FILENAME),
gateway_registrations: data_dir.join(DEFAULT_NR_GATEWAYS_DB_FILENAME),
}
}
pub fn to_common_client_paths(&self) -> CommonClientPaths {
CommonClientPaths {
keys: ClientKeysPaths {
private_identity_key_file: self.private_ed25519_identity_key_file.clone(),
public_identity_key_file: self.public_ed25519_identity_key_file.clone(),
private_encryption_key_file: self.private_x25519_diffie_hellman_key_file.clone(),
public_encryption_key_file: self.public_x25519_diffie_hellman_key_file.clone(),
ack_key_file: self.ack_key_file.clone(),
},
gateway_registrations: self.gateway_registrations.clone(),
// not needed for embedded providers
credentials_database: Default::default(),
reply_surb_database: self.reply_surb_database.clone(),
}
}
pub fn ed25519_identity_storage_paths(&self) -> nym_pemstore::KeyPairPath {
nym_pemstore::KeyPairPath::new(
&self.private_ed25519_identity_key_file,
&self.public_ed25519_identity_key_file,
)
}
pub fn x25519_diffie_hellman_storage_paths(&self) -> nym_pemstore::KeyPairPath {
nym_pemstore::KeyPairPath::new(
&self.private_x25519_diffie_hellman_key_file,
&self.public_x25519_diffie_hellman_key_file,
)
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct IpPacketRouterPathsV7 {
@@ -781,56 +674,6 @@ pub struct IpPacketRouterPathsV7 {
// it's possible we might have to add credential storage here for return tickets
}
impl IpPacketRouterPathsV7 {
pub fn new<P: AsRef<Path>>(data_dir: P) -> Self {
let data_dir = data_dir.as_ref();
IpPacketRouterPathsV7 {
private_ed25519_identity_key_file: data_dir
.join(DEFAULT_ED25519_IPR_PRIVATE_IDENTITY_KEY_FILENAME),
public_ed25519_identity_key_file: data_dir
.join(DEFAULT_ED25519_IPR_PUBLIC_IDENTITY_KEY_FILENAME),
private_x25519_diffie_hellman_key_file: data_dir
.join(DEFAULT_X25519_IPR_PRIVATE_DH_KEY_FILENAME),
public_x25519_diffie_hellman_key_file: data_dir
.join(DEFAULT_X25519_IPR_PUBLIC_DH_KEY_FILENAME),
ack_key_file: data_dir.join(DEFAULT_IPR_ACK_KEY_FILENAME),
reply_surb_database: data_dir.join(DEFAULT_IPR_REPLY_SURB_DB_FILENAME),
gateway_registrations: data_dir.join(DEFAULT_IPR_GATEWAYS_DB_FILENAME),
}
}
pub fn to_common_client_paths(&self) -> CommonClientPaths {
CommonClientPaths {
keys: ClientKeysPaths {
private_identity_key_file: self.private_ed25519_identity_key_file.clone(),
public_identity_key_file: self.public_ed25519_identity_key_file.clone(),
private_encryption_key_file: self.private_x25519_diffie_hellman_key_file.clone(),
public_encryption_key_file: self.public_x25519_diffie_hellman_key_file.clone(),
ack_key_file: self.ack_key_file.clone(),
},
gateway_registrations: self.gateway_registrations.clone(),
// not needed for embedded providers
credentials_database: Default::default(),
reply_surb_database: self.reply_surb_database.clone(),
}
}
pub fn ed25519_identity_storage_paths(&self) -> nym_pemstore::KeyPairPath {
nym_pemstore::KeyPairPath::new(
&self.private_ed25519_identity_key_file,
&self.public_ed25519_identity_key_file,
)
}
pub fn x25519_diffie_hellman_storage_paths(&self) -> nym_pemstore::KeyPairPath {
nym_pemstore::KeyPairPath::new(
&self.private_x25519_diffie_hellman_key_file,
&self.public_x25519_diffie_hellman_key_file,
)
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct AuthenticatorPathsV7 {
@@ -861,56 +704,6 @@ pub struct AuthenticatorPathsV7 {
// it's possible we might have to add credential storage here for return tickets
}
impl AuthenticatorPathsV7 {
pub fn new<P: AsRef<Path>>(data_dir: P) -> Self {
let data_dir = data_dir.as_ref();
AuthenticatorPathsV7 {
private_ed25519_identity_key_file: data_dir
.join(DEFAULT_ED25519_AUTH_PRIVATE_IDENTITY_KEY_FILENAME),
public_ed25519_identity_key_file: data_dir
.join(DEFAULT_ED25519_AUTH_PUBLIC_IDENTITY_KEY_FILENAME),
private_x25519_diffie_hellman_key_file: data_dir
.join(DEFAULT_X25519_AUTH_PRIVATE_DH_KEY_FILENAME),
public_x25519_diffie_hellman_key_file: data_dir
.join(DEFAULT_X25519_AUTH_PUBLIC_DH_KEY_FILENAME),
ack_key_file: data_dir.join(DEFAULT_AUTH_ACK_KEY_FILENAME),
reply_surb_database: data_dir.join(DEFAULT_AUTH_REPLY_SURB_DB_FILENAME),
gateway_registrations: data_dir.join(DEFAULT_AUTH_GATEWAYS_DB_FILENAME),
}
}
pub fn to_common_client_paths(&self) -> CommonClientPaths {
CommonClientPaths {
keys: ClientKeysPaths {
private_identity_key_file: self.private_ed25519_identity_key_file.clone(),
public_identity_key_file: self.public_ed25519_identity_key_file.clone(),
private_encryption_key_file: self.private_x25519_diffie_hellman_key_file.clone(),
public_encryption_key_file: self.public_x25519_diffie_hellman_key_file.clone(),
ack_key_file: self.ack_key_file.clone(),
},
gateway_registrations: self.gateway_registrations.clone(),
// not needed for embedded providers
credentials_database: Default::default(),
reply_surb_database: self.reply_surb_database.clone(),
}
}
pub fn ed25519_identity_storage_paths(&self) -> nym_pemstore::KeyPairPath {
nym_pemstore::KeyPairPath::new(
&self.private_ed25519_identity_key_file,
&self.public_ed25519_identity_key_file,
)
}
pub fn x25519_diffie_hellman_storage_paths(&self) -> nym_pemstore::KeyPairPath {
nym_pemstore::KeyPairPath::new(
&self.private_x25519_diffie_hellman_key_file,
&self.public_x25519_diffie_hellman_key_file,
)
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ExitGatewayPathsV7 {
@@ -1106,53 +899,6 @@ pub struct GatewayTasksPathsV7 {
pub cosmos_mnemonic: PathBuf,
}
impl GatewayTasksPathsV7 {
pub fn new<P: AsRef<Path>>(data_dir: P) -> Self {
GatewayTasksPathsV7 {
clients_storage: data_dir.as_ref().join(DEFAULT_CLIENTS_STORAGE_FILENAME),
stats_storage: data_dir.as_ref().join(DEFAULT_STATS_STORAGE_FILENAME),
cosmos_mnemonic: data_dir.as_ref().join(DEFAULT_MNEMONIC_FILENAME),
}
}
pub fn load_mnemonic_from_file(&self) -> Result<Zeroizing<bip39::Mnemonic>, EntryGatewayError> {
let stringified =
Zeroizing::new(fs::read_to_string(&self.cosmos_mnemonic).map_err(|source| {
EntryGatewayError::MnemonicLoadFailure {
path: self.cosmos_mnemonic.clone(),
source,
}
})?);
Ok(Zeroizing::new(bip39::Mnemonic::parse::<&str>(
stringified.as_ref(),
)?))
}
pub fn save_mnemonic_to_file(
&self,
mnemonic: &bip39::Mnemonic,
) -> Result<(), EntryGatewayError> {
// wrapper for io errors
fn _save_to_file(path: &Path, mnemonic: &bip39::Mnemonic) -> io::Result<()> {
if let Some(parent) = path.parent() {
create_dir_all(parent)?;
}
info!("saving entry gateway mnemonic to '{}'", path.display());
let stringified = Zeroizing::new(mnemonic.to_string());
fs::write(path, &stringified)
}
_save_to_file(&self.cosmos_mnemonic, mnemonic).map_err(|source| {
EntryGatewayError::MnemonicSaveFailure {
path: self.cosmos_mnemonic.clone(),
source,
}
})
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct StaleMessageDebugV7 {
/// Specifies how often the clean-up task should check for stale data.
@@ -1258,19 +1004,6 @@ pub struct GatewayTasksConfigV7 {
pub debug: GatewayTasksConfigDebugV7,
}
impl GatewayTasksConfigV7 {
pub fn new_default<P: AsRef<Path>>(data_dir: P) -> Self {
GatewayTasksConfigV7 {
storage_paths: GatewayTasksPathsV7::new(data_dir),
enforce_zk_nyms: false,
bind_address: SocketAddr::new(in6addr_any_init(), DEFAULT_WS_PORT),
announce_ws_port: None,
announce_wss_port: None,
debug: Default::default(),
}
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ServiceProvidersPathsV7 {
@@ -1288,19 +1021,6 @@ pub struct ServiceProvidersPathsV7 {
pub authenticator: AuthenticatorPathsV7,
}
impl ServiceProvidersPathsV7 {
pub fn new<P: AsRef<Path>>(data_dir: P) -> Self {
let data_dir = data_dir.as_ref();
ServiceProvidersPathsV7 {
clients_storage: data_dir.join(DEFAULT_CLIENTS_STORAGE_FILENAME),
stats_storage: data_dir.join(DEFAULT_STATS_STORAGE_FILENAME),
network_requester: NetworkRequesterPathsV7::new(data_dir),
ip_packet_router: IpPacketRouterPathsV7::new(data_dir),
authenticator: AuthenticatorPathsV7::new(data_dir),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ServiceProvidersConfigDebugV7 {
@@ -1342,25 +1062,6 @@ pub struct ServiceProvidersConfigV7 {
pub debug: ServiceProvidersConfigDebugV7,
}
impl ServiceProvidersConfigV7 {
pub fn new_default<P: AsRef<Path>>(data_dir: P) -> Self {
#[allow(clippy::expect_used)]
// SAFETY:
// we expect our default values to be well-formed
ServiceProvidersConfigV7 {
storage_paths: ServiceProvidersPathsV7::new(data_dir),
open_proxy: false,
upstream_exit_policy_url: mainnet::EXIT_POLICY_URL
.parse()
.expect("invalid default exit policy URL"),
network_requester: Default::default(),
ip_packet_router: Default::default(),
authenticator: Default::default(),
debug: Default::default(),
}
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct MetricsConfigV7 {
@@ -1498,7 +1199,7 @@ impl ConfigV7 {
pub async fn try_upgrade_config_v7<P: AsRef<Path>>(
path: P,
prev_config: Option<ConfigV7>,
) -> Result<Config, NymNodeError> {
) -> Result<ConfigV8, NymNodeError> {
debug!("attempting to load v7 config...");
let old_cfg = if let Some(prev_config) = prev_config {
@@ -1507,20 +1208,20 @@ pub async fn try_upgrade_config_v7<P: AsRef<Path>>(
ConfigV7::read_from_path(&path)?
};
let cfg = Config {
let cfg = ConfigV8 {
save_path: old_cfg.save_path,
id: old_cfg.id,
modes: NodeModes {
modes: NodeModesV8 {
mixnode: old_cfg.modes.mixnode,
entry: old_cfg.modes.entry,
exit: old_cfg.modes.exit,
},
host: Host {
host: HostV8 {
public_ips: old_cfg.host.public_ips,
hostname: old_cfg.host.hostname,
location: old_cfg.host.location,
},
mixnet: Mixnet {
mixnet: MixnetV8 {
bind_address: {
if old_cfg.mixnet.bind_address.ip().is_unspecified() {
SocketAddr::new(in6addr_any_init(), old_cfg.mixnet.bind_address.port())
@@ -1531,7 +1232,7 @@ pub async fn try_upgrade_config_v7<P: AsRef<Path>>(
announce_port: old_cfg.mixnet.announce_port,
nym_api_urls: old_cfg.mixnet.nym_api_urls,
nyxd_urls: old_cfg.mixnet.nyxd_urls,
debug: MixnetDebug {
debug: MixnetDebugV8 {
maximum_forward_packet_delay: old_cfg.mixnet.debug.maximum_forward_packet_delay,
packet_forwarding_initial_backoff: old_cfg
.mixnet
@@ -1546,8 +1247,8 @@ pub async fn try_upgrade_config_v7<P: AsRef<Path>>(
unsafe_disable_noise: old_cfg.mixnet.debug.unsafe_disable_noise,
},
},
storage_paths: NymNodePaths {
keys: KeysPaths {
storage_paths: NymNodePathsV8 {
keys: KeysPathsV8 {
private_ed25519_identity_key_file: old_cfg
.storage_paths
.keys
@@ -1575,7 +1276,7 @@ pub async fn try_upgrade_config_v7<P: AsRef<Path>>(
},
description: old_cfg.storage_paths.description,
},
http: Http {
http: HttpV8 {
bind_address: {
if old_cfg.http.bind_address.ip().is_unspecified() {
SocketAddr::new(in6addr_any_init(), old_cfg.http.bind_address.port())
@@ -1590,7 +1291,7 @@ pub async fn try_upgrade_config_v7<P: AsRef<Path>>(
expose_crypto_hardware: old_cfg.http.expose_crypto_hardware,
..Default::default()
},
verloc: Verloc {
verloc: VerlocV8 {
bind_address: {
if old_cfg.verloc.bind_address.ip().is_unspecified() {
SocketAddr::new(in6addr_any_init(), old_cfg.verloc.bind_address.port())
@@ -1599,7 +1300,7 @@ pub async fn try_upgrade_config_v7<P: AsRef<Path>>(
}
},
announce_port: old_cfg.verloc.announce_port,
debug: VerlocDebug {
debug: VerlocDebugV8 {
packets_per_node: old_cfg.verloc.debug.packets_per_node,
connection_timeout: old_cfg.verloc.debug.connection_timeout,
packet_timeout: old_cfg.verloc.debug.packet_timeout,
@@ -1609,7 +1310,7 @@ pub async fn try_upgrade_config_v7<P: AsRef<Path>>(
retry_timeout: old_cfg.verloc.debug.retry_timeout,
},
},
wireguard: Wireguard {
wireguard: WireguardV8 {
enabled: old_cfg.wireguard.enabled,
bind_address: {
if old_cfg.wireguard.bind_address.ip().is_unspecified() {
@@ -1623,7 +1324,7 @@ pub async fn try_upgrade_config_v7<P: AsRef<Path>>(
announced_port: old_cfg.wireguard.announced_port,
private_network_prefix_v4: old_cfg.wireguard.private_network_prefix_v4,
private_network_prefix_v6: old_cfg.wireguard.private_network_prefix_v6,
storage_paths: WireguardPaths {
storage_paths: WireguardPathsV8 {
private_diffie_hellman_key_file: old_cfg
.wireguard
.storage_paths
@@ -1634,8 +1335,8 @@ pub async fn try_upgrade_config_v7<P: AsRef<Path>>(
.public_diffie_hellman_key_file,
},
},
gateway_tasks: GatewayTasksConfig {
storage_paths: GatewayTasksPaths {
gateway_tasks: GatewayTasksConfigV8 {
storage_paths: GatewayTasksPathsV8 {
clients_storage: old_cfg.gateway_tasks.storage_paths.clients_storage,
stats_storage: old_cfg.gateway_tasks.storage_paths.stats_storage,
cosmos_mnemonic: old_cfg.gateway_tasks.storage_paths.cosmos_mnemonic,
@@ -1653,9 +1354,9 @@ pub async fn try_upgrade_config_v7<P: AsRef<Path>>(
},
announce_ws_port: old_cfg.gateway_tasks.announce_ws_port,
announce_wss_port: old_cfg.gateway_tasks.announce_wss_port,
debug: gateway_tasks::Debug {
debug: GatewayTasksConfigDebugV8 {
message_retrieval_limit: old_cfg.gateway_tasks.debug.message_retrieval_limit,
zk_nym_tickets: ZkNymTicketHandlerDebug {
zk_nym_tickets: ZkNymTicketHandlerDebugV8 {
revocation_bandwidth_penalty: old_cfg
.gateway_tasks
.debug
@@ -1681,11 +1382,11 @@ pub async fn try_upgrade_config_v7<P: AsRef<Path>>(
..Default::default()
},
},
service_providers: ServiceProvidersConfig {
storage_paths: ServiceProvidersPaths {
service_providers: ServiceProvidersConfigV8 {
storage_paths: ServiceProvidersPathsV8 {
clients_storage: old_cfg.service_providers.storage_paths.clients_storage,
stats_storage: old_cfg.service_providers.storage_paths.stats_storage,
network_requester: NetworkRequesterPaths {
network_requester: NetworkRequesterPathsV8 {
private_ed25519_identity_key_file: old_cfg
.service_providers
.storage_paths
@@ -1722,7 +1423,7 @@ pub async fn try_upgrade_config_v7<P: AsRef<Path>>(
.network_requester
.gateway_registrations,
},
ip_packet_router: IpPacketRouterPaths {
ip_packet_router: IpPacketRouterPathsV8 {
private_ed25519_identity_key_file: old_cfg
.service_providers
.storage_paths
@@ -1759,7 +1460,7 @@ pub async fn try_upgrade_config_v7<P: AsRef<Path>>(
.ip_packet_router
.gateway_registrations,
},
authenticator: AuthenticatorPaths {
authenticator: AuthenticatorPathsV8 {
private_ed25519_identity_key_file: old_cfg
.service_providers
.storage_paths
@@ -1799,8 +1500,8 @@ pub async fn try_upgrade_config_v7<P: AsRef<Path>>(
},
open_proxy: old_cfg.service_providers.open_proxy,
upstream_exit_policy_url: old_cfg.service_providers.upstream_exit_policy_url,
network_requester: NetworkRequester {
debug: NetworkRequesterDebug {
network_requester: NetworkRequesterV8 {
debug: NetworkRequesterDebugV8 {
enabled: old_cfg.service_providers.network_requester.debug.enabled,
disable_poisson_rate: old_cfg
.service_providers
@@ -1814,8 +1515,8 @@ pub async fn try_upgrade_config_v7<P: AsRef<Path>>(
.client_debug,
},
},
ip_packet_router: IpPacketRouter {
debug: IpPacketRouterDebug {
ip_packet_router: IpPacketRouterV8 {
debug: IpPacketRouterDebugV8 {
enabled: old_cfg.service_providers.ip_packet_router.debug.enabled,
disable_poisson_rate: old_cfg
.service_providers
@@ -1829,8 +1530,8 @@ pub async fn try_upgrade_config_v7<P: AsRef<Path>>(
.client_debug,
},
},
authenticator: Authenticator {
debug: AuthenticatorDebug {
authenticator: AuthenticatorV8 {
debug: AuthenticatorDebugV8 {
enabled: old_cfg.service_providers.authenticator.debug.enabled,
disable_poisson_rate: old_cfg
.service_providers
@@ -1840,12 +1541,12 @@ pub async fn try_upgrade_config_v7<P: AsRef<Path>>(
client_debug: old_cfg.service_providers.authenticator.debug.client_debug,
},
},
debug: service_providers::Debug {
debug: ServiceProvidersConfigDebugV8 {
message_retrieval_limit: old_cfg.service_providers.debug.message_retrieval_limit,
},
},
metrics: Default::default(),
logging: LoggingSettings {},
logging: LoggingSettingsV8 {},
debug: Default::default(),
};
Ok(cfg)
File diff suppressed because it is too large Load Diff
+35
View File
@@ -55,6 +55,12 @@ pub const DEFAULT_AUTH_GATEWAYS_DB_FILENAME: &str = "auth_gateways_info_store.sq
pub const DEFAULT_X25519_WG_DH_KEY_FILENAME: &str = "x25519_wg_dh";
pub const DEFAULT_X25519_WG_PUBLIC_DH_KEY_FILENAME: &str = "x25519_wg_dh.pub";
// Replay Detection
pub const DEFAULT_RD_BLOOMFILTER_SUBDIR: &str = "replay-detection";
pub const DEFAULT_RD_BLOOMFILTER_FILE_EXT: &str = "bloom";
pub const DEFAULT_RD_BLOOMFILTER_FLUSH_FILE_EXT: &str = "flush";
pub const CURRENT_RD_BLOOMFILTER_FILENAME: &str = "current";
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct NymNodePaths {
@@ -490,3 +496,32 @@ impl WireguardPaths {
)
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ReplayProtectionPaths {
/// Path to the directory storing currently used bloomfilter(s).
pub current_bloomfilters_directory: PathBuf,
}
impl ReplayProtectionPaths {
pub fn current_bloomfilter_filepath(&self) -> PathBuf {
self.current_bloomfilters_directory
.join(CURRENT_RD_BLOOMFILTER_FILENAME)
.with_extension(DEFAULT_RD_BLOOMFILTER_FILE_EXT)
}
pub fn current_bloomfilter_being_flushed_filepath(&self) -> PathBuf {
self.current_bloomfilters_directory
.join(CURRENT_RD_BLOOMFILTER_FILENAME)
.with_extension(DEFAULT_RD_BLOOMFILTER_FLUSH_FILE_EXT)
}
}
impl ReplayProtectionPaths {
pub fn new<P: AsRef<Path>>(data_dir: P) -> Self {
ReplayProtectionPaths {
current_bloomfilters_directory: data_dir.as_ref().join(DEFAULT_RD_BLOOMFILTER_SUBDIR),
}
}
}
+6
View File
@@ -65,6 +65,12 @@ nyxd_urls = [
{{#each mixnet.nyxd_urls }}'{{this}}',{{/each}}
]
[mixnet.replay_protection]
[mixnet.replay_protection.storage_paths]
# Path to the directory storing currently used bloomfilter(s).
current_bloomfilters_directory = '{{ mixnet.replay_protection.storage_paths.current_bloomfilters_directory }}'
# Storage paths to persistent nym-node data, such as its long term keys.
[storage_paths]
+5 -2
View File
@@ -15,7 +15,8 @@ async fn try_upgrade_config(path: &Path) -> Result<(), NymNodeError> {
let cfg = try_upgrade_config_v4(path, cfg).await.ok();
let cfg = try_upgrade_config_v5(path, cfg).await.ok();
let cfg = try_upgrade_config_v6(path, cfg).await.ok();
match try_upgrade_config_v7(path, cfg).await {
let cfg = try_upgrade_config_v7(path, cfg).await.ok();
match try_upgrade_config_v8(path, cfg).await {
Ok(cfg) => cfg.save(),
Err(e) => {
tracing::error!("Failed to finish upgrade - {e}");
@@ -35,5 +36,7 @@ pub async fn try_load_current_config<P: AsRef<Path>>(
}
try_upgrade_config(config_path.as_ref()).await?;
Config::read_from_toml_file(config_path)
let loaded = Config::read_from_toml_file(config_path)?;
loaded.validate()?;
Ok(loaded)
}
+1
View File
@@ -40,6 +40,7 @@ pub mod vars {
pub const NYMNODE_NYM_APIS_ARG: &str = "NYMNODE_NYM_APIS";
pub const NYMNODE_NYXD_URLS_ARG: &str = "NYMNODE_NYXD";
pub const NYMNODE_UNSAFE_DISABLE_NOISE: &str = "UNSAFE_DISABLE_NOISE";
pub const NYMNODE_UNSAFE_DISABLE_REPLAY_PROTECTION: &str = "UNSAFE_DISABLE_REPLAY_PROTECTION";
// wireguard:
pub const NYMNODE_WG_ENABLED_ARG: &str = "NYMNODE_WG_ENABLED";
+21
View File
@@ -82,6 +82,9 @@ pub enum NymNodeError {
source: io::Error,
},
#[error("failed to validate loaded config: {error}")]
ConfigValidationFailure { error: String },
#[error("the node description file is malformed: {source}")]
MalformedDescriptionFile {
#[source]
@@ -148,6 +151,12 @@ pub enum NymNodeError {
)]
InitialTopologyQueryFailure { source: ValidatorClientError },
#[error("experienced critical failure with the replay detection bloomfilter: {message}")]
BloomfilterFailure { message: &'static str },
#[error("failed to save/load the bloomfilter: {source} using path: {}", path.display())]
BloomfilterIoFailure { source: io::Error, path: PathBuf },
#[error(transparent)]
GatewayFailure(#[from] nym_gateway::GatewayError),
@@ -168,6 +177,18 @@ pub enum NymNodeError {
FailedUpgrade,
}
impl NymNodeError {
pub fn config_validation_failure<S: Into<String>>(error: S) -> Self {
NymNodeError::ConfigValidationFailure {
error: error.into(),
}
}
pub fn bloomfilter_failure(message: &'static str) -> Self {
NymNodeError::BloomfilterFailure { message }
}
}
#[derive(Debug, Error)]
pub enum EntryGatewayError {
#[error(transparent)]
+16 -17
View File
@@ -1,35 +1,34 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use tracing::level_filters::LevelFilter;
use tracing_subscriber::{filter::Directive, EnvFilter};
use nym_bin_common::logging::{default_tracing_env_filter, default_tracing_fmt_layer};
use tracing_subscriber::filter::Directive;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
pub(crate) fn setup_tracing_logger() -> anyhow::Result<()> {
pub(crate) fn granual_filtered_env() -> anyhow::Result<tracing_subscriber::filter::EnvFilter> {
fn directive_checked(directive: impl Into<String>) -> anyhow::Result<Directive> {
directive.into().parse().map_err(From::from)
}
let log_builder = tracing_subscriber::fmt()
// Use a more compact, abbreviated log format
.compact()
// Display source code file paths
.with_file(true)
// Display source code line numbers
.with_line_number(true)
// Don't display the event's target (module path)
.with_target(false);
let mut filter = default_tracing_env_filter();
let mut filter = EnvFilter::builder()
// if RUST_LOG isn't set, set default level
.with_default_directive(LevelFilter::INFO.into())
.from_env_lossy();
// these crates are more granularly filtered
let filter_crates = ["defguard_wireguard_rs"];
for crate_name in filter_crates {
filter = filter.add_directive(directive_checked(format!("{}=warn", crate_name))?);
}
Ok(filter)
}
log_builder.with_env_filter(filter).init();
pub(crate) fn build_tracing_logger() -> anyhow::Result<impl SubscriberExt> {
Ok(tracing_subscriber::registry()
.with(default_tracing_fmt_layer(std::io::stderr))
.with(granual_filtered_env()?))
}
pub(crate) fn setup_tracing_logger() -> anyhow::Result<()> {
build_tracing_logger()?.init();
Ok(())
}
+4 -5
View File
@@ -4,7 +4,7 @@
#![warn(clippy::expect_used)]
#![warn(clippy::unwrap_used)]
use crate::{cli::Cli, logging::setup_tracing_logger};
use crate::cli::Cli;
use clap::{crate_name, crate_version, Parser};
use nym_bin_common::logging::maybe_print_banner;
use nym_config::defaults::setup_env;
@@ -15,10 +15,10 @@ mod env;
pub(crate) mod error;
mod logging;
pub(crate) mod node;
pub(crate) mod throughput_tester;
pub(crate) mod wireguard;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
fn main() -> anyhow::Result<()> {
// std::env::set_var(
// "RUST_LOG",
// "trace,handlebars=warn,tendermint_rpc=warn,h2=warn,hyper=warn,rustls=warn,reqwest=warn,tungstenite=warn,async_tungstenite=warn,tokio_util=warn,tokio_tungstenite=warn,tokio-util=warn",
@@ -26,13 +26,12 @@ async fn main() -> anyhow::Result<()> {
let cli = Cli::parse();
setup_env(cli.config_env_file.as_ref());
setup_tracing_logger()?;
if !cli.no_banner {
maybe_print_banner(crate_name!(), crate_version!());
}
cli.execute().await?;
cli.execute()?;
Ok(())
}
@@ -38,6 +38,7 @@ fn build_response(metrics: &NymNodeMetrics) -> PacketsStats {
forward_hop_packets_received: metrics.mixnet.ingress.forward_hop_packets_received(),
final_hop_packets_received: metrics.mixnet.ingress.final_hop_packets_received(),
malformed_packets_received: metrics.mixnet.ingress.malformed_packets_received(),
replayed_packets_received: metrics.mixnet.ingress.replayed_packets_received(),
excessive_delay_packets: metrics.mixnet.ingress.excessive_delay_packets(),
forward_hop_packets_dropped: metrics.mixnet.ingress.forward_hop_packets_dropped(),
final_hop_packets_dropped: metrics.mixnet.ingress.final_hop_packets_dropped(),
@@ -64,6 +64,10 @@ impl OnUpdateMetricsHandler for PrometheusGlobalNodeMetricsRegistryUpdater {
MixnetIngressMalformedPacketsReceived,
self.metrics.mixnet.ingress.malformed_packets_received() as i64,
);
self.prometheus_wrapper.set(
MixnetIngressReplayedPacketsReceived,
self.metrics.mixnet.ingress.replayed_packets_received() as i64,
);
self.prometheus_wrapper.set(
MixnetIngressExcessiveDelayPacketsReceived,
self.metrics.mixnet.ingress.excessive_delay_packets() as i64,
+233 -32
View File
@@ -3,24 +3,69 @@
use crate::node::mixnet::shared::SharedData;
use futures::StreamExt;
use nym_metrics::nanos;
use nym_sphinx_forwarding::packet::MixPacket;
use nym_sphinx_framing::codec::NymCodec;
use nym_sphinx_framing::packet::FramedNymPacket;
use nym_sphinx_framing::processing::{
process_framed_packet, MixProcessingResultData, ProcessedFinalHop,
process_framed_packet, MixProcessingResult, MixProcessingResultData, PacketProcessingError,
PartiallyUnwrappedPacket, ProcessedFinalHop,
};
use nym_sphinx_types::Delay;
use nym_sphinx_types::{Delay, REPLAY_TAG_SIZE};
use std::mem;
use std::net::SocketAddr;
use tokio::net::TcpStream;
use tokio::time::Instant;
use tokio_util::codec::Framed;
use tracing::{debug, error, instrument, trace};
use tracing::{debug, error, instrument, trace, warn};
struct PendingReplayCheckPackets {
packets: Vec<PartiallyUnwrappedPacket>,
last_acquired_mutex: Instant,
}
impl PendingReplayCheckPackets {
fn new() -> PendingReplayCheckPackets {
PendingReplayCheckPackets {
packets: vec![],
last_acquired_mutex: Instant::now(),
}
}
fn reset(&mut self, now: Instant) -> Vec<PartiallyUnwrappedPacket> {
self.last_acquired_mutex = now;
mem::take(&mut self.packets)
}
fn push(&mut self, now: Instant, packet: PartiallyUnwrappedPacket) {
if self.packets.is_empty() {
self.last_acquired_mutex = now;
}
self.packets.push(packet);
}
fn replay_tags(&self) -> Vec<&[u8; REPLAY_TAG_SIZE]> {
let mut replay_tags = Vec::with_capacity(self.packets.len());
for packet in &self.packets {
let Some(replay_tag) = packet.replay_tag() else {
error!(
"corrupted batch of {} packets - replay tag was missing",
self.packets.len()
);
return Vec::new();
};
replay_tags.push(replay_tag);
}
replay_tags
}
}
pub(crate) struct ConnectionHandler {
shared: SharedData,
mixnet_connection: Framed<TcpStream, NymCodec>,
remote_address: SocketAddr,
// packets pending for replay detection
pending_packets: PendingReplayCheckPackets,
}
impl Drop for ConnectionHandler {
@@ -45,6 +90,7 @@ impl ConnectionHandler {
shared: SharedData {
processing_config: shared.processing_config,
sphinx_keys: shared.sphinx_keys.clone(),
replay_protection_filter: shared.replay_protection_filter.clone(),
mixnet_forwarder: shared.mixnet_forwarder.clone(),
final_hop: shared.final_hop.clone(),
metrics: shared.metrics.clone(),
@@ -52,6 +98,7 @@ impl ConnectionHandler {
},
remote_address,
mixnet_connection: Framed::new(tcp_stream, NymCodec),
pending_packets: PendingReplayCheckPackets::new(),
}
}
@@ -60,9 +107,8 @@ impl ConnectionHandler {
/// the skew caused by being stuck in the channel queue.
/// This method also clamps the maximum allowed delay so that nobody could send a bunch of packets
/// with, for example, delays of 1 year thus causing denial of service
fn create_delay_target(&self, delay: Option<Delay>) -> Option<Instant> {
fn create_delay_target(&self, now: Instant, delay: Option<Delay>) -> Option<Instant> {
let delay = delay?.to_duration();
let now = Instant::now();
let delay = if delay > self.shared.processing_config.maximum_packet_delay {
self.shared.processing_config.maximum_packet_delay
@@ -77,14 +123,14 @@ impl ConnectionHandler {
Some(now + delay)
}
fn handle_forward_packet(&self, mix_packet: MixPacket, delay: Option<Delay>) {
fn handle_forward_packet(&self, now: Instant, mix_packet: MixPacket, delay: Option<Delay>) {
if !self.shared.processing_config.forward_hop_processing_enabled {
trace!("this nym-node does not support forward hop packets");
self.shared.dropped_forward_packet(self.remote_address.ip());
return;
}
let forward_instant = self.create_delay_target(delay);
let forward_instant = self.create_delay_target(now, delay);
self.shared.forward_mix_packet(mix_packet, forward_instant);
}
@@ -128,33 +174,188 @@ impl ConnectionHandler {
self.shared.forward_ack_packet(final_hop_data.forward_ack);
}
#[instrument(skip(self, packet), level = "debug")]
async fn handle_received_nym_packet(&self, packet: FramedNymPacket) {
// TODO: here be replay attack detection with bloomfilters and all the fancy stuff
//
fn within_deferral_threshold(&self, now: Instant) -> bool {
let time_threshold = now
.saturating_duration_since(self.pending_packets.last_acquired_mutex)
<= self
.shared
.processing_config
.maximum_replay_detection_deferral;
nanos!("handle_received_nym_packet", {
// 1. attempt to unwrap the packet
let count_threshold = self.pending_packets.packets.len()
< self
.shared
.processing_config
.maximum_replay_detection_pending_packets;
// time threshold is ignored if we currently have 0 packets queued up
if self.pending_packets.packets.is_empty() {
return true;
}
trace!(
"within deferral time threshold: {time_threshold}, count threshold: {count_threshold}"
);
if !time_threshold {
warn!(
"{}: time failure - {}",
self.remote_address,
self.pending_packets.packets.len()
)
}
if !count_threshold {
warn!("{}, count failure", self.remote_address)
}
time_threshold && count_threshold
}
async fn handle_received_packet_with_replay_detection(
&mut self,
now: Instant,
packet: FramedNymPacket,
) {
// 1. derive and expand shared secret
// also check the header integrity
let partially_unwrapped = match PartiallyUnwrappedPacket::new(
packet,
self.shared.sphinx_keys.private_key().as_ref(),
) {
Ok(unwrapped) => unwrapped,
Err(err) => {
trace!("failed to process received mix packet: {err}");
self.shared
.metrics
.mixnet
.ingress_malformed_packet(self.remote_address.ip());
return;
}
};
self.pending_packets.push(now, partially_unwrapped);
// 2. check for packet replay
// 2.1 first try it without locking
if self.handle_pending_packets_batch_no_locking(now).await {
return;
}
// 2.2 if we're within deferral threshold, just leave it queued up for another call
if self.within_deferral_threshold(now) {
return;
}
// 2.3. otherwise block until we obtain the lock and clear the whole batch
self.handle_pending_packets_batch(now).await;
}
async fn handle_unwrapped_packet(
&self,
now: Instant,
unwrapped_packet: Result<MixProcessingResult, PacketProcessingError>,
) {
// 2. increment our favourite metrics stats
self.shared
.update_metrics(&unwrapped_packet, self.remote_address.ip());
// 3. forward the packet to the relevant sink (if enabled)
match unwrapped_packet {
Err(err) => trace!("failed to process received mix packet: {err}"),
Ok(processed_packet) => match processed_packet.processing_data {
MixProcessingResultData::ForwardHop { packet, delay } => {
self.handle_forward_packet(now, packet, delay);
}
MixProcessingResultData::FinalHop { final_hop_data } => {
self.handle_final_hop(final_hop_data).await;
}
},
}
}
async fn handle_post_replay_detection_packets(
&self,
now: Instant,
packets: Vec<PartiallyUnwrappedPacket>,
replay_check_results: Vec<bool>,
) {
for (packet, replayed) in packets.into_iter().zip(replay_check_results) {
let unwrapped_packet = if replayed {
Err(PacketProcessingError::PacketReplay)
} else {
packet.finalise_unwrapping()
};
self.handle_unwrapped_packet(now, unwrapped_packet).await;
}
}
async fn handle_pending_packets_batch_no_locking(&mut self, now: Instant) -> bool {
let replay_tags = self.pending_packets.replay_tags();
if replay_tags.is_empty() {
return false;
}
let replay_check_results = match self
.shared
.replay_protection_filter
.batch_try_check_and_set(&replay_tags)
{
None => return false,
Some(Ok(replay_check_results)) => replay_check_results,
Some(Err(_)) => {
// our mutex got poisoned - we have to shut down
error!("CRITICAL FAILURE: replay bloomfilter mutex poisoning!");
self.shared.shutdown.cancel();
return false;
}
};
let batch = self.pending_packets.reset(now);
self.handle_post_replay_detection_packets(now, batch, replay_check_results)
.await;
true
}
async fn handle_pending_packets_batch(&mut self, now: Instant) {
let batch = self.pending_packets.reset(now);
let replay_tags = self.pending_packets.replay_tags();
if replay_tags.is_empty() {
return;
}
let Ok(replay_check_results) = self
.shared
.replay_protection_filter
.batch_check_and_set(&replay_tags)
else {
// our mutex got poisoned - we have to shut down
error!("CRITICAL FAILURE: replay bloomfilter mutex poisoning!");
self.shared.shutdown.cancel();
return;
};
self.handle_post_replay_detection_packets(now, batch, replay_check_results)
.await;
}
#[instrument(skip(self, packet), level = "debug")]
async fn handle_received_nym_packet(&mut self, packet: FramedNymPacket) {
let now = Instant::now();
// 1. attempt to unwrap the packet
// if it's a sphinx packet attempt to do pre-processing and replay detection
if packet.is_sphinx() && !self.shared.replay_protection_filter.disabled() {
self.handle_received_packet_with_replay_detection(now, packet)
.await;
} else {
// otherwise just skip that whole procedure and go straight to payload unwrapping
// (assuming the basic framing is valid)
let unwrapped_packet =
process_framed_packet(packet, self.shared.sphinx_keys.private_key().as_ref());
// 2. increment our favourite metrics stats
self.shared
.update_metrics(&unwrapped_packet, self.remote_address.ip());
// 3. forward the packet to the relevant sink (if enabled)
match unwrapped_packet {
Err(err) => trace!("failed to process received mix packet: {err}"),
Ok(processed_packet) => match processed_packet.processing_data {
MixProcessingResultData::ForwardHop { packet, delay } => {
self.handle_forward_packet(packet, delay);
}
MixProcessingResultData::FinalHop { final_hop_data } => {
self.handle_final_hop(final_hop_data).await;
}
},
}
})
self.handle_unwrapped_packet(now, unwrapped_packet).await;
};
}
#[instrument(
@@ -1,8 +1,7 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::node::mixnet::packet_forwarding::global::is_global_ip;
use crate::node::shared_network::RoutingFilter;
use crate::node::routing_filter::RoutingFilter;
use futures::StreamExt;
use nym_mixnet_client::forwarder::{
mix_forwarding_channels, MixForwardingReceiver, MixForwardingSender, PacketToForward,
@@ -13,38 +12,33 @@ use nym_nonexhaustive_delayqueue::{Expired, NonExhaustiveDelayQueue};
use nym_sphinx_forwarding::packet::MixPacket;
use nym_task::ShutdownToken;
use std::io;
use std::net::IpAddr;
use tokio::time::Instant;
use tracing::{debug, error, trace, warn};
pub(crate) mod global;
pub struct PacketForwarder<C> {
testnet: bool,
pub struct PacketForwarder<C, F> {
delay_queue: NonExhaustiveDelayQueue<MixPacket>,
mixnet_client: C,
metrics: NymNodeMetrics,
routing_filter: RoutingFilter,
routing_filter: F,
packet_sender: MixForwardingSender,
packet_receiver: MixForwardingReceiver,
shutdown: ShutdownToken,
}
impl<C> PacketForwarder<C> {
impl<C, F> PacketForwarder<C, F> {
pub fn new(
client: C,
testnet: bool,
routing_filter: RoutingFilter,
routing_filter: F,
metrics: NymNodeMetrics,
shutdown: ShutdownToken,
) -> Self {
let (packet_sender, packet_receiver) = mix_forwarding_channels();
PacketForwarder {
testnet,
delay_queue: NonExhaustiveDelayQueue::new(),
mixnet_client: client,
metrics,
@@ -59,29 +53,13 @@ impl<C> PacketForwarder<C> {
self.packet_sender.clone()
}
fn should_route(&mut self, ip_addr: IpAddr) -> bool {
// only allow non-global ips on testnets
if self.testnet && !is_global_ip(&ip_addr) {
return true;
}
self.routing_filter.attempt_resolve(ip_addr).should_route()
}
fn forward_packet(&mut self, packet: MixPacket)
where
C: SendWithoutResponse,
F: RoutingFilter,
{
let next_hop = packet.next_hop();
if !self.should_route(next_hop.as_ref().ip()) {
debug!("dropping packet as the egress address does not belong to any known node");
self.metrics
.mixnet
.egress_dropped_forward_packet(next_hop.into());
return;
}
let packet_type = packet.packet_type();
let packet = packet.into_packet();
@@ -113,6 +91,7 @@ impl<C> PacketForwarder<C> {
fn handle_done_delaying(&mut self, packet: Expired<MixPacket>)
where
C: SendWithoutResponse,
F: RoutingFilter,
{
let delayed_packet = packet.into_inner();
self.forward_packet(delayed_packet);
@@ -121,7 +100,18 @@ impl<C> PacketForwarder<C> {
fn handle_new_packet(&mut self, new_packet: PacketToForward)
where
C: SendWithoutResponse,
F: RoutingFilter,
{
let next_hop = new_packet.packet.next_hop();
if !self.routing_filter.should_route(next_hop.as_ref().ip()) {
debug!("dropping packet as the egress address does not belong to any known node");
self.metrics
.mixnet
.egress_dropped_forward_packet(next_hop.into());
return;
}
// in case of a zero delay packet, don't bother putting it in the delay queue,
// just forward it immediately
if let Some(instant) = new_packet.forward_delay_target {
@@ -152,6 +142,7 @@ impl<C> PacketForwarder<C> {
pub async fn run(&mut self)
where
C: SendWithoutResponse,
F: RoutingFilter,
{
let mut processed = 0;
trace!("starting PacketForwarder");
+21
View File
@@ -4,6 +4,7 @@
use crate::config::Config;
use crate::node::mixnet::handler::ConnectionHandler;
use crate::node::mixnet::SharedFinalHopData;
use crate::node::replay_protection::bloomfilter::ReplayProtectionBloomfilter;
use nym_crypto::asymmetric::x25519;
use nym_gateway::node::GatewayStorageError;
use nym_mixnet_client::forwarder::{MixForwardingSender, PacketToForward};
@@ -29,6 +30,13 @@ pub(crate) mod final_hop;
#[derive(Clone, Copy)]
pub(crate) struct ProcessingConfig {
pub(crate) maximum_packet_delay: Duration,
/// how long the task is willing to skip mutex acquisition before it will block the thread
/// until it actually obtains it
pub(crate) maximum_replay_detection_deferral: Duration,
/// how many packets the task is willing to queue before it will block the thread
/// until it obtains the mutex
pub(crate) maximum_replay_detection_pending_packets: usize,
pub(crate) forward_hop_processing_enabled: bool,
pub(crate) final_hop_processing_enabled: bool,
@@ -38,6 +46,16 @@ impl ProcessingConfig {
pub(crate) fn new(config: &Config) -> Self {
ProcessingConfig {
maximum_packet_delay: config.mixnet.debug.maximum_forward_packet_delay,
maximum_replay_detection_deferral: config
.mixnet
.replay_protection
.debug
.maximum_replay_detection_deferral,
maximum_replay_detection_pending_packets: config
.mixnet
.replay_protection
.debug
.maximum_replay_detection_pending_packets,
forward_hop_processing_enabled: config.modes.mixnode,
final_hop_processing_enabled: config.modes.expects_final_hop_traffic()
|| config.wireguard.enabled,
@@ -49,6 +67,7 @@ impl ProcessingConfig {
pub(crate) struct SharedData {
pub(super) processing_config: ProcessingConfig,
pub(super) sphinx_keys: Arc<x25519::KeyPair>,
pub(super) replay_protection_filter: ReplayProtectionBloomfilter,
// used for FORWARD mix packets and FINAL ack packets
pub(super) mixnet_forwarder: MixForwardingSender,
@@ -71,6 +90,7 @@ impl SharedData {
pub(crate) fn new(
processing_config: ProcessingConfig,
x25519_keys: Arc<x25519::KeyPair>,
replay_protection_filter: ReplayProtectionBloomfilter,
mixnet_forwarder: MixForwardingSender,
final_hop: SharedFinalHopData,
metrics: NymNodeMetrics,
@@ -79,6 +99,7 @@ impl SharedData {
SharedData {
processing_config,
sphinx_keys: x25519_keys,
replay_protection_filter,
mixnet_forwarder,
final_hop,
metrics,
+68 -13
View File
@@ -28,9 +28,10 @@ use crate::node::metrics::handler::pending_egress_packets_updater::PendingEgress
use crate::node::mixnet::packet_forwarding::PacketForwarder;
use crate::node::mixnet::shared::ProcessingConfig;
use crate::node::mixnet::SharedFinalHopData;
use crate::node::shared_network::{
CachedNetwork, CachedTopologyProvider, NetworkRefresher, RoutingFilter,
};
use crate::node::replay_protection::background_task::ReplayProtectionBackgroundTask;
use crate::node::replay_protection::bloomfilter::ReplayProtectionBloomfilter;
use crate::node::routing_filter::{OpenFilter, RoutingFilter};
use crate::node::shared_network::{CachedNetwork, CachedTopologyProvider, NetworkRefresher};
use nym_bin_common::bin_info;
use nym_crypto::asymmetric::{ed25519, x25519};
use nym_gateway::node::{ActiveClientsStore, GatewayTasksBuilder};
@@ -69,6 +70,8 @@ pub mod helpers;
pub(crate) mod http;
pub(crate) mod metrics;
pub(crate) mod mixnet;
pub(crate) mod replay_protection;
mod routing_filter;
mod shared_network;
pub struct GatewayTasksData {
@@ -461,6 +464,10 @@ impl NymNode {
&self.config
}
pub(crate) fn shutdown_token<S: Into<String>>(&self, child_suffix: S) -> ShutdownToken {
self.shutdown_manager.clone_token(child_suffix)
}
pub(crate) fn with_accepted_operator_terms_and_conditions(
mut self,
accepted_operator_terms_and_conditions: bool,
@@ -528,12 +535,17 @@ impl NymNode {
self.x25519_sphinx_keys.public_key()
}
pub(crate) fn x25519_sphinx_keys(&self) -> Arc<x25519::KeyPair> {
self.x25519_sphinx_keys.clone()
}
pub(crate) fn x25519_noise_key(&self) -> &x25519::PublicKey {
self.x25519_noise_keys.public_key()
}
async fn build_network_refresher(&self) -> Result<NetworkRefresher, NymNodeError> {
NetworkRefresher::initialise_new(
self.config.debug.testnet,
self.user_agent(),
self.config.mixnet.nym_api_urls.clone(),
self.config.debug.topology_cache_ttl,
@@ -970,12 +982,38 @@ impl NymNode {
events_sender
}
pub(crate) fn start_mixnet_listener(
pub(crate) async fn setup_replay_detection(
&self,
) -> Result<ReplayProtectionBloomfilter, NymNodeError> {
if self.config.mixnet.replay_protection.debug.unsafe_disabled {
return Ok(ReplayProtectionBloomfilter::new_disabled());
}
// create the background task for the bloomfilter
// to reset it and flush it to disk
let mut replay_detection_background = ReplayProtectionBackgroundTask::new(
&self.config,
self.metrics.clone(),
self.shutdown_manager
.clone_token("replay-detection-background"),
)
.await?;
let replay_protection_bloomfilter = replay_detection_background.global_bloomfilter();
self.shutdown_manager
.spawn(async move { replay_detection_background.run().await });
Ok(replay_protection_bloomfilter)
}
pub(crate) async fn start_mixnet_listener<F>(
&self,
active_clients_store: &ActiveClientsStore,
routing_filter: RoutingFilter,
routing_filter: F,
shutdown: ShutdownToken,
) -> (MixForwardingSender, ActiveConnections) {
) -> Result<(MixForwardingSender, ActiveConnections), NymNodeError>
where
F: RoutingFilter + Send + Sync + 'static,
{
let processing_config = ProcessingConfig::new(&self.config);
// we're ALWAYS listening for mixnet packets, either for forward or final hops (or both)
@@ -1000,9 +1038,9 @@ impl NymNode {
);
let active_connections = mixnet_client.active_connections();
let replay_protection_bloomfilter = self.setup_replay_detection().await?;
let mut packet_forwarder = PacketForwarder::new(
mixnet_client,
self.config.debug.testnet,
routing_filter,
self.metrics.clone(),
shutdown.clone_with_suffix("mix-packet-forwarder"),
@@ -1018,6 +1056,7 @@ impl NymNode {
let shared = mixnet::SharedData::new(
processing_config,
self.x25519_sphinx_keys.clone(),
replay_protection_bloomfilter,
mix_packet_sender.clone(),
final_hop_data,
self.metrics.clone(),
@@ -1025,7 +1064,21 @@ impl NymNode {
);
mixnet::Listener::new(self.config.mixnet.bind_address, shared).start();
(mix_packet_sender, active_connections)
Ok((mix_packet_sender, active_connections))
}
pub(crate) async fn run_minimal_mixnet_processing(self) -> Result<(), NymNodeError> {
self.start_mixnet_listener(
&ActiveClientsStore::new(),
OpenFilter,
self.shutdown_manager.clone_token("mixnet-traffic"),
)
.await?;
self.shutdown_manager.close();
self.shutdown_manager.wait_for_shutdown_signal().await;
Ok(())
}
pub(crate) async fn run(mut self) -> Result<(), NymNodeError> {
@@ -1057,11 +1110,13 @@ impl NymNode {
let network_refresher = self.build_network_refresher().await?;
let active_clients_store = ActiveClientsStore::new();
let (mix_packet_sender, active_egress_mixnet_connections) = self.start_mixnet_listener(
&active_clients_store,
network_refresher.routing_filter(),
self.shutdown_manager.clone_token("mixnet-traffic"),
);
let (mix_packet_sender, active_egress_mixnet_connections) = self
.start_mixnet_listener(
&active_clients_store,
network_refresher.routing_filter(),
self.shutdown_manager.clone_token("mixnet-traffic"),
)
.await?;
let metrics_sender = self.setup_metrics_backend(
active_clients_store.clone(),
@@ -0,0 +1,229 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::config::Config;
use crate::error::NymNodeError;
use crate::node::replay_protection::bloomfilter::ReplayProtectionBloomfilter;
use crate::node::replay_protection::items_in_bloomfilter;
use human_repr::HumanCount;
use nym_node_metrics::NymNodeMetrics;
use nym_task::ShutdownToken;
use std::cmp::max;
use std::fs;
use std::path::PathBuf;
use std::time::Duration;
use tokio::time::{interval, Instant};
use tracing::{error, info, trace, warn};
struct LastResetData {
packets_received_at_last_reset: usize,
reset_time: Instant,
}
struct ReplayProtectionBackgroundTaskConfig {
current_bloomfilter_path: PathBuf,
current_bloomfilter_temp_flush_path: PathBuf,
false_positive_rate: f64,
filter_reset_rate: Duration,
disk_flushing_rate: Duration,
bloomfilter_size_multiplier: f64,
minimum_bloomfilter_packets_per_second: usize,
}
impl From<&Config> for ReplayProtectionBackgroundTaskConfig {
fn from(config: &Config) -> Self {
ReplayProtectionBackgroundTaskConfig {
current_bloomfilter_path: config
.mixnet
.replay_protection
.storage_paths
.current_bloomfilter_filepath(),
current_bloomfilter_temp_flush_path: config
.mixnet
.replay_protection
.storage_paths
.current_bloomfilter_being_flushed_filepath(),
false_positive_rate: config.mixnet.replay_protection.debug.false_positive_rate,
filter_reset_rate: config.mixnet.replay_protection.debug.bloomfilter_reset_rate,
disk_flushing_rate: config
.mixnet
.replay_protection
.debug
.bloomfilter_disk_flushing_rate,
bloomfilter_size_multiplier: config
.mixnet
.replay_protection
.debug
.bloomfilter_size_multiplier,
minimum_bloomfilter_packets_per_second: config
.mixnet
.replay_protection
.debug
.bloomfilter_minimum_packets_per_second_size,
}
}
}
// background task responsible for periodically flushing the bloomfilter to disk
// as well as clearing it up on the specified timer
// (in the future this will be enforced by key rotation)
pub struct ReplayProtectionBackgroundTask {
config: ReplayProtectionBackgroundTaskConfig,
last_reset: LastResetData,
filter: ReplayProtectionBloomfilter,
metrics: NymNodeMetrics,
shutdown_token: ShutdownToken,
}
impl ReplayProtectionBackgroundTask {
pub(crate) async fn new(
config: &Config,
metrics: NymNodeMetrics,
shutdown_token: ShutdownToken,
) -> Result<Self, NymNodeError> {
let task_config: ReplayProtectionBackgroundTaskConfig = config.into();
if task_config.current_bloomfilter_temp_flush_path.exists() {
error!(
"bloomfilter didn't get successfully flushed to disk and its data got corrupted"
);
fs::remove_file(&task_config.current_bloomfilter_temp_flush_path).map_err(|source| {
NymNodeError::BloomfilterIoFailure {
source,
path: task_config.current_bloomfilter_temp_flush_path.clone(),
}
})?
}
// if there's nothing on disk, we must create a new filter
let bloomfilter = if task_config.current_bloomfilter_path.exists() {
ReplayProtectionBloomfilter::load(&task_config.current_bloomfilter_path).await?
} else {
let bf_items = items_in_bloomfilter(
task_config.filter_reset_rate,
config
.mixnet
.replay_protection
.debug
.initial_expected_packets_per_second,
);
ReplayProtectionBloomfilter::new_empty(bf_items, task_config.false_positive_rate)?
};
Ok(ReplayProtectionBackgroundTask {
config: task_config,
last_reset: LastResetData {
packets_received_at_last_reset: 0,
reset_time: Instant::now(),
},
filter: bloomfilter,
metrics,
shutdown_token,
})
}
pub(crate) fn global_bloomfilter(&self) -> ReplayProtectionBloomfilter {
self.filter.clone()
}
async fn flush_to_disk(&self) -> Result<(), NymNodeError> {
if let Some(temp_parent) = self.config.current_bloomfilter_temp_flush_path.parent() {
fs::create_dir_all(temp_parent).map_err(|source| {
NymNodeError::BloomfilterIoFailure {
source,
path: temp_parent.to_path_buf(),
}
})?
}
if let Some(current_parent) = self.config.current_bloomfilter_temp_flush_path.parent() {
fs::create_dir_all(current_parent).map_err(|source| {
NymNodeError::BloomfilterIoFailure {
source,
path: current_parent.to_path_buf(),
}
})?
}
// because it takes a while to actually write the file to disk,
// we first write bytes to temporary location,
// and then we move it to the correct path
let temp = &self.config.current_bloomfilter_temp_flush_path;
self.filter.flush_to_disk(temp).await?;
fs::rename(temp, &self.config.current_bloomfilter_path).map_err(|source| {
NymNodeError::BloomfilterIoFailure {
source,
path: self.config.current_bloomfilter_path.clone(),
}
})?;
Ok(())
}
fn reset_bloomfilter(&mut self) -> Result<(), NymNodeError> {
// 1. determine parameters for new bloomfilter
let received = self.metrics.mixnet.ingress.forward_hop_packets_received()
+ self.metrics.mixnet.ingress.final_hop_packets_received();
let time_delta = self.last_reset.reset_time.elapsed();
let received_since_last_reset = received - self.last_reset.packets_received_at_last_reset;
let received_per_second =
(received_since_last_reset as f64 / time_delta.as_secs_f64()).round() as usize;
let bf_received = max(
received_per_second,
self.config.minimum_bloomfilter_packets_per_second,
);
let items_in_new_filter = items_in_bloomfilter(self.config.filter_reset_rate, bf_received);
let adjusted =
(items_in_new_filter as f64 * self.config.bloomfilter_size_multiplier).round() as usize;
info!(
"resetting bloom filter. new expected number of packets: {} that preserve fp rate of {}",
adjusted.human_count_bare(),
self.config.false_positive_rate
);
// 2. update the filter
self.last_reset.reset_time = Instant::now();
self.last_reset.packets_received_at_last_reset = received_since_last_reset;
// if this fails with the mutex getting poisoned, the next received packet is going to cause
// a shutdown, so we don't have to propagate it here
self.filter.reset(adjusted, self.config.false_positive_rate)
}
pub(crate) async fn run(&mut self) {
let mut reset_timer = interval(self.config.filter_reset_rate);
reset_timer.reset();
let mut flush_timer = interval(self.config.disk_flushing_rate);
flush_timer.reset();
loop {
tokio::select! {
biased;
_ = self.shutdown_token.cancelled() => {
trace!("ReplayProtectionBackgroundTask: Received shutdown");
break;
}
_ = reset_timer.tick() => {
if let Err(err) = self.reset_bloomfilter() {
error!("failed to reset the bloomfilter: {err}")
}
}
_ = flush_timer.tick() => {
if let Err(err) = self.flush_to_disk().await {
error!("failed to flush bloomfilter to disk: {err}")
}
}
}
}
info!("SHUTDOWN: flushing replay detection bloomfilter to disk. this might take a while. DO NOT INTERRUPT THIS PROCESS");
if let Err(err) = self.flush_to_disk().await {
warn!("failed to flush replay detection bloom filter on shutdown: {err}");
}
}
}
@@ -0,0 +1,223 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::error::NymNodeError;
use bloomfilter::Bloom;
use human_repr::HumanDuration;
use nym_sphinx_types::REPLAY_TAG_SIZE;
use std::path::Path;
use std::sync::{Arc, PoisonError, TryLockError};
use tokio::fs::File;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::time::Instant;
use tracing::{debug, info};
// it appears that now std Mutex is faster (or comparable) to parking_lot
// in high contention situations: https://github.com/rust-lang/rust/pull/95035#issuecomment-1073966631
// (tokio's async Mutex has too much overhead due to the number of access required)
#[derive(Clone)]
pub(crate) struct ReplayProtectionBloomfilter {
disabled: bool,
inner: Arc<std::sync::Mutex<ReplayProtectionBloomfilterInner>>,
}
impl ReplayProtectionBloomfilter {
pub(crate) fn new_empty(items_count: usize, fp_p: f64) -> Result<Self, NymNodeError> {
Ok(ReplayProtectionBloomfilter {
disabled: false,
inner: Arc::new(std::sync::Mutex::new(ReplayProtectionBloomfilterInner {
current_filter: Bloom::new_for_fp_rate(items_count, fp_p)
.map_err(NymNodeError::bloomfilter_failure)?,
})),
})
}
// SAFETY: the hardcoded values of 1,1 are valid
#[allow(clippy::unwrap_used)]
pub(crate) fn new_disabled() -> Self {
// well, technically it's not fully empty, but the memory footprint is negligible
ReplayProtectionBloomfilter {
disabled: true,
inner: Arc::new(std::sync::Mutex::new(ReplayProtectionBloomfilterInner {
current_filter: Bloom::new(1, 1).unwrap(),
})),
}
}
pub(crate) fn disabled(&self) -> bool {
self.disabled
}
pub(crate) fn reset(&self, items_count: usize, fp_p: f64) -> Result<(), NymNodeError> {
// 1. build the new filter
let new_inner = ReplayProtectionBloomfilterInner {
current_filter: Bloom::new_for_fp_rate(items_count, fp_p)
.map_err(NymNodeError::bloomfilter_failure)?,
};
// 2. swap it
let mut guard = self
.inner
.lock()
.map_err(|_| NymNodeError::BloomfilterFailure {
message: "mutex got poisoned",
})?;
*guard = new_inner;
Ok(())
}
// NOTE: with key rotations we'll have to check whether the file is still valid and which
// key it corresponds to, but that's a future problem
pub(crate) async fn load<P: AsRef<Path>>(path: P) -> Result<Self, NymNodeError> {
info!("attempting to load prior replay detection bloomfilter...");
let path = path.as_ref();
let mut file =
File::open(path)
.await
.map_err(|source| NymNodeError::BloomfilterIoFailure {
source,
path: path.to_path_buf(),
})?;
let mut buf = Vec::new();
file.read_to_end(&mut buf)
.await
.map_err(|source| NymNodeError::BloomfilterIoFailure {
source,
path: path.to_path_buf(),
})?;
Ok(ReplayProtectionBloomfilter {
disabled: false,
inner: Arc::new(std::sync::Mutex::new(ReplayProtectionBloomfilterInner {
current_filter: Bloom::from_bytes(buf)
.map_err(NymNodeError::bloomfilter_failure)?,
})),
})
}
// average HDD has the write speed of ~80MB/s so a 2GB bloomfilter would take almost 30s to write...
// and this function is explicitly async and using tokio's async operations, because otherwise
// we'd have to go through the whole hassle of using spawn_blocking and awaiting that one instead
pub(crate) async fn flush_to_disk<P: AsRef<Path>>(&self, path: P) -> Result<(), NymNodeError> {
debug!("flushing replay protection bloomfilter to disk...");
let start = Instant::now();
let path = path.as_ref();
let mut file =
File::create(path)
.await
.map_err(|source| NymNodeError::BloomfilterIoFailure {
source,
path: path.to_path_buf(),
})?;
let data = self.bytes().map_err(|_| NymNodeError::BloomfilterFailure {
message: "mutex got poisoned",
})?;
file.write_all(&data)
.await
.map_err(|source| NymNodeError::BloomfilterIoFailure {
source,
path: path.to_path_buf(),
})?;
let elapsed = start.elapsed();
info!(
"flushed replay protection bloomfilter to disk. it took: {}",
elapsed.human_duration()
);
Ok(())
}
}
struct ReplayProtectionBloomfilterInner {
// metadata to do with epochs, etc.
current_filter: Bloom<[u8; REPLAY_TAG_SIZE]>,
// overlap_filter: bloomfilter::Bloom<[u8; REPLAY_TAG_SIZE]>,
}
impl ReplayProtectionBloomfilter {
#[allow(dead_code)]
pub(crate) fn check_and_set(
&self,
replay_tag: &[u8; REPLAY_TAG_SIZE],
) -> Result<bool, PoisonError<()>> {
let Ok(mut guard) = self.inner.lock() else {
return Err(PoisonError::new(()));
};
Ok(guard.current_filter.check_and_set(replay_tag))
}
#[allow(dead_code)]
pub(crate) fn try_check_and_set(
&self,
replay_tag: &[u8; REPLAY_TAG_SIZE],
) -> Option<Result<bool, PoisonError<()>>> {
let mut guard = match self.inner.try_lock() {
Ok(guard) => guard,
Err(TryLockError::Poisoned(_)) => return Some(Err(PoisonError::new(()))),
Err(TryLockError::WouldBlock) => return None,
};
Some(Ok(guard.current_filter.check_and_set(replay_tag)))
}
pub(crate) fn batch_try_check_and_set(
&self,
reply_tags: &[&[u8; REPLAY_TAG_SIZE]],
) -> Option<Result<Vec<bool>, PoisonError<()>>> {
let mut guard = match self.inner.try_lock() {
Ok(guard) => guard,
Err(TryLockError::Poisoned(_)) => return Some(Err(PoisonError::new(()))),
Err(TryLockError::WouldBlock) => return None,
};
let mut result = Vec::with_capacity(reply_tags.len());
for tag in reply_tags {
result.push(guard.current_filter.check_and_set(tag));
}
// for testing throughput without disabling checks:
// return Some(Ok(vec![false; reply_tags.len()]));
Some(Ok(result))
}
pub(crate) fn batch_check_and_set(
&self,
reply_tags: &[&[u8; REPLAY_TAG_SIZE]],
) -> Result<Vec<bool>, PoisonError<()>> {
let Ok(mut guard) = self.inner.lock() else {
return Err(PoisonError::new(()));
};
let mut result = Vec::with_capacity(reply_tags.len());
for tag in reply_tags {
result.push(guard.current_filter.check_and_set(tag));
}
// for testing throughput without disabling checks:
// return Ok(vec![false; reply_tags.len()]);
Ok(result)
}
#[allow(dead_code)]
pub(crate) fn clear(&self) -> Result<(), PoisonError<()>> {
let mut guard = self.inner.lock().map_err(|_| PoisonError::new(()))?;
guard.current_filter.clear();
Ok(())
}
// due to the size of the bloomfilter, extra caution has to be applied when using this method
// note: we're not getting reference to bytes as this method is used when flushing data to the disk
// (which takes ~30s) and we can't block the mutex for that long.
fn bytes(&self) -> Result<Vec<u8>, PoisonError<()>> {
let guard = self.inner.lock().map_err(|_| PoisonError::new(()))?;
Ok(guard.current_filter.to_bytes())
}
}
@@ -0,0 +1,52 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use std::f64::consts::LN_2;
use std::time::Duration;
pub(crate) mod background_task;
pub(crate) mod bloomfilter;
pub fn bitmap_size(false_positive_rate: f64, items_in_filter: usize) -> usize {
/// Equivalent to ln(1 / 2^ln(2)) = ln^2(2)
const NEG_LN_2_POW_2: f64 = -0.48045301391820144f64;
assert!(items_in_filter < f64::MAX.floor() as usize);
((items_in_filter as f64 * false_positive_rate.ln()) / NEG_LN_2_POW_2).ceil() as usize
}
#[allow(dead_code)]
pub fn num_of_hash_functions(items_in_filter: usize, bitmap_size: usize) -> usize {
((bitmap_size as f64 / items_in_filter as f64) * LN_2).round() as usize
}
pub fn items_in_bloomfilter(reset_rate: Duration, packets_per_second: usize) -> usize {
reset_rate.as_secs() as usize * packets_per_second
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn calculating_bitmap_size() {
let fpr = 1e-5;
let items_in_filter = 725760000;
let expected_bitmap_size = 17391129920;
assert_eq!(bitmap_size(fpr, items_in_filter), expected_bitmap_size);
}
#[test]
fn calculating_number_of_hash_functions() {
let items_in_filter = 725760000;
let bitmap_size = 17391129920;
let expected_hashes = 17;
assert_eq!(
num_of_hash_functions(items_in_filter, bitmap_size),
expected_hashes
);
}
}
+44
View File
@@ -0,0 +1,44 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use std::net::IpAddr;
pub(crate) mod network_filter;
pub(crate) trait RoutingFilter {
fn should_route(&self, ip: IpAddr) -> bool;
}
#[derive(Debug, Copy, Clone, Default)]
pub(crate) struct OpenFilter;
impl RoutingFilter for OpenFilter {
fn should_route(&self, _: IpAddr) -> bool {
true
}
}
// #[derive(Default)]
// pub(crate) struct ComposedRoutingFilter {
// layers: Vec<Box<dyn RoutingFilter + Send + Sync + 'static>>,
// }
//
// impl ComposedRoutingFilter {
// pub(crate) fn new() -> Self {
// Self::default()
// }
//
// pub(crate) fn with_filter<F: RoutingFilter + Send + Sync + 'static>(
// mut self,
// filter: F,
// ) -> Self {
// self.layers.push(Box::new(filter));
// self
// }
// }
//
// impl RoutingFilter for ComposedRoutingFilter {
// fn should_route(&self, ip: IpAddr) -> bool {
// self.layers.iter().all(|l| l.should_route(ip))
// }
// }
@@ -0,0 +1,128 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::node::mixnet::packet_forwarding::global::is_global_ip;
use crate::node::routing_filter::RoutingFilter;
use arc_swap::ArcSwap;
use std::collections::HashSet;
use std::net::IpAddr;
use std::sync::Arc;
use tokio::sync::RwLock;
impl RoutingFilter for NetworkRoutingFilter {
fn should_route(&self, ip: IpAddr) -> bool {
// only allow non-global ips on testnets
if self.testnet_mode && !is_global_ip(&ip) {
return true;
}
self.attempt_resolve(ip).should_route()
}
}
#[derive(Clone)]
pub(crate) struct NetworkRoutingFilter {
testnet_mode: bool,
pub(crate) resolved: KnownNodes,
// while this is technically behind a lock, it should not be called too often as once resolved it will
// be present on the arcswap in either allowed or denied section
pub(crate) pending: UnknownNodes,
}
impl NetworkRoutingFilter {
pub(crate) fn new_empty(testnet_mode: bool) -> Self {
NetworkRoutingFilter {
testnet_mode,
resolved: Default::default(),
pending: Default::default(),
}
}
pub(crate) fn attempt_resolve(&self, ip: IpAddr) -> Resolution {
if self.resolved.inner.allowed.load().contains(&ip) {
Resolution::Accept
} else if self.resolved.inner.denied.load().contains(&ip) {
Resolution::Deny
} else {
self.pending.try_insert(ip);
Resolution::Unknown
}
}
pub(crate) fn allowed_nodes_copy(&self) -> HashSet<IpAddr> {
self.resolved.inner.allowed.load_full().as_ref().clone()
}
pub(crate) fn denied_nodes_copy(&self) -> HashSet<IpAddr> {
self.resolved.inner.denied.load_full().as_ref().clone()
}
}
#[derive(Clone, Default)]
pub(crate) struct UnknownNodes(Arc<RwLock<HashSet<IpAddr>>>);
impl UnknownNodes {
fn try_insert(&self, ip: IpAddr) {
// if we can immediately grab the lock to push it into the pending queue, amazing, let's do it
// otherwise we can do it next time we see this ip
// (if we can't hold the lock, it means it's being updated at this very moment which is actually a good thing)
if let Ok(mut guard) = self.0.try_write() {
guard.insert(ip);
}
}
pub(crate) async fn clear(&self) {
self.0.write().await.clear();
}
pub(crate) async fn nodes(&self) -> HashSet<IpAddr> {
self.0.read().await.clone()
}
}
// for now we don't care about keys, etc.
// we only want to know if given ip belongs to a known node
#[derive(Debug, Default, Clone)]
pub(crate) struct KnownNodes {
inner: Arc<KnownNodesInner>,
}
#[derive(Debug, Default)]
struct KnownNodesInner {
allowed: ArcSwap<HashSet<IpAddr>>,
denied: ArcSwap<HashSet<IpAddr>>,
}
pub(crate) enum Resolution {
Unknown,
Deny,
Accept,
}
impl From<bool> for Resolution {
fn from(value: bool) -> Self {
if value {
Resolution::Accept
} else {
Resolution::Deny
}
}
}
impl Resolution {
pub(crate) fn should_route(&self) -> bool {
matches!(self, Resolution::Accept)
}
}
impl KnownNodes {
pub(crate) fn swap_allowed(&self, new: HashSet<IpAddr>) {
self.inner.allowed.store(Arc::new(new))
}
pub(crate) fn swap_denied(&self, new: HashSet<IpAddr>) {
self.inner.denied.store(Arc::new(new))
}
}
+8 -123
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::error::NymNodeError;
use arc_swap::ArcSwap;
use crate::node::routing_filter::network_filter::NetworkRoutingFilter;
use async_trait::async_trait;
use nym_gateway::node::UserAgent;
use nym_node_metrics::prometheus_wrapper::{PrometheusMetric, PROMETHEUS_METRICS};
@@ -22,102 +22,6 @@ use tracing::log::error;
use tracing::{debug, trace, warn};
use url::Url;
#[derive(Clone)]
pub(crate) struct RoutingFilter {
resolved: KnownNodes,
// while this is technically behind a lock, it should not be called too often as once resolved it will
// be present on the arcswap in either allowed or denied section
pending: UnknownNodes,
}
impl RoutingFilter {
fn new_empty() -> Self {
RoutingFilter {
resolved: Default::default(),
pending: Default::default(),
}
}
pub(crate) fn attempt_resolve(&self, ip: IpAddr) -> Resolution {
if self.resolved.inner.allowed.load().contains(&ip) {
Resolution::Accept
} else if self.resolved.inner.denied.load().contains(&ip) {
Resolution::Deny
} else {
self.pending.try_insert(ip);
Resolution::Unknown
}
}
}
#[derive(Clone, Default)]
struct UnknownNodes(Arc<RwLock<HashSet<IpAddr>>>);
impl UnknownNodes {
fn try_insert(&self, ip: IpAddr) {
// if we can immediately grab the lock to push it into the pending queue, amazing, let's do it
// otherwise we can do it next time we see this ip
// (if we can't hold the lock, it means it's being updated at this very moment which is actually a good thing)
if let Ok(mut guard) = self.0.try_write() {
guard.insert(ip);
}
}
async fn clear(&self) {
self.0.write().await.clear();
}
async fn nodes(&self) -> HashSet<IpAddr> {
self.0.read().await.clone()
}
}
// for now we don't care about keys, etc.
// we only want to know if given ip belongs to a known node
#[derive(Debug, Default, Clone)]
pub(crate) struct KnownNodes {
inner: Arc<KnownNodesInner>,
}
#[derive(Debug, Default)]
struct KnownNodesInner {
allowed: ArcSwap<HashSet<IpAddr>>,
denied: ArcSwap<HashSet<IpAddr>>,
}
pub(crate) enum Resolution {
Unknown,
Deny,
Accept,
}
impl From<bool> for Resolution {
fn from(value: bool) -> Self {
if value {
Resolution::Accept
} else {
Resolution::Deny
}
}
}
impl Resolution {
pub(crate) fn should_route(&self) -> bool {
matches!(self, Resolution::Accept)
}
}
impl KnownNodes {
fn swap_allowed(&self, new: HashSet<IpAddr>) {
self.inner.allowed.store(Arc::new(new))
}
fn swap_denied(&self, new: HashSet<IpAddr>) {
self.inner.denied.store(Arc::new(new))
}
}
struct NodesQuerier {
client: NymApiClient,
nym_api_urls: Vec<Url>,
@@ -254,11 +158,12 @@ pub struct NetworkRefresher {
shutdown_token: ShutdownToken,
network: CachedNetwork,
routing_filter: RoutingFilter,
routing_filter: NetworkRoutingFilter,
}
impl NetworkRefresher {
pub(crate) async fn initialise_new(
testnet: bool,
user_agent: UserAgent,
nym_api_urls: Vec<Url>,
full_refresh_interval: Duration,
@@ -280,33 +185,13 @@ impl NetworkRefresher {
pending_check_interval,
shutdown_token,
network: CachedNetwork::new_empty(),
routing_filter: RoutingFilter::new_empty(),
routing_filter: NetworkRoutingFilter::new_empty(testnet),
};
this.obtain_initial_network().await?;
Ok(this)
}
fn allowed_nodes_copy(&self) -> HashSet<IpAddr> {
self.routing_filter
.resolved
.inner
.allowed
.load_full()
.as_ref()
.clone()
}
fn denied_nodes_copy(&self) -> HashSet<IpAddr> {
self.routing_filter
.resolved
.inner
.denied
.load_full()
.as_ref()
.clone()
}
async fn inspect_pending(&mut self) {
let to_resolve = self.routing_filter.pending.nodes().await;
@@ -315,8 +200,8 @@ impl NetworkRefresher {
return;
}
let mut allowed = self.allowed_nodes_copy();
let mut denied = self.denied_nodes_copy();
let mut allowed = self.routing_filter().allowed_nodes_copy();
let mut denied = self.routing_filter().denied_nodes_copy();
// short circuit: check if the pending nodes are not already resolved
// (it could happen due to lack of full sync between pending lock and arcswap(s))
@@ -360,7 +245,7 @@ impl NetworkRefresher {
.collect::<HashSet<_>>();
let pending = self.routing_filter.pending.nodes().await;
let mut current_denied = self.denied_nodes_copy();
let mut current_denied = self.routing_filter.denied_nodes_copy();
for allowed in &known_nodes {
// if some node has become known, it should be removed from the denied set
@@ -403,7 +288,7 @@ impl NetworkRefresher {
.map_err(|source| NymNodeError::InitialTopologyQueryFailure { source })
}
pub(crate) fn routing_filter(&self) -> RoutingFilter {
pub(crate) fn routing_filter(&self) -> NetworkRoutingFilter {
self.routing_filter.clone()
}

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