Compare commits
81 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 600bf42a95 | |||
| 748e3e4248 | |||
| 8cf1b6427a | |||
| 7a888c6fdf | |||
| 9a9bb89d89 | |||
| 4cc14ddcc4 | |||
| 2dbf9d97cb | |||
| 91b6f3cc3e | |||
| 84cccffcbd | |||
| af16b3f059 | |||
| b1cde0716e | |||
| 45bcdb03d8 | |||
| 44682b5ef0 | |||
| 51c9b012e2 | |||
| 50b1175622 | |||
| 29ee5984fb | |||
| e542b25ffc | |||
| 516d3f04cf | |||
| 08c09781c7 | |||
| c92de832e4 | |||
| d9d62195cb | |||
| da9115d51b | |||
| 1367cad99d | |||
| 4f6d65ab95 | |||
| 4292d8ac03 | |||
| dcb6de2421 | |||
| 1f5ed41bb3 | |||
| 091e98aa74 | |||
| 0e38126fc5 | |||
| ecbe192a88 | |||
| f0ee49788c | |||
| d2ff3cb88d | |||
| 873d15a5e1 | |||
| 53792cc839 | |||
| 415ef1bf13 | |||
| a4f6426bf9 | |||
| 0870911b3c | |||
| 9f23887cc0 | |||
| 8ab269fa05 | |||
| 7b75f22a8e | |||
| ca0449e03d | |||
| 224e63d275 | |||
| 3d77283056 | |||
| 7cc473005b | |||
| f874284850 | |||
| 7b6077ba64 | |||
| 12026305d5 | |||
| 257e36ddcb | |||
| ad81c6d27e | |||
| ae52b7b71f | |||
| 854d3cceac | |||
| 1bdf867fdb | |||
| 5a88b5b6a8 | |||
| 5ab4d3c22c | |||
| b529883b81 | |||
| 07f624660c | |||
| 71f8e736d8 | |||
| d3573e78e0 | |||
| e6e74855af | |||
| b4865520a4 | |||
| f52ebfb9c3 | |||
| 6ca2a3c539 | |||
| 717c9066d6 | |||
| 2760a17323 | |||
| 4e9f1bc0ed | |||
| d35023d14b | |||
| 400aa6ba6d | |||
| 2ba74ae120 | |||
| 99d8aebea9 | |||
| 0bde4dfc84 | |||
| a56068e28a | |||
| 9a4293a5b9 | |||
| cdddb44099 | |||
| d309b44ad7 | |||
| d062524d32 | |||
| 89eea3100e | |||
| d893c806c2 | |||
| 7846058802 | |||
| 3c98c9021e | |||
| f705884a53 | |||
| 84b6068ac9 |
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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/"
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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)]
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ pub enum NodeType {
|
||||
|
||||
impl NodeType {
|
||||
pub fn is_mixnode(&self) -> bool {
|
||||
matches!(self, NodeType::Mixnode { .. })
|
||||
matches!(self, NodeType::Mixnode)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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" }
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(_))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Generated
+8
-8
@@ -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
@@ -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
|
||||
|
||||
@@ -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);
|
||||
@@ -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,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(())
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
@@ -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,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,
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user